-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Support modifying event content from ThirdPartyRules modules #8535
Changes from all commits
d59378d
123711e
617e8a4
898196f
091e948
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Support modifying event content in `ThirdPartyRules` modules. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1507,18 +1507,9 @@ async def on_make_join_request( | |
event, context = await self.event_creation_handler.create_new_client_event( | ||
builder=builder | ||
) | ||
except AuthError as e: | ||
except SynapseError as e: | ||
logger.warning("Failed to create join to %s because %s", room_id, e) | ||
raise e | ||
|
||
event_allowed = await self.third_party_event_rules.check_event_allowed( | ||
event, context | ||
) | ||
if not event_allowed: | ||
logger.info("Creation of join %s forbidden by third-party rules", event) | ||
raise SynapseError( | ||
403, "This event is not allowed in this context", Codes.FORBIDDEN | ||
) | ||
raise | ||
|
||
# The remote hasn't signed it yet, obviously. We'll do the full checks | ||
# when we get the event back in `on_send_join_request` | ||
|
@@ -1567,15 +1558,6 @@ async def on_send_join_request(self, origin, pdu): | |
|
||
context = await self._handle_new_event(origin, event) | ||
|
||
event_allowed = await self.third_party_event_rules.check_event_allowed( | ||
event, context | ||
) | ||
if not event_allowed: | ||
logger.info("Sending of join %s forbidden by third-party rules", event) | ||
raise SynapseError( | ||
403, "This event is not allowed in this context", Codes.FORBIDDEN | ||
) | ||
|
||
logger.debug( | ||
"on_send_join_request: After _handle_new_event: %s, sigs: %s", | ||
event.event_id, | ||
|
@@ -1748,15 +1730,6 @@ async def on_make_leave_request( | |
builder=builder | ||
) | ||
|
||
event_allowed = await self.third_party_event_rules.check_event_allowed( | ||
event, context | ||
) | ||
if not event_allowed: | ||
logger.warning("Creation of leave %s forbidden by third-party rules", event) | ||
raise SynapseError( | ||
403, "This event is not allowed in this context", Codes.FORBIDDEN | ||
) | ||
|
||
try: | ||
# The remote hasn't signed it yet, obviously. We'll do the full checks | ||
# when we get the event back in `on_send_leave_request` | ||
|
@@ -1789,16 +1762,7 @@ async def on_send_leave_request(self, origin, pdu): | |
|
||
event.internal_metadata.outlier = False | ||
|
||
context = await self._handle_new_event(origin, event) | ||
|
||
event_allowed = await self.third_party_event_rules.check_event_allowed( | ||
event, context | ||
) | ||
if not event_allowed: | ||
logger.info("Sending of leave %s forbidden by third-party rules", event) | ||
raise SynapseError( | ||
403, "This event is not allowed in this context", Codes.FORBIDDEN | ||
) | ||
await self._handle_new_event(origin, event) | ||
|
||
logger.debug( | ||
"on_send_leave_request: After _handle_new_event: %s, sigs: %s", | ||
|
@@ -2694,18 +2658,6 @@ async def exchange_third_party_invite( | |
builder=builder | ||
) | ||
|
||
event_allowed = await self.third_party_event_rules.check_event_allowed( | ||
event, context | ||
) | ||
if not event_allowed: | ||
logger.info( | ||
"Creation of threepid invite %s forbidden by third-party rules", | ||
event, | ||
) | ||
raise SynapseError( | ||
403, "This event is not allowed in this context", Codes.FORBIDDEN | ||
) | ||
|
||
Comment on lines
-2697
to
-2708
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. afaict, this was redundant, because we later call |
||
event, context = await self.add_display_name_to_third_party_invite( | ||
room_version, event_dict, event, context | ||
) | ||
|
@@ -2756,18 +2708,6 @@ async def on_exchange_third_party_invite_request( | |
event, context = await self.event_creation_handler.create_new_client_event( | ||
builder=builder | ||
) | ||
|
||
event_allowed = await self.third_party_event_rules.check_event_allowed( | ||
event, context | ||
) | ||
if not event_allowed: | ||
logger.warning( | ||
"Exchange of threepid invite %s forbidden by third-party rules", event | ||
) | ||
raise SynapseError( | ||
403, "This event is not allowed in this context", Codes.FORBIDDEN | ||
) | ||
|
||
Comment on lines
-2759
to
-2770
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (ditto) |
||
event, context = await self.add_display_name_to_third_party_invite( | ||
room_version, event_dict, event, context | ||
) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -795,6 +795,23 @@ async def create_new_client_event( | |
if requester: | ||
context.app_service = requester.app_service | ||
|
||
third_party_result = await self.third_party_event_rules.check_event_allowed( | ||
event, context | ||
) | ||
if not third_party_result: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect it is fine, but returning an empty dict ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think that's fine. |
||
logger.info( | ||
"Event %s forbidden by third-party rules", event, | ||
) | ||
raise SynapseError( | ||
403, "This event is not allowed in this context", Codes.FORBIDDEN | ||
) | ||
elif isinstance(third_party_result, dict): | ||
# the third-party rules want to replace the event. We'll need to build a new | ||
# event. | ||
event, context = await self._rebuild_event_after_third_party_rules( | ||
third_party_result, event | ||
) | ||
|
||
self.validator.validate_new(event, self.config) | ||
|
||
# If this event is an annotation then we check that that the sender | ||
|
@@ -881,14 +898,6 @@ async def handle_new_client_event( | |
else: | ||
room_version = await self.store.get_room_version_id(event.room_id) | ||
|
||
event_allowed = await self.third_party_event_rules.check_event_allowed( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this call path is somewhat less obvious than the others, since there are 6 callers of
All the above create the event by calling
This one is brought in line by #8537. |
||
event, context | ||
) | ||
if not event_allowed: | ||
raise SynapseError( | ||
403, "This event is not allowed in this context", Codes.FORBIDDEN | ||
) | ||
|
||
if event.internal_metadata.is_out_of_band_membership(): | ||
# the only sort of out-of-band-membership events we expect to see here | ||
# are invite rejections we have generated ourselves. | ||
|
@@ -1291,3 +1300,57 @@ def _expire_rooms_to_exclude_from_dummy_event_insertion(self): | |
room_id, | ||
) | ||
del self._rooms_to_exclude_from_dummy_event_insertion[room_id] | ||
|
||
async def _rebuild_event_after_third_party_rules( | ||
self, third_party_result: dict, original_event: EventBase | ||
) -> Tuple[EventBase, EventContext]: | ||
# the third_party_event_rules want to replace the event. | ||
# we do some basic checks, and then return the replacement event and context. | ||
|
||
# Construct a new EventBuilder and validate it, which helps with the | ||
# rest of these checks. | ||
try: | ||
builder = self.event_builder_factory.for_room_version( | ||
original_event.room_version, third_party_result | ||
) | ||
self.validator.validate_builder(builder) | ||
except SynapseError as e: | ||
raise Exception( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each of these is a bare There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, exactly. |
||
"Third party rules module created an invalid event: " + e.msg, | ||
) | ||
|
||
immutable_fields = [ | ||
# changing the room is going to break things: we've already checked that the | ||
# room exists, and are holding a concurrency limiter token for that room. | ||
# Also, we might need to use a different room version. | ||
"room_id", | ||
# changing the type or state key might work, but we'd need to check that the | ||
# calling functions aren't making assumptions about them. | ||
"type", | ||
"state_key", | ||
] | ||
|
||
for k in immutable_fields: | ||
if getattr(builder, k, None) != original_event.get(k): | ||
raise Exception( | ||
"Third party rules module created an invalid event: " | ||
"cannot change field " + k | ||
) | ||
|
||
# check that the new sender belongs to this HS | ||
if not self.hs.is_mine_id(builder.sender): | ||
raise Exception( | ||
"Third party rules module created an invalid event: " | ||
"invalid sender " + builder.sender | ||
) | ||
|
||
# copy over the original internal metadata | ||
for k, v in original_event.internal_metadata.get_dict().items(): | ||
setattr(builder.internal_metadata, k, v) | ||
|
||
event = await builder.build(prev_event_ids=original_event.prev_event_ids()) | ||
|
||
# we rebuild the event context, to be on the safe side. If nothing else, | ||
# delta_ids might need an update. | ||
context = await self.state.compute_event_context(event) | ||
return event, context |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm changing this because
create_new_client_event
will now raise a SynapseError if the third_party_rules check blocks the event.