mirror of
https://mau.dev/maunium/synapse.git
synced 2024-10-01 01:36:05 -04:00
Move _persist_auth_tree into FederationEventHandler (#11115)
This is just a lift-and-shift, because it fits more naturally here. We do rename it to `process_remote_join` at the same time though.
This commit is contained in:
parent
0170774b19
commit
f3efa0036b
1
changelog.d/11115.misc
Normal file
1
changelog.d/11115.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Clean up some of the federation event authentication code for clarity.
|
@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
"""Contains handlers for federation events."""
|
"""Contains handlers for federation events."""
|
||||||
|
|
||||||
import itertools
|
|
||||||
import logging
|
import logging
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Tuple, Union
|
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Tuple, Union
|
||||||
@ -27,12 +26,7 @@ from unpaddedbase64 import decode_base64
|
|||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse import event_auth
|
from synapse import event_auth
|
||||||
from synapse.api.constants import (
|
from synapse.api.constants import EventContentFields, EventTypes, Membership
|
||||||
EventContentFields,
|
|
||||||
EventTypes,
|
|
||||||
Membership,
|
|
||||||
RejectedReason,
|
|
||||||
)
|
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
AuthError,
|
AuthError,
|
||||||
CodeMessageException,
|
CodeMessageException,
|
||||||
@ -43,12 +37,9 @@ from synapse.api.errors import (
|
|||||||
RequestSendFailed,
|
RequestSendFailed,
|
||||||
SynapseError,
|
SynapseError,
|
||||||
)
|
)
|
||||||
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion, RoomVersions
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion
|
||||||
from synapse.crypto.event_signing import compute_event_signature
|
from synapse.crypto.event_signing import compute_event_signature
|
||||||
from synapse.event_auth import (
|
from synapse.event_auth import validate_event_for_room_version
|
||||||
check_auth_rules_for_event,
|
|
||||||
validate_event_for_room_version,
|
|
||||||
)
|
|
||||||
from synapse.events import EventBase
|
from synapse.events import EventBase
|
||||||
from synapse.events.snapshot import EventContext
|
from synapse.events.snapshot import EventContext
|
||||||
from synapse.events.validator import EventValidator
|
from synapse.events.validator import EventValidator
|
||||||
@ -519,7 +510,7 @@ class FederationHandler:
|
|||||||
auth_events=auth_chain,
|
auth_events=auth_chain,
|
||||||
)
|
)
|
||||||
|
|
||||||
max_stream_id = await self._persist_auth_tree(
|
max_stream_id = await self._federation_event_handler.process_remote_join(
|
||||||
origin, room_id, auth_chain, state, event, room_version_obj
|
origin, room_id, auth_chain, state, event, room_version_obj
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1095,117 +1086,6 @@ class FederationHandler:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _persist_auth_tree(
|
|
||||||
self,
|
|
||||||
origin: str,
|
|
||||||
room_id: str,
|
|
||||||
auth_events: List[EventBase],
|
|
||||||
state: List[EventBase],
|
|
||||||
event: EventBase,
|
|
||||||
room_version: RoomVersion,
|
|
||||||
) -> int:
|
|
||||||
"""Checks the auth chain is valid (and passes auth checks) for the
|
|
||||||
state and event. Then persists the auth chain and state atomically.
|
|
||||||
Persists the event separately. Notifies about the persisted events
|
|
||||||
where appropriate.
|
|
||||||
|
|
||||||
Will attempt to fetch missing auth events.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
origin: Where the events came from
|
|
||||||
room_id,
|
|
||||||
auth_events
|
|
||||||
state
|
|
||||||
event
|
|
||||||
room_version: The room version we expect this room to have, and
|
|
||||||
will raise if it doesn't match the version in the create event.
|
|
||||||
"""
|
|
||||||
events_to_context = {}
|
|
||||||
for e in itertools.chain(auth_events, state):
|
|
||||||
e.internal_metadata.outlier = True
|
|
||||||
events_to_context[e.event_id] = EventContext.for_outlier()
|
|
||||||
|
|
||||||
event_map = {
|
|
||||||
e.event_id: e for e in itertools.chain(auth_events, state, [event])
|
|
||||||
}
|
|
||||||
|
|
||||||
create_event = None
|
|
||||||
for e in auth_events:
|
|
||||||
if (e.type, e.state_key) == (EventTypes.Create, ""):
|
|
||||||
create_event = e
|
|
||||||
break
|
|
||||||
|
|
||||||
if create_event is None:
|
|
||||||
# If the state doesn't have a create event then the room is
|
|
||||||
# invalid, and it would fail auth checks anyway.
|
|
||||||
raise SynapseError(400, "No create event in state")
|
|
||||||
|
|
||||||
room_version_id = create_event.content.get(
|
|
||||||
"room_version", RoomVersions.V1.identifier
|
|
||||||
)
|
|
||||||
|
|
||||||
if room_version.identifier != room_version_id:
|
|
||||||
raise SynapseError(400, "Room version mismatch")
|
|
||||||
|
|
||||||
missing_auth_events = set()
|
|
||||||
for e in itertools.chain(auth_events, state, [event]):
|
|
||||||
for e_id in e.auth_event_ids():
|
|
||||||
if e_id not in event_map:
|
|
||||||
missing_auth_events.add(e_id)
|
|
||||||
|
|
||||||
for e_id in missing_auth_events:
|
|
||||||
m_ev = await self.federation_client.get_pdu(
|
|
||||||
[origin],
|
|
||||||
e_id,
|
|
||||||
room_version=room_version,
|
|
||||||
outlier=True,
|
|
||||||
timeout=10000,
|
|
||||||
)
|
|
||||||
if m_ev and m_ev.event_id == e_id:
|
|
||||||
event_map[e_id] = m_ev
|
|
||||||
else:
|
|
||||||
logger.info("Failed to find auth event %r", e_id)
|
|
||||||
|
|
||||||
for e in itertools.chain(auth_events, state, [event]):
|
|
||||||
auth_for_e = [
|
|
||||||
event_map[e_id] for e_id in e.auth_event_ids() if e_id in event_map
|
|
||||||
]
|
|
||||||
if create_event:
|
|
||||||
auth_for_e.append(create_event)
|
|
||||||
|
|
||||||
try:
|
|
||||||
validate_event_for_room_version(room_version, e)
|
|
||||||
check_auth_rules_for_event(room_version, e, auth_for_e)
|
|
||||||
except SynapseError as err:
|
|
||||||
# we may get SynapseErrors here as well as AuthErrors. For
|
|
||||||
# instance, there are a couple of (ancient) events in some
|
|
||||||
# rooms whose senders do not have the correct sigil; these
|
|
||||||
# cause SynapseErrors in auth.check. We don't want to give up
|
|
||||||
# the attempt to federate altogether in such cases.
|
|
||||||
|
|
||||||
logger.warning("Rejecting %s because %s", e.event_id, err.msg)
|
|
||||||
|
|
||||||
if e == event:
|
|
||||||
raise
|
|
||||||
events_to_context[e.event_id].rejected = RejectedReason.AUTH_ERROR
|
|
||||||
|
|
||||||
if auth_events or state:
|
|
||||||
await self._federation_event_handler.persist_events_and_notify(
|
|
||||||
room_id,
|
|
||||||
[
|
|
||||||
(e, events_to_context[e.event_id])
|
|
||||||
for e in itertools.chain(auth_events, state)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
new_event_context = await self.state_handler.compute_event_context(
|
|
||||||
event, old_state=state
|
|
||||||
)
|
|
||||||
|
|
||||||
return await self._federation_event_handler.persist_events_and_notify(
|
|
||||||
room_id, [(event, new_event_context)]
|
|
||||||
)
|
|
||||||
|
|
||||||
async def on_get_missing_events(
|
async def on_get_missing_events(
|
||||||
self,
|
self,
|
||||||
origin: str,
|
origin: str,
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import (
|
from typing import (
|
||||||
@ -45,7 +46,7 @@ from synapse.api.errors import (
|
|||||||
RequestSendFailed,
|
RequestSendFailed,
|
||||||
SynapseError,
|
SynapseError,
|
||||||
)
|
)
|
||||||
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion, RoomVersions
|
||||||
from synapse.event_auth import (
|
from synapse.event_auth import (
|
||||||
auth_types_for_event,
|
auth_types_for_event,
|
||||||
check_auth_rules_for_event,
|
check_auth_rules_for_event,
|
||||||
@ -390,6 +391,119 @@ class FederationEventHandler:
|
|||||||
prev_member_event,
|
prev_member_event,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def process_remote_join(
|
||||||
|
self,
|
||||||
|
origin: str,
|
||||||
|
room_id: str,
|
||||||
|
auth_events: List[EventBase],
|
||||||
|
state: List[EventBase],
|
||||||
|
event: EventBase,
|
||||||
|
room_version: RoomVersion,
|
||||||
|
) -> int:
|
||||||
|
"""Persists the events returned by a send_join
|
||||||
|
|
||||||
|
Checks the auth chain is valid (and passes auth checks) for the
|
||||||
|
state and event. Then persists the auth chain and state atomically.
|
||||||
|
Persists the event separately. Notifies about the persisted events
|
||||||
|
where appropriate.
|
||||||
|
|
||||||
|
Will attempt to fetch missing auth events.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
origin: Where the events came from
|
||||||
|
room_id,
|
||||||
|
auth_events
|
||||||
|
state
|
||||||
|
event
|
||||||
|
room_version: The room version we expect this room to have, and
|
||||||
|
will raise if it doesn't match the version in the create event.
|
||||||
|
"""
|
||||||
|
events_to_context = {}
|
||||||
|
for e in itertools.chain(auth_events, state):
|
||||||
|
e.internal_metadata.outlier = True
|
||||||
|
events_to_context[e.event_id] = EventContext.for_outlier()
|
||||||
|
|
||||||
|
event_map = {
|
||||||
|
e.event_id: e for e in itertools.chain(auth_events, state, [event])
|
||||||
|
}
|
||||||
|
|
||||||
|
create_event = None
|
||||||
|
for e in auth_events:
|
||||||
|
if (e.type, e.state_key) == (EventTypes.Create, ""):
|
||||||
|
create_event = e
|
||||||
|
break
|
||||||
|
|
||||||
|
if create_event is None:
|
||||||
|
# If the state doesn't have a create event then the room is
|
||||||
|
# invalid, and it would fail auth checks anyway.
|
||||||
|
raise SynapseError(400, "No create event in state")
|
||||||
|
|
||||||
|
room_version_id = create_event.content.get(
|
||||||
|
"room_version", RoomVersions.V1.identifier
|
||||||
|
)
|
||||||
|
|
||||||
|
if room_version.identifier != room_version_id:
|
||||||
|
raise SynapseError(400, "Room version mismatch")
|
||||||
|
|
||||||
|
missing_auth_events = set()
|
||||||
|
for e in itertools.chain(auth_events, state, [event]):
|
||||||
|
for e_id in e.auth_event_ids():
|
||||||
|
if e_id not in event_map:
|
||||||
|
missing_auth_events.add(e_id)
|
||||||
|
|
||||||
|
for e_id in missing_auth_events:
|
||||||
|
m_ev = await self._federation_client.get_pdu(
|
||||||
|
[origin],
|
||||||
|
e_id,
|
||||||
|
room_version=room_version,
|
||||||
|
outlier=True,
|
||||||
|
timeout=10000,
|
||||||
|
)
|
||||||
|
if m_ev and m_ev.event_id == e_id:
|
||||||
|
event_map[e_id] = m_ev
|
||||||
|
else:
|
||||||
|
logger.info("Failed to find auth event %r", e_id)
|
||||||
|
|
||||||
|
for e in itertools.chain(auth_events, state, [event]):
|
||||||
|
auth_for_e = [
|
||||||
|
event_map[e_id] for e_id in e.auth_event_ids() if e_id in event_map
|
||||||
|
]
|
||||||
|
if create_event:
|
||||||
|
auth_for_e.append(create_event)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate_event_for_room_version(room_version, e)
|
||||||
|
check_auth_rules_for_event(room_version, e, auth_for_e)
|
||||||
|
except SynapseError as err:
|
||||||
|
# we may get SynapseErrors here as well as AuthErrors. For
|
||||||
|
# instance, there are a couple of (ancient) events in some
|
||||||
|
# rooms whose senders do not have the correct sigil; these
|
||||||
|
# cause SynapseErrors in auth.check. We don't want to give up
|
||||||
|
# the attempt to federate altogether in such cases.
|
||||||
|
|
||||||
|
logger.warning("Rejecting %s because %s", e.event_id, err.msg)
|
||||||
|
|
||||||
|
if e == event:
|
||||||
|
raise
|
||||||
|
events_to_context[e.event_id].rejected = RejectedReason.AUTH_ERROR
|
||||||
|
|
||||||
|
if auth_events or state:
|
||||||
|
await self.persist_events_and_notify(
|
||||||
|
room_id,
|
||||||
|
[
|
||||||
|
(e, events_to_context[e.event_id])
|
||||||
|
for e in itertools.chain(auth_events, state)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
new_event_context = await self._state_handler.compute_event_context(
|
||||||
|
event, old_state=state
|
||||||
|
)
|
||||||
|
|
||||||
|
return await self.persist_events_and_notify(
|
||||||
|
room_id, [(event, new_event_context)]
|
||||||
|
)
|
||||||
|
|
||||||
@log_function
|
@log_function
|
||||||
async def backfill(
|
async def backfill(
|
||||||
self, dest: str, room_id: str, limit: int, extremities: Iterable[str]
|
self, dest: str, room_id: str, limit: int, extremities: Iterable[str]
|
||||||
|
Loading…
Reference in New Issue
Block a user