From 1a90bb4331a6b288fa79935111b87ac6591e29ed Mon Sep 17 00:00:00 2001 From: miruka Date: Thu, 9 May 2019 13:58:46 -0400 Subject: [PATCH] Add Backend.devices ListModelMap The Device ListModels will be populated after login. --- TODO.md | 9 ++++++ harmonyqml/backend/backend.py | 6 ++++ harmonyqml/backend/client.py | 42 +++++++++++++++------------- harmonyqml/backend/model/items.py | 17 +++++++++++ harmonyqml/backend/signal_manager.py | 28 +++++++++++++++++-- 5 files changed, 79 insertions(+), 23 deletions(-) diff --git a/TODO.md b/TODO.md index a388dffa..f42b8284 100644 --- a/TODO.md +++ b/TODO.md @@ -71,3 +71,12 @@ - Distribution - Review setup.py, add dependencies - README.md + - Use PyInstaller or pyqtdeploy + - Test command: + ``` + pyinstaller --onefile --windowed --name harmonyqml \ + --add-data 'harmonyqml/components:harmonyqml/components' \ + --additional-hooks-dir . \ + --upx-dir ~/opt/upx-3.95-amd64_linux \ + run.py + ``` diff --git a/harmonyqml/backend/backend.py b/harmonyqml/backend/backend.py index 28948dc3..9c4fc11f 100644 --- a/harmonyqml/backend/backend.py +++ b/harmonyqml/backend/backend.py @@ -30,6 +30,7 @@ class Backend(QObject): self._accounts: ListModel = ListModel(parent=parent) self._room_events: ListModelMap = ListModelMap(Deque, parent) + self._devices: ListModelMap = ListModelMap(parent=parent) from .signal_manager import SignalManager self._signal_manager: SignalManager = SignalManager(self) @@ -53,6 +54,10 @@ class Backend(QObject): def roomEvents(self): return self._room_events + @pyqtProperty("QVariant", constant=True) + def devices(self): + return self._devices + @pyqtProperty("QVariant", constant=True) def signals(self): return self._signal_manager @@ -141,6 +146,7 @@ class Backend(QObject): cl = self.clients ac = self.accounts re = self.roomEvents + de = self.devices tcl = lambda user: cl[f"@test_{user}:matrix.org"] diff --git a/harmonyqml/backend/client.py b/harmonyqml/backend/client.py index 2307243d..5c58360d 100644 --- a/harmonyqml/backend/client.py +++ b/harmonyqml/backend/client.py @@ -13,6 +13,7 @@ from PyQt5.QtCore import ( import nio +from .model.items import Trust from .network_manager import NetworkManager from .pyqt_future import PyQtFuture, futurize @@ -81,21 +82,21 @@ class Client(QObject): @futurize(max_running=1, discard_if_max_running=True, pyqt=False) - def _keys_upload(self) -> None: + def uploadE2EKeys(self) -> None: self.net.talk(self.nio.keys_upload) - def _keys_query(self) -> None: + def queryE2EKeys(self) -> None: self.net.talk(self.nio.keys_query) - def _keys_claim(self, room_id: str) -> None: + def claimE2EKeysForRoom(self, room_id: str) -> None: self.net.talk(self.nio.keys_claim, room_id) - def _share_group_session(self, - room_id: str, - ignore_missing_sessions: bool = False) -> None: + def shareRoomE2ESession(self, + room_id: str, + ignore_missing_sessions: bool = False) -> None: self.net.talk( self.nio.share_group_session, room_id = room_id, @@ -103,16 +104,21 @@ class Client(QObject): ) + def getDeviceTrust(self, device: nio.crypto.OlmDevice) -> Trust: + olm = self.nio.olm + return ( + Trust.trusted if olm.is_device_verified(device) else + Trust.blacklisted if olm.is_device_blacklisted(device) else + Trust.undecided + ) + + @pyqtSlot(str, result="QVariant") @pyqtSlot(str, str, result="QVariant") @futurize() def login(self, password: str, device_name: str = "") -> "Client": response = self.net.talk(self.nio.login, password, device_name) self.nio_sync.receive_response(response) - - if not self.nio.olm_account_shared: - self._keys_upload() - return self @@ -123,10 +129,6 @@ class Client(QObject): response = nio.LoginResponse(user_id, device_id, token) self.nio.receive_response(response) self.nio_sync.receive_response(response) - - if not self.nio.olm_account_shared: - self._keys_upload() - return self @@ -158,10 +160,10 @@ class Client(QObject): self.nio.receive_response(response) if self.nio.should_upload_keys: - self._keys_upload() + self.uploadE2EKeys() if self.nio.should_query_keys: - self._keys_query() + self.queryE2EKeys() for room_id, room_info in response.rooms.invite.items(): for ev in room_info.invite_state: @@ -280,12 +282,12 @@ class Client(QObject): except nio.GroupEncryptionError as err: log.warning(err) try: - self._share_group_session(room_id) + self.shareRoomE2ESession(room_id) except nio.EncryptionError as err: log.warning(err) - self._keys_claim(room_id) - self._share_group_session(room_id, - ignore_missing_sessions=True) + self.claimE2EKeysForRoom(room_id) + self.shareRoomE2ESession(room_id, + ignore_missing_sessions=True) log.debug("Final try to send %r to %r", content, room_id) return talk() diff --git a/harmonyqml/backend/model/items.py b/harmonyqml/backend/model/items.py index ebc0b569..c1f80691 100644 --- a/harmonyqml/backend/model/items.py +++ b/harmonyqml/backend/model/items.py @@ -1,3 +1,4 @@ +from enum import Enum from typing import Any, Dict, List, Optional from PyQt5.QtCore import QDateTime, QSortFilterProxyModel @@ -42,6 +43,22 @@ class RoomCategory(ListItem): sortedRooms: QSortFilterProxyModel = QSortFilterProxyModel() +class Trust(Enum): + blacklisted = -1 + undecided = 0 + trusted = 1 + + +class Device(ListItem): + _required_init_values = {"deviceId", "ed25519Key"} + _constant = {"deviceId", "ed25519Key"} + + deviceId: str = "" + ed25519Key: str = "" + displayName: Optional[str] = None + trust: Trust = Trust.undecided + + class Account(ListItem): _required_init_values = {"userId", "roomCategories"} _constant = {"userId", "roomCategories"} diff --git a/harmonyqml/backend/signal_manager.py b/harmonyqml/backend/signal_manager.py index 97caf45c..0b255a17 100644 --- a/harmonyqml/backend/signal_manager.py +++ b/harmonyqml/backend/signal_manager.py @@ -11,7 +11,9 @@ from nio.rooms import MatrixRoom from .backend import Backend from .client import Client -from .model.items import Account, ListModel, Room, RoomCategory, RoomEvent +from .model.items import ( + Account, Device, ListModel, Room, RoomCategory, RoomEvent +) from .model.sort_filter_proxy import SortFilterProxy Inviter = Optional[Dict[str, str]] @@ -35,8 +37,7 @@ class SignalManager(QObject): def onClientAdded(self, client: Client) -> None: - self.connectClient(client) - + # Build an Account item for the Backend.accounts model room_categories_kwargs: List[Dict[str, Any]] = [ {"name": "Invites", "rooms": ListModel()}, {"name": "Rooms", "rooms": ListModel()}, @@ -59,6 +60,27 @@ class SignalManager(QObject): displayName = self.backend.getUserDisplayName(client.userId), )) + # Upload our E2E keys to the matrix server if needed + if not client.nio.olm_account_shared: + client.uploadE2EKeys() + + # Add our devices to the Backend.devices model + store = client.nio.device_store + + for user_id in store.users: + self.backend.devices[user_id].clear() + self.backend.devices[user_id].extend([ + Device( + deviceId = dev.id, + ed25519Key = dev.ed25519, + trust = client.getDeviceTrust(dev), + # displayName = TODO + ) for dev in store.active_user_devices(user_id) + ]) + + # Finally, connect all client signals + self.connectClient(client) + def onClientDeleted(self, user_id: str) -> None: del self.backend.accounts[user_id]