def delete_expired_event_txn(txn): # Delete the expiry timestamp associated with this event from the database. self._delete_event_expiry_txn(txn, event_id) if not event: # If we can't find the event, log a warning and delete the expiry date # from the database so that we don't try to expire it again in the # future. logger.warning( "Can't expire event %s because we don't have it.", event_id ) return # Prune the event's dict then convert it to JSON. pruned_json = frozendict_json_encoder.encode( prune_event_dict(event.room_version, event.get_dict()) ) # Update the event_json table to replace the event's JSON with the pruned # JSON. self._censor_event_txn(txn, event.event_id, pruned_json) # We need to invalidate the event cache entry for this event because we # changed its content in the database. We can't call # self._invalidate_cache_and_stream because self.get_event_cache isn't of the # right type. txn.call_after(self._get_event_cache.invalidate, (event.event_id,)) # Send that invalidation to replication so that other workers also invalidate # the event cache. self._send_invalidation_to_replication( txn, "_get_event_cache", (event.event_id,) )
def handle_new_client_event( self, requester, event, context, ratelimit=True, extra_users=[], ): """Processes a new event. This includes checking auth, persisting it, notifying users, sending to remote servers, etc. If called from a worker will hit out to the master process for final processing. Args: requester (Requester) event (FrozenEvent) context (EventContext) ratelimit (bool) extra_users (list(UserID)): Any extra users to notify about event """ try: yield self.auth.check_from_context(event, context) except AuthError as err: logger.warn("Denying new event %r because %s", event, err) raise err # Ensure that we can round trip before trying to persist in db try: dump = frozendict_json_encoder.encode(event.content) simplejson.loads(dump) except Exception: logger.exception("Failed to encode content: %r", event.content) raise yield self.action_generator.handle_push_actions_for_event( event, context) try: # If we're a worker we need to hit out to the master. if self.config.worker_app: yield send_event_to_master( self.http_client, host=self.config.worker_replication_host, port=self.config.worker_replication_http_port, requester=requester, event=event, context=context, ratelimit=ratelimit, extra_users=extra_users, ) return yield self.persist_and_notify_client_event( requester, event, context, ratelimit=ratelimit, extra_users=extra_users, ) except: # noqa: E722, as we reraise the exception this is fine. # Ensure that we actually remove the entries in the push actions # staging area, if we calculated them. preserve_fn(self.store.remove_push_actions_from_staging)( event.event_id) raise
async def handle_new_client_event(self, requester, event, context, ratelimit=True, extra_users=[]) -> int: """Processes a new event. This includes checking auth, persisting it, notifying users, sending to remote servers, etc. If called from a worker will hit out to the master process for final processing. Args: requester (Requester) event (FrozenEvent) context (EventContext) ratelimit (bool) extra_users (list(UserID)): Any extra users to notify about event Return: The stream_id of the persisted event. """ if event.is_state() and (event.type, event.state_key) == ( EventTypes.Create, "", ): room_version = event.content.get("room_version", RoomVersions.V1.identifier) else: room_version = await self.store.get_room_version_id(event.room_id) event_allowed = await self.third_party_event_rules.check_event_allowed( 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. assert event.type == EventTypes.Member assert event.content["membership"] == Membership.LEAVE else: try: await self.auth.check_from_context(room_version, event, context) except AuthError as err: logger.warning("Denying new event %r because %s", event, err) raise err # Ensure that we can round trip before trying to persist in db try: dump = frozendict_json_encoder.encode(event.content) json.loads(dump) except Exception: logger.exception("Failed to encode content: %r", event.content) raise await self.action_generator.handle_push_actions_for_event( event, context) # reraise does not allow inlineCallbacks to preserve the stacktrace, so we # hack around with a try/finally instead. success = False try: # If we're a worker we need to hit out to the master. if not self._is_event_writer: result = await self.send_event( instance_name=self.config.worker.writers.events, event_id=event.event_id, store=self.store, requester=requester, event=event, context=context, ratelimit=ratelimit, extra_users=extra_users, ) stream_id = result["stream_id"] event.internal_metadata.stream_ordering = stream_id success = True return stream_id stream_id = await self.persist_and_notify_client_event( requester, event, context, ratelimit=ratelimit, extra_users=extra_users) success = True return stream_id finally: if not success: # Ensure that we actually remove the entries in the push actions # staging area, if we calculated them. run_in_background(self.store.remove_push_actions_from_staging, event.event_id)
def handle_new_client_event( self, requester, event, context, ratelimit=True, extra_users=[] ): """Processes a new event. This includes checking auth, persisting it, notifying users, sending to remote servers, etc. If called from a worker will hit out to the master process for final processing. Args: requester (Requester) event (FrozenEvent) context (EventContext) ratelimit (bool) extra_users (list(UserID)): Any extra users to notify about event """ if event.is_state() and (event.type, event.state_key) == ( EventTypes.Create, "", ): room_version = event.content.get("room_version", RoomVersions.V1.identifier) else: room_version = yield self.store.get_room_version(event.room_id) event_allowed = yield self.third_party_event_rules.check_event_allowed( event, context ) if not event_allowed: raise SynapseError( 403, "This event is not allowed in this context", Codes.FORBIDDEN ) try: yield self.auth.check_from_context(room_version, event, context) except AuthError as err: logger.warning("Denying new event %r because %s", event, err) raise err # Ensure that we can round trip before trying to persist in db try: dump = frozendict_json_encoder.encode(event.content) json.loads(dump) except Exception: logger.exception("Failed to encode content: %r", event.content) raise yield self.action_generator.handle_push_actions_for_event(event, context) # reraise does not allow inlineCallbacks to preserve the stacktrace, so we # hack around with a try/finally instead. success = False try: # If we're a worker we need to hit out to the master. if self.config.worker_app: yield self.send_event_to_master( event_id=event.event_id, store=self.store, requester=requester, event=event, context=context, ratelimit=ratelimit, extra_users=extra_users, ) success = True return yield self.persist_and_notify_client_event( requester, event, context, ratelimit=ratelimit, extra_users=extra_users ) success = True finally: if not success: # Ensure that we actually remove the entries in the push actions # staging area, if we calculated them. run_in_background( self.store.remove_push_actions_from_staging, event.event_id )
def handle_new_client_event( self, requester, event, context, ratelimit=True, extra_users=[], ): """Processes a new event. This includes checking auth, persisting it, notifying users, sending to remote servers, etc. If called from a worker will hit out to the master process for final processing. Args: requester (Requester) event (FrozenEvent) context (EventContext) ratelimit (bool) extra_users (list(UserID)): Any extra users to notify about event """ if event.is_state() and (event.type, event.state_key) == (EventTypes.Create, ""): room_version = event.content.get( "room_version", RoomVersions.V1.identifier ) else: room_version = yield self.store.get_room_version(event.room_id) try: yield self.auth.check_from_context(room_version, event, context) except AuthError as err: logger.warn("Denying new event %r because %s", event, err) raise err # Ensure that we can round trip before trying to persist in db try: dump = frozendict_json_encoder.encode(event.content) json.loads(dump) except Exception: logger.exception("Failed to encode content: %r", event.content) raise yield self.action_generator.handle_push_actions_for_event( event, context ) # reraise does not allow inlineCallbacks to preserve the stacktrace, so we # hack around with a try/finally instead. success = False try: # If we're a worker we need to hit out to the master. if self.config.worker_app: yield self.send_event_to_master( event_id=event.event_id, store=self.store, requester=requester, event=event, context=context, ratelimit=ratelimit, extra_users=extra_users, ) success = True return yield self.persist_and_notify_client_event( requester, event, context, ratelimit=ratelimit, extra_users=extra_users, ) success = True finally: if not success: # Ensure that we actually remove the entries in the push actions # staging area, if we calculated them. run_in_background( self.store.remove_push_actions_from_staging, event.event_id, )
async def _censor_redactions(self): """Censors all redactions older than the configured period that haven't been censored yet. By censor we mean update the event_json table with the redacted event. """ if self.hs.config.redaction_retention_period is None: return if not ( await self.db_pool.updates.has_completed_background_update( "redactions_have_censored_ts_idx" ) ): # We don't want to run this until the appropriate index has been # created. return before_ts = self._clock.time_msec() - self.hs.config.redaction_retention_period # We fetch all redactions that: # 1. point to an event we have, # 2. has a received_ts from before the cut off, and # 3. we haven't yet censored. # # This is limited to 100 events to ensure that we don't try and do too # much at once. We'll get called again so this should eventually catch # up. sql = """ SELECT redactions.event_id, redacts FROM redactions LEFT JOIN events AS original_event ON ( redacts = original_event.event_id ) WHERE NOT have_censored AND redactions.received_ts <= ? ORDER BY redactions.received_ts ASC LIMIT ? """ rows = await self.db_pool.execute( "_censor_redactions_fetch", None, sql, before_ts, 100 ) updates = [] for redaction_id, event_id in rows: redaction_event = await self.get_event(redaction_id, allow_none=True) original_event = await self.get_event( event_id, allow_rejected=True, allow_none=True ) # The SQL above ensures that we have both the redaction and # original event, so if the `get_event` calls return None it # means that the redaction wasn't allowed. Either way we know that # the result won't change so we mark the fact that we've checked. if ( redaction_event and original_event and original_event.internal_metadata.is_redacted() ): # Redaction was allowed pruned_json = frozendict_json_encoder.encode( prune_event_dict( original_event.room_version, original_event.get_dict() ) ) else: # Redaction wasn't allowed pruned_json = None updates.append((redaction_id, event_id, pruned_json)) def _update_censor_txn(txn): for redaction_id, event_id, pruned_json in updates: if pruned_json: self._censor_event_txn(txn, event_id, pruned_json) self.db_pool.simple_update_one_txn( txn, table="redactions", keyvalues={"event_id": redaction_id}, updatevalues={"have_censored": True}, ) await self.db_pool.runInteraction("_update_censor_txn", _update_censor_txn)
def handle_new_client_event( self, requester, event, context, ratelimit=True, extra_users=[], ): """Processes a new event. This includes checking auth, persisting it, notifying users, sending to remote servers, etc. If called from a worker will hit out to the master process for final processing. Args: requester (Requester) event (FrozenEvent) context (EventContext) ratelimit (bool) extra_users (list(UserID)): Any extra users to notify about event """ try: yield self.auth.check_from_context(event, context) except AuthError as err: logger.warn("Denying new event %r because %s", event, err) raise err # Ensure that we can round trip before trying to persist in db try: dump = frozendict_json_encoder.encode(event.content) simplejson.loads(dump) except Exception: logger.exception("Failed to encode content: %r", event.content) raise yield self.action_generator.handle_push_actions_for_event( event, context ) try: # If we're a worker we need to hit out to the master. if self.config.worker_app: yield send_event_to_master( self.http_client, host=self.config.worker_replication_host, port=self.config.worker_replication_http_port, requester=requester, event=event, context=context, ratelimit=ratelimit, extra_users=extra_users, ) return yield self.persist_and_notify_client_event( requester, event, context, ratelimit=ratelimit, extra_users=extra_users, ) except: # noqa: E722, as we reraise the exception this is fine. # Ensure that we actually remove the entries in the push actions # staging area, if we calculated them. tp, value, tb = sys.exc_info() run_in_background( self.store.remove_push_actions_from_staging, event.event_id, ) six.reraise(tp, value, tb)