Remove MSC2697 (legacy dehydrated devices) (#19346)
Fixes #19347 This deprecates MSC2697 which has been closed since May 2024. As per #19347 this seems to be a thing we can just rip out. The crypto team have moved onto MSC3814 and are suggesting that developers who rely on MSC2697 should use MSC3814 instead. MSC2697 implementation originally introduced by https://github.com/matrix-org/synapse/pull/8380
This commit is contained in:
1
changelog.d/19346.removal
Normal file
1
changelog.d/19346.removal
Normal file
@@ -0,0 +1 @@
|
||||
MSC2697 (Dehydrated devices) has been removed, as the MSC is closed. Developers should migrate to MSC3814.
|
||||
@@ -125,6 +125,14 @@ Ubuntu 25.04 Plucky Puffin [is end-of-life as of 17 Jan
|
||||
2026](https://endoflife.date/ubuntu). This release drops support for Ubuntu
|
||||
25.04, and in its place adds support for Ubuntu 25.10 Questing Quokka.
|
||||
|
||||
## Removal of MSC2697 (Legacy) Dehydrated devices
|
||||
|
||||
The endpoints for
|
||||
[MSC2697](https://github.com/matrix-org/matrix-spec-proposals/pull/2697) have now
|
||||
been removed, since the MSC is closed. Developers who rely on this feature should
|
||||
migrate to [MSC3814](https://github.com/matrix-org/matrix-spec-proposals/pull/3814)
|
||||
which introduces support for a newer version of dehydrated devices.
|
||||
|
||||
# Upgrading to v1.144.0
|
||||
|
||||
## Worker support for unstable MSC4140 `/restart` endpoint
|
||||
|
||||
@@ -378,27 +378,9 @@ class ExperimentalConfig(Config):
|
||||
# MSC3026 (busy presence state)
|
||||
self.msc3026_enabled: bool = experimental.get("msc3026_enabled", False)
|
||||
|
||||
# MSC2697 (device dehydration)
|
||||
# Enabled by default since this option was added after adding the feature.
|
||||
# It is not recommended that both MSC2697 and MSC3814 both be enabled at
|
||||
# once.
|
||||
self.msc2697_enabled: bool = experimental.get("msc2697_enabled", True)
|
||||
|
||||
# MSC3814 (dehydrated devices with SSSS)
|
||||
# This is an alternative method to achieve the same goals as MSC2697.
|
||||
# It is not recommended that both MSC2697 and MSC3814 both be enabled at
|
||||
# once.
|
||||
self.msc3814_enabled: bool = experimental.get("msc3814_enabled", False)
|
||||
|
||||
if self.msc2697_enabled and self.msc3814_enabled:
|
||||
raise ConfigError(
|
||||
"MSC2697 and MSC3814 should not both be enabled.",
|
||||
(
|
||||
"experimental_features",
|
||||
"msc3814_enabled",
|
||||
),
|
||||
)
|
||||
|
||||
# MSC3244 (room version capabilities)
|
||||
self.msc3244_enabled: bool = experimental.get("msc3244_enabled", True)
|
||||
|
||||
|
||||
@@ -441,8 +441,8 @@ class DeviceHandler:
|
||||
user_id: str,
|
||||
device_id: str | None,
|
||||
device_data: JsonDict,
|
||||
initial_device_display_name: str | None = None,
|
||||
keys_for_device: JsonDict | None = None,
|
||||
initial_device_display_name: str | None,
|
||||
keys_for_device: JsonDict,
|
||||
) -> str:
|
||||
"""Store a dehydrated device for a user, optionally storing the keys associated with
|
||||
it as well. If the user had a previous dehydrated device, it is removed.
|
||||
@@ -473,46 +473,6 @@ class DeviceHandler:
|
||||
|
||||
return device_id
|
||||
|
||||
async def rehydrate_device(
|
||||
self, user_id: str, access_token: str, device_id: str
|
||||
) -> dict:
|
||||
"""Process a rehydration request from the user.
|
||||
|
||||
Args:
|
||||
user_id: the user who is rehydrating the device
|
||||
access_token: the access token used for the request
|
||||
device_id: the ID of the device that will be rehydrated
|
||||
Returns:
|
||||
a dict containing {"success": True}
|
||||
"""
|
||||
success = await self.store.remove_dehydrated_device(user_id, device_id)
|
||||
|
||||
if not success:
|
||||
raise errors.NotFoundError()
|
||||
|
||||
# If the dehydrated device was successfully deleted (the device ID
|
||||
# matched the stored dehydrated device), then modify the access
|
||||
# token and refresh token to use the dehydrated device's ID and
|
||||
# copy the old device display name to the dehydrated device,
|
||||
# and destroy the old device ID
|
||||
old_device_id = await self.store.set_device_for_access_token(
|
||||
access_token, device_id
|
||||
)
|
||||
await self.store.set_device_for_refresh_token(user_id, old_device_id, device_id)
|
||||
old_device = await self.store.get_device(user_id, old_device_id)
|
||||
if old_device is None:
|
||||
raise errors.NotFoundError()
|
||||
await self.store.update_device(user_id, device_id, old_device["display_name"])
|
||||
# can't call self.delete_device because that will clobber the
|
||||
# access token so call the storage layer directly
|
||||
await self.store.delete_devices(user_id, [old_device_id])
|
||||
|
||||
# tell everyone that the old device is gone and that the dehydrated
|
||||
# device has a new display name
|
||||
await self.notify_device_update(user_id, [old_device_id, device_id])
|
||||
|
||||
return {"success": True}
|
||||
|
||||
async def delete_dehydrated_device(self, user_id: str, device_id: str) -> None:
|
||||
"""
|
||||
Delete a stored dehydrated device.
|
||||
|
||||
@@ -253,131 +253,6 @@ class DehydratedDeviceDataModel(RequestBodyModel):
|
||||
algorithm: StrictStr
|
||||
|
||||
|
||||
class DehydratedDeviceServlet(RestServlet):
|
||||
"""Retrieve or store a dehydrated device.
|
||||
|
||||
Implements MSC2697.
|
||||
|
||||
GET /org.matrix.msc2697.v2/dehydrated_device
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"device_id": "dehydrated_device_id",
|
||||
"device_data": {
|
||||
"algorithm": "org.matrix.msc2697.v1.dehydration.v1.olm",
|
||||
"account": "dehydrated_device"
|
||||
}
|
||||
}
|
||||
|
||||
PUT /org.matrix.msc2697.v2/dehydrated_device
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"device_data": {
|
||||
"algorithm": "org.matrix.msc2697.v1.dehydration.v1.olm",
|
||||
"account": "dehydrated_device"
|
||||
}
|
||||
}
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"device_id": "dehydrated_device_id"
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
PATTERNS = client_patterns(
|
||||
"/org.matrix.msc2697.v2/dehydrated_device$",
|
||||
releases=(),
|
||||
)
|
||||
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
super().__init__()
|
||||
self.hs = hs
|
||||
self.auth = hs.get_auth()
|
||||
handler = hs.get_device_handler()
|
||||
self.device_handler = handler
|
||||
|
||||
async def on_GET(self, request: SynapseRequest) -> tuple[int, JsonDict]:
|
||||
requester = await self.auth.get_user_by_req(request)
|
||||
dehydrated_device = await self.device_handler.get_dehydrated_device(
|
||||
requester.user.to_string()
|
||||
)
|
||||
if dehydrated_device is not None:
|
||||
(device_id, device_data) = dehydrated_device
|
||||
result = {"device_id": device_id, "device_data": device_data}
|
||||
return 200, result
|
||||
else:
|
||||
raise errors.NotFoundError("No dehydrated device available")
|
||||
|
||||
class PutBody(RequestBodyModel):
|
||||
device_data: DehydratedDeviceDataModel
|
||||
initial_device_display_name: StrictStr | None = None
|
||||
|
||||
async def on_PUT(self, request: SynapseRequest) -> tuple[int, JsonDict]:
|
||||
submission = parse_and_validate_json_object_from_request(request, self.PutBody)
|
||||
requester = await self.auth.get_user_by_req(request)
|
||||
|
||||
device_id = await self.device_handler.store_dehydrated_device(
|
||||
requester.user.to_string(),
|
||||
None,
|
||||
submission.device_data.dict(),
|
||||
submission.initial_device_display_name,
|
||||
)
|
||||
return 200, {"device_id": device_id}
|
||||
|
||||
|
||||
class ClaimDehydratedDeviceServlet(RestServlet):
|
||||
"""Claim a dehydrated device.
|
||||
|
||||
POST /org.matrix.msc2697.v2/dehydrated_device/claim
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"device_id": "dehydrated_device_id"
|
||||
}
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"success": true,
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
PATTERNS = client_patterns(
|
||||
"/org.matrix.msc2697.v2/dehydrated_device/claim", releases=()
|
||||
)
|
||||
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
super().__init__()
|
||||
self.hs = hs
|
||||
self.auth = hs.get_auth()
|
||||
handler = hs.get_device_handler()
|
||||
self.device_handler = handler
|
||||
|
||||
class PostBody(RequestBodyModel):
|
||||
device_id: StrictStr
|
||||
|
||||
async def on_POST(self, request: SynapseRequest) -> tuple[int, JsonDict]:
|
||||
requester = await self.auth.get_user_by_req(request)
|
||||
|
||||
submission = parse_and_validate_json_object_from_request(request, self.PostBody)
|
||||
|
||||
result = await self.device_handler.rehydrate_device(
|
||||
requester.user.to_string(),
|
||||
self.auth.get_access_token_from_request(request),
|
||||
submission.device_id,
|
||||
)
|
||||
|
||||
return 200, result
|
||||
|
||||
|
||||
class DehydratedDeviceEventsServlet(RestServlet):
|
||||
PATTERNS = client_patterns(
|
||||
"/org.matrix.msc3814.v1/dehydrated_device/(?P<device_id>[^/]*)/events$",
|
||||
@@ -579,9 +454,6 @@ def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
||||
DevicesRestServlet(hs).register(http_server)
|
||||
DeviceRestServlet(hs).register(http_server)
|
||||
|
||||
if hs.config.experimental.msc2697_enabled:
|
||||
DehydratedDeviceServlet(hs).register(http_server)
|
||||
ClaimDehydratedDeviceServlet(hs).register(http_server)
|
||||
if hs.config.experimental.msc3814_enabled:
|
||||
DehydratedDeviceV2Servlet(hs).register(http_server)
|
||||
DehydratedDeviceEventsServlet(hs).register(http_server)
|
||||
|
||||
@@ -44,7 +44,7 @@ from synapse.http.servlet import (
|
||||
validate_json_object,
|
||||
)
|
||||
from synapse.http.site import SynapseRequest
|
||||
from synapse.logging.opentracing import log_kv, set_tag
|
||||
from synapse.logging.opentracing import set_tag
|
||||
from synapse.rest.client._base import client_patterns, interactive_auth_handler
|
||||
from synapse.types import JsonDict, StreamToken
|
||||
from synapse.types.rest import RequestBodyModel
|
||||
@@ -105,7 +105,7 @@ class KeyUploadServlet(RestServlet):
|
||||
|
||||
"""
|
||||
|
||||
PATTERNS = client_patterns("/keys/upload(/(?P<device_id>[^/]+))?$")
|
||||
PATTERNS = client_patterns("/keys/upload$")
|
||||
CATEGORY = "Encryption requests"
|
||||
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
@@ -220,9 +220,7 @@ class KeyUploadServlet(RestServlet):
|
||||
)
|
||||
return v
|
||||
|
||||
async def on_POST(
|
||||
self, request: SynapseRequest, device_id: str | None
|
||||
) -> tuple[int, JsonDict]:
|
||||
async def on_POST(self, request: SynapseRequest) -> tuple[int, JsonDict]:
|
||||
requester = await self.auth.get_user_by_req(request, allow_guest=True)
|
||||
user_id = requester.user.to_string()
|
||||
|
||||
@@ -236,31 +234,7 @@ class KeyUploadServlet(RestServlet):
|
||||
body = parse_json_object_from_request(request)
|
||||
validate_json_object(body, self.KeyUploadRequestBody)
|
||||
|
||||
if device_id is not None:
|
||||
# Providing the device_id should only be done for setting keys
|
||||
# for dehydrated devices; however, we allow it for any device for
|
||||
# compatibility with older clients.
|
||||
if requester.device_id is not None and device_id != requester.device_id:
|
||||
dehydrated_device = await self.device_handler.get_dehydrated_device(
|
||||
user_id
|
||||
)
|
||||
if dehydrated_device is not None and device_id != dehydrated_device[0]:
|
||||
set_tag("error", True)
|
||||
log_kv(
|
||||
{
|
||||
"message": "Client uploading keys for a different device",
|
||||
"logged_in_id": requester.device_id,
|
||||
"key_being_uploaded": device_id,
|
||||
}
|
||||
)
|
||||
logger.warning(
|
||||
"Client uploading keys for a different device "
|
||||
"(logged in as %s, uploading for %s)",
|
||||
requester.device_id,
|
||||
device_id,
|
||||
)
|
||||
else:
|
||||
device_id = requester.device_id
|
||||
device_id = requester.device_id
|
||||
|
||||
if device_id is None:
|
||||
raise SynapseError(
|
||||
|
||||
@@ -1482,33 +1482,29 @@ class DeviceWorkerStore(RoomMemberWorkerStore, EndToEndKeyWorkerStore):
|
||||
device_id: str,
|
||||
device_data: str,
|
||||
time: int,
|
||||
keys: JsonDict | None = None,
|
||||
keys: JsonDict,
|
||||
) -> str | None:
|
||||
# TODO: make keys non-optional once support for msc2697 is dropped
|
||||
if keys:
|
||||
device_keys = keys.get("device_keys", None)
|
||||
if device_keys:
|
||||
self._set_e2e_device_keys_txn(
|
||||
txn, user_id, device_id, time, device_keys
|
||||
)
|
||||
device_keys = keys.get("device_keys", None)
|
||||
if device_keys:
|
||||
self._set_e2e_device_keys_txn(txn, user_id, device_id, time, device_keys)
|
||||
|
||||
one_time_keys = keys.get("one_time_keys", None)
|
||||
if one_time_keys:
|
||||
key_list = []
|
||||
for key_id, key_obj in one_time_keys.items():
|
||||
algorithm, key_id = key_id.split(":")
|
||||
key_list.append(
|
||||
(
|
||||
algorithm,
|
||||
key_id,
|
||||
encode_canonical_json(key_obj).decode("ascii"),
|
||||
)
|
||||
one_time_keys = keys.get("one_time_keys", None)
|
||||
if one_time_keys:
|
||||
key_list = []
|
||||
for key_id, key_obj in one_time_keys.items():
|
||||
algorithm, key_id = key_id.split(":")
|
||||
key_list.append(
|
||||
(
|
||||
algorithm,
|
||||
key_id,
|
||||
encode_canonical_json(key_obj).decode("ascii"),
|
||||
)
|
||||
self._add_e2e_one_time_keys_txn(txn, user_id, device_id, time, key_list)
|
||||
)
|
||||
self._add_e2e_one_time_keys_txn(txn, user_id, device_id, time, key_list)
|
||||
|
||||
fallback_keys = keys.get("fallback_keys", None)
|
||||
if fallback_keys:
|
||||
self._set_e2e_fallback_keys_txn(txn, user_id, device_id, fallback_keys)
|
||||
fallback_keys = keys.get("fallback_keys", None)
|
||||
if fallback_keys:
|
||||
self._set_e2e_fallback_keys_txn(txn, user_id, device_id, fallback_keys)
|
||||
|
||||
old_device_id = self.db_pool.simple_select_one_onecol_txn(
|
||||
txn,
|
||||
@@ -1532,7 +1528,7 @@ class DeviceWorkerStore(RoomMemberWorkerStore, EndToEndKeyWorkerStore):
|
||||
device_id: str,
|
||||
device_data: JsonDict,
|
||||
time_now: int,
|
||||
keys: dict | None = None,
|
||||
keys: dict,
|
||||
) -> str | None:
|
||||
"""Store a dehydrated device for a user.
|
||||
|
||||
|
||||
@@ -2092,58 +2092,6 @@ class RegistrationWorkerStore(StatsStore, CacheInvalidationWorkerStore):
|
||||
"replace_refresh_token", _replace_refresh_token_txn
|
||||
)
|
||||
|
||||
async def set_device_for_refresh_token(
|
||||
self, user_id: str, old_device_id: str, device_id: str
|
||||
) -> None:
|
||||
"""Moves refresh tokens from old device to current device
|
||||
|
||||
Args:
|
||||
user_id: The user of the devices.
|
||||
old_device_id: The old device.
|
||||
device_id: The new device ID.
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
await self.db_pool.simple_update(
|
||||
"refresh_tokens",
|
||||
keyvalues={"user_id": user_id, "device_id": old_device_id},
|
||||
updatevalues={"device_id": device_id},
|
||||
desc="set_device_for_refresh_token",
|
||||
)
|
||||
|
||||
def _set_device_for_access_token_txn(
|
||||
self, txn: LoggingTransaction, token: str, device_id: str
|
||||
) -> str:
|
||||
old_device_id = self.db_pool.simple_select_one_onecol_txn(
|
||||
txn, "access_tokens", {"token": token}, "device_id"
|
||||
)
|
||||
|
||||
self.db_pool.simple_update_txn(
|
||||
txn, "access_tokens", {"token": token}, {"device_id": device_id}
|
||||
)
|
||||
|
||||
self._invalidate_cache_and_stream(txn, self.get_user_by_access_token, (token,))
|
||||
|
||||
return old_device_id
|
||||
|
||||
async def set_device_for_access_token(self, token: str, device_id: str) -> str:
|
||||
"""Sets the device ID associated with an access token.
|
||||
|
||||
Args:
|
||||
token: The access token to modify.
|
||||
device_id: The new device ID.
|
||||
Returns:
|
||||
The old device ID associated with the access token.
|
||||
"""
|
||||
|
||||
return await self.db_pool.runInteraction(
|
||||
"set_device_for_access_token",
|
||||
self._set_device_for_access_token_txn,
|
||||
token,
|
||||
device_id,
|
||||
)
|
||||
|
||||
async def add_login_token_to_user(
|
||||
self,
|
||||
user_id: str,
|
||||
|
||||
@@ -497,90 +497,7 @@ class DehydrationTestCase(unittest.HomeserverTestCase):
|
||||
self.store = hs.get_datastores().main
|
||||
return hs
|
||||
|
||||
def test_dehydrate_and_rehydrate_device(self) -> None:
|
||||
user_id = "@boris:dehydration"
|
||||
|
||||
self.get_success(self.store.register_user(user_id, "foobar"))
|
||||
|
||||
# First check if we can store and fetch a dehydrated device
|
||||
stored_dehydrated_device_id = self.get_success(
|
||||
self.handler.store_dehydrated_device(
|
||||
user_id=user_id,
|
||||
device_id=None,
|
||||
device_data={"device_data": {"foo": "bar"}},
|
||||
initial_device_display_name="dehydrated device",
|
||||
)
|
||||
)
|
||||
|
||||
result = self.get_success(self.handler.get_dehydrated_device(user_id=user_id))
|
||||
assert result is not None
|
||||
retrieved_device_id, device_data = result
|
||||
|
||||
self.assertEqual(retrieved_device_id, stored_dehydrated_device_id)
|
||||
self.assertEqual(device_data, {"device_data": {"foo": "bar"}})
|
||||
|
||||
# Create a new login for the user and dehydrated the device
|
||||
device_id, access_token, _expiration_time, refresh_token = self.get_success(
|
||||
self.registration.register_device(
|
||||
user_id=user_id,
|
||||
device_id=None,
|
||||
initial_display_name="new device",
|
||||
should_issue_refresh_token=True,
|
||||
)
|
||||
)
|
||||
|
||||
# Trying to claim a nonexistent device should throw an error
|
||||
self.get_failure(
|
||||
self.handler.rehydrate_device(
|
||||
user_id=user_id,
|
||||
access_token=access_token,
|
||||
device_id="not the right device ID",
|
||||
),
|
||||
NotFoundError,
|
||||
)
|
||||
|
||||
# dehydrating the right devices should succeed and change our device ID
|
||||
# to the dehydrated device's ID
|
||||
res = self.get_success(
|
||||
self.handler.rehydrate_device(
|
||||
user_id=user_id,
|
||||
access_token=access_token,
|
||||
device_id=retrieved_device_id,
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(res, {"success": True})
|
||||
|
||||
# make sure that our device ID has changed
|
||||
user_info = self.get_success(self.auth.get_user_by_access_token(access_token))
|
||||
|
||||
self.assertEqual(user_info.device_id, retrieved_device_id)
|
||||
|
||||
# make sure the user device has the refresh token
|
||||
assert refresh_token is not None
|
||||
self.get_success(
|
||||
self.auth_handler.refresh_token(refresh_token, 5 * 60 * 1000, 5 * 60 * 1000)
|
||||
)
|
||||
|
||||
# make sure the device has the display name that was set from the login
|
||||
res = self.get_success(self.handler.get_device(user_id, retrieved_device_id))
|
||||
|
||||
self.assertEqual(res["display_name"], "new device")
|
||||
|
||||
# make sure that the device ID that we were initially assigned no longer exists
|
||||
self.get_failure(
|
||||
self.handler.get_device(user_id, device_id),
|
||||
NotFoundError,
|
||||
)
|
||||
|
||||
# make sure that there's no device available for dehydrating now
|
||||
ret = self.get_success(self.handler.get_dehydrated_device(user_id=user_id))
|
||||
|
||||
self.assertIsNone(ret)
|
||||
|
||||
@unittest.override_config(
|
||||
{"experimental_features": {"msc2697_enabled": False, "msc3814_enabled": True}}
|
||||
)
|
||||
@unittest.override_config({"experimental_features": {"msc3814_enabled": True}})
|
||||
def test_dehydrate_v2_and_fetch_events(self) -> None:
|
||||
user_id = "@boris:server"
|
||||
|
||||
@@ -593,6 +510,7 @@ class DehydrationTestCase(unittest.HomeserverTestCase):
|
||||
device_id=None,
|
||||
device_data={"device_data": {"foo": "bar"}},
|
||||
initial_device_display_name="dehydrated device",
|
||||
keys_for_device={},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -388,9 +388,7 @@ class DevicesRestTestCase(unittest.HomeserverTestCase):
|
||||
self.assertEqual(0, channel.json_body["total"])
|
||||
self.assertEqual(0, len(channel.json_body["devices"]))
|
||||
|
||||
@unittest.override_config(
|
||||
{"experimental_features": {"msc2697_enabled": False, "msc3814_enabled": True}}
|
||||
)
|
||||
@unittest.override_config({"experimental_features": {"msc3814_enabled": True}})
|
||||
def test_get_devices(self) -> None:
|
||||
"""
|
||||
Tests that a normal lookup for devices is successfully
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
# [This file includes modifications made by New Vector Limited]
|
||||
#
|
||||
#
|
||||
from http import HTTPStatus
|
||||
|
||||
from twisted.internet.defer import ensureDeferred
|
||||
from twisted.internet.testing import MemoryReactor
|
||||
|
||||
@@ -85,48 +83,7 @@ class DehydratedDeviceTestCase(unittest.HomeserverTestCase):
|
||||
self.registration = hs.get_registration_handler()
|
||||
self.message_handler = hs.get_device_message_handler()
|
||||
|
||||
def test_PUT(self) -> None:
|
||||
"""Sanity-check that we can PUT a dehydrated device.
|
||||
|
||||
Detects https://github.com/matrix-org/synapse/issues/14334.
|
||||
"""
|
||||
alice = self.register_user("alice", "correcthorse")
|
||||
token = self.login(alice, "correcthorse")
|
||||
|
||||
# Have alice update their device list
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
"_matrix/client/unstable/org.matrix.msc2697.v2/dehydrated_device",
|
||||
{
|
||||
"device_data": {
|
||||
"algorithm": "org.matrix.msc2697.v1.dehydration.v1.olm",
|
||||
"account": "dehydrated_device",
|
||||
},
|
||||
"device_keys": {
|
||||
"user_id": "@alice:test",
|
||||
"device_id": "device1",
|
||||
"valid_until_ts": "80",
|
||||
"algorithms": [
|
||||
"m.olm.curve25519-aes-sha2",
|
||||
],
|
||||
"keys": {
|
||||
"<algorithm>:<device_id>": "<key_base64>",
|
||||
},
|
||||
"signatures": {
|
||||
"<user_id>": {"<algorithm>:<device_id>": "<signature_base64>"}
|
||||
},
|
||||
},
|
||||
},
|
||||
access_token=token,
|
||||
shorthand=False,
|
||||
)
|
||||
self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
|
||||
device_id = channel.json_body.get("device_id")
|
||||
self.assertIsInstance(device_id, str)
|
||||
|
||||
@unittest.override_config(
|
||||
{"experimental_features": {"msc2697_enabled": False, "msc3814_enabled": True}}
|
||||
)
|
||||
@unittest.override_config({"experimental_features": {"msc3814_enabled": True}})
|
||||
def test_dehydrate_msc3814(self) -> None:
|
||||
user = self.register_user("mikey", "pass")
|
||||
token = self.login(user, "pass", device_id="device1")
|
||||
@@ -320,9 +277,7 @@ class DehydratedDeviceTestCase(unittest.HomeserverTestCase):
|
||||
)
|
||||
self.assertEqual(channel.code, 401)
|
||||
|
||||
@unittest.override_config(
|
||||
{"experimental_features": {"msc2697_enabled": False, "msc3814_enabled": True}}
|
||||
)
|
||||
@unittest.override_config({"experimental_features": {"msc3814_enabled": True}})
|
||||
def test_msc3814_dehydrated_device_delete_works(self) -> None:
|
||||
user = self.register_user("mikey", "pass")
|
||||
token = self.login(user, "pass", device_id="device1")
|
||||
|
||||
Reference in New Issue
Block a user