async def _handle_request(self, request): with Measure(self.clock, "repl_fed_send_events_parse"): content = parse_json_object_from_request(request) backfilled = content["backfilled"] event_payloads = content["events"] event_and_contexts = [] for event_payload in event_payloads: event_dict = event_payload["event"] format_ver = event_payload["event_format_version"] internal_metadata = event_payload["internal_metadata"] rejected_reason = event_payload["rejected_reason"] EventType = event_type_from_format_version(format_ver) event = EventType(event_dict, internal_metadata, rejected_reason) context = EventContext.deserialize(self.storage, event_payload["context"]) event_and_contexts.append((event, context)) logger.info("Got %d events from federation", len(event_and_contexts)) await self.federation_handler.persist_events_and_notify( event_and_contexts, backfilled) return 200, {}
def event_from_pdu_json(pdu_json, event_format_version, outlier=False): """Construct a FrozenEvent from an event json received over federation Args: pdu_json (object): pdu as received over federation event_format_version (int): The event format version outlier (bool): True to mark this event as an outlier Returns: FrozenEvent Raises: SynapseError: if the pdu is missing required fields or is otherwise not a valid matrix event """ # we could probably enforce a bunch of other fields here (room_id, sender, # origin, etc etc) assert_params_in_dict(pdu_json, ("type", "depth")) depth = pdu_json["depth"] if not isinstance(depth, six.integer_types): raise SynapseError(400, "Depth %r not an intger" % (depth, ), Codes.BAD_JSON) if depth < 0: raise SynapseError(400, "Depth too small", Codes.BAD_JSON) elif depth > MAX_DEPTH: raise SynapseError(400, "Depth too large", Codes.BAD_JSON) event = event_type_from_format_version(event_format_version)(pdu_json) event.internal_metadata.outlier = outlier return event
async def _handle_request(self, request, event_id): with Measure(self.clock, "repl_send_event_parse"): content = parse_json_object_from_request(request) event_dict = content["event"] format_ver = content["event_format_version"] internal_metadata = content["internal_metadata"] rejected_reason = content["rejected_reason"] EventType = event_type_from_format_version(format_ver) event = EventType(event_dict, internal_metadata, rejected_reason) requester = Requester.deserialize(self.store, content["requester"]) context = EventContext.deserialize(self.storage, content["context"]) ratelimit = content["ratelimit"] extra_users = [ UserID.from_string(u) for u in content["extra_users"] ] if requester.user: request.authenticated_entity = requester.user.to_string() logger.info("Got event to send with ID: %s into room: %s", event.event_id, event.room_id) await self.event_creation_handler.persist_and_notify_client_event( requester, event, context, ratelimit=ratelimit, extra_users=extra_users) return 200, {}
def _handle_request(self, request, event_id): with Measure(self.clock, "repl_send_event_parse"): content = parse_json_object_from_request(request) event_dict = content["event"] format_ver = content["event_format_version"] internal_metadata = content["internal_metadata"] rejected_reason = content["rejected_reason"] EventType = event_type_from_format_version(format_ver) event = EventType(event_dict, internal_metadata, rejected_reason) requester = Requester.deserialize(self.store, content["requester"]) context = yield EventContext.deserialize(self.store, content["context"]) ratelimit = content["ratelimit"] extra_users = [UserID.from_string(u) for u in content["extra_users"]] if requester.user: request.authenticated_entity = requester.user.to_string() logger.info( "Got event to send with ID: %s into room: %s", event.event_id, event.room_id, ) yield self.event_creation_handler.persist_and_notify_client_event( requester, event, context, ratelimit=ratelimit, extra_users=extra_users, ) defer.returnValue((200, {}))
def _get_event_from_row(self, internal_metadata, js, redactions, format_version, rejected_reason=None): """Parse an event row which has been read from the database Args: internal_metadata (str): json-encoded internal_metadata column js (str): json-encoded event body from event_json redactions (list[str]): a list of the events which claim to have redacted this event, from the redactions table format_version: (str): the 'format_version' column rejected_reason (str|None): the reason this event was rejected, if any Returns: _EventCacheEntry """ with Measure(self._clock, "_get_event_from_row"): d = json.loads(js) internal_metadata = json.loads(internal_metadata) if format_version is None: # This means that we stored the event before we had the concept # of a event format version, so it must be a V1 event. format_version = EventFormatVersions.V1 original_ev = event_type_from_format_version(format_version)( event_dict=d, internal_metadata_dict=internal_metadata, rejected_reason=rejected_reason, ) redacted_event = yield self._maybe_redact_event_row( original_ev, redactions) cache_entry = _EventCacheEntry(event=original_ev, redacted_event=redacted_event) self._get_event_cache.prefill((original_ev.event_id, ), cache_entry) defer.returnValue(cache_entry)
def create_local_event_from_event_dict( clock: Clock, hostname: str, signing_key: SigningKey, room_version: RoomVersion, event_dict: JsonDict, internal_metadata_dict: Optional[JsonDict] = None, ) -> EventBase: """Takes a fully formed event dict, ensuring that fields like `origin` and `origin_server_ts` have correct values for a locally produced event, then signs and hashes it. """ format_version = room_version.event_format if format_version not in KNOWN_EVENT_FORMAT_VERSIONS: raise Exception("No event format defined for version %r" % (format_version, )) if internal_metadata_dict is None: internal_metadata_dict = {} time_now = int(clock.time_msec()) if format_version == EventFormatVersions.V1: event_dict["event_id"] = _create_event_id(clock, hostname) event_dict["origin"] = hostname event_dict.setdefault("origin_server_ts", time_now) event_dict.setdefault("unsigned", {}) age = event_dict["unsigned"].pop("age", 0) event_dict["unsigned"].setdefault("age_ts", time_now - age) event_dict.setdefault("signatures", {}) add_hashes_and_signatures(room_version, event_dict, hostname, signing_key) return event_type_from_format_version(format_version)( event_dict, internal_metadata_dict=internal_metadata_dict)
def _get_events_from_db(self, event_ids, allow_rejected=False): """Fetch a bunch of events from the database. Returned events will be added to the cache for future lookups. Args: event_ids (Iterable[str]): The event_ids of the events to fetch allow_rejected (bool): Whether to include rejected events Returns: Deferred[Dict[str, _EventCacheEntry]]: map from event id to result. May return extra events which weren't asked for. """ fetched_events = {} events_to_fetch = event_ids while events_to_fetch: row_map = yield self._enqueue_events(events_to_fetch) # we need to recursively fetch any redactions of those events redaction_ids = set() for event_id in events_to_fetch: row = row_map.get(event_id) fetched_events[event_id] = row if row: redaction_ids.update(row["redactions"]) events_to_fetch = redaction_ids.difference(fetched_events.keys()) if events_to_fetch: logger.debug("Also fetching redaction events %s", events_to_fetch) # build a map from event_id to EventBase event_map = {} for event_id, row in fetched_events.items(): if not row: continue assert row["event_id"] == event_id rejected_reason = row["rejected_reason"] if not allow_rejected and rejected_reason: continue d = json.loads(row["json"]) internal_metadata = json.loads(row["internal_metadata"]) format_version = row["format_version"] if format_version is None: # This means that we stored the event before we had the concept # of a event format version, so it must be a V1 event. format_version = EventFormatVersions.V1 original_ev = event_type_from_format_version(format_version)( event_dict=d, internal_metadata_dict=internal_metadata, rejected_reason=rejected_reason, ) event_map[event_id] = original_ev # finally, we can decide whether each one nededs redacting, and build # the cache entries. result_map = {} for event_id, original_ev in event_map.items(): redactions = fetched_events[event_id]["redactions"] redacted_event = self._maybe_redact_event_row( original_ev, redactions, event_map) cache_entry = _EventCacheEntry(event=original_ev, redacted_event=redacted_event) self._get_event_cache.prefill((event_id, ), cache_entry) result_map[event_id] = cache_entry return result_map
def _get_event_from_row(self, internal_metadata, js, redacted, format_version, rejected_reason=None): with Measure(self._clock, "_get_event_from_row"): d = json.loads(js) internal_metadata = json.loads(internal_metadata) if rejected_reason: rejected_reason = yield self._simple_select_one_onecol( table="rejections", keyvalues={"event_id": rejected_reason}, retcol="reason", desc="_get_event_from_row_rejected_reason", ) if format_version is None: # This means that we stored the event before we had the concept # of a event format version, so it must be a V1 event. format_version = EventFormatVersions.V1 original_ev = event_type_from_format_version(format_version)( event_dict=d, internal_metadata_dict=internal_metadata, rejected_reason=rejected_reason, ) redacted_event = None if redacted: redacted_event = prune_event(original_ev) redaction_id = yield self._simple_select_one_onecol( table="redactions", keyvalues={"redacts": redacted_event.event_id}, retcol="event_id", desc="_get_event_from_row_redactions", ) redacted_event.unsigned["redacted_by"] = redaction_id # Get the redaction event. because = yield self.get_event( redaction_id, check_redacted=False, allow_none=True, ) if because: # It's fine to do add the event directly, since get_pdu_json # will serialise this field correctly redacted_event.unsigned["redacted_because"] = because # Starting in room version v3, some redactions need to be # rechecked if we didn't have the redacted event at the # time, so we recheck on read instead. if because.internal_metadata.need_to_check_redaction(): expected_domain = get_domain_from_id( original_ev.sender) if get_domain_from_id( because.sender) == expected_domain: # This redaction event is allowed. Mark as not needing a # recheck. because.internal_metadata.recheck_redaction = False else: # Senders don't match, so the event isn't actually redacted redacted_event = None cache_entry = _EventCacheEntry( event=original_ev, redacted_event=redacted_event, ) self._get_event_cache.prefill((original_ev.event_id, ), cache_entry) defer.returnValue(cache_entry)