Update for v2.
This commit is contained in:
@@ -125,6 +125,8 @@ class EventTypes:
|
||||
|
||||
Reaction: Final = "m.reaction"
|
||||
|
||||
Hub: Final = "m.room.hub"
|
||||
|
||||
|
||||
class ToDeviceEventTypes:
|
||||
RoomKeyRequest: Final = "m.room_key_request"
|
||||
|
||||
@@ -468,8 +468,13 @@ class RoomVersions:
|
||||
msc3989_redaction_rules=True, # Used by MSC3820
|
||||
linearized_matrix=False,
|
||||
)
|
||||
# Based on room version 11:
|
||||
#
|
||||
# - Enable MSC2176, MSC2175, MSC3989, MSC2174, MSC1767, MSC3821.
|
||||
# - Disable 'restricted' and 'knock_restricted' join rules.
|
||||
# - Mark as linearized.
|
||||
LINEARIZED = RoomVersion(
|
||||
"org.matrix.i-d.ralston-mimi-linearized-matrix.00",
|
||||
"org.matrix.i-d.ralston-mimi-linearized-matrix.02",
|
||||
RoomDisposition.UNSTABLE,
|
||||
EventFormatVersions.LINEARIZED,
|
||||
StateResolutionVersions.V2,
|
||||
@@ -479,13 +484,13 @@ class RoomVersions:
|
||||
limit_notifications_power_levels=True,
|
||||
msc2175_implicit_room_creator=True,
|
||||
msc2176_redaction_rules=True,
|
||||
msc3083_join_rules=True,
|
||||
msc3375_redaction_rules=True,
|
||||
msc3083_join_rules=False,
|
||||
msc3375_redaction_rules=False,
|
||||
msc2403_knocking=True,
|
||||
msc3389_relation_redactions=False,
|
||||
msc3787_knock_restricted_join_rule=True,
|
||||
msc3787_knock_restricted_join_rule=False,
|
||||
msc3667_int_only_power_levels=True,
|
||||
msc3821_redaction_rules=False,
|
||||
msc3821_redaction_rules=True,
|
||||
msc3931_push_features=(),
|
||||
msc3989_redaction_rules=True,
|
||||
linearized_matrix=True,
|
||||
|
||||
@@ -55,7 +55,7 @@ from synapse.types import (
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
# conditional imports to avoid import cycle
|
||||
from synapse.events import EventBase, FrozenLinearizedEvent
|
||||
from synapse.events import EventBase
|
||||
from synapse.events.builder import EventBuilder
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -126,20 +126,13 @@ def validate_event_for_room_version(event: "EventBase") -> None:
|
||||
raise AuthError(403, "Event not signed by sending server")
|
||||
|
||||
if event.format_version == EventFormatVersions.LINEARIZED:
|
||||
assert isinstance(event, FrozenLinearizedEvent)
|
||||
|
||||
# TODO Are these handling DAG-native events properly? Is the null-checks
|
||||
# a bypass?
|
||||
|
||||
# CHeck that the authorizing domain signed it.
|
||||
owner_server = event.owner_server
|
||||
if owner_server and not event.signatures.get(owner_server):
|
||||
raise AuthError(403, "Event not signed by owner server")
|
||||
|
||||
# If this is a delegated PDU, check the original domain signed it.
|
||||
delegated_server = event.delegated_server
|
||||
if delegated_server and not event.signatures.get(delegated_server):
|
||||
raise AuthError(403, "Event not signed by delegated server")
|
||||
# Check that the hub server signed it.
|
||||
hub_server = event.hub_server
|
||||
if hub_server and not event.signatures.get(hub_server):
|
||||
raise AuthError(403, "Event not signed by hub server")
|
||||
|
||||
is_invite_via_allow_rule = (
|
||||
event.room_version.msc3083_join_rules
|
||||
@@ -293,6 +286,13 @@ def check_state_dependent_auth_rules(
|
||||
|
||||
auth_dict = {(e.type, e.state_key): e for e in auth_events}
|
||||
|
||||
# If the event was sent from a hub server it must be the current hub server.
|
||||
if event.room_version.linearized_matrix:
|
||||
if event.hub_server:
|
||||
current_hub_server = get_hub_server(auth_dict)
|
||||
if current_hub_server != event.hub_server:
|
||||
raise AuthError(403, "Event sent from incorrect hub server.")
|
||||
|
||||
# additional check for m.federate
|
||||
creating_domain = get_domain_from_id(event.room_id)
|
||||
originating_domain = get_domain_from_id(event.sender)
|
||||
@@ -347,6 +347,12 @@ def check_state_dependent_auth_rules(
|
||||
logger.debug("Allowing! %s", event)
|
||||
return
|
||||
|
||||
# Hub events must be signed by the current hub.
|
||||
if event.type == EventTypes.Hub:
|
||||
current_hub_server = get_hub_server(auth_dict)
|
||||
if not event.signatures.get(current_hub_server):
|
||||
raise AuthError(403, "Unsigned hub event")
|
||||
|
||||
_can_send_event(event, auth_dict)
|
||||
|
||||
if event.type == EventTypes.PowerLevels:
|
||||
@@ -1081,6 +1087,16 @@ def _verify_third_party_invite(
|
||||
return False
|
||||
|
||||
|
||||
def get_hub_server(auth_events: StateMap["EventBase"]) -> str:
|
||||
# The current hub is the sender of the current hub event...
|
||||
hub_event = auth_events.get((EventTypes.Hub, ""), None)
|
||||
if not hub_event:
|
||||
# ...or the room creator if there is no hub event.
|
||||
hub_event = auth_events[(EventTypes.Create, "")]
|
||||
|
||||
return get_domain_from_id(hub_event.sender)
|
||||
|
||||
|
||||
def get_public_keys(invite_event: "EventBase") -> List[Dict[str, Any]]:
|
||||
public_keys = []
|
||||
if "public_key" in invite_event.content:
|
||||
@@ -1134,4 +1150,9 @@ def auth_types_for_event(
|
||||
)
|
||||
auth_types.add(key)
|
||||
|
||||
# Events sent from a hub server must reference the hub state-event, if one
|
||||
# exists.
|
||||
if event.hub_server:
|
||||
auth_types.add((EventTypes.Hub, ""))
|
||||
|
||||
return auth_types
|
||||
|
||||
@@ -335,12 +335,7 @@ class EventBase(metaclass=abc.ABCMeta):
|
||||
type: DictProperty[str] = DictProperty("type")
|
||||
user_id: DictProperty[str] = DictProperty("sender")
|
||||
# Should only matter for Linearized Matrix.
|
||||
owner_server: DictProperty[Optional[str]] = DefaultDictProperty(
|
||||
"owner_server", None
|
||||
)
|
||||
delegated_server: DictProperty[Optional[str]] = DefaultDictProperty(
|
||||
"delegated_server", None
|
||||
)
|
||||
hub_server: DictProperty[Optional[str]] = DefaultDictProperty("hub_server", None)
|
||||
|
||||
@property
|
||||
def event_id(self) -> str:
|
||||
@@ -615,31 +610,17 @@ class FrozenLinearizedEvent(FrozenEventV3):
|
||||
"""The domain which added this event to the DAG.
|
||||
|
||||
It could be the authorized server or the sender."""
|
||||
if self.delegated_server is not None:
|
||||
return self.delegated_server
|
||||
if self.hub_server is not None:
|
||||
return self.hub_server
|
||||
return super().pdu_domain
|
||||
|
||||
def get_linearized_pdu_json(self, /, delegated: bool) -> JsonDict:
|
||||
# if delegated and self.delegated_server is None:
|
||||
# # TODO Better error.
|
||||
# raise ValueError("Invalid")
|
||||
# TODO Is this form correct? Are there other fields?
|
||||
result = {
|
||||
"room_id": self.room_id,
|
||||
"type": self.type,
|
||||
# Should state key be left out if it isn't a state event?
|
||||
"state_key": self.state_key,
|
||||
"sender": self.sender,
|
||||
"origin_server_ts": self.origin_server_ts,
|
||||
# If we want the delegated version use the owner_server
|
||||
# if it exists.
|
||||
"owner_server": self.delegated_server,
|
||||
"content": self.content,
|
||||
"hashes": self.hashes,
|
||||
}
|
||||
if delegated and self.delegated_server:
|
||||
result["delegated_server"] = self.delegated_server
|
||||
return result
|
||||
def get_linearized_pdu_json(self) -> JsonDict:
|
||||
# Get the full PDU and then remove fields from it.
|
||||
pdu = self.get_pdu_json()
|
||||
pdu.pop("hashes")
|
||||
pdu.pop("auth_events")
|
||||
pdu.pop("prev_events")
|
||||
return pdu
|
||||
|
||||
|
||||
def _event_type_from_format_version(
|
||||
|
||||
@@ -87,6 +87,9 @@ class EventBuilder:
|
||||
_redacts: Optional[str] = None
|
||||
_origin_server_ts: Optional[int] = None
|
||||
|
||||
# TODO(LM) If Synapse is acting as a hub-server this should be itself.
|
||||
hub_server: Optional[str] = None
|
||||
|
||||
internal_metadata: _EventInternalMetadata = attr.Factory(
|
||||
lambda: _EventInternalMetadata({})
|
||||
)
|
||||
|
||||
@@ -65,9 +65,6 @@ def prune_event(event: EventBase) -> EventBase:
|
||||
type, state_key etc.
|
||||
"""
|
||||
|
||||
# TODO Check users of prune_event and prune_event_dict to ensure they shouldn't
|
||||
# be doing something with linearized matrix.
|
||||
|
||||
pruned_event_dict = prune_event_dict(event.room_version, event.get_dict())
|
||||
|
||||
from . import make_event_from_dict
|
||||
@@ -115,6 +112,9 @@ def prune_event_dict(room_version: RoomVersion, event_dict: JsonDict) -> JsonDic
|
||||
# Room versions from before MSC2176 had additional allowed keys.
|
||||
if not room_version.msc2176_redaction_rules:
|
||||
allowed_keys.extend(["prev_state", "membership"])
|
||||
# The hub server should not be redacted for linear matrix.
|
||||
if room_version.linearized_matrix:
|
||||
allowed_keys.append("hub_server")
|
||||
|
||||
# Room versions before MSC3989 kept the origin field.
|
||||
if not room_version.msc3989_redaction_rules:
|
||||
|
||||
@@ -21,7 +21,7 @@ from synapse.api.room_versions import EventFormatVersions, RoomVersion
|
||||
from synapse.crypto.event_signing import check_event_content_hash
|
||||
from synapse.crypto.keyring import Keyring
|
||||
from synapse.events import EventBase, FrozenLinearizedEvent, make_event_from_dict
|
||||
from synapse.events.utils import prune_event, validate_canonicaljson
|
||||
from synapse.events.utils import prune_event, prune_event_dict, validate_canonicaljson
|
||||
from synapse.http.servlet import assert_params_in_dict
|
||||
from synapse.logging.opentracing import log_kv, trace
|
||||
from synapse.types import JsonDict, get_domain_from_id
|
||||
@@ -174,7 +174,7 @@ async def _check_sigs_on_pdu(
|
||||
|
||||
# we want to check that the event is signed by:
|
||||
#
|
||||
# (a) the sender's (or the delagated sender's) server
|
||||
# (a) the sender's server or the hub
|
||||
#
|
||||
# - except in the case of invites created from a 3pid invite, which are exempt
|
||||
# from this check, because the sender has to match that of the original 3pid
|
||||
@@ -250,50 +250,26 @@ async def _check_sigs_on_pdu(
|
||||
pdu.event_id,
|
||||
) from None
|
||||
|
||||
# If this is a linearized PDU we may need to check signatures of the owner
|
||||
# If this is a linearized PDU we may need to check signatures of the hub
|
||||
# and sender.
|
||||
if room_version.event_format == EventFormatVersions.LINEARIZED:
|
||||
assert isinstance(pdu, FrozenLinearizedEvent)
|
||||
|
||||
# Check that the fields are not invalid.
|
||||
if pdu.delegated_server and not pdu.owner_server:
|
||||
raise InvalidEventSignatureError(
|
||||
"PDU missing owner_server", pdu.event_id
|
||||
) from None
|
||||
|
||||
# If the event was sent from a linear server, check the authorized server.
|
||||
# If the event was sent via a hub server, check the signature of the
|
||||
# sender against the Linear PDU. (But only if the sender isn't the hub.)
|
||||
#
|
||||
# Note that the signature of the delegated server, if one exists, is
|
||||
# checked against the full PDU above.
|
||||
if pdu.owner_server:
|
||||
# Construct the Linear PDU and check the signature against the sender.
|
||||
#
|
||||
# If the owner & sender are the same, then only a Delegated Linear PDU
|
||||
# is created (the Linear PDU is not signed by itself).
|
||||
if pdu.owner_server != sender_domain:
|
||||
try:
|
||||
await keyring.verify_json_for_server(
|
||||
sender_domain,
|
||||
pdu.get_linearized_pdu_json(delegated=False),
|
||||
origin_server_ts_for_signing,
|
||||
)
|
||||
except Exception as e:
|
||||
raise InvalidEventSignatureError(
|
||||
f"unable to verify signature for sender {sender_domain}: {e}",
|
||||
pdu.event_id,
|
||||
) from None
|
||||
|
||||
# Construct the Delegated Linear PDU and check the signature
|
||||
# against owner_server.
|
||||
# Note that the signature of the hub server, if one exists, is checked
|
||||
# against the full PDU above.
|
||||
if pdu.hub_server and pdu.hub_server != sender_domain:
|
||||
try:
|
||||
await keyring.verify_json_for_server(
|
||||
pdu.owner_server,
|
||||
pdu.get_linearized_pdu_json(delegated=True),
|
||||
sender_domain,
|
||||
prune_event_dict(pdu.room_version, pdu.get_linearized_pdu_json()),
|
||||
origin_server_ts_for_signing,
|
||||
)
|
||||
except Exception as e:
|
||||
raise InvalidEventSignatureError(
|
||||
f"unable to verify signature for owner_server {pdu.owner_server}: {e}",
|
||||
f"unable to verify signature for sender {sender_domain}: {e}",
|
||||
pdu.event_id,
|
||||
) from None
|
||||
|
||||
|
||||
@@ -290,6 +290,11 @@ class FederationServer(FederationBase):
|
||||
# accurate as possible.
|
||||
request_time = self._clock.time_msec()
|
||||
|
||||
# TODO(LM): If we are the hub server and the destination is a participant
|
||||
# server then pdus are Linear PDUs.
|
||||
#
|
||||
# TODO(LM): If we are the hub's DAG server we need to linearize and give
|
||||
# it the results.
|
||||
transaction = Transaction(
|
||||
transaction_id=transaction_id,
|
||||
destination=destination,
|
||||
|
||||
@@ -85,6 +85,7 @@ class FakeEvent:
|
||||
self.state_key = state_key
|
||||
self.content = content
|
||||
self.room_id = ROOM_ID
|
||||
self.hub_server = None
|
||||
|
||||
def to_event(self, auth_events: List[str], prev_events: List[str]) -> EventBase:
|
||||
"""Given the auth_events and prev_events, convert to a Frozen Event
|
||||
|
||||
Reference in New Issue
Block a user