Skip to content
Draft
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
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Contributor Guide

Synapse is a Python application that has Rust modules via pyo3 for performance.

## Dev Environment Tips
- Source code is primarily in `synapse/`, tests are in `tests/`.
- Run `poetry install --dev` to install development python dependencies. This will also build and install the Synapse rust code.
- Use `./scripts-dev/lint.sh` to lint the codebase (this attempts to fix issues as well). This should be run and produce no errors before every commit.

## Testing Instructions
- Find the CI plan in the .github/workflows folder.
- Use `poetry run trial tests` to run all unit tests, or `poetry run trial tests.metrics.test_phone_home_stats.PhoneHomeStatsTestCase` (for example) to run a single test case. The commit should pass all tests before you merge.
- Some typing warnings are expected currently. Fix any test or type *errors* until the whole suite is green.
- Add or update relevant tests for the code you change, even if nobody asked.
14 changes: 11 additions & 3 deletions synapse/storage/databases/main/end_to_end_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from canonicaljson import encode_canonical_json

from synapse.api.constants import DeviceKeyAlgorithms
from synapse.api.errors import Codes, StoreError
from synapse.appservice import (
TransactionOneTimeKeysCount,
TransactionUnusedFallbackKeys,
Expand Down Expand Up @@ -1619,9 +1620,16 @@ def _set_e2e_device_keys_txn(
# returns unicode while encode_canonical_json returns bytes.
new_key_json = encode_canonical_json(device_keys).decode("utf-8")

if old_key_json == new_key_json:
log_kv({"Message": "Device key already stored."})
return False
if old_key_json is not None:
if old_key_json == new_key_json:
log_kv({"Message": "Device key already stored."})
return False

raise StoreError(
400,
"Device keys for this device have already been uploaded",
Codes.INVALID_PARAM,
)

self.db_pool.simple_upsert_txn(
txn,
Expand Down
42 changes: 42 additions & 0 deletions tests/handlers/test_e2e_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,48 @@ def test_change_one_time_keys(self) -> None:
SynapseError,
)

def test_change_device_keys(self) -> None:
"""uploading different device keys should fail"""

local_user = "@boris:" + self.hs.hostname
device_id = "xyz"

keys1 = {
"user_id": local_user,
"device_id": device_id,
"algorithms": ["m.olm.curve25519-aes-sha2"],
"keys": {"ed25519:" + device_id: "key1"},
}

keys2 = {
"user_id": local_user,
"device_id": device_id,
"algorithms": ["m.olm.curve25519-aes-sha2"],
"keys": {"ed25519:" + device_id: "key2"},
}

# initial upload succeeds
self.get_success(
self.handler.upload_keys_for_user(
local_user, device_id, {"device_keys": keys1}
)
)

# uploading the same keys again should be fine
self.get_success(
self.handler.upload_keys_for_user(
local_user, device_id, {"device_keys": keys1}
)
)

# uploading different keys should fail
self.get_failure(
self.handler.upload_keys_for_user(
local_user, device_id, {"device_keys": keys2}
),
SynapseError,
)

def test_claim_one_time_key(self) -> None:
local_user = "@boris:" + self.hs.hostname
device_id = "xyz"
Expand Down
15 changes: 15 additions & 0 deletions tests/storage/test_end_to_end_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from twisted.test.proto_helpers import MemoryReactor

from synapse.api.errors import StoreError
from synapse.server import HomeServer
from synapse.util import Clock

Expand Down Expand Up @@ -65,6 +66,20 @@ def test_reupload_key(self) -> None:
)
self.assertFalse(changed)

def test_change_key_rejected(self) -> None:
now = 1470174257070
json = {"key": "value"}
json2 = {"key": "other"}

self.get_success(self.store.store_device("user", "device", None))

self.get_success(self.store.set_e2e_device_keys("user", "device", now, json))

self.get_failure(
self.store.set_e2e_device_keys("user", "device", now, json2),
StoreError,
)

def test_get_key_with_device_name(self) -> None:
now = 1470174257070
json = {"key": "value"}
Expand Down