Keep fallback key marked as used if it's re-uploaded (#11382)
This commit is contained in:
parent
e2e9bea1ce
commit
eca7cffb73
|
@ -0,0 +1 @@
|
||||||
|
Keep fallback key marked as used if it's re-uploaded.
|
|
@ -408,29 +408,58 @@ class EndToEndKeyWorkerStore(EndToEndKeyBackgroundStore):
|
||||||
fallback_keys: the keys to set. This is a map from key ID (which is
|
fallback_keys: the keys to set. This is a map from key ID (which is
|
||||||
of the form "algorithm:id") to key data.
|
of the form "algorithm:id") to key data.
|
||||||
"""
|
"""
|
||||||
|
await self.db_pool.runInteraction(
|
||||||
|
"set_e2e_fallback_keys_txn",
|
||||||
|
self._set_e2e_fallback_keys_txn,
|
||||||
|
user_id,
|
||||||
|
device_id,
|
||||||
|
fallback_keys,
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.invalidate_cache_and_stream(
|
||||||
|
"get_e2e_unused_fallback_key_types", (user_id, device_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _set_e2e_fallback_keys_txn(
|
||||||
|
self, txn: Connection, user_id: str, device_id: str, fallback_keys: JsonDict
|
||||||
|
) -> None:
|
||||||
# fallback_keys will usually only have one item in it, so using a for
|
# fallback_keys will usually only have one item in it, so using a for
|
||||||
# loop (as opposed to calling simple_upsert_many_txn) won't be too bad
|
# loop (as opposed to calling simple_upsert_many_txn) won't be too bad
|
||||||
# FIXME: make sure that only one key per algorithm is uploaded
|
# FIXME: make sure that only one key per algorithm is uploaded
|
||||||
for key_id, fallback_key in fallback_keys.items():
|
for key_id, fallback_key in fallback_keys.items():
|
||||||
algorithm, key_id = key_id.split(":", 1)
|
algorithm, key_id = key_id.split(":", 1)
|
||||||
await self.db_pool.simple_upsert(
|
old_key_json = self.db_pool.simple_select_one_onecol_txn(
|
||||||
"e2e_fallback_keys_json",
|
txn,
|
||||||
|
table="e2e_fallback_keys_json",
|
||||||
keyvalues={
|
keyvalues={
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
"device_id": device_id,
|
"device_id": device_id,
|
||||||
"algorithm": algorithm,
|
"algorithm": algorithm,
|
||||||
},
|
},
|
||||||
values={
|
retcol="key_json",
|
||||||
"key_id": key_id,
|
allow_none=True,
|
||||||
"key_json": json_encoder.encode(fallback_key),
|
|
||||||
"used": False,
|
|
||||||
},
|
|
||||||
desc="set_e2e_fallback_key",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.invalidate_cache_and_stream(
|
new_key_json = encode_canonical_json(fallback_key).decode("utf-8")
|
||||||
"get_e2e_unused_fallback_key_types", (user_id, device_id)
|
|
||||||
)
|
# If the uploaded key is the same as the current fallback key,
|
||||||
|
# don't do anything. This prevents marking the key as unused if it
|
||||||
|
# was already used.
|
||||||
|
if old_key_json != new_key_json:
|
||||||
|
self.db_pool.simple_upsert_txn(
|
||||||
|
txn,
|
||||||
|
table="e2e_fallback_keys_json",
|
||||||
|
keyvalues={
|
||||||
|
"user_id": user_id,
|
||||||
|
"device_id": device_id,
|
||||||
|
"algorithm": algorithm,
|
||||||
|
},
|
||||||
|
values={
|
||||||
|
"key_id": key_id,
|
||||||
|
"key_json": json_encoder.encode(fallback_key),
|
||||||
|
"used": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
@cached(max_entries=10000)
|
@cached(max_entries=10000)
|
||||||
async def get_e2e_unused_fallback_key_types(
|
async def get_e2e_unused_fallback_key_types(
|
||||||
|
|
|
@ -162,6 +162,7 @@ class E2eKeysHandlerTestCase(unittest.HomeserverTestCase):
|
||||||
local_user = "@boris:" + self.hs.hostname
|
local_user = "@boris:" + self.hs.hostname
|
||||||
device_id = "xyz"
|
device_id = "xyz"
|
||||||
fallback_key = {"alg1:k1": "key1"}
|
fallback_key = {"alg1:k1": "key1"}
|
||||||
|
fallback_key2 = {"alg1:k2": "key2"}
|
||||||
otk = {"alg1:k2": "key2"}
|
otk = {"alg1:k2": "key2"}
|
||||||
|
|
||||||
# we shouldn't have any unused fallback keys yet
|
# we shouldn't have any unused fallback keys yet
|
||||||
|
@ -213,6 +214,35 @@ class E2eKeysHandlerTestCase(unittest.HomeserverTestCase):
|
||||||
{"failures": {}, "one_time_keys": {local_user: {device_id: fallback_key}}},
|
{"failures": {}, "one_time_keys": {local_user: {device_id: fallback_key}}},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# re-uploading the same fallback key should still result in no unused fallback
|
||||||
|
# keys
|
||||||
|
self.get_success(
|
||||||
|
self.handler.upload_keys_for_user(
|
||||||
|
local_user,
|
||||||
|
device_id,
|
||||||
|
{"org.matrix.msc2732.fallback_keys": fallback_key},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
res = self.get_success(
|
||||||
|
self.store.get_e2e_unused_fallback_key_types(local_user, device_id)
|
||||||
|
)
|
||||||
|
self.assertEqual(res, [])
|
||||||
|
|
||||||
|
# uploading a new fallback key should result in an unused fallback key
|
||||||
|
self.get_success(
|
||||||
|
self.handler.upload_keys_for_user(
|
||||||
|
local_user,
|
||||||
|
device_id,
|
||||||
|
{"org.matrix.msc2732.fallback_keys": fallback_key2},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
res = self.get_success(
|
||||||
|
self.store.get_e2e_unused_fallback_key_types(local_user, device_id)
|
||||||
|
)
|
||||||
|
self.assertEqual(res, ["alg1"])
|
||||||
|
|
||||||
# if the user uploads a one-time key, the next claim should fetch the
|
# if the user uploads a one-time key, the next claim should fetch the
|
||||||
# one-time key, and then go back to the fallback
|
# one-time key, and then go back to the fallback
|
||||||
self.get_success(
|
self.get_success(
|
||||||
|
@ -238,7 +268,7 @@ class E2eKeysHandlerTestCase(unittest.HomeserverTestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
res,
|
res,
|
||||||
{"failures": {}, "one_time_keys": {local_user: {device_id: fallback_key}}},
|
{"failures": {}, "one_time_keys": {local_user: {device_id: fallback_key2}}},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_replace_master_key(self):
|
def test_replace_master_key(self):
|
||||||
|
|
Loading…
Reference in New Issue