2828 parse_integer ,
2929)
3030from synapse .http .site import SynapseRequest
31+ from synapse .replication .http .devices import ReplicationUploadKeysForUserRestServlet
3132from synapse .rest .client ._base import client_patterns , interactive_auth_handler
3233from synapse .rest .client .models import AuthenticationData
3334from synapse .rest .models import RequestBodyModel
@@ -301,6 +302,7 @@ async def on_PUT(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
301302
302303 device_id = await self .device_handler .store_dehydrated_device (
303304 requester .user .to_string (),
305+ None ,
304306 submission .device_data .dict (),
305307 submission .initial_device_display_name ,
306308 )
@@ -390,6 +392,170 @@ async def on_POST(
390392 return 200 , msgs
391393
392394
395+ class DehydratedDeviceV2Servlet (RestServlet ):
396+ """Upload, retrieve, or delete a dehydrated device.
397+
398+ GET /org.matrix.msc3814.v1/dehydrated_device
399+
400+ HTTP/1.1 200 OK
401+ Content-Type: application/json
402+
403+ {
404+ "device_id": "dehydrated_device_id",
405+ "device_data": {
406+ "algorithm": "org.matrix.msc2697.v1.dehydration.v1.olm",
407+ "account": "dehydrated_device"
408+ }
409+ }
410+
411+ PUT /org.matrix.msc3814.v1/dehydrated_device
412+ Content-Type: application/json
413+
414+ {
415+ "device_id": "dehydrated_device_id",
416+ "device_data": {
417+ "algorithm": "org.matrix.msc2697.v1.dehydration.v1.olm",
418+ "account": "dehydrated_device"
419+ },
420+ "device_keys": {
421+ "user_id": "<user_id>",
422+ "device_id": "<device_id>",
423+ "valid_until_ts": <millisecond_timestamp>,
424+ "algorithms": [
425+ "m.olm.curve25519-aes-sha2",
426+ ]
427+ "keys": {
428+ "<algorithm>:<device_id>": "<key_base64>",
429+ },
430+ "signatures:" {
431+ "<user_id>" {
432+ "<algorithm>:<device_id>": "<signature_base64>"
433+ }
434+ }
435+ },
436+ "fallback_keys": {
437+ "<algorithm>:<device_id>": "<key_base64>",
438+ "signed_<algorithm>:<device_id>": {
439+ "fallback": true,
440+ "key": "<key_base64>",
441+ "signatures": {
442+ "<user_id>": {
443+ "<algorithm>:<device_id>": "<key_base64>"
444+ }
445+ }
446+ }
447+ }
448+ "one_time_keys": {
449+ "<algorithm>:<key_id>": "<key_base64>"
450+ },
451+
452+ }
453+
454+ HTTP/1.1 200 OK
455+ Content-Type: application/json
456+
457+ {
458+ "device_id": "dehydrated_device_id"
459+ }
460+
461+ DELETE /org.matrix.msc3814.v1/dehydrated_device
462+
463+ HTTP/1.1 200 OK
464+ Content-Type: application/json
465+
466+ {
467+ "device_id": "dehydrated_device_id",
468+ }
469+ """
470+
471+ PATTERNS = [
472+ * client_patterns ("/org.matrix.msc3814.v1/dehydrated_device$" , releases = ()),
473+ ]
474+
475+ def __init__ (self , hs : "HomeServer" ):
476+ super ().__init__ ()
477+ self .hs = hs
478+ self .auth = hs .get_auth ()
479+ handler = hs .get_device_handler ()
480+ assert isinstance (handler , DeviceHandler )
481+ self .e2e_keys_handler = hs .get_e2e_keys_handler ()
482+ self .device_handler = handler
483+
484+ if hs .config .worker .worker_app is None :
485+ # if main process
486+ self .key_uploader = self .e2e_keys_handler .upload_keys_for_user
487+ else :
488+ # then a worker
489+ self .key_uploader = ReplicationUploadKeysForUserRestServlet .make_client (hs )
490+
491+ async def on_GET (self , request : SynapseRequest ) -> Tuple [int , JsonDict ]:
492+ requester = await self .auth .get_user_by_req (request )
493+
494+ dehydrated_device = await self .device_handler .get_dehydrated_device (
495+ requester .user .to_string ()
496+ )
497+
498+ if dehydrated_device is not None :
499+ (device_id , device_data ) = dehydrated_device
500+ result = {"device_id" : device_id , "device_data" : device_data }
501+ return 200 , result
502+ else :
503+ raise errors .NotFoundError ("No dehydrated device available" )
504+
505+ async def on_DELETE (self , request : SynapseRequest ) -> Tuple [int , JsonDict ]:
506+ requester = await self .auth .get_user_by_req (request )
507+
508+ dehydrated_device = await self .device_handler .get_dehydrated_device (
509+ requester .user .to_string ()
510+ )
511+
512+ if dehydrated_device is not None :
513+ (device_id , device_data ) = dehydrated_device
514+
515+ result = await self .device_handler .rehydrate_device (
516+ requester .user .to_string (),
517+ self .auth .get_access_token_from_request (request ),
518+ device_id ,
519+ )
520+
521+ result = {"device_id" : device_id }
522+
523+ return 200 , result
524+ else :
525+ raise errors .NotFoundError ("No dehydrated device available" )
526+
527+ class PutBody (RequestBodyModel ):
528+ device_data : DehydratedDeviceDataModel
529+ device_id : Optional [StrictStr ]
530+ initial_device_display_name : Optional [StrictStr ]
531+
532+ class Config :
533+ extra = Extra .allow
534+
535+ async def on_PUT (self , request : SynapseRequest ) -> Tuple [int , JsonDict ]:
536+ submission = parse_and_validate_json_object_from_request (request , self .PutBody )
537+ requester = await self .auth .get_user_by_req (request )
538+ user_id = requester .user .to_string ()
539+
540+ # TODO: Those two operations, creating a device and storing the
541+ # device's keys should be atomic.
542+ device_id = await self .device_handler .store_dehydrated_device (
543+ requester .user .to_string (),
544+ submission .device_id ,
545+ submission .device_data .dict (),
546+ submission .initial_device_display_name ,
547+ )
548+
549+ # TODO: Do we need to do something with the result here?
550+ await self .key_uploader (
551+ user_id = user_id ,
552+ device_id = submission .device_id ,
553+ keys = submission .dict ()
554+ )
555+
556+ return 200 , {"device_id" : device_id }
557+
558+
393559def register_servlets (hs : "HomeServer" , http_server : HttpServer ) -> None :
394560 if (
395561 hs .config .worker .worker_app is None
@@ -404,5 +570,5 @@ def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
404570 DehydratedDeviceServlet (hs , msc2697 = True ).register (http_server )
405571 ClaimDehydratedDeviceServlet (hs ).register (http_server )
406572 if hs .config .experimental .msc3814_enabled :
407- DehydratedDeviceServlet (hs , msc2697 = False ).register (http_server )
573+ DehydratedDeviceV2Servlet (hs ).register (http_server )
408574 DehydratedDeviceEventsServlet (hs ).register (http_server )
0 commit comments