Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

add updating of backup versions #4580

Merged
merged 6 commits into from
Feb 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/4580.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ability to update backup versions
74 changes: 69 additions & 5 deletions synapse/handlers/e2e_room_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@

from twisted.internet import defer

from synapse.api.errors import NotFoundError, RoomKeysVersionError, StoreError
from synapse.api.errors import (
Codes,
NotFoundError,
RoomKeysVersionError,
StoreError,
SynapseError,
)
from synapse.util.async_helpers import Linearizer

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -267,7 +273,7 @@ def get_version_info(self, user_id, version=None):
version(str): Optional; if None gives the most recent version
otherwise a historical one.
Raises:
StoreError: code 404 if the requested backup version doesn't exist
NotFoundError: if the requested backup version doesn't exist
Returns:
A deferred of a info dict that gives the info about the new version.

Expand All @@ -279,7 +285,13 @@ def get_version_info(self, user_id, version=None):
"""

with (yield self._upload_linearizer.queue(user_id)):
res = yield self.store.get_e2e_room_keys_version_info(user_id, version)
try:
res = yield self.store.get_e2e_room_keys_version_info(user_id, version)
except StoreError as e:
if e.code == 404:
raise NotFoundError("Unknown backup version")
else:
raise
defer.returnValue(res)

@defer.inlineCallbacks
Expand All @@ -290,8 +302,60 @@ def delete_version(self, user_id, version=None):
user_id(str): the user whose current backup version we're deleting
version(str): the version id of the backup being deleted
Raises:
StoreError: code 404 if this backup version doesn't exist
NotFoundError: if this backup version doesn't exist
"""

with (yield self._upload_linearizer.queue(user_id)):
yield self.store.delete_e2e_room_keys_version(user_id, version)
try:
yield self.store.delete_e2e_room_keys_version(user_id, version)
except StoreError as e:
if e.code == 404:
raise NotFoundError("Unknown backup version")
else:
raise

@defer.inlineCallbacks
def update_version(self, user_id, version, version_info):
"""Update the info about a given version of the user's backup

Args:
user_id(str): the user whose current backup version we're updating
version(str): the backup version we're updating
version_info(dict): the new information about the backup
Raises:
NotFoundError: if the requested backup version doesn't exist
Returns:
A deferred of an empty dict.
"""
if "version" not in version_info:
raise SynapseError(
400,
"Missing version in body",
Codes.MISSING_PARAM
)
if version_info["version"] != version:
raise SynapseError(
400,
"Version in body does not match",
Codes.INVALID_PARAM
)
with (yield self._upload_linearizer.queue(user_id)):
try:
old_info = yield self.store.get_e2e_room_keys_version_info(
user_id, version
)
except StoreError as e:
if e.code == 404:
raise NotFoundError("Unknown backup version")
else:
raise
if old_info["algorithm"] != version_info["algorithm"]:
raise SynapseError(
400,
"Algorithm does not match",
Codes.INVALID_PARAM
)

yield self.store.update_e2e_room_keys_version(user_id, version, version_info)

defer.returnValue({})
34 changes: 34 additions & 0 deletions synapse/rest/client/v2_alpha/room_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,40 @@ def on_DELETE(self, request, version):
)
defer.returnValue((200, {}))

@defer.inlineCallbacks
def on_PUT(self, request, version):
"""
Update the information about a given version of the user's room_keys backup.

POST /room_keys/version/12345 HTTP/1.1
Content-Type: application/json
{
"algorithm": "m.megolm_backup.v1",
"auth_data": {
"public_key": "abcdefg",
"signatures": {
"ed25519:something": "hijklmnop"
}
},
"version": "42"
}

HTTP/1.1 200 OK
Content-Type: application/json
{}
"""
requester = yield self.auth.get_user_by_req(request, allow_guest=False)
user_id = requester.user.to_string()
info = parse_json_object_from_request(request)

if version is None:
raise SynapseError(400, "No version specified to update", Codes.MISSING_PARAM)

yield self.e2e_room_keys_handler.update_version(
user_id, version, info
)
defer.returnValue((200, {}))


def register_servlets(hs, http_server):
RoomKeysServlet(hs).register(http_server)
Expand Down
21 changes: 21 additions & 0 deletions synapse/storage/e2e_room_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,27 @@ def _create_e2e_room_keys_version_txn(txn):
"create_e2e_room_keys_version_txn", _create_e2e_room_keys_version_txn
)

def update_e2e_room_keys_version(self, user_id, version, info):
"""Update a given backup version

Args:
user_id(str): the user whose backup version we're updating
version(str): the version ID of the backup version we're updating
info(dict): the new backup version info to store
"""

return self._simple_update(
table="e2e_room_keys_versions",
keyvalues={
"user_id": user_id,
"version": version,
},
updatevalues={
"auth_data": json.dumps(info["auth_data"]),
},
desc="update_e2e_room_keys_version"
)

def delete_e2e_room_keys_version(self, user_id, version=None):
"""Delete a given backup version of the user's room keys.
Doesn't delete their actual key data.
Expand Down
72 changes: 72 additions & 0 deletions tests/handlers/test_e2e_room_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,78 @@ def test_create_version(self):
"auth_data": "second_version_auth_data",
})

@defer.inlineCallbacks
def test_update_version(self):
"""Check that we can update versions.
"""
version = yield self.handler.create_version(self.local_user, {
"algorithm": "m.megolm_backup.v1",
"auth_data": "first_version_auth_data",
})
self.assertEqual(version, "1")

res = yield self.handler.update_version(self.local_user, version, {
"algorithm": "m.megolm_backup.v1",
"auth_data": "revised_first_version_auth_data",
"version": version
})
self.assertDictEqual(res, {})

# check we can retrieve it as the current version
res = yield self.handler.get_version_info(self.local_user)
self.assertDictEqual(res, {
"algorithm": "m.megolm_backup.v1",
"auth_data": "revised_first_version_auth_data",
"version": version
})

@defer.inlineCallbacks
def test_update_missing_version(self):
"""Check that we get a 404 on updating nonexistent versions
"""
res = None
try:
yield self.handler.update_version(self.local_user, "1", {
"algorithm": "m.megolm_backup.v1",
"auth_data": "revised_first_version_auth_data",
"version": "1"
})
except errors.SynapseError as e:
res = e.code
self.assertEqual(res, 404)

@defer.inlineCallbacks
def test_update_bad_version(self):
"""Check that we get a 400 if the version in the body is missing or
doesn't match
"""
version = yield self.handler.create_version(self.local_user, {
"algorithm": "m.megolm_backup.v1",
"auth_data": "first_version_auth_data",
})
self.assertEqual(version, "1")

res = None
try:
yield self.handler.update_version(self.local_user, version, {
"algorithm": "m.megolm_backup.v1",
"auth_data": "revised_first_version_auth_data"
})
except errors.SynapseError as e:
res = e.code
self.assertEqual(res, 400)

res = None
try:
yield self.handler.update_version(self.local_user, version, {
"algorithm": "m.megolm_backup.v1",
"auth_data": "revised_first_version_auth_data",
"version": "incorrect"
})
except errors.SynapseError as e:
res = e.code
self.assertEqual(res, 400)

@defer.inlineCallbacks
def test_delete_missing_version(self):
"""Check that we get a 404 on deleting nonexistent versions
Expand Down