mirror of
https://github.com/matrix-org/pantalaimon.git
synced 2024-10-01 03:35:38 -04:00
daemon: Expose daemon users and user devices over a dbus interface.
This commit is contained in:
parent
42fc14ee35
commit
7038462e01
@ -4,18 +4,27 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from enum import Enum, auto
|
||||||
|
from functools import partial
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
|
from queue import Empty
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import attr
|
import attr
|
||||||
import click
|
import click
|
||||||
|
import dbus
|
||||||
|
import dbus.exceptions
|
||||||
|
import dbus.service
|
||||||
|
import janus
|
||||||
import keyring
|
import keyring
|
||||||
import logbook
|
import logbook
|
||||||
from aiohttp import ClientSession, web
|
from aiohttp import ClientSession, web
|
||||||
from aiohttp.client_exceptions import ContentTypeError
|
from aiohttp.client_exceptions import ContentTypeError
|
||||||
from appdirs import user_data_dir
|
from appdirs import user_data_dir
|
||||||
|
from dbus.mainloop.glib import DBusGMainLoop
|
||||||
|
from gi.repository import GLib
|
||||||
from logbook import StderrHandler
|
from logbook import StderrHandler
|
||||||
from multidict import CIMultiDict
|
from multidict import CIMultiDict
|
||||||
from nio import EncryptionError, GroupEncryptionError, LoginResponse
|
from nio import EncryptionError, GroupEncryptionError, LoginResponse
|
||||||
@ -25,10 +34,74 @@ from pantalaimon.log import logger
|
|||||||
from pantalaimon.store import ClientInfo, PanStore
|
from pantalaimon.store import ClientInfo, PanStore
|
||||||
|
|
||||||
|
|
||||||
|
class Tasks(Enum):
|
||||||
|
shutdown = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class Devices(dbus.service.Object):
|
||||||
|
def __init__(self, bus_name, device_list):
|
||||||
|
super().__init__(bus_name, "/org/pantalaimon/Devices")
|
||||||
|
self.device_list = device_list
|
||||||
|
|
||||||
|
@dbus.service.method("org.pantalaimon.devices.list",
|
||||||
|
out_signature="a{sa{saa{ss}}}")
|
||||||
|
def list(self):
|
||||||
|
return self.device_list
|
||||||
|
|
||||||
|
|
||||||
|
class Users(dbus.service.Object):
|
||||||
|
def __init__(self, bus_name, user_list=None):
|
||||||
|
super().__init__(bus_name, "/org/pantalaimon/Users")
|
||||||
|
self.users = user_list
|
||||||
|
|
||||||
|
@dbus.service.method("org.pantalaimon.users.list",
|
||||||
|
out_signature="a(ss)")
|
||||||
|
def list(self):
|
||||||
|
return self.users
|
||||||
|
|
||||||
|
|
||||||
|
def dbus_loop(task_queue, data_dir):
|
||||||
|
DBusGMainLoop(set_as_default=True)
|
||||||
|
loop = GLib.MainLoop()
|
||||||
|
|
||||||
|
bus_name = dbus.service.BusName("org.pantalaimon",
|
||||||
|
bus=dbus.SessionBus(),
|
||||||
|
do_not_queue=True)
|
||||||
|
|
||||||
|
store = PanStore(data_dir)
|
||||||
|
users = store.load_all_users()
|
||||||
|
devices = store.load_all_devices()
|
||||||
|
|
||||||
|
# TODO update bus data if the asyncio thread tells us so.
|
||||||
|
Users(bus_name, users)
|
||||||
|
Devices(bus_name, devices)
|
||||||
|
|
||||||
|
def task_callback():
|
||||||
|
try:
|
||||||
|
task = task_queue.get_nowait()
|
||||||
|
except Empty:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if task == Tasks.shutdown:
|
||||||
|
task_queue.task_done()
|
||||||
|
loop.quit()
|
||||||
|
return False
|
||||||
|
|
||||||
|
GLib.timeout_add(100, task_callback)
|
||||||
|
|
||||||
|
loop.run()
|
||||||
|
|
||||||
|
|
||||||
|
async def shutdown_dbus(future, queue, app):
|
||||||
|
await queue.put(Tasks.shutdown)
|
||||||
|
await future
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@attr.s
|
||||||
class ProxyDaemon:
|
class ProxyDaemon:
|
||||||
homeserver = attr.ib()
|
homeserver = attr.ib()
|
||||||
data_dir = attr.ib()
|
data_dir = attr.ib()
|
||||||
|
queue = attr.ib()
|
||||||
proxy = attr.ib(default=None)
|
proxy = attr.ib(default=None)
|
||||||
ssl = attr.ib(default=None)
|
ssl = attr.ib(default=None)
|
||||||
|
|
||||||
@ -420,7 +493,7 @@ class ProxyDaemon:
|
|||||||
self.default_session = None
|
self.default_session = None
|
||||||
|
|
||||||
|
|
||||||
async def init(homeserver, http_proxy, ssl):
|
async def init(homeserver, http_proxy, ssl, queue):
|
||||||
"""Initialize the proxy and the http server."""
|
"""Initialize the proxy and the http server."""
|
||||||
data_dir = user_data_dir("pantalaimon", "")
|
data_dir = user_data_dir("pantalaimon", "")
|
||||||
|
|
||||||
@ -429,7 +502,13 @@ async def init(homeserver, http_proxy, ssl):
|
|||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
proxy = ProxyDaemon(homeserver, data_dir, proxy=http_proxy, ssl=ssl)
|
proxy = ProxyDaemon(
|
||||||
|
homeserver,
|
||||||
|
data_dir,
|
||||||
|
queue=queue,
|
||||||
|
proxy=http_proxy,
|
||||||
|
ssl=ssl,
|
||||||
|
)
|
||||||
|
|
||||||
app = web.Application()
|
app = web.Application()
|
||||||
app.add_routes([
|
app.add_routes([
|
||||||
@ -626,12 +705,24 @@ def start(
|
|||||||
logger.level = logbook.DEBUG
|
logger.level = logbook.DEBUG
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
queue = janus.Queue(loop=loop)
|
||||||
|
|
||||||
proxy, app = loop.run_until_complete(init(
|
proxy, app = loop.run_until_complete(init(
|
||||||
homeserver,
|
homeserver,
|
||||||
proxy.geturl() if proxy else None,
|
proxy.geturl() if proxy else None,
|
||||||
ssl
|
ssl,
|
||||||
|
queue.async_q
|
||||||
))
|
))
|
||||||
|
|
||||||
|
data_dir = user_data_dir("pantalaimon", "")
|
||||||
|
fut = loop.run_in_executor(None, dbus_loop, queue.sync_q, data_dir)
|
||||||
|
|
||||||
|
kill_dbus_loop = partial(shutdown_dbus, fut, queue.async_q)
|
||||||
|
|
||||||
|
app.on_shutdown.append(proxy.shutdown)
|
||||||
|
app.on_shutdown.append(kill_dbus_loop)
|
||||||
|
|
||||||
web.run_app(app, host=str(listen_address), port=listen_port)
|
web.run_app(app, host=str(listen_address), port=listen_port)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
|
from collections import defaultdict
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
from nio.store import Accounts, use_database
|
from nio.store import (Accounts, DeviceKeys, DeviceTrustState, TrustState,
|
||||||
|
use_database)
|
||||||
from peewee import (SQL, DoesNotExist, ForeignKeyField, Model, SqliteDatabase,
|
from peewee import (SQL, DoesNotExist, ForeignKeyField, Model, SqliteDatabase,
|
||||||
TextField)
|
TextField)
|
||||||
|
|
||||||
@ -57,13 +59,30 @@ class ClientInfo:
|
|||||||
access_token = attr.ib(type=str)
|
access_token = attr.ib(type=str)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class OlmDevice:
|
||||||
|
user_id = attr.ib()
|
||||||
|
id = attr.ib()
|
||||||
|
fp_key = attr.ib()
|
||||||
|
sender_key = attr.ib()
|
||||||
|
trust_state = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@attr.s
|
||||||
class PanStore:
|
class PanStore:
|
||||||
store_path = attr.ib(type=str)
|
store_path = attr.ib(type=str)
|
||||||
database_name = attr.ib(type=str, default="pan.db")
|
database_name = attr.ib(type=str, default="pan.db")
|
||||||
database = attr.ib(type=SqliteDatabase, init=False)
|
database = attr.ib(type=SqliteDatabase, init=False)
|
||||||
database_path = attr.ib(type=str, init=False)
|
database_path = attr.ib(type=str, init=False)
|
||||||
models = [Accounts, AccessTokens, Clients, Servers, ServerUsers]
|
models = [
|
||||||
|
Accounts,
|
||||||
|
AccessTokens,
|
||||||
|
Clients,
|
||||||
|
Servers,
|
||||||
|
ServerUsers,
|
||||||
|
DeviceKeys,
|
||||||
|
DeviceTrustState,
|
||||||
|
]
|
||||||
|
|
||||||
def __attrs_post_init__(self):
|
def __attrs_post_init__(self):
|
||||||
self.database_path = os.path.join(
|
self.database_path = os.path.join(
|
||||||
@ -191,3 +210,32 @@ class PanStore:
|
|||||||
clients[c.token] = client
|
clients[c.token] = client
|
||||||
|
|
||||||
return clients
|
return clients
|
||||||
|
|
||||||
|
@use_database
|
||||||
|
def load_all_devices(self):
|
||||||
|
# type (str, str) -> Dict[str, Dict[str, DeviceStore]]
|
||||||
|
store = defaultdict(dict)
|
||||||
|
|
||||||
|
query = Accounts.select()
|
||||||
|
|
||||||
|
for account in query:
|
||||||
|
device_store = []
|
||||||
|
|
||||||
|
for d in account.device_keys:
|
||||||
|
|
||||||
|
try:
|
||||||
|
trust_state = d.trust_state[0].state
|
||||||
|
except IndexError:
|
||||||
|
trust_state = TrustState.unset
|
||||||
|
|
||||||
|
device_store.append({
|
||||||
|
"user_id": d.user_id,
|
||||||
|
"device_id": d.device_id,
|
||||||
|
"fingerprint_key": d.fp_key,
|
||||||
|
"sender_key": d.sender_key,
|
||||||
|
"trust_state": trust_state.name
|
||||||
|
})
|
||||||
|
|
||||||
|
store[account.user_id][account.device_id] = device_store
|
||||||
|
|
||||||
|
return store
|
||||||
|
Loading…
Reference in New Issue
Block a user