def get_user_by_req(self, request, allow_guest=False, rights="access"): """ Get a registered user's ID. Args: request - An HTTP request with an access_token query parameter. Returns: tuple of: UserID (str) Access token ID (str) Raises: AuthError if no user by that token exists or the token is invalid. """ # Can optionally look elsewhere in the request (e.g. headers) try: user_id = yield self._get_appservice_user_id(request.args) if user_id: request.authenticated_entity = user_id defer.returnValue( Requester(UserID.from_string(user_id), "", False)) access_token = request.args["access_token"][0] user_info = yield self.get_user_by_access_token( access_token, rights) user = user_info["user"] token_id = user_info["token_id"] is_guest = user_info["is_guest"] ip_addr = self.hs.get_ip_from_request(request) user_agent = request.requestHeaders.getRawHeaders("User-Agent", default=[""])[0] if user and access_token and ip_addr: preserve_context_over_fn(self.store.insert_client_ip, user=user, access_token=access_token, ip=ip_addr, user_agent=user_agent) if is_guest and not allow_guest: raise AuthError(403, "Guest access not allowed", errcode=Codes.GUEST_ACCESS_FORBIDDEN) request.authenticated_entity = user.to_string() defer.returnValue(Requester(user, token_id, is_guest)) except KeyError: raise AuthError(self.TOKEN_NOT_FOUND_HTTP_STATUS, "Missing access token.", errcode=Codes.MISSING_TOKEN)
def test_can_rerun_update(self): # First make sure we have completed all updates. while not self.get_success( self.store.has_completed_background_updates()): self.get_success(self.store.do_next_background_update(100), by=0.1) # Now let's create a room, which will insert a membership user = UserID("alice", "test") requester = Requester(user, None, False, None, None) self.get_success(self.room_creator.create_room(requester, {})) # Register the background update to run again. self.get_success( self.store._simple_insert( table="background_updates", values={ "update_name": "current_state_events_membership", "progress_json": "{}", "depends_on": None, }, )) # ... and tell the DataStore that it hasn't finished all updates yet self.store._all_done = False # Now let's actually drive the updates to completion while not self.get_success( self.store.has_completed_background_updates()): self.get_success(self.store.do_next_background_update(100), by=0.1)
def _handle_request(self, request, room_id, user_id): content = parse_json_object_from_request(request) remote_room_hosts = content["remote_room_hosts"] requester = Requester.deserialize(self.store, content["requester"]) if requester.user: request.authenticated_entity = requester.user.to_string() logger.info("remote_reject_invite: %s out of room: %s", user_id, room_id) try: event = yield self.federation_handler.do_remotely_reject_invite( remote_room_hosts, room_id, user_id ) ret = event.get_pdu_json() except Exception as e: # if we were unable to reject the exception, just mark # it as rejected on our end and plough ahead. # # The 'except' clause is very broad, but we need to # capture everything from DNS failures upwards # logger.warn("Failed to reject invite: %s", e) yield self.store.locally_reject_invite(user_id, room_id) ret = {} defer.returnValue((200, ret))
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"] room_ver = KNOWN_ROOM_VERSIONS[content["room_version"]] internal_metadata = content["internal_metadata"] rejected_reason = content["rejected_reason"] event = make_event_from_dict(event_dict, room_ver, 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) stream_id = await self.event_creation_handler.persist_and_notify_client_event( requester, event, context, ratelimit=ratelimit, extra_users=extra_users) return 200, {"stream_id": stream_id}
def on_POST(self, request): content = parse_json_object_from_request(request) remote_room_hosts = content["remote_room_hosts"] room_id = content["room_id"] user_id = content["user_id"] event_content = content["content"] requester = Requester.deserialize(self.store, content["requester"]) if requester.user: request.authenticated_entity = requester.user.to_string() logger.info( "remote_join: %s into room: %s", user_id, room_id, ) yield self.federation_handler.do_invite_join( remote_room_hosts, room_id, user_id, event_content, ) defer.returnValue((200, {}))
def test_exposed_to_prometheus(self): """ Forward extremity counts are exposed via Prometheus. """ room_creator = self.hs.get_room_creation_handler() user = UserID("alice", "test") requester = Requester(user, None, False, None, None) # Real events, forward extremities events = [(3, 2), (6, 2), (4, 6)] for event_count, extrems in events: info = self.get_success(room_creator.create_room(requester, {})) room_id = info["room_id"] last_event = None # Make a real event chain for i in range(event_count): ev = self.create_and_send_event(room_id, user, False, last_event) last_event = [ev] # Sprinkle in some extremities for i in range(extrems): ev = self.create_and_send_event(room_id, user, False, last_event) # Let it run for a while, then pull out the statistics from the # Prometheus client registry self.reactor.advance(60 * 60 * 1000) self.pump(1) items = set( filter( lambda x: b"synapse_forward_extremities_" in x, generate_latest(REGISTRY).split(b"\n"), ) ) expected = set( [ b'synapse_forward_extremities_bucket{le="1.0"} 0.0', b'synapse_forward_extremities_bucket{le="2.0"} 2.0', b'synapse_forward_extremities_bucket{le="3.0"} 2.0', b'synapse_forward_extremities_bucket{le="5.0"} 2.0', b'synapse_forward_extremities_bucket{le="7.0"} 3.0', b'synapse_forward_extremities_bucket{le="10.0"} 3.0', b'synapse_forward_extremities_bucket{le="15.0"} 3.0', b'synapse_forward_extremities_bucket{le="20.0"} 3.0', b'synapse_forward_extremities_bucket{le="50.0"} 3.0', b'synapse_forward_extremities_bucket{le="100.0"} 3.0', b'synapse_forward_extremities_bucket{le="200.0"} 3.0', b'synapse_forward_extremities_bucket{le="500.0"} 3.0', b'synapse_forward_extremities_bucket{le="+Inf"} 3.0', b"synapse_forward_extremities_count 3.0", b"synapse_forward_extremities_sum 10.0", ] ) self.assertEqual(items, expected)
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"] internal_metadata = content["internal_metadata"] rejected_reason = content["rejected_reason"] event = FrozenEvent(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, {}))
async def _serialize_payload( # type: ignore[override] event_id: str, store: "DataStore", event: EventBase, context: EventContext, requester: Requester, ratelimit: bool, extra_users: List[UserID], ) -> JsonDict: """ Args: event_id store requester event context ratelimit extra_users: Any extra users to notify about event """ serialized_context = await context.serialize(event, store) payload = { "event": event.get_pdu_json(), "room_version": event.room_version.identifier, "event_format_version": event.format_version, "internal_metadata": event.internal_metadata.get_dict(), "outlier": event.internal_metadata.is_outlier(), "rejected_reason": event.rejected_reason, "context": serialized_context, "requester": requester.serialize(), "ratelimit": ratelimit, "extra_users": [u.to_string() for u in extra_users], } return payload
def _update_join_states(self, requester): user = requester.user if not self.hs.is_mine(user): return self.ratelimit(requester) joins = yield self.store.get_rooms_for_user( user.to_string(), ) for j in joins: handler = self.hs.get_handlers().room_member_handler try: # Assume the user isn't a guest because we don't let guests set # profile or avatar data. requester = Requester(user, "", False) yield handler.update_membership( requester, user, j.room_id, "join", # We treat a profile update like a join. ratelimit=False, # Try to hide that these events aren't atomic. ) except Exception as e: logger.warn( "Failed to update join event for room %s - %s", j.room_id, str(e.message) )
def test_blocking_mau__appservice_requester_disallowed_when_tracking_ips( self): self.auth_blocking._max_mau_value = 50 self.auth_blocking._limit_usage_by_mau = True self.auth_blocking._track_appservice_user_ips = True self.store.get_monthly_active_count = simple_async_mock(100) self.store.user_last_seen_monthly_active = simple_async_mock() self.store.is_trial_user = simple_async_mock() appservice = ApplicationService( "abcd", self.hs.config.server.server_name, id="1234", namespaces={ "users": [{ "regex": "@_appservice.*:sender", "exclusive": True }] }, sender="@appservice:sender", ) requester = Requester( user="******", access_token_id=None, device_id="FOOBAR", is_guest=False, shadow_banned=False, app_service=appservice, authenticated_entity="@appservice:server", ) self.get_failure(self.auth.check_auth_blocking(requester=requester), ResourceLimitError)
def test_events(self): get = self.get(events="-1", timeout="0") yield self.hs.get_handlers().room_creation_handler.create_room( Requester(self.user, "", False), {}) code, body = yield get self.assertEquals(code, 200) self.assertEquals(body["events"]["field_names"], ["position", "internal", "json"])
def prepare(self, reactor, clock, homeserver): self.store = homeserver.get_datastore() self.room_creator = homeserver.get_room_creation_handler() # Create a test user and room self.user = UserID("alice", "test") self.requester = Requester(self.user, None, False, None, None) info, _ = self.get_success(self.room_creator.create_room(self.requester, {})) self.room_id = info["room_id"]
def test_events_and_state(self): get = self.get(events="-1", state="-1", timeout="0") yield self.hs.get_handlers().room_creation_handler.create_room( Requester(self.user, "", False), {}) code, body = yield get self.assertEquals(code, 200) self.assertEquals(body["events"]["field_names"], ["position", "internal", "json", "state_group"]) self.assertEquals(body["state_groups"]["field_names"], ["position", "room_id", "event_id"]) self.assertEquals(body["state_group_state"]["field_names"], ["position", "type", "state_key", "event_id"])
def get_or_create_user(self, localpart, displayname, duration_seconds): """Creates a new user if the user does not exist, else revokes all previous access tokens and generates a new one. Args: localpart : The local part of the user ID to register. If None, one will be randomly generated. Returns: A tuple of (user_id, access_token). Raises: RegistrationError if there was a problem registering. """ yield run_on_reactor() if localpart is None: raise SynapseError(400, "Request must include user id") need_register = True try: yield self.check_username(localpart) except SynapseError as e: if e.errcode == Codes.USER_IN_USE: need_register = False else: raise user = UserID(localpart, self.hs.hostname) user_id = user.to_string() auth_handler = self.hs.get_handlers().auth_handler token = auth_handler.generate_short_term_login_token( user_id, duration_seconds) if need_register: yield self.store.register(user_id=user_id, token=token, password_hash=None) yield registered_user(self.distributor, user) else: yield self.store.user_delete_access_tokens(user_id=user_id) yield self.store.add_access_token_to_user(user_id=user_id, token=token) if displayname is not None: logger.info("setting user display name: %s -> %s", user_id, displayname) profile_handler = self.hs.get_handlers().profile_handler yield profile_handler.set_displayname( user, Requester(user, token, False), displayname) defer.returnValue((user_id, token))
def prepare(self, reactor, clock, homeserver): self.store = homeserver.get_datastore() self.room_creator = homeserver.get_room_creation_handler() self.event_creator_handler = homeserver.get_event_creation_handler() # Create a test user and room self.user = UserID.from_string(self.register_user("user1", "password")) self.token1 = self.login("user1", "password") self.requester = Requester(self.user, None, False, None, None) info, _ = self.get_success(self.room_creator.create_room(self.requester, {})) self.room_id = info["room_id"] self.event_creator = homeserver.get_event_creation_handler() homeserver.config.user_consent_version = self.CONSENT_VERSION
async def _handle_request(self, request, invite_event_id): content = parse_json_object_from_request(request) txn_id = content["txn_id"] event_content = content["content"] requester = Requester.deserialize(self.store, content["requester"]) request.requester = requester # hopefully we're now on the master, so this won't recurse! event_id, stream_id = await self.member_handler.remote_reject_invite( invite_event_id, txn_id, requester, event_content, ) return 200, {"event_id": event_id, "stream_id": stream_id}
async def _handle_request( # type: ignore self, request: SynapseRequest, room_id: str, user_id: str) -> Tuple[int, JsonDict]: content = parse_json_object_from_request(request) remote_room_hosts = content["remote_room_hosts"] event_content = content["content"] requester = Requester.deserialize(self.store, content["requester"]) request.requester = requester logger.info("remote_join: %s into room: %s", user_id, room_id) event_id, stream_id = await self.federation_handler.do_invite_join( remote_room_hosts, room_id, user_id, event_content) return 200, {"event_id": event_id, "stream_id": stream_id}
def create_and_send_event(self, room_id, user, soft_failed=False, prev_event_ids=None): """ Create and send an event. Args: soft_failed (bool): Whether to create a soft failed event or not prev_event_ids (list[str]|None): Explicitly set the prev events, or if None just use the default Returns: str: The new event's ID. """ event_creator = self.hs.get_event_creation_handler() secrets = self.hs.get_secrets() requester = Requester(user, None, False, None, None) prev_events_and_hashes = None if prev_event_ids: prev_events_and_hashes = [[p, {}, 0] for p in prev_event_ids] event, context = self.get_success( event_creator.create_event( requester, { "type": EventTypes.Message, "room_id": room_id, "sender": user.to_string(), "content": { "body": secrets.token_hex(), "msgtype": "m.text" }, }, prev_events_and_hashes=prev_events_and_hashes, )) if soft_failed: event.internal_metadata.soft_failed = True self.get_success( event_creator.send_nonmember_event(requester, event, context)) return event.event_id
async def _handle_request(self, request, room_id, user_id): content = parse_json_object_from_request(request) remote_room_hosts = content["remote_room_hosts"] event_content = content["content"] requester = Requester.deserialize(self.store, content["requester"]) if requester.user: request.authenticated_entity = requester.user.to_string() logger.info("remote_join: %s into room: %s", user_id, room_id) event_id, stream_id = await self.federation_handler.do_invite_join( remote_room_hosts, room_id, user_id, event_content) return 200, {"event_id": event_id, "stream_id": stream_id}
async def _serialize_payload( # type: ignore knock_event_id: str, txn_id: Optional[str], requester: Requester, content: JsonDict, ): """ Args: knock_event_id: The ID of the knock to be rescinded. txn_id: An optional transaction ID supplied by the client. requester: The user making the rescind request, according to the access token. content: The content to include in the rescind event. """ return { "txn_id": txn_id, "requester": requester.serialize(), "content": content, }
def _handle_request(self, request): content = parse_json_object_from_request(request) medium = content["medium"] address = content["address"] inviter_user_id = content["inviter_user_id"] requester = Requester.deserialize(self.store, content["requester"]) if requester.user: request.authenticated_entity = requester.user.to_string() logger.info("get_or_register_3pid_guest: %r", content) ret = yield self.registeration_handler.get_or_register_3pid_guest( medium, address, inviter_user_id) defer.returnValue((200, ret))
async def _serialize_payload( # type: ignore invite_event_id: str, txn_id: Optional[str], requester: Requester, content: JsonDict, ): """ Args: invite_event_id: ID of the invite to be rejected txn_id: optional transaction ID supplied by the client requester: user making the rejection request, according to the access token content: additional content to include in the rejection event. Normally an empty dict. """ return { "txn_id": txn_id, "requester": requester.serialize(), "content": content, }
def _handle_request(self, request): content = parse_json_object_from_request(request) medium = content["medium"] address = content["address"] inviter_user_id = content["inviter_user_id"] requester = Requester.deserialize(self.store, content["requester"]) if requester.user: request.authenticated_entity = requester.user.to_string() logger.info("get_or_register_3pid_guest: %r", content) ret = yield self.registeration_handler.get_or_register_3pid_guest( medium, address, inviter_user_id, ) defer.returnValue((200, ret))
async def _serialize_payload( # type: ignore requester: Requester, room_id: str, user_id: str, remote_room_hosts: List[str], content: JsonDict, ): """ Args: requester: The user making the request, according to the access token. room_id: The ID of the room to knock on. user_id: The ID of the knocking user. remote_room_hosts: Servers to try and send the knock via. content: The event content to use for the knock event. """ return { "requester": requester.serialize(), "remote_room_hosts": remote_room_hosts, "content": content, }
async def _handle_request( # type: ignore self, request: SynapseRequest, room_id: str, user_id: str, ): content = parse_json_object_from_request(request) remote_room_hosts = content["remote_room_hosts"] event_content = content["content"] requester = Requester.deserialize(self.store, content["requester"]) request.requester = requester logger.debug("remote_knock: %s on room: %s", user_id, room_id) event_id, stream_id = await self.federation_handler.do_knock( remote_room_hosts, room_id, user_id, event_content) return 200, {"event_id": event_id, "stream_id": stream_id}
async def _handle_request( # type: ignore[override] self, request: Request, event_id: str) -> Tuple[int, JsonDict]: with Measure(self.clock, "repl_send_event_parse"): content = parse_json_object_from_request(request) event_dict = content["event"] room_ver = KNOWN_ROOM_VERSIONS[content["room_version"]] internal_metadata = content["internal_metadata"] rejected_reason = content["rejected_reason"] event = make_event_from_dict(event_dict, room_ver, internal_metadata, rejected_reason) event.internal_metadata.outlier = content["outlier"] requester = Requester.deserialize(self.store, content["requester"]) context = EventContext.deserialize(self._storage_controllers, content["context"]) ratelimit = content["ratelimit"] extra_users = [ UserID.from_string(u) for u in content["extra_users"] ] logger.info("Got event to send with ID: %s into room: %s", event.event_id, event.room_id) event = await self.event_creation_handler.persist_and_notify_client_event( requester, event, context, ratelimit=ratelimit, extra_users=extra_users) return ( 200, { "stream_id": event.internal_metadata.stream_ordering, "event_id": event.event_id, }, )
def on_POST(self, request): content = parse_json_object_from_request(request) remote_room_hosts = content["remote_room_hosts"] room_id = content["room_id"] user_id = content["user_id"] requester = Requester.deserialize(self.store, content["requester"]) if requester.user: request.authenticated_entity = requester.user.to_string() logger.info( "remote_reject_invite: %s out of room: %s", user_id, room_id, ) try: event = yield self.federation_handler.do_remotely_reject_invite( remote_room_hosts, room_id, user_id, ) ret = event.get_pdu_json() except Exception as e: # if we were unable to reject the exception, just mark # it as rejected on our end and plough ahead. # # The 'except' clause is very broad, but we need to # capture everything from DNS failures upwards # logger.warn("Failed to reject invite: %s", e) yield self.store.locally_reject_invite( user_id, room_id ) ret = {} defer.returnValue((200, ret))
async def _serialize_payload( # type: ignore[override] invite_event_id: str, txn_id: Optional[str], requester: Requester, content: JsonDict, ) -> JsonDict: """ Args: invite_event_id: The ID of the invite to be rejected. txn_id: Optional transaction ID supplied by the client requester: User making the rejection request, according to the access token content: Additional content to include in the rejection event. Normally an empty dict. Returns: A dict representing the payload of the request. """ return { "txn_id": txn_id, "requester": requester.serialize(), "content": content, }
async def _handle_request( # type: ignore[override] self, request: SynapseRequest, knock_event_id: str, ) -> Tuple[int, JsonDict]: content = parse_json_object_from_request(request) txn_id = content["txn_id"] event_content = content["content"] requester = Requester.deserialize(self.store, content["requester"]) request.requester = requester # hopefully we're now on the master, so this won't recurse! event_id, stream_id = await self.member_handler.remote_rescind_knock( knock_event_id, txn_id, requester, event_content, ) return 200, {"event_id": event_id, "stream_id": stream_id}
async def _serialize_payload( # type: ignore requester: Requester, room_id: str, user_id: str, remote_room_hosts: List[str], content: JsonDict, ) -> JsonDict: """ Args: requester: The user making the request according to the access token room_id: The ID of the room. user_id: The ID of the user. remote_room_hosts: Servers to try and join via content: The event content to use for the join event Returns: A dict representing the payload of the request. """ return { "requester": requester.serialize(), "remote_room_hosts": remote_room_hosts, "content": content, }
def kick_guest_users(self, current_state): for member_event in current_state: try: if member_event.type != EventTypes.Member: continue target_user = UserID.from_string(member_event.state_key) if not self.hs.is_mine(target_user): continue if member_event.content["membership"] not in { Membership.JOIN, Membership.INVITE }: continue if ("kind" not in member_event.content or member_event.content["kind"] != "guest"): continue # We make the user choose to leave, rather than have the # event-sender kick them. This is partially because we don't # need to worry about power levels, and partially because guest # users are a concept which doesn't hugely work over federation, # and having homeservers have their own users leave keeps more # of that decision-making and control local to the guest-having # homeserver. requester = Requester(target_user, "", True) handler = self.hs.get_handlers().room_member_handler yield handler.update_membership( requester, target_user, member_event.room_id, "leave", ratelimit=False, ) except Exception as e: logger.warn("Error kicking guest user: %s" % (e, ))
async def _handle_request(self, request, room_id, user_id): content = parse_json_object_from_request(request) remote_room_hosts = content["remote_room_hosts"] event_content = content["content"] requester = Requester.deserialize(self.store, content["requester"]) if requester.user: request.authenticated_entity = requester.user.to_string() logger.info("remote_reject_invite: %s out of room: %s", user_id, room_id) try: event, stream_id = await self.federation_handler.do_remotely_reject_invite( remote_room_hosts, room_id, user_id, event_content, ) event_id = event.event_id except Exception as e: # if we were unable to reject the exception, just mark # it as rejected on our end and plough ahead. # # The 'except' clause is very broad, but we need to # capture everything from DNS failures upwards # logger.warning("Failed to reject invite: %s", e) stream_id = await self.member_handler.locally_reject_invite( user_id, room_id) event_id = None return 200, {"event_id": event_id, "stream_id": stream_id}