Compare commits

...

22 commits

Author SHA1 Message Date
Hubert Chathi 1e2d261510 comment fixes 2019-11-27 15:44:28 -05:00
Hubert Chathi a2a3f75824 Merge branch 'develop' into uhoreg/e2e_backup_hash 2019-11-27 15:33:29 -05:00
Hubert Chathi 1e6e3e98a3 apply suggestions from review 2019-11-27 15:27:42 -05:00
Hubert Chathi 3991e7891a drop unsigned
because it looks like PostgreSQL wants the data type to be one word
2019-11-06 22:05:16 -05:00
Hubert Chathi a02574682b PostgreSQL doesn't like INTEGER UNSIGNED 2019-11-06 21:40:15 -05:00
Hubert Chathi 8721067448 fix unit test to use new API
and improve docstring
2019-11-06 20:47:14 -05:00
Hubert Chathi 00fa0c9f14 update the changelog too 2019-11-06 15:04:24 -05:00
Hubert Chathi 744d744916 black 2019-11-06 14:54:49 -05:00
Hubert Chathi 160f434e13 rename hash to etag, to match MSC 2019-11-06 14:45:20 -05:00
Hubert Chathi a5a59ab8ac Merge branch 'develop' into uhoreg/e2e_backup_hash 2019-11-06 11:59:22 -05:00
Hubert Chathi f589390842 fix merge conflict 2019-09-04 19:04:58 -04:00
Hubert Chathi c75ad8a6d0 Merge branch 'develop' into uhoreg/e2e_backup_hash 2019-09-04 18:45:09 -04:00
Hubert Chathi 438c8398ab black 2019-08-31 16:30:37 -07:00
Hubert Chathi 0dc4c4e1ce apply suggestions from PR review 2019-08-31 16:20:28 -07:00
Hubert Chathi e9d587d0d8
Apply suggestions from code review
Co-Authored-By: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2019-08-31 15:46:49 -07:00
Hubert Chathi 72fb8ac9c8 black 2019-08-15 20:59:16 -07:00
Hubert Chathi d081883f1e add count and hash when deleting keys 2019-08-15 20:26:08 -07:00
Hubert Chathi 0355a75285 Merge branch 'develop' into uhoreg/e2e_backup_hash 2019-08-15 20:00:37 -07:00
Hubert Chathi b0fd375e83 fix postgres compatibility 2019-08-14 22:56:07 -07:00
Hubert Chathi a1b0cbc262 fix formatting 2019-08-14 22:42:20 -07:00
Hubert Chathi 049a93901c add changelog 2019-08-14 22:38:57 -07:00
Hubert Chathi 84abdfdb30 add hash and count to key backup endpoints 2019-08-14 17:54:50 -07:00
7 changed files with 295 additions and 122 deletions

1
changelog.d/5858.feature Normal file
View file

@ -0,0 +1 @@
Add etag and count fields to key backup endpoints to help clients guess if there are new keys.

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2017, 2018 New Vector Ltd # Copyright 2017, 2018 New Vector Ltd
# Copyright 2019 Matrix.org Foundation C.I.C.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -103,14 +104,35 @@ class E2eRoomKeysHandler(object):
rooms rooms
session_id(string): session ID to delete keys for, for None to delete keys session_id(string): session ID to delete keys for, for None to delete keys
for all sessions for all sessions
Raises:
NotFoundError: if the backup version does not exist
Returns: Returns:
A deferred of the deletion transaction A dict containing the count and etag for the backup version
""" """
# lock for consistency with uploading # lock for consistency with uploading
with (yield self._upload_linearizer.queue(user_id)): with (yield self._upload_linearizer.queue(user_id)):
# make sure the backup version exists
try:
version_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
yield self.store.delete_e2e_room_keys(user_id, version, room_id, session_id) yield self.store.delete_e2e_room_keys(user_id, version, room_id, session_id)
version_etag = version_info["etag"] + 1
yield self.store.update_e2e_room_keys_version(
user_id, version, None, version_etag
)
count = yield self.store.count_e2e_room_keys(user_id, version)
return {"etag": str(version_etag), "count": count}
@trace @trace
@defer.inlineCallbacks @defer.inlineCallbacks
def upload_room_keys(self, user_id, version, room_keys): def upload_room_keys(self, user_id, version, room_keys):
@ -138,6 +160,9 @@ class E2eRoomKeysHandler(object):
} }
} }
Returns:
A dict containing the count and etag for the backup version
Raises: Raises:
NotFoundError: if there are no versions defined NotFoundError: if there are no versions defined
RoomKeysVersionError: if the uploaded version is not the current version RoomKeysVersionError: if the uploaded version is not the current version
@ -171,59 +196,62 @@ class E2eRoomKeysHandler(object):
else: else:
raise raise
# go through the room_keys. # Fetch any existing room keys for the sessions that have been
# XXX: this should/could be done concurrently, given we're in a lock. # submitted. Then compare them with the submitted keys. If the
# key is new, insert it; if the key should be updated, then update
# it; otherwise, drop it.
existing_keys = yield self.store.get_e2e_room_keys_multi(
user_id, version, room_keys["rooms"]
)
to_insert = [] # batch the inserts together
changed = False # if anything has changed, we need to update the etag
for room_id, room in iteritems(room_keys["rooms"]): for room_id, room in iteritems(room_keys["rooms"]):
for session_id, session in iteritems(room["sessions"]): for session_id, room_key in iteritems(room["sessions"]):
yield self._upload_room_key( log_kv(
user_id, version, room_id, session_id, session {
"message": "Trying to upload room key",
"room_id": room_id,
"session_id": session_id,
"user_id": user_id,
}
) )
current_room_key = existing_keys.get(room_id, {}).get(session_id)
if current_room_key:
if self._should_replace_room_key(current_room_key, room_key):
log_kv({"message": "Replacing room key."})
# updates are done one at a time in the DB, so send
# updates right away rather than batching them up,
# like we do with the inserts
yield self.store.update_e2e_room_key(
user_id, version, room_id, session_id, room_key
)
changed = True
else:
log_kv({"message": "Not replacing room_key."})
else:
log_kv(
{
"message": "Room key not found.",
"room_id": room_id,
"user_id": user_id,
}
)
log_kv({"message": "Replacing room key."})
to_insert.append((room_id, session_id, room_key))
changed = True
@defer.inlineCallbacks if len(to_insert):
def _upload_room_key(self, user_id, version, room_id, session_id, room_key): yield self.store.add_e2e_room_keys(user_id, version, to_insert)
"""Upload a given room_key for a given room and session into a given
version of the backup. Merges the key with any which might already exist.
Args: version_etag = version_info["etag"]
user_id(str): the user whose backup we're setting if changed:
version(str): the version ID of the backup we're updating version_etag = version_etag + 1
room_id(str): the ID of the room whose keys we're setting yield self.store.update_e2e_room_keys_version(
session_id(str): the session whose room_key we're setting user_id, version, None, version_etag
room_key(dict): the room_key being set
"""
log_kv(
{
"message": "Trying to upload room key",
"room_id": room_id,
"session_id": session_id,
"user_id": user_id,
}
)
# get the room_key for this particular row
current_room_key = None
try:
current_room_key = yield self.store.get_e2e_room_key(
user_id, version, room_id, session_id
)
except StoreError as e:
if e.code == 404:
log_kv(
{
"message": "Room key not found.",
"room_id": room_id,
"user_id": user_id,
}
) )
else:
raise
if self._should_replace_room_key(current_room_key, room_key): count = yield self.store.count_e2e_room_keys(user_id, version)
log_kv({"message": "Replacing room key."}) return {"etag": str(version_etag), "count": count}
yield self.store.set_e2e_room_key(
user_id, version, room_id, session_id, room_key
)
else:
log_kv({"message": "Not replacing room_key."})
@staticmethod @staticmethod
def _should_replace_room_key(current_room_key, room_key): def _should_replace_room_key(current_room_key, room_key):
@ -314,6 +342,8 @@ class E2eRoomKeysHandler(object):
raise NotFoundError("Unknown backup version") raise NotFoundError("Unknown backup version")
else: else:
raise raise
res["count"] = yield self.store.count_e2e_room_keys(user_id, res["version"])
return res return res
@trace @trace

View file

@ -134,8 +134,8 @@ class RoomKeysServlet(RestServlet):
if room_id: if room_id:
body = {"rooms": {room_id: body}} body = {"rooms": {room_id: body}}
yield self.e2e_room_keys_handler.upload_room_keys(user_id, version, body) ret = yield self.e2e_room_keys_handler.upload_room_keys(user_id, version, body)
return 200, {} return 200, ret
@defer.inlineCallbacks @defer.inlineCallbacks
def on_GET(self, request, room_id, session_id): def on_GET(self, request, room_id, session_id):
@ -239,10 +239,10 @@ class RoomKeysServlet(RestServlet):
user_id = requester.user.to_string() user_id = requester.user.to_string()
version = parse_string(request, "version") version = parse_string(request, "version")
yield self.e2e_room_keys_handler.delete_room_keys( ret = yield self.e2e_room_keys_handler.delete_room_keys(
user_id, version, room_id, session_id user_id, version, room_id, session_id
) )
return 200, {} return 200, ret
class RoomKeysNewVersionServlet(RestServlet): class RoomKeysNewVersionServlet(RestServlet):

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2017 New Vector Ltd # Copyright 2017 New Vector Ltd
# Copyright 2019 Matrix.org Foundation C.I.C.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -24,49 +25,8 @@ from synapse.storage._base import SQLBaseStore
class EndToEndRoomKeyStore(SQLBaseStore): class EndToEndRoomKeyStore(SQLBaseStore):
@defer.inlineCallbacks @defer.inlineCallbacks
def get_e2e_room_key(self, user_id, version, room_id, session_id): def update_e2e_room_key(self, user_id, version, room_id, session_id, room_key):
"""Get the encrypted E2E room key for a given session from a given """Replaces the encrypted E2E room key for a given session in a given backup
backup version of room_keys. We only store the 'best' room key for a given
session at a given time, as determined by the handler.
Args:
user_id(str): the user whose backup we're querying
version(str): the version ID of the backup for the set of keys we're querying
room_id(str): the ID of the room whose keys we're querying.
This is a bit redundant as it's implied by the session_id, but
we include for consistency with the rest of the API.
session_id(str): the session whose room_key we're querying.
Returns:
A deferred dict giving the session_data and message metadata for
this room key.
"""
row = yield self._simple_select_one(
table="e2e_room_keys",
keyvalues={
"user_id": user_id,
"version": version,
"room_id": room_id,
"session_id": session_id,
},
retcols=(
"first_message_index",
"forwarded_count",
"is_verified",
"session_data",
),
desc="get_e2e_room_key",
)
row["session_data"] = json.loads(row["session_data"])
return row
@defer.inlineCallbacks
def set_e2e_room_key(self, user_id, version, room_id, session_id, room_key):
"""Replaces or inserts the encrypted E2E room key for a given session in
a given backup
Args: Args:
user_id(str): the user whose backup we're setting user_id(str): the user whose backup we're setting
@ -78,7 +38,7 @@ class EndToEndRoomKeyStore(SQLBaseStore):
StoreError StoreError
""" """
yield self._simple_upsert( yield self._simple_update_one(
table="e2e_room_keys", table="e2e_room_keys",
keyvalues={ keyvalues={
"user_id": user_id, "user_id": user_id,
@ -86,21 +46,51 @@ class EndToEndRoomKeyStore(SQLBaseStore):
"room_id": room_id, "room_id": room_id,
"session_id": session_id, "session_id": session_id,
}, },
values={ updatevalues={
"first_message_index": room_key["first_message_index"], "first_message_index": room_key["first_message_index"],
"forwarded_count": room_key["forwarded_count"], "forwarded_count": room_key["forwarded_count"],
"is_verified": room_key["is_verified"], "is_verified": room_key["is_verified"],
"session_data": json.dumps(room_key["session_data"]), "session_data": json.dumps(room_key["session_data"]),
}, },
lock=False, desc="update_e2e_room_key",
) )
log_kv(
{ @defer.inlineCallbacks
"message": "Set room key", def add_e2e_room_keys(self, user_id, version, room_keys):
"room_id": room_id, """Bulk add room keys to a given backup.
"session_id": session_id,
"room_key": room_key, Args:
} user_id (str): the user whose backup we're adding to
version (str): the version ID of the backup for the set of keys we're adding to
room_keys (iterable[(str, str, dict)]): the keys to add, in the form
(roomID, sessionID, keyData)
"""
values = []
for (room_id, session_id, room_key) in room_keys:
values.append(
{
"user_id": user_id,
"version": version,
"room_id": room_id,
"session_id": session_id,
"first_message_index": room_key["first_message_index"],
"forwarded_count": room_key["forwarded_count"],
"is_verified": room_key["is_verified"],
"session_data": json.dumps(room_key["session_data"]),
}
)
log_kv(
{
"message": "Set room key",
"room_id": room_id,
"session_id": session_id,
"room_key": room_key,
}
)
yield self._simple_insert_many(
table="e2e_room_keys", values=values, desc="add_e2e_room_keys"
) )
@trace @trace
@ -110,11 +100,11 @@ class EndToEndRoomKeyStore(SQLBaseStore):
room, or a given session. room, or a given session.
Args: Args:
user_id(str): the user whose backup we're querying user_id (str): the user whose backup we're querying
version(str): the version ID of the backup for the set of keys we're querying version (str): the version ID of the backup for the set of keys we're querying
room_id(str): Optional. the ID of the room whose keys we're querying, if any. room_id (str): Optional. the ID of the room whose keys we're querying, if any.
If not specified, we return the keys for all the rooms in the backup. If not specified, we return the keys for all the rooms in the backup.
session_id(str): Optional. the session whose room_key we're querying, if any. session_id (str): Optional. the session whose room_key we're querying, if any.
If specified, we also require the room_id to be specified. If specified, we also require the room_id to be specified.
If not specified, we return all the keys in this version of If not specified, we return all the keys in this version of
the backup (or for the specified room) the backup (or for the specified room)
@ -162,6 +152,95 @@ class EndToEndRoomKeyStore(SQLBaseStore):
return sessions return sessions
def get_e2e_room_keys_multi(self, user_id, version, room_keys):
"""Get multiple room keys at a time. The difference between this function and
get_e2e_room_keys is that this function can be used to retrieve
multiple specific keys at a time, whereas get_e2e_room_keys is used for
getting all the keys in a backup version, all the keys for a room, or a
specific key.
Args:
user_id (str): the user whose backup we're querying
version (str): the version ID of the backup we're querying about
room_keys (dict[str, dict[str, iterable[str]]]): a map from
room ID -> {"session": [session ids]} indicating the session IDs
that we want to query
Returns:
Deferred[dict[str, dict[str, dict]]]: a map of room IDs to session IDs to room key
"""
return self.runInteraction(
"get_e2e_room_keys_multi",
self._get_e2e_room_keys_multi_txn,
user_id,
version,
room_keys,
)
@staticmethod
def _get_e2e_room_keys_multi_txn(txn, user_id, version, room_keys):
if not room_keys:
return {}
where_clauses = []
params = [user_id, version]
for room_id, room in room_keys.items():
sessions = list(room["sessions"])
if not sessions:
continue
params.append(room_id)
params.extend(sessions)
where_clauses.append(
"(room_id = ? AND session_id IN (%s))"
% (",".join(["?" for _ in sessions]),)
)
# check if we're actually querying something
if not where_clauses:
return {}
sql = """
SELECT room_id, session_id, first_message_index, forwarded_count,
is_verified, session_data
FROM e2e_room_keys
WHERE user_id = ? AND version = ? AND (%s)
""" % (
" OR ".join(where_clauses)
)
txn.execute(sql, params)
ret = {}
for row in txn:
room_id = row[0]
session_id = row[1]
ret.setdefault(room_id, {})
ret[room_id][session_id] = {
"first_message_index": row[2],
"forwarded_count": row[3],
"is_verified": row[4],
"session_data": json.loads(row[5]),
}
return ret
def count_e2e_room_keys(self, user_id, version):
"""Get the number of keys in a backup version.
Args:
user_id (str): the user whose backup we're querying
version (str): the version ID of the backup we're querying about
"""
return self._simple_select_one_onecol(
table="e2e_room_keys",
keyvalues={"user_id": user_id, "version": version},
retcol="COUNT(*)",
desc="count_e2e_room_keys",
)
@trace @trace
@defer.inlineCallbacks @defer.inlineCallbacks
def delete_e2e_room_keys(self, user_id, version, room_id=None, session_id=None): def delete_e2e_room_keys(self, user_id, version, room_id=None, session_id=None):
@ -219,6 +298,7 @@ class EndToEndRoomKeyStore(SQLBaseStore):
version(str) version(str)
algorithm(str) algorithm(str)
auth_data(object): opaque dict supplied by the client auth_data(object): opaque dict supplied by the client
etag(int): tag of the keys in the backup
""" """
def _get_e2e_room_keys_version_info_txn(txn): def _get_e2e_room_keys_version_info_txn(txn):
@ -236,10 +316,12 @@ class EndToEndRoomKeyStore(SQLBaseStore):
txn, txn,
table="e2e_room_keys_versions", table="e2e_room_keys_versions",
keyvalues={"user_id": user_id, "version": this_version, "deleted": 0}, keyvalues={"user_id": user_id, "version": this_version, "deleted": 0},
retcols=("version", "algorithm", "auth_data"), retcols=("version", "algorithm", "auth_data", "etag"),
) )
result["auth_data"] = json.loads(result["auth_data"]) result["auth_data"] = json.loads(result["auth_data"])
result["version"] = str(result["version"]) result["version"] = str(result["version"])
if result["etag"] is None:
result["etag"] = 0
return result return result
return self.runInteraction( return self.runInteraction(
@ -288,21 +370,33 @@ class EndToEndRoomKeyStore(SQLBaseStore):
) )
@trace @trace
def update_e2e_room_keys_version(self, user_id, version, info): def update_e2e_room_keys_version(
self, user_id, version, info=None, version_etag=None
):
"""Update a given backup version """Update a given backup version
Args: Args:
user_id(str): the user whose backup version we're updating user_id(str): the user whose backup version we're updating
version(str): the version ID of the 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 info (dict): the new backup version info to store. If None, then
the backup version info is not updated
version_etag (Optional[int]): etag of the keys in the backup. If
None, then the etag is not updated
""" """
updatevalues = {}
return self._simple_update( if info is not None and "auth_data" in info:
table="e2e_room_keys_versions", updatevalues["auth_data"] = json.dumps(info["auth_data"])
keyvalues={"user_id": user_id, "version": version}, if version_etag is not None:
updatevalues={"auth_data": json.dumps(info["auth_data"])}, updatevalues["etag"] = version_etag
desc="update_e2e_room_keys_version",
) if updatevalues:
return self._simple_update(
table="e2e_room_keys_versions",
keyvalues={"user_id": user_id, "version": version},
updatevalues=updatevalues,
desc="update_e2e_room_keys_version",
)
@trace @trace
def delete_e2e_room_keys_version(self, user_id, version=None): def delete_e2e_room_keys_version(self, user_id, version=None):

View file

@ -0,0 +1,17 @@
/* Copyright 2019 Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- store the current etag of backup version
ALTER TABLE e2e_room_keys_versions ADD COLUMN etag BIGINT;

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2016 OpenMarket Ltd # Copyright 2016 OpenMarket Ltd
# Copyright 2017 New Vector Ltd # Copyright 2017 New Vector Ltd
# Copyright 2019 Matrix.org Foundation C.I.C.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -94,23 +95,29 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
# check we can retrieve it as the current version # check we can retrieve it as the current version
res = yield self.handler.get_version_info(self.local_user) res = yield self.handler.get_version_info(self.local_user)
version_etag = res["etag"]
del res["etag"]
self.assertDictEqual( self.assertDictEqual(
res, res,
{ {
"version": "1", "version": "1",
"algorithm": "m.megolm_backup.v1", "algorithm": "m.megolm_backup.v1",
"auth_data": "first_version_auth_data", "auth_data": "first_version_auth_data",
"count": 0,
}, },
) )
# check we can retrieve it as a specific version # check we can retrieve it as a specific version
res = yield self.handler.get_version_info(self.local_user, "1") res = yield self.handler.get_version_info(self.local_user, "1")
self.assertEqual(res["etag"], version_etag)
del res["etag"]
self.assertDictEqual( self.assertDictEqual(
res, res,
{ {
"version": "1", "version": "1",
"algorithm": "m.megolm_backup.v1", "algorithm": "m.megolm_backup.v1",
"auth_data": "first_version_auth_data", "auth_data": "first_version_auth_data",
"count": 0,
}, },
) )
@ -126,12 +133,14 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
# check we can retrieve it as the current version # check we can retrieve it as the current version
res = yield self.handler.get_version_info(self.local_user) res = yield self.handler.get_version_info(self.local_user)
del res["etag"]
self.assertDictEqual( self.assertDictEqual(
res, res,
{ {
"version": "2", "version": "2",
"algorithm": "m.megolm_backup.v1", "algorithm": "m.megolm_backup.v1",
"auth_data": "second_version_auth_data", "auth_data": "second_version_auth_data",
"count": 0,
}, },
) )
@ -158,12 +167,14 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
# check we can retrieve it as the current version # check we can retrieve it as the current version
res = yield self.handler.get_version_info(self.local_user) res = yield self.handler.get_version_info(self.local_user)
del res["etag"]
self.assertDictEqual( self.assertDictEqual(
res, res,
{ {
"algorithm": "m.megolm_backup.v1", "algorithm": "m.megolm_backup.v1",
"auth_data": "revised_first_version_auth_data", "auth_data": "revised_first_version_auth_data",
"version": version, "version": version,
"count": 0,
}, },
) )
@ -207,12 +218,14 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
# check we can retrieve it as the current version # check we can retrieve it as the current version
res = yield self.handler.get_version_info(self.local_user) res = yield self.handler.get_version_info(self.local_user)
del res["etag"] # etag is opaque, so don't test its contents
self.assertDictEqual( self.assertDictEqual(
res, res,
{ {
"algorithm": "m.megolm_backup.v1", "algorithm": "m.megolm_backup.v1",
"auth_data": "revised_first_version_auth_data", "auth_data": "revised_first_version_auth_data",
"version": version, "version": version,
"count": 0,
}, },
) )
@ -409,6 +422,11 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
yield self.handler.upload_room_keys(self.local_user, version, room_keys) yield self.handler.upload_room_keys(self.local_user, version, room_keys)
# get the etag to compare to future versions
res = yield self.handler.get_version_info(self.local_user)
backup_etag = res["etag"]
self.assertEqual(res["count"], 1)
new_room_keys = copy.deepcopy(room_keys) new_room_keys = copy.deepcopy(room_keys)
new_room_key = new_room_keys["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"] new_room_key = new_room_keys["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"]
@ -423,6 +441,10 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
"SSBBTSBBIEZJU0gK", "SSBBTSBBIEZJU0gK",
) )
# the etag should be the same since the session did not change
res = yield self.handler.get_version_info(self.local_user)
self.assertEqual(res["etag"], backup_etag)
# test that marking the session as verified however /does/ replace it # test that marking the session as verified however /does/ replace it
new_room_key["is_verified"] = True new_room_key["is_verified"] = True
yield self.handler.upload_room_keys(self.local_user, version, new_room_keys) yield self.handler.upload_room_keys(self.local_user, version, new_room_keys)
@ -432,6 +454,11 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
res["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"]["session_data"], "new" res["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"]["session_data"], "new"
) )
# the etag should NOT be equal now, since the key changed
res = yield self.handler.get_version_info(self.local_user)
self.assertNotEqual(res["etag"], backup_etag)
backup_etag = res["etag"]
# test that a session with a higher forwarded_count doesn't replace one # test that a session with a higher forwarded_count doesn't replace one
# with a lower forwarding count # with a lower forwarding count
new_room_key["forwarded_count"] = 2 new_room_key["forwarded_count"] = 2
@ -443,6 +470,10 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
res["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"]["session_data"], "new" res["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"]["session_data"], "new"
) )
# the etag should be the same since the session did not change
res = yield self.handler.get_version_info(self.local_user)
self.assertEqual(res["etag"], backup_etag)
# TODO: check edge cases as well as the common variations here # TODO: check edge cases as well as the common variations here
@defer.inlineCallbacks @defer.inlineCallbacks

View file

@ -39,8 +39,8 @@ class E2eRoomKeysHandlerTestCase(unittest.HomeserverTestCase):
) )
self.get_success( self.get_success(
self.store.set_e2e_room_key( self.store.add_e2e_room_keys(
"user_id", version1, "room", "session", room_key "user_id", version1, [("room", "session", room_key)]
) )
) )
@ -51,8 +51,8 @@ class E2eRoomKeysHandlerTestCase(unittest.HomeserverTestCase):
) )
self.get_success( self.get_success(
self.store.set_e2e_room_key( self.store.add_e2e_room_keys(
"user_id", version2, "room", "session", room_key "user_id", version2, [("room", "session", room_key)]
) )
) )