Check appservices for devices during a /user/devices query. (#15539)
MSC3984 proxies /keys/query requests to appservices, but servers will
can also requests devices / keys from the /user/devices endpoint.
The formats are close enough that we can "proxy" that /user/devices to
appservices (by calling /keys/query) and then change the format of the
returned data before returning it over federation.
1 files changed, 28 insertions, 0 deletions
diff --git a/synapse/handlers/device.py b/synapse/handlers/device.py
index b9d3b7fbc6..5d12a39e26 100644
--- a/synapse/handlers/device.py
+++ b/synapse/handlers/device.py
@@ -75,10 +75,14 @@ class DeviceWorkerHandler:
self.store = hs.get_datastores().main
self.notifier = hs.get_notifier()
self.state = hs.get_state_handler()
+ self._appservice_handler = hs.get_application_service_handler()
self._state_storage = hs.get_storage_controllers().state
self._auth_handler = hs.get_auth_handler()
self.server_name = hs.hostname
self._msc3852_enabled = hs.config.experimental.msc3852_enabled
+ self._query_appservices_for_keys = (
+ hs.config.experimental.msc3984_appservice_key_query
+ )
self.device_list_updater = DeviceListWorkerUpdater(hs)
@@ -328,6 +332,30 @@ class DeviceWorkerHandler:
user_id, "self_signing"
)
+ # Check if the application services have any results.
+ if self._query_appservices_for_keys:
+ # Query the appservice for all devices for this user.
+ query: Dict[str, Optional[List[str]]] = {user_id: None}
+
+ # Query the appservices for any keys.
+ appservice_results = await self._appservice_handler.query_keys(query)
+
+ # Merge results, overriding anything from the database.
+ appservice_devices = appservice_results.get("device_keys", {}).get(
+ user_id, {}
+ )
+
+ # Filter the database results to only those devices that the appservice has
+ # *not* responded with.
+ devices = [d for d in devices if d["device_id"] not in appservice_devices]
+ # Append the appservice response by wrapping each result in another dictionary.
+ devices.extend(
+ {"device_id": device_id, "keys": device}
+ for device_id, device in appservice_devices.items()
+ )
+
+ # TODO Handle cross-signing keys.
+
return {
"user_id": user_id,
"stream_id": stream_id,
|