0
0
Fork 1
mirror of https://mau.dev/maunium/synapse.git synced 2024-12-14 14:03:54 +01:00

add etag and count to key backup endpoints (#5858)

This commit is contained in:
Hubert Chathi 2019-11-27 16:14:44 -05:00 committed by GitHub
parent 6f4a63df00
commit 0d27aba900
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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,26 +196,17 @@ 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
for room_id, room in iteritems(room_keys["rooms"]): # key is new, insert it; if the key should be updated, then update
for session_id, session in iteritems(room["sessions"]): # it; otherwise, drop it.
yield self._upload_room_key( existing_keys = yield self.store.get_e2e_room_keys_multi(
user_id, version, room_id, session_id, session user_id, version, room_keys["rooms"]
) )
to_insert = [] # batch the inserts together
@defer.inlineCallbacks changed = False # if anything has changed, we need to update the etag
def _upload_room_key(self, user_id, version, room_id, session_id, room_key): for room_id, room in iteritems(room_keys["rooms"]):
"""Upload a given room_key for a given room and session into a given for session_id, room_key in iteritems(room["sessions"]):
version of the backup. Merges the key with any which might already exist.
Args:
user_id(str): the user whose backup we're setting
version(str): the version ID of the backup we're updating
room_id(str): the ID of the room whose keys we're setting
session_id(str): the session whose room_key we're setting
room_key(dict): the room_key being set
"""
log_kv( log_kv(
{ {
"message": "Trying to upload room key", "message": "Trying to upload room key",
@ -199,14 +215,20 @@ class E2eRoomKeysHandler(object):
"user_id": user_id, "user_id": user_id,
} }
) )
# get the room_key for this particular row current_room_key = existing_keys.get(room_id, {}).get(session_id)
current_room_key = None if current_room_key:
try: if self._should_replace_room_key(current_room_key, room_key):
current_room_key = yield self.store.get_e2e_room_key( log_kv({"message": "Replacing room key."})
user_id, version, room_id, session_id # 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
) )
except StoreError as e: changed = True
if e.code == 404: else:
log_kv({"message": "Not replacing room_key."})
else:
log_kv( log_kv(
{ {
"message": "Room key not found.", "message": "Room key not found.",
@ -214,16 +236,22 @@ class E2eRoomKeysHandler(object):
"user_id": user_id, "user_id": user_id,
} }
) )
else:
raise
if self._should_replace_room_key(current_room_key, room_key):
log_kv({"message": "Replacing room key."}) log_kv({"message": "Replacing room key."})
yield self.store.set_e2e_room_key( to_insert.append((room_id, session_id, room_key))
user_id, version, room_id, session_id, room_key changed = True
if len(to_insert):
yield self.store.add_e2e_room_keys(user_id, version, to_insert)
version_etag = version_info["etag"]
if changed:
version_etag = version_etag + 1
yield self.store.update_e2e_room_keys_version(
user_id, version, None, version_etag
) )
else:
log_kv({"message": "Not replacing room_key."}) count = yield self.store.count_e2e_room_keys(user_id, version)
return {"etag": str(version_etag), "count": count}
@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,13 +46,39 @@ 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",
)
@defer.inlineCallbacks
def add_e2e_room_keys(self, user_id, version, room_keys):
"""Bulk add room keys to a given backup.
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( log_kv(
{ {
@ -103,6 +89,10 @@ class EndToEndRoomKeyStore(SQLBaseStore):
} }
) )
yield self._simple_insert_many(
table="e2e_room_keys", values=values, desc="add_e2e_room_keys"
)
@trace @trace
@defer.inlineCallbacks @defer.inlineCallbacks
def get_e2e_room_keys(self, user_id, version, room_id=None, session_id=None): def get_e2e_room_keys(self, user_id, version, room_id=None, session_id=None):
@ -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,19 +370,31 @@ 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 = {}
if info is not None and "auth_data" in info:
updatevalues["auth_data"] = json.dumps(info["auth_data"])
if version_etag is not None:
updatevalues["etag"] = version_etag
if updatevalues:
return self._simple_update( return self._simple_update(
table="e2e_room_keys_versions", table="e2e_room_keys_versions",
keyvalues={"user_id": user_id, "version": version}, keyvalues={"user_id": user_id, "version": version},
updatevalues={"auth_data": json.dumps(info["auth_data"])}, updatevalues=updatevalues,
desc="update_e2e_room_keys_version", desc="update_e2e_room_keys_version",
) )

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)]
) )
) )