@@ -811,6 +811,23 @@ async def create_new_client_event(
811811 if requester :
812812 context .app_service = requester .app_service
813813
814+ third_party_result = await self .third_party_event_rules .check_event_allowed (
815+ event , context
816+ )
817+ if not third_party_result :
818+ logger .info (
819+ "Event %s forbidden by third-party rules" , event ,
820+ )
821+ raise SynapseError (
822+ 403 , "This event is not allowed in this context" , Codes .FORBIDDEN
823+ )
824+ elif isinstance (third_party_result , dict ):
825+ # the third-party rules want to replace the event. We'll need to build a new
826+ # event.
827+ event , context = await self ._rebuild_event_after_third_party_rules (
828+ third_party_result , event
829+ )
830+
814831 self .validator .validate_new (event , self .config )
815832
816833 # If this event is an annotation then we check that that the sender
@@ -897,14 +914,6 @@ async def handle_new_client_event(
897914 else :
898915 room_version = await self .store .get_room_version_id (event .room_id )
899916
900- event_allowed = await self .third_party_event_rules .check_event_allowed (
901- event , context
902- )
903- if not event_allowed :
904- raise SynapseError (
905- 403 , "This event is not allowed in this context" , Codes .FORBIDDEN
906- )
907-
908917 if event .internal_metadata .is_out_of_band_membership ():
909918 # the only sort of out-of-band-membership events we expect to see here
910919 # are invite rejections we have generated ourselves.
@@ -1307,3 +1316,57 @@ def _expire_rooms_to_exclude_from_dummy_event_insertion(self):
13071316 room_id ,
13081317 )
13091318 del self ._rooms_to_exclude_from_dummy_event_insertion [room_id ]
1319+
1320+ async def _rebuild_event_after_third_party_rules (
1321+ self , third_party_result : dict , original_event : EventBase
1322+ ) -> Tuple [EventBase , EventContext ]:
1323+ # the third_party_event_rules want to replace the event.
1324+ # we do some basic checks, and then return the replacement event and context.
1325+
1326+ # Construct a new EventBuilder and validate it, which helps with the
1327+ # rest of these checks.
1328+ try :
1329+ builder = self .event_builder_factory .for_room_version (
1330+ original_event .room_version , third_party_result
1331+ )
1332+ self .validator .validate_builder (builder )
1333+ except SynapseError as e :
1334+ raise Exception (
1335+ "Third party rules module created an invalid event: " + e .msg ,
1336+ )
1337+
1338+ immutable_fields = [
1339+ # changing the room is going to break things: we've already checked that the
1340+ # room exists, and are holding a concurrency limiter token for that room.
1341+ # Also, we might need to use a different room version.
1342+ "room_id" ,
1343+ # changing the type or state key might work, but we'd need to check that the
1344+ # calling functions aren't making assumptions about them.
1345+ "type" ,
1346+ "state_key" ,
1347+ ]
1348+
1349+ for k in immutable_fields :
1350+ if getattr (builder , k , None ) != original_event .get (k ):
1351+ raise Exception (
1352+ "Third party rules module created an invalid event: "
1353+ "cannot change field " + k
1354+ )
1355+
1356+ # check that the new sender belongs to this HS
1357+ if not self .hs .is_mine_id (builder .sender ):
1358+ raise Exception (
1359+ "Third party rules module created an invalid event: "
1360+ "invalid sender " + builder .sender
1361+ )
1362+
1363+ # copy over the original internal metadata
1364+ for k , v in original_event .internal_metadata .get_dict ().items ():
1365+ setattr (builder .internal_metadata , k , v )
1366+
1367+ event = await builder .build (prev_event_ids = original_event .prev_event_ids ())
1368+
1369+ # we rebuild the event context, to be on the safe side. If nothing else,
1370+ # delta_ids might need an update.
1371+ context = await self .state .compute_event_context (event )
1372+ return event , context
0 commit comments