def on_POST(self, request): body = parse_json_object_from_request(request) assert_params_in_dict(body, ['medium', 'address']) requester = yield self.auth.get_user_by_req(request) user_id = requester.user.to_string() try: ret = yield self.auth_handler.delete_threepid( user_id, body['medium'], body['address'], body.get("id_server"), ) except Exception: # NB. This endpoint should succeed if there is nothing to # delete, so it should only throw if something is wrong # that we ought to care about. logger.exception("Failed to remove threepid") raise SynapseError(500, "Failed to remove threepid") if ret: id_server_unbind_result = "success" else: id_server_unbind_result = "no-support" defer.returnValue((200, { "id_server_unbind_result": id_server_unbind_result, }))
def on_POST(self, request, room_id, event_id): requester = yield self.auth.get_user_by_req(request) user_id = requester.user.to_string() body = parse_json_object_from_request(request) assert_params_in_dict(body, ("reason", "score")) if not isinstance(body["reason"], string_types): raise SynapseError( http_client.BAD_REQUEST, "Param 'reason' must be a string", Codes.BAD_JSON, ) if not isinstance(body["score"], int): raise SynapseError( http_client.BAD_REQUEST, "Param 'score' must be an integer", Codes.BAD_JSON, ) yield self.store.add_event_report( room_id=room_id, event_id=event_id, user_id=user_id, reason=body["reason"], content=body, received_ts=self.clock.time_msec(), ) defer.returnValue((200, {}))
def event_from_pdu_json(pdu_json, outlier=False): """Construct a FrozenEvent from an event json received over federation Args: pdu_json (object): pdu as received over federation 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, ('event_id', '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 = FrozenEvent( pdu_json ) event.internal_metadata.outlier = outlier return event
def on_POST(self, request): requester = yield self.auth.get_user_by_req(request) try: body = parse_json_object_from_request(request) except errors.SynapseError as e: if e.errcode == errors.Codes.NOT_JSON: # DELETE # deal with older clients which didn't pass a JSON dict # the same as those that pass an empty dict body = {} else: raise e assert_params_in_dict(body, ["devices"]) yield self.auth_handler.validate_user_via_ui_auth( requester, body, self.hs.get_ip_from_request(request), ) yield self.device_handler.delete_devices( requester.user.to_string(), body['devices'], ) defer.returnValue((200, {}))
def on_POST(self, request): body = parse_json_object_from_request(request) assert_params_in_dict(body, [ 'id_server', 'client_secret', 'country', 'phone_number', 'send_attempt', ]) msisdn = phone_number_to_msisdn(body['country'], body['phone_number']) if not check_3pid_allowed(self.hs, "msisdn", msisdn): raise SynapseError( 403, "Account phone numbers are not authorized on this server", Codes.THREEPID_DENIED, ) existingUid = yield self.datastore.get_user_id_by_threepid( 'msisdn', msisdn ) if existingUid is None: raise SynapseError(400, "MSISDN not found", Codes.THREEPID_NOT_FOUND) ret = yield self.identity_handler.requestMsisdnToken(**body) defer.returnValue((200, ret))
def on_POST(self, request, target_user_id): """Post request to get specific number of users from Synapse.. This needs user to have administrator access in Synapse. Example: http://localhost:8008/_synapse/admin/v1/users_paginate/ @admin:user?access_token=admin_access_token JsonBodyToSend: { "start": "0", "limit": "10 } Returns: 200 OK with json object {list[dict[str, Any]], count} or empty object. """ yield assert_requester_is_admin(self.auth, request) UserID.from_string(target_user_id) order = "name" # order by name in user table params = parse_json_object_from_request(request) assert_params_in_dict(params, ["limit", "start"]) limit = params['limit'] start = params['start'] logger.info("limit: %s, start: %s", limit, start) ret = yield self.handlers.admin_handler.get_users_paginate( order, start, limit ) defer.returnValue((200, ret))
def on_POST(self, request, room_id, membership_action, txn_id=None): requester = yield self.auth.get_user_by_req( request, allow_guest=True, ) if requester.is_guest and membership_action not in { Membership.JOIN, Membership.LEAVE }: raise AuthError(403, "Guest access not allowed") try: content = parse_json_object_from_request(request) except Exception: # Turns out we used to ignore the body entirely, and some clients # cheekily send invalid bodies. content = {} if membership_action == "invite" and self._has_3pid_invite_keys(content): yield self.room_member_handler.do_3pid_invite( room_id, requester.user, content["medium"], content["address"], content["id_server"], requester, txn_id ) defer.returnValue((200, {})) return target = requester.user if membership_action in ["invite", "ban", "unban", "kick"]: assert_params_in_dict(content, ["user_id"]) target = UserID.from_string(content["user_id"]) event_content = None if 'reason' in content and membership_action in ['kick', 'ban']: event_content = {'reason': content['reason']} yield self.room_member_handler.update_membership( requester=requester, target=target, room_id=room_id, action=membership_action, txn_id=txn_id, third_party_signed=content.get("third_party_signed", None), content=event_content, ) return_value = {} if membership_action == "join": return_value["room_id"] = room_id defer.returnValue((200, return_value))
def on_POST(self, request): requester = yield self.auth.get_user_by_req(request) user = requester.user content = parse_json_object_from_request(request) if ('pushkey' in content and 'app_id' in content and 'kind' in content and content['kind'] is None): yield self.pusher_pool.remove_pusher( content['app_id'], content['pushkey'], user_id=user.to_string() ) defer.returnValue((200, {})) assert_params_in_dict( content, ['kind', 'app_id', 'app_display_name', 'device_display_name', 'pushkey', 'lang', 'data'] ) logger.debug("set pushkey %s to kind %s", content['pushkey'], content['kind']) logger.debug("Got pushers request with body: %r", content) append = False if 'append' in content: append = content['append'] if not append: yield self.pusher_pool.remove_pushers_by_app_id_and_pushkey_not_user( app_id=content['app_id'], pushkey=content['pushkey'], not_user_id=user.to_string() ) try: yield self.pusher_pool.add_pusher( user_id=user.to_string(), access_token=requester.access_token_id, kind=content['kind'], app_id=content['app_id'], app_display_name=content['app_display_name'], device_display_name=content['device_display_name'], pushkey=content['pushkey'], lang=content['lang'], data=content['data'], profile_tag=content.get('profile_tag', ""), ) except PusherConfigException as pce: raise SynapseError(400, "Config Error: " + str(pce), errcode=Codes.MISSING_PARAM) self.notifier.on_new_replication_data() defer.returnValue((200, {}))
def on_POST(self, request): body = parse_json_object_from_request(request) # there are two possibilities here. Either the user does not have an # access token, and needs to do a password reset; or they have one and # need to validate their identity. # # In the first case, we offer a couple of means of identifying # themselves (email and msisdn, though it's unclear if msisdn actually # works). # # In the second case, we require a password to confirm their identity. if self.auth.has_access_token(request): requester = yield self.auth.get_user_by_req(request) params = yield self.auth_handler.validate_user_via_ui_auth( requester, body, self.hs.get_ip_from_request(request), ) user_id = requester.user.to_string() else: requester = None result, params, _ = yield self.auth_handler.check_auth( [[LoginType.EMAIL_IDENTITY], [LoginType.MSISDN]], body, self.hs.get_ip_from_request(request), ) if LoginType.EMAIL_IDENTITY in result: threepid = result[LoginType.EMAIL_IDENTITY] if 'medium' not in threepid or 'address' not in threepid: raise SynapseError(500, "Malformed threepid") if threepid['medium'] == 'email': # For emails, transform the address to lowercase. # We store all email addreses as lowercase in the DB. # (See add_threepid in synapse/handlers/auth.py) threepid['address'] = threepid['address'].lower() # if using email, we must know about the email they're authing with! threepid_user_id = yield self.datastore.get_user_id_by_threepid( threepid['medium'], threepid['address'] ) if not threepid_user_id: raise SynapseError(404, "Email address not found", Codes.NOT_FOUND) user_id = threepid_user_id else: logger.error("Auth succeeded but no known type! %r", result.keys()) raise SynapseError(500, "", Codes.UNKNOWN) assert_params_in_dict(params, ["new_password"]) new_password = params['new_password'] yield self._set_password_handler.set_password( user_id, new_password, requester ) defer.returnValue((200, {}))
def on_POST(self, request, target_user_id): """Post request to allow an administrator reset password for a user. This needs user to have administrator access in Synapse. """ requester = yield self.auth.get_user_by_req(request) yield assert_user_is_admin(self.auth, requester.user) UserID.from_string(target_user_id) params = parse_json_object_from_request(request) assert_params_in_dict(params, ["new_password"]) new_password = params['new_password'] yield self._set_password_handler.set_password( target_user_id, new_password, requester ) defer.returnValue((200, {}))
def on_POST(self, request, target_user_id): """Post request to allow an administrator reset password for a user. This needs user to have administrator access in Synapse. """ UserID.from_string(target_user_id) requester = yield self.auth.get_user_by_req(request) is_admin = yield self.auth.is_server_admin(requester.user) if not is_admin: raise AuthError(403, "You are not a server admin") params = parse_json_object_from_request(request) assert_params_in_dict(params, ["new_password"]) new_password = params['new_password'] logger.info("new_password: %r", new_password) yield self._set_password_handler.set_password( target_user_id, new_password, requester ) defer.returnValue((200, {}))
def _register_msisdn_threepid(self, user_id, threepid, bind_msisdn): """Add a phone number as a 3pid identifier Also optionally binds msisdn to the given user_id on the identity server Must be called on master. Args: user_id (str): id of user threepid (object): m.login.msisdn auth response token (str): access_token for the user bind_email (bool): true if the client requested the email to be bound at the identity server Returns: defer.Deferred: """ try: assert_params_in_dict(threepid, ['medium', 'address', 'validated_at']) except SynapseError as ex: if ex.errcode == Codes.MISSING_PARAM: # This will only happen if the ID server returns a malformed response logger.info("Can't add incomplete 3pid") defer.returnValue(None) raise yield self._auth_handler.add_threepid( user_id, threepid['medium'], threepid['address'], threepid['validated_at'], ) if bind_msisdn: logger.info("bind_msisdn specified: binding") logger.debug("Binding msisdn %s to %s", threepid, user_id) yield self.identity_handler.bind_threepid( threepid['threepid_creds'], user_id ) else: logger.info("bind_msisdn not specified: not binding msisdn")
def on_POST(self, request, room_id): requester = yield self._auth.get_user_by_req(request) content = parse_json_object_from_request(request) assert_params_in_dict(content, ("new_version", )) new_version = content["new_version"] if new_version not in KNOWN_ROOM_VERSIONS: raise SynapseError( 400, "Your homeserver does not support this room version", Codes.UNSUPPORTED_ROOM_VERSION, ) new_room_id = yield self._room_creation_handler.upgrade_room( requester, room_id, new_version ) ret = { "replacement_room": new_room_id, } defer.returnValue((200, ret))
def on_POST(self, request): body = parse_json_object_from_request(request) assert_params_in_dict(body, [ 'id_server', 'client_secret', 'email', 'send_attempt' ]) if not check_3pid_allowed(self.hs, "email", body['email']): raise SynapseError( 403, "Your email domain is not authorized on this server", Codes.THREEPID_DENIED, ) existingUid = yield self.hs.get_datastore().get_user_id_by_threepid( 'email', body['email'] ) if existingUid is None: raise SynapseError(400, "Email not found", Codes.THREEPID_NOT_FOUND) ret = yield self.identity_handler.requestEmailToken(**body) defer.returnValue((200, ret))
def on_POST(self, request, txn_id=None): yield assert_requester_is_admin(self.auth, request) body = parse_json_object_from_request(request) assert_params_in_dict(body, ("user_id", "content")) event_type = body.get("type", EventTypes.Message) state_key = body.get("state_key") if not self.snm.is_enabled(): raise SynapseError(400, "Server notices are not enabled on this server") user_id = body["user_id"] UserID.from_string(user_id) if not self.hs.is_mine_id(user_id): raise SynapseError(400, "Server notices can only be sent to local users") event = yield self.snm.send_notice( user_id=body["user_id"], type=event_type, state_key=state_key, event_content=body["content"], ) defer.returnValue((200, {"event_id": event.event_id}))
def on_POST(self, request, room_id): requester = yield self.auth.get_user_by_req(request) is_admin = yield self.auth.is_server_admin(requester.user) if not is_admin: raise AuthError(403, "You are not a server admin") content = parse_json_object_from_request(request) assert_params_in_dict(content, ["new_room_user_id"]) new_room_user_id = content["new_room_user_id"] room_creator_requester = create_requester(new_room_user_id) message = content.get("message", self.DEFAULT_MESSAGE) room_name = content.get("room_name", "Content Violation Notification") info = yield self._room_creation_handler.create_room( room_creator_requester, config={ "preset": "public_chat", "name": room_name, "power_level_content_override": { "users_default": -10, }, }, ratelimit=False, ) new_room_id = info["room_id"] yield self.event_creation_handler.create_and_send_nonmember_event( room_creator_requester, { "type": "m.room.message", "content": {"body": message, "msgtype": "m.text"}, "room_id": new_room_id, "sender": new_room_user_id, }, ratelimit=False, ) requester_user_id = requester.user.to_string() logger.info("Shutting down room %r", room_id) yield self.store.block_room(room_id, requester_user_id) users = yield self.state.get_current_user_in_room(room_id) kicked_users = [] for user_id in users: if not self.hs.is_mine_id(user_id): continue logger.info("Kicking %r from %r...", user_id, room_id) target_requester = create_requester(user_id) yield self.room_member_handler.update_membership( requester=target_requester, target=target_requester.user, room_id=room_id, action=Membership.LEAVE, content={}, ratelimit=False ) yield self.room_member_handler.forget(target_requester.user, room_id) yield self.room_member_handler.update_membership( requester=target_requester, target=target_requester.user, room_id=new_room_id, action=Membership.JOIN, content={}, ratelimit=False ) kicked_users.append(user_id) aliases_for_room = yield self.store.get_aliases_for_room(room_id) yield self.store.update_aliases_for_room( room_id, new_room_id, requester_user_id ) defer.returnValue((200, { "kicked_users": kicked_users, "local_aliases": aliases_for_room, "new_room_id": new_room_id, }))
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]: if self.config.email.threepid_behaviour_email == ThreepidBehaviour.OFF: if self.config.email.local_threepid_handling_disabled_due_to_email_config: logger.warning( "Adding emails have been disabled due to lack of an email config" ) raise SynapseError( 400, "Adding an email to your account is disabled on this server") body = parse_json_object_from_request(request) assert_params_in_dict(body, ["client_secret", "email", "send_attempt"]) client_secret = body["client_secret"] assert_valid_client_secret(client_secret) # Canonicalise the email address. The addresses are all stored canonicalised # in the database. # This ensures that the validation email is sent to the canonicalised address # as it will later be entered into the database. # Otherwise the email will be sent to "*****@*****.**" and stored as # "*****@*****.**" in database. try: email = validate_email(body["email"]) except ValueError as e: raise SynapseError(400, str(e)) send_attempt = body["send_attempt"] next_link = body.get("next_link") # Optional param if not await check_3pid_allowed(self.hs, "email", email): raise SynapseError( 403, "Your email domain is not authorized on this server", Codes.THREEPID_DENIED, ) await self.identity_handler.ratelimit_request_token_requests( request, "email", email) if next_link: # Raise if the provided next_link value isn't valid assert_valid_next_link(self.hs, next_link) existing_user_id = await self.store.get_user_id_by_threepid( "email", email) if existing_user_id is not None: if self.config.server.request_token_inhibit_3pid_errors: # Make the client think the operation succeeded. See the rationale in the # comments for request_token_inhibit_3pid_errors. # Also wait for some random amount of time between 100ms and 1s to make it # look like we did something. await self.hs.get_clock().sleep(random.randint(1, 10) / 10) return 200, {"sid": random_string(16)} raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE) if self.config.email.threepid_behaviour_email == ThreepidBehaviour.REMOTE: assert self.hs.config.registration.account_threepid_delegate_email # Have the configured identity server handle the request ret = await self.identity_handler.requestEmailToken( self.hs.config.registration.account_threepid_delegate_email, email, client_secret, send_attempt, next_link, ) else: # Send threepid validation emails from Synapse sid = await self.identity_handler.send_threepid_validation( email, client_secret, send_attempt, self.mailer.send_add_threepid_mail, next_link, ) # Wrap the session id in a JSON object ret = {"sid": sid} threepid_send_requests.labels( type="email", reason="add_threepid").observe(send_attempt) return 200, ret
async def persist_state_events_at_start( self, state_events_at_start: List[JsonDict], room_id: str, initial_auth_event_ids: List[str], app_service_requester: Requester, ) -> List[str]: """Takes all `state_events_at_start` event dictionaries and creates/persists them as floating state events which don't resolve into the current room state. They are floating because they reference a fake prev_event which doesn't connect to the normal DAG at all. Args: state_events_at_start: room_id: Room where you want the events persisted in. initial_auth_event_ids: These will be the auth_events for the first state event created. Each event created afterwards will be added to the list of auth events for the next state event created. app_service_requester: The requester of an application service. Returns: List of state event ID's we just persisted """ assert app_service_requester.app_service state_event_ids_at_start = [] auth_event_ids = initial_auth_event_ids.copy() # Make the state events float off on their own so we don't have a # bunch of `@mxid joined the room` noise between each batch prev_event_id_for_state_chain = generate_fake_event_id() for state_event in state_events_at_start: assert_params_in_dict( state_event, ["type", "origin_server_ts", "content", "sender"]) logger.debug( "RoomBatchSendEventRestServlet inserting state_event=%s, auth_event_ids=%s", state_event, auth_event_ids, ) event_dict = { "type": state_event["type"], "origin_server_ts": state_event["origin_server_ts"], "content": state_event["content"], "room_id": room_id, "sender": state_event["sender"], "state_key": state_event["state_key"], } # Mark all events as historical event_dict["content"][EventContentFields.MSC2716_HISTORICAL] = True # TODO: This is pretty much the same as some other code to handle inserting state in this file if event_dict["type"] == EventTypes.Member: membership = event_dict["content"].get("membership", None) event_id, _ = await self.room_member_handler.update_membership( await self.create_requester_for_user_id_from_app_service( state_event["sender"], app_service_requester.app_service), target=UserID.from_string(event_dict["state_key"]), room_id=room_id, action=membership, content=event_dict["content"], outlier=True, historical=True, prev_event_ids=[prev_event_id_for_state_chain], # Make sure to use a copy of this list because we modify it # later in the loop here. Otherwise it will be the same # reference and also update in the event when we append later. auth_event_ids=auth_event_ids.copy(), ) else: # TODO: Add some complement tests that adds state that is not member joins # and will use this code path. Maybe we only want to support join state events # and can get rid of this `else`? ( event, _, ) = await self.event_creation_handler.create_and_send_nonmember_event( await self.create_requester_for_user_id_from_app_service( state_event["sender"], app_service_requester.app_service), event_dict, outlier=True, historical=True, prev_event_ids=[prev_event_id_for_state_chain], # Make sure to use a copy of this list because we modify it # later in the loop here. Otherwise it will be the same # reference and also update in the event when we append later. auth_event_ids=auth_event_ids.copy(), ) event_id = event.event_id state_event_ids_at_start.append(event_id) auth_event_ids.append(event_id) # Connect all the state in a floating chain prev_event_id_for_state_chain = event_id return state_event_ids_at_start
async def on_POST(self, request): body = parse_json_object_from_request(request) assert_params_in_dict( body, ["client_secret", "country", "phone_number", "send_attempt"]) client_secret = body["client_secret"] assert_valid_client_secret(client_secret) country = body["country"] phone_number = body["phone_number"] send_attempt = body["send_attempt"] next_link = body.get("next_link") # Optional param msisdn = phone_number_to_msisdn(country, phone_number) if not check_3pid_allowed(self.hs, "msisdn", msisdn): raise SynapseError( 403, "Phone numbers are not authorized to register on this server", Codes.THREEPID_DENIED, ) await self.identity_handler.ratelimit_request_token_requests( request, "msisdn", msisdn) existing_user_id = await self.hs.get_datastore( ).get_user_id_by_threepid("msisdn", msisdn) if existing_user_id is not None: if self.hs.config.request_token_inhibit_3pid_errors: # Make the client think the operation succeeded. See the rationale in the # comments for request_token_inhibit_3pid_errors. # Also wait for some random amount of time between 100ms and 1s to make it # look like we did something. await self.hs.get_clock().sleep(random.randint(1, 10) / 10) return 200, {"sid": random_string(16)} raise SynapseError(400, "Phone number is already in use", Codes.THREEPID_IN_USE) if not self.hs.config.account_threepid_delegate_msisdn: logger.warning( "No upstream msisdn account_threepid_delegate configured on the server to " "handle this request") raise SynapseError( 400, "Registration by phone number is not supported on this homeserver" ) ret = await self.identity_handler.requestMsisdnToken( self.hs.config.account_threepid_delegate_msisdn, country, phone_number, client_secret, send_attempt, next_link, ) threepid_send_requests.labels(type="msisdn", reason="register").observe(send_attempt) return 200, ret
def on_POST(self, request): body = parse_json_object_from_request(request) kind = "user" if "kind" in request.args: kind = request.args["kind"][0] if kind == "guest": ret = yield self._do_guest_registration(body) defer.returnValue(ret) return elif kind != "user": raise UnrecognizedRequestError( "Do not understand membership kind: %s" % (kind, )) # we do basic sanity checks here because the auth layer will store these # in sessions. Pull out the username/password provided to us. desired_password = None if 'password' in body: if (not isinstance(body['password'], string_types) or len(body['password']) > 512): raise SynapseError(400, "Invalid password") desired_password = body["password"] desired_username = None if 'username' in body: if (not isinstance(body['username'], string_types) or len(body['username']) > 512): raise SynapseError(400, "Invalid username") desired_username = body['username'] appservice = None if self.auth.has_access_token(request): appservice = yield self.auth.get_appservice_by_req(request) # fork off as soon as possible for ASes and shared secret auth which # have completely different registration flows to normal users # == Application Service Registration == if appservice: # Set the desired user according to the AS API (which uses the # 'user' key not 'username'). Since this is a new addition, we'll # fallback to 'username' if they gave one. desired_username = body.get("user", desired_username) # XXX we should check that desired_username is valid. Currently # we give appservices carte blanche for any insanity in mxids, # because the IRC bridges rely on being able to register stupid # IDs. access_token = self.auth.get_access_token_from_request(request) if isinstance(desired_username, string_types): result = yield self._do_appservice_registration( desired_username, access_token, body) defer.returnValue((200, result)) # we throw for non 200 responses return # for either shared secret or regular registration, downcase the # provided username before attempting to register it. This should mean # that people who try to register with upper-case in their usernames # don't get a nasty surprise. (Note that we treat username # case-insenstively in login, so they are free to carry on imagining # that their username is CrAzYh4cKeR if that keeps them happy) if desired_username is not None: desired_username = desired_username.lower() # == Shared Secret Registration == (e.g. create new user scripts) if 'mac' in body: # FIXME: Should we really be determining if this is shared secret # auth based purely on the 'mac' key? result = yield self._do_shared_secret_registration( desired_username, desired_password, body) defer.returnValue((200, result)) # we throw for non 200 responses return # == Normal User Registration == (everyone else) if not self.hs.config.enable_registration: raise SynapseError(403, "Registration has been disabled") guest_access_token = body.get("guest_access_token", None) if ('initial_device_display_name' in body and 'password' not in body): # ignore 'initial_device_display_name' if sent without # a password to work around a client bug where it sent # the 'initial_device_display_name' param alone, wiping out # the original registration params logger.warn( "Ignoring initial_device_display_name without password") del body['initial_device_display_name'] session_id = self.auth_handler.get_session_id(body) registered_user_id = None if session_id: # if we get a registered user id out of here, it means we previously # registered a user for this session, so we could just return the # user here. We carry on and go through the auth checks though, # for paranoia. registered_user_id = self.auth_handler.get_session_data( session_id, "registered_user_id", None) if desired_username is not None: yield self.registration_handler.check_username( desired_username, guest_access_token=guest_access_token, assigned_user_id=registered_user_id, ) # Only give msisdn flows if the x_show_msisdn flag is given: # this is a hack to work around the fact that clients were shipped # that use fallback registration if they see any flows that they don't # recognise, which means we break registration for these clients if we # advertise msisdn flows. Once usage of Riot iOS <=0.3.9 and Riot # Android <=0.6.9 have fallen below an acceptable threshold, this # parameter should go away and we should always advertise msisdn flows. show_msisdn = False if 'x_show_msisdn' in body and body['x_show_msisdn']: show_msisdn = True # FIXME: need a better error than "no auth flow found" for scenarios # where we required 3PID for registration but the user didn't give one require_email = 'email' in self.hs.config.registrations_require_3pid require_msisdn = 'msisdn' in self.hs.config.registrations_require_3pid flows = [] if self.hs.config.enable_registration_captcha: # only support 3PIDless registration if no 3PIDs are required if not require_email and not require_msisdn: flows.extend([[LoginType.RECAPTCHA]]) # only support the email-only flow if we don't require MSISDN 3PIDs if not require_msisdn: flows.extend([[LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA]]) if show_msisdn: # only support the MSISDN-only flow if we don't require email 3PIDs if not require_email: flows.extend([[LoginType.MSISDN, LoginType.RECAPTCHA]]) # always let users provide both MSISDN & email flows.extend([ [ LoginType.MSISDN, LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA ], ]) else: # only support 3PIDless registration if no 3PIDs are required if not require_email and not require_msisdn: flows.extend([[LoginType.DUMMY]]) # only support the email-only flow if we don't require MSISDN 3PIDs if not require_msisdn: flows.extend([[LoginType.EMAIL_IDENTITY]]) if show_msisdn: # only support the MSISDN-only flow if we don't require email 3PIDs if not require_email or require_msisdn: flows.extend([[LoginType.MSISDN]]) # always let users provide both MSISDN & email flows.extend([[LoginType.MSISDN, LoginType.EMAIL_IDENTITY]]) auth_result, params, session_id = yield self.auth_handler.check_auth( flows, body, self.hs.get_ip_from_request(request)) # Check that we're not trying to register a denied 3pid. # # the user-facing checks will probably already have happened in # /register/email/requestToken when we requested a 3pid, but that's not # guaranteed. if auth_result: for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]: if login_type in auth_result: medium = auth_result[login_type]['medium'] address = auth_result[login_type]['address'] if not check_3pid_allowed(self.hs, medium, address): raise SynapseError( 403, "Third party identifier is not allowed", Codes.THREEPID_DENIED, ) if registered_user_id is not None: logger.info("Already registered user ID %r for this session", registered_user_id) # don't re-register the threepids add_email = False add_msisdn = False else: # NB: This may be from the auth handler and NOT from the POST assert_params_in_dict(params, ["password"]) desired_username = params.get("username", None) new_password = params.get("password", None) guest_access_token = params.get("guest_access_token", None) if desired_username is not None: desired_username = desired_username.lower() (registered_user_id, _) = yield self.registration_handler.register( localpart=desired_username, password=new_password, guest_access_token=guest_access_token, generate_token=False, ) # remember that we've now registered that user account, and with # what user ID (since the user may not have specified) self.auth_handler.set_session_data(session_id, "registered_user_id", registered_user_id) add_email = True add_msisdn = True return_dict = yield self._create_registration_details( registered_user_id, params) if add_email and auth_result and LoginType.EMAIL_IDENTITY in auth_result: threepid = auth_result[LoginType.EMAIL_IDENTITY] yield self._register_email_threepid(registered_user_id, threepid, return_dict["access_token"], params.get("bind_email")) if add_msisdn and auth_result and LoginType.MSISDN in auth_result: threepid = auth_result[LoginType.MSISDN] yield self._register_msisdn_threepid(registered_user_id, threepid, return_dict["access_token"], params.get("bind_msisdn")) defer.returnValue((200, return_dict))
async def persist_state_events_at_start( self, state_events_at_start: List[JsonDict], room_id: str, initial_state_event_ids: List[str], app_service_requester: Requester, ) -> List[str]: """Takes all `state_events_at_start` event dictionaries and creates/persists them in a floating state event chain which don't resolve into the current room state. They are floating because they reference no prev_events which disconnects them from the normal DAG. Args: state_events_at_start: room_id: Room where you want the events persisted in. initial_state_event_ids: The base set of state for the historical batch which the floating state chain will derive from. This should probably be the state from the `prev_event` defined by `/batch_send?prev_event_id=$abc`. app_service_requester: The requester of an application service. Returns: List of state event ID's we just persisted """ assert app_service_requester.app_service state_event_ids_at_start = [] state_event_ids = initial_state_event_ids.copy() # Make the state events float off on their own by specifying no # prev_events for the first one in the chain so we don't have a bunch of # `@mxid joined the room` noise between each batch. prev_event_ids_for_state_chain: List[str] = [] for index, state_event in enumerate(state_events_at_start): assert_params_in_dict( state_event, ["type", "origin_server_ts", "content", "sender"]) logger.debug( "RoomBatchSendEventRestServlet inserting state_event=%s", state_event) event_dict = { "type": state_event["type"], "origin_server_ts": state_event["origin_server_ts"], "content": state_event["content"], "room_id": room_id, "sender": state_event["sender"], "state_key": state_event["state_key"], } # Mark all events as historical event_dict["content"][EventContentFields.MSC2716_HISTORICAL] = True # TODO: This is pretty much the same as some other code to handle inserting state in this file if event_dict["type"] == EventTypes.Member: membership = event_dict["content"].get("membership", None) event_id, _ = await self.room_member_handler.update_membership( await self.create_requester_for_user_id_from_app_service( state_event["sender"], app_service_requester.app_service), target=UserID.from_string(event_dict["state_key"]), room_id=room_id, action=membership, content=event_dict["content"], historical=True, # Only the first event in the state chain should be floating. # The rest should hang off each other in a chain. allow_no_prev_events=index == 0, prev_event_ids=prev_event_ids_for_state_chain, # The first event in the state chain is floating with no # `prev_events` which means it can't derive state from # anywhere automatically. So we need to set some state # explicitly. # # Make sure to use a copy of this list because we modify it # later in the loop here. Otherwise it will be the same # reference and also update in the event when we append # later. state_event_ids=state_event_ids.copy(), ) else: ( event, _, ) = await self.event_creation_handler.create_and_send_nonmember_event( await self.create_requester_for_user_id_from_app_service( state_event["sender"], app_service_requester.app_service), event_dict, historical=True, # Only the first event in the state chain should be floating. # The rest should hang off each other in a chain. allow_no_prev_events=index == 0, prev_event_ids=prev_event_ids_for_state_chain, # The first event in the state chain is floating with no # `prev_events` which means it can't derive state from # anywhere automatically. So we need to set some state # explicitly. # # Make sure to use a copy of this list because we modify it # later in the loop here. Otherwise it will be the same # reference and also update in the event when we append later. state_event_ids=state_event_ids.copy(), ) event_id = event.event_id state_event_ids_at_start.append(event_id) state_event_ids.append(event_id) # Connect all the state in a floating chain prev_event_ids_for_state_chain = [event_id] return state_event_ids_at_start
def on_POST(self, request): body = parse_json_object_from_request(request) client_addr = request.getClientIP() time_now = self.clock.time() allowed, time_allowed = self.ratelimiter.can_do_action( client_addr, time_now_s=time_now, rate_hz=self.hs.config.rc_registration.per_second, burst_count=self.hs.config.rc_registration.burst_count, update=False, ) if not allowed: raise LimitExceededError( retry_after_ms=int(1000 * (time_allowed - time_now)), ) kind = b"user" if b"kind" in request.args: kind = request.args[b"kind"][0] if kind == b"guest": ret = yield self._do_guest_registration(body, address=client_addr) defer.returnValue(ret) return elif kind != b"user": raise UnrecognizedRequestError( "Do not understand membership kind: %s" % (kind,) ) # we do basic sanity checks here because the auth layer will store these # in sessions. Pull out the username/password provided to us. desired_password = None if 'password' in body: if (not isinstance(body['password'], string_types) or len(body['password']) > 512): raise SynapseError(400, "Invalid password") desired_password = body["password"] desired_username = None if 'username' in body: if (not isinstance(body['username'], string_types) or len(body['username']) > 512): raise SynapseError(400, "Invalid username") desired_username = body['username'] appservice = None if self.auth.has_access_token(request): appservice = yield self.auth.get_appservice_by_req(request) # fork off as soon as possible for ASes and shared secret auth which # have completely different registration flows to normal users # == Application Service Registration == if appservice: # Set the desired user according to the AS API (which uses the # 'user' key not 'username'). Since this is a new addition, we'll # fallback to 'username' if they gave one. desired_username = body.get("user", desired_username) # XXX we should check that desired_username is valid. Currently # we give appservices carte blanche for any insanity in mxids, # because the IRC bridges rely on being able to register stupid # IDs. access_token = self.auth.get_access_token_from_request(request) if isinstance(desired_username, string_types): result = yield self._do_appservice_registration( desired_username, access_token, body ) defer.returnValue((200, result)) # we throw for non 200 responses return # for either shared secret or regular registration, downcase the # provided username before attempting to register it. This should mean # that people who try to register with upper-case in their usernames # don't get a nasty surprise. (Note that we treat username # case-insenstively in login, so they are free to carry on imagining # that their username is CrAzYh4cKeR if that keeps them happy) if desired_username is not None: desired_username = desired_username.lower() # == Shared Secret Registration == (e.g. create new user scripts) if 'mac' in body: # FIXME: Should we really be determining if this is shared secret # auth based purely on the 'mac' key? result = yield self._do_shared_secret_registration( desired_username, desired_password, body ) defer.returnValue((200, result)) # we throw for non 200 responses return # == Normal User Registration == (everyone else) if not self.hs.config.enable_registration: raise SynapseError(403, "Registration has been disabled") guest_access_token = body.get("guest_access_token", None) if ( 'initial_device_display_name' in body and 'password' not in body ): # ignore 'initial_device_display_name' if sent without # a password to work around a client bug where it sent # the 'initial_device_display_name' param alone, wiping out # the original registration params logger.warn("Ignoring initial_device_display_name without password") del body['initial_device_display_name'] session_id = self.auth_handler.get_session_id(body) registered_user_id = None if session_id: # if we get a registered user id out of here, it means we previously # registered a user for this session, so we could just return the # user here. We carry on and go through the auth checks though, # for paranoia. registered_user_id = self.auth_handler.get_session_data( session_id, "registered_user_id", None ) if desired_username is not None: yield self.registration_handler.check_username( desired_username, guest_access_token=guest_access_token, assigned_user_id=registered_user_id, ) # FIXME: need a better error than "no auth flow found" for scenarios # where we required 3PID for registration but the user didn't give one require_email = 'email' in self.hs.config.registrations_require_3pid require_msisdn = 'msisdn' in self.hs.config.registrations_require_3pid show_msisdn = True if self.hs.config.disable_msisdn_registration: show_msisdn = False require_msisdn = False flows = [] if self.hs.config.enable_registration_captcha: # only support 3PIDless registration if no 3PIDs are required if not require_email and not require_msisdn: # Also add a dummy flow here, otherwise if a client completes # recaptcha first we'll assume they were going for this flow # and complete the request, when they could have been trying to # complete one of the flows with email/msisdn auth. flows.extend([[LoginType.RECAPTCHA, LoginType.DUMMY]]) # only support the email-only flow if we don't require MSISDN 3PIDs if not require_msisdn: flows.extend([[LoginType.RECAPTCHA, LoginType.EMAIL_IDENTITY]]) if show_msisdn: # only support the MSISDN-only flow if we don't require email 3PIDs if not require_email: flows.extend([[LoginType.RECAPTCHA, LoginType.MSISDN]]) # always let users provide both MSISDN & email flows.extend([ [LoginType.RECAPTCHA, LoginType.MSISDN, LoginType.EMAIL_IDENTITY], ]) else: # only support 3PIDless registration if no 3PIDs are required if not require_email and not require_msisdn: flows.extend([[LoginType.DUMMY]]) # only support the email-only flow if we don't require MSISDN 3PIDs if not require_msisdn: flows.extend([[LoginType.EMAIL_IDENTITY]]) if show_msisdn: # only support the MSISDN-only flow if we don't require email 3PIDs if not require_email or require_msisdn: flows.extend([[LoginType.MSISDN]]) # always let users provide both MSISDN & email flows.extend([ [LoginType.MSISDN, LoginType.EMAIL_IDENTITY] ]) # Append m.login.terms to all flows if we're requiring consent if self.hs.config.user_consent_at_registration: new_flows = [] for flow in flows: inserted = False # m.login.terms should go near the end but before msisdn or email auth for i, stage in enumerate(flow): if stage == LoginType.EMAIL_IDENTITY or stage == LoginType.MSISDN: flow.insert(i, LoginType.TERMS) inserted = True break if not inserted: flow.append(LoginType.TERMS) flows.extend(new_flows) auth_result, params, session_id = yield self.auth_handler.check_auth( flows, body, self.hs.get_ip_from_request(request) ) # Check that we're not trying to register a denied 3pid. # # the user-facing checks will probably already have happened in # /register/email/requestToken when we requested a 3pid, but that's not # guaranteed. if auth_result: for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]: if login_type in auth_result: medium = auth_result[login_type]['medium'] address = auth_result[login_type]['address'] if not check_3pid_allowed(self.hs, medium, address): raise SynapseError( 403, "Third party identifiers (email/phone numbers)" + " are not authorized on this server", Codes.THREEPID_DENIED, ) if registered_user_id is not None: logger.info( "Already registered user ID %r for this session", registered_user_id ) # don't re-register the threepids registered = False else: # NB: This may be from the auth handler and NOT from the POST assert_params_in_dict(params, ["password"]) desired_username = params.get("username", None) guest_access_token = params.get("guest_access_token", None) new_password = params.get("password", None) if desired_username is not None: desired_username = desired_username.lower() threepid = None if auth_result: threepid = auth_result.get(LoginType.EMAIL_IDENTITY) # Also check that we're not trying to register a 3pid that's already # been registered. # # This has probably happened in /register/email/requestToken as well, # but if a user hits this endpoint twice then clicks on each link from # the two activation emails, they would register the same 3pid twice. for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]: if login_type in auth_result: medium = auth_result[login_type]['medium'] address = auth_result[login_type]['address'] existingUid = yield self.store.get_user_id_by_threepid( medium, address, ) if existingUid is not None: raise SynapseError( 400, "%s is already in use" % medium, Codes.THREEPID_IN_USE, ) (registered_user_id, _) = yield self.registration_handler.register( localpart=desired_username, password=new_password, guest_access_token=guest_access_token, generate_token=False, threepid=threepid, address=client_addr, ) # Necessary due to auth checks prior to the threepid being # written to the db if threepid: if is_threepid_reserved( self.hs.config.mau_limits_reserved_threepids, threepid ): yield self.store.upsert_monthly_active_user(registered_user_id) # remember that we've now registered that user account, and with # what user ID (since the user may not have specified) self.auth_handler.set_session_data( session_id, "registered_user_id", registered_user_id ) registered = True return_dict = yield self._create_registration_details( registered_user_id, params ) if registered: yield self.registration_handler.post_registration_actions( user_id=registered_user_id, auth_result=auth_result, access_token=return_dict.get("access_token"), bind_email=params.get("bind_email"), bind_msisdn=params.get("bind_msisdn"), ) defer.returnValue((200, return_dict))
async def on_POST(self, request): body = parse_json_object_from_request(request) client_addr = request.getClientIP() time_now = self.clock.time() allowed, time_allowed = self.ratelimiter.can_do_action( client_addr, time_now_s=time_now, rate_hz=self.hs.config.rc_registration.per_second, burst_count=self.hs.config.rc_registration.burst_count, update=False, ) if not allowed: raise LimitExceededError( retry_after_ms=int(1000 * (time_allowed - time_now))) kind = b"user" if b"kind" in request.args: kind = request.args[b"kind"][0] if kind == b"guest": ret = await self._do_guest_registration(body, address=client_addr) return ret elif kind != b"user": raise UnrecognizedRequestError( "Do not understand membership kind: %s" % (kind.decode("utf8"), )) # we do basic sanity checks here because the auth layer will store these # in sessions. Pull out the username/password provided to us. if "password" in body: password = body.pop("password") if not isinstance(password, string_types) or len(password) > 512: raise SynapseError(400, "Invalid password") self.password_policy_handler.validate_password(password) # If the password is valid, hash it and store it back on the request. # This ensures the hashed password is handled everywhere. if "password_hash" in body: raise SynapseError(400, "Unexpected property: password_hash") body["password_hash"] = await self.auth_handler.hash(password) desired_username = None if "username" in body: if (not isinstance(body["username"], string_types) or len(body["username"]) > 512): raise SynapseError(400, "Invalid username") desired_username = body["username"] appservice = None if self.auth.has_access_token(request): appservice = await self.auth.get_appservice_by_req(request) # fork off as soon as possible for ASes which have completely # different registration flows to normal users # == Application Service Registration == if appservice: # Set the desired user according to the AS API (which uses the # 'user' key not 'username'). Since this is a new addition, we'll # fallback to 'username' if they gave one. desired_username = body.get("user", desired_username) # XXX we should check that desired_username is valid. Currently # we give appservices carte blanche for any insanity in mxids, # because the IRC bridges rely on being able to register stupid # IDs. access_token = self.auth.get_access_token_from_request(request) if isinstance(desired_username, string_types): result = await self._do_appservice_registration( desired_username, access_token, body) return 200, result # we throw for non 200 responses # for regular registration, downcase the provided username before # attempting to register it. This should mean # that people who try to register with upper-case in their usernames # don't get a nasty surprise. (Note that we treat username # case-insenstively in login, so they are free to carry on imagining # that their username is CrAzYh4cKeR if that keeps them happy) if desired_username is not None: desired_username = desired_username.lower() # == Normal User Registration == (everyone else) if not self.hs.config.enable_registration: raise SynapseError(403, "Registration has been disabled") guest_access_token = body.get("guest_access_token", None) if "initial_device_display_name" in body and "password_hash" not in body: # ignore 'initial_device_display_name' if sent without # a password to work around a client bug where it sent # the 'initial_device_display_name' param alone, wiping out # the original registration params logger.warning( "Ignoring initial_device_display_name without password") del body["initial_device_display_name"] session_id = self.auth_handler.get_session_id(body) registered_user_id = None if session_id: # if we get a registered user id out of here, it means we previously # registered a user for this session, so we could just return the # user here. We carry on and go through the auth checks though, # for paranoia. registered_user_id = await self.auth_handler.get_session_data( session_id, "registered_user_id", None) if desired_username is not None: await self.registration_handler.check_username( desired_username, guest_access_token=guest_access_token, assigned_user_id=registered_user_id, ) auth_result, params, session_id = await self.auth_handler.check_auth( self._registration_flows, request, body, self.hs.get_ip_from_request(request), "register a new account", ) # Check that we're not trying to register a denied 3pid. # # the user-facing checks will probably already have happened in # /register/email/requestToken when we requested a 3pid, but that's not # guaranteed. if auth_result: for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]: if login_type in auth_result: medium = auth_result[login_type]["medium"] address = auth_result[login_type]["address"] if not check_3pid_allowed(self.hs, medium, address): raise SynapseError( 403, "Third party identifiers (email/phone numbers)" + " are not authorized on this server", Codes.THREEPID_DENIED, ) if registered_user_id is not None: logger.info("Already registered user ID %r for this session", registered_user_id) # don't re-register the threepids registered = False else: # NB: This may be from the auth handler and NOT from the POST assert_params_in_dict(params, ["password_hash"]) desired_username = params.get("username", None) guest_access_token = params.get("guest_access_token", None) new_password_hash = params.get("password_hash", None) if desired_username is not None: desired_username = desired_username.lower() threepid = None if auth_result: threepid = auth_result.get(LoginType.EMAIL_IDENTITY) # Also check that we're not trying to register a 3pid that's already # been registered. # # This has probably happened in /register/email/requestToken as well, # but if a user hits this endpoint twice then clicks on each link from # the two activation emails, they would register the same 3pid twice. for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]: if login_type in auth_result: medium = auth_result[login_type]["medium"] address = auth_result[login_type]["address"] existing_user_id = await self.store.get_user_id_by_threepid( medium, address) if existing_user_id is not None: raise SynapseError( 400, "%s is already in use" % medium, Codes.THREEPID_IN_USE, ) registered_user_id = await self.registration_handler.register_user( localpart=desired_username, password_hash=new_password_hash, guest_access_token=guest_access_token, threepid=threepid, address=client_addr, ) # Necessary due to auth checks prior to the threepid being # written to the db if threepid: if is_threepid_reserved( self.hs.config.mau_limits_reserved_threepids, threepid): await self.store.upsert_monthly_active_user( registered_user_id) # remember that we've now registered that user account, and with # what user ID (since the user may not have specified) await self.auth_handler.set_session_data(session_id, "registered_user_id", registered_user_id) registered = True return_dict = await self._create_registration_details( registered_user_id, params) if registered: await self.registration_handler.post_registration_actions( user_id=registered_user_id, auth_result=auth_result, access_token=return_dict.get("access_token"), ) return 200, return_dict
async def on_PUT(self, request: SynapseRequest, user_id: str) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request) await assert_user_is_admin(self.auth, requester.user) target_user = UserID.from_string(user_id) body = parse_json_object_from_request(request) if not self.hs.is_mine(target_user): raise SynapseError( 400, "This endpoint can only be used with local users") user = await self.admin_handler.get_user(target_user) user_id = target_user.to_string() if user: # modify user if "displayname" in body: await self.profile_handler.set_displayname( target_user, requester, body["displayname"], True) if "threepids" in body: # check for required parameters for each threepid for threepid in body["threepids"]: assert_params_in_dict(threepid, ["medium", "address"]) # remove old threepids from user threepids = await self.store.user_get_threepids(user_id) for threepid in threepids: try: await self.auth_handler.delete_threepid( user_id, threepid["medium"], threepid["address"], None) except Exception: logger.exception("Failed to remove threepids") raise SynapseError(500, "Failed to remove threepids") # add new threepids to user current_time = self.hs.get_clock().time_msec() for threepid in body["threepids"]: await self.auth_handler.add_threepid( user_id, threepid["medium"], threepid["address"], current_time) if "avatar_url" in body and type(body["avatar_url"]) == str: await self.profile_handler.set_avatar_url( target_user, requester, body["avatar_url"], True) if "admin" in body: set_admin_to = bool(body["admin"]) if set_admin_to != user["admin"]: auth_user = requester.user if target_user == auth_user and not set_admin_to: raise SynapseError(400, "You may not demote yourself.") await self.store.set_server_admin(target_user, set_admin_to) if "password" in body: if not isinstance(body["password"], str) or len(body["password"]) > 512: raise SynapseError(400, "Invalid password") else: new_password = body["password"] logout_devices = True new_password_hash = await self.auth_handler.hash( new_password) await self.set_password_handler.set_password( target_user.to_string(), new_password_hash, logout_devices, requester, ) if "deactivated" in body: deactivate = body["deactivated"] if not isinstance(deactivate, bool): raise SynapseError( 400, "'deactivated' parameter is not of type boolean") if deactivate and not user["deactivated"]: await self.deactivate_account_handler.deactivate_account( target_user.to_string(), False, requester, by_admin=True) elif not deactivate and user["deactivated"]: if ("password" not in body and self.auth_handler.can_change_password()): raise SynapseError( 400, "Must provide a password to re-activate an account." ) await self.deactivate_account_handler.activate_account( target_user.to_string()) user = await self.admin_handler.get_user(target_user) assert user is not None return 200, user else: # create user password = body.get("password") password_hash = None if password is not None: if not isinstance(password, str) or len(password) > 512: raise SynapseError(400, "Invalid password") password_hash = await self.auth_handler.hash(password) admin = body.get("admin", None) user_type = body.get("user_type", None) displayname = body.get("displayname", None) if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES: raise SynapseError(400, "Invalid user type") user_id = await self.registration_handler.register_user( localpart=target_user.localpart, password_hash=password_hash, admin=bool(admin), default_display_name=displayname, user_type=user_type, by_admin=True, ) if "threepids" in body: # check for required parameters for each threepid for threepid in body["threepids"]: assert_params_in_dict(threepid, ["medium", "address"]) current_time = self.hs.get_clock().time_msec() for threepid in body["threepids"]: await self.auth_handler.add_threepid( user_id, threepid["medium"], threepid["address"], current_time) if (self.hs.config.email_enable_notifs and self.hs.config.email_notif_for_new_users): await self.pusher_pool.add_pusher( user_id=user_id, access_token=None, kind="email", app_id="m.email", app_display_name="Email Notifications", device_display_name=threepid["address"], pushkey=threepid["address"], lang=None, # We don't know a user's language here data={}, ) if "avatar_url" in body and isinstance(body["avatar_url"], str): await self.profile_handler.set_avatar_url( target_user, requester, body["avatar_url"], True) user = await self.admin_handler.get_user(target_user) assert user is not None return 201, user
async def on_POST(self, request, room_id): requester = await self.auth.get_user_by_req(request) await assert_user_is_admin(self.auth, requester.user) content = parse_json_object_from_request(request) assert_params_in_dict(content, ["new_room_user_id"]) new_room_user_id = content["new_room_user_id"] room_creator_requester = create_requester(new_room_user_id) message = content.get("message", self.DEFAULT_MESSAGE) room_name = content.get("room_name", "Content Violation Notification") info = await self._room_creation_handler.create_room( room_creator_requester, config={ "preset": "public_chat", "name": room_name, "power_level_content_override": { "users_default": -10 }, }, ratelimit=False, ) new_room_id = info["room_id"] requester_user_id = requester.user.to_string() logger.info("Shutting down room %r, joining to new room: %r", room_id, new_room_id) # This will work even if the room is already blocked, but that is # desirable in case the first attempt at blocking the room failed below. await self.store.block_room(room_id, requester_user_id) users = await self.state.get_current_users_in_room(room_id) kicked_users = [] failed_to_kick_users = [] for user_id in users: if not self.hs.is_mine_id(user_id): continue logger.info("Kicking %r from %r...", user_id, room_id) try: target_requester = create_requester(user_id) await self.room_member_handler.update_membership( requester=target_requester, target=target_requester.user, room_id=room_id, action=Membership.LEAVE, content={}, ratelimit=False, require_consent=False, ) await self.room_member_handler.forget(target_requester.user, room_id) await self.room_member_handler.update_membership( requester=target_requester, target=target_requester.user, room_id=new_room_id, action=Membership.JOIN, content={}, ratelimit=False, require_consent=False, ) kicked_users.append(user_id) except Exception: logger.exception( "Failed to leave old room and join new room for %r", user_id) failed_to_kick_users.append(user_id) await self.event_creation_handler.create_and_send_nonmember_event( room_creator_requester, { "type": "m.room.message", "content": { "body": message, "msgtype": "m.text" }, "room_id": new_room_id, "sender": new_room_user_id, }, ratelimit=False, ) aliases_for_room = await maybe_awaitable( self.store.get_aliases_for_room(room_id)) await self.store.update_aliases_for_room(room_id, new_room_id, requester_user_id) return ( 200, { "kicked_users": kicked_users, "failed_to_kick_users": failed_to_kick_users, "local_aliases": aliases_for_room, "new_room_id": new_room_id, }, )
async def on_POST(self, request): body = parse_json_object_from_request(request) # we do basic sanity checks here because the auth layer will store these # in sessions. Pull out the new password provided to us. if "new_password" in body: new_password = body.pop("new_password") if not isinstance(new_password, str) or len(new_password) > 512: raise SynapseError(400, "Invalid password") self.password_policy_handler.validate_password(new_password) # If the password is valid, hash it and store it back on the body. # This ensures that only the hashed password is handled everywhere. if "new_password_hash" in body: raise SynapseError(400, "Unexpected property: new_password_hash") body["new_password_hash"] = await self.auth_handler.hash( new_password) # there are two possibilities here. Either the user does not have an # access token, and needs to do a password reset; or they have one and # need to validate their identity. # # In the first case, we offer a couple of means of identifying # themselves (email and msisdn, though it's unclear if msisdn actually # works). # # In the second case, we require a password to confirm their identity. if self.auth.has_access_token(request): requester = await self.auth.get_user_by_req(request) params = await self.auth_handler.validate_user_via_ui_auth( requester, request, body, self.hs.get_ip_from_request(request), "modify your account password", ) user_id = requester.user.to_string() else: requester = None result, params, _ = await self.auth_handler.check_auth( [[LoginType.EMAIL_IDENTITY]], request, body, self.hs.get_ip_from_request(request), "modify your account password", ) if LoginType.EMAIL_IDENTITY in result: threepid = result[LoginType.EMAIL_IDENTITY] if "medium" not in threepid or "address" not in threepid: raise SynapseError(500, "Malformed threepid") if threepid["medium"] == "email": # For emails, transform the address to lowercase. # We store all email addreses as lowercase in the DB. # (See add_threepid in synapse/handlers/auth.py) threepid["address"] = threepid["address"].lower() # if using email, we must know about the email they're authing with! threepid_user_id = await self.datastore.get_user_id_by_threepid( threepid["medium"], threepid["address"]) if not threepid_user_id: raise SynapseError(404, "Email address not found", Codes.NOT_FOUND) user_id = threepid_user_id else: logger.error("Auth succeeded but no known type! %r", result.keys()) raise SynapseError(500, "", Codes.UNKNOWN) assert_params_in_dict(params, ["new_password_hash"]) new_password_hash = params["new_password_hash"] logout_devices = params.get("logout_devices", True) await self._set_password_handler.set_password(user_id, new_password_hash, logout_devices, requester) return 200, {}
async def on_POST(self, request): if self.hs.config.threepid_behaviour_email == ThreepidBehaviour.OFF: if self.hs.config.local_threepid_handling_disabled_due_to_email_config: logger.warning( "Email registration has been disabled due to lack of email config" ) raise SynapseError( 400, "Email-based registration has been disabled on this server") body = parse_json_object_from_request(request) assert_params_in_dict(body, ["client_secret", "email", "send_attempt"]) # Extract params from body client_secret = body["client_secret"] assert_valid_client_secret(client_secret) # For emails, canonicalise the address. # We store all email addresses canonicalised in the DB. # (See on_POST in EmailThreepidRequestTokenRestServlet # in synapse/rest/client/v2_alpha/account.py) try: email = canonicalise_email(body["email"]) except ValueError as e: raise SynapseError(400, str(e)) send_attempt = body["send_attempt"] next_link = body.get("next_link") # Optional param if not check_3pid_allowed(self.hs, "email", email): raise SynapseError( 403, "Your email domain is not authorized to register on this server", Codes.THREEPID_DENIED, ) self.identity_handler.ratelimit_request_token_requests( request, "email", email) existing_user_id = await self.hs.get_datastore( ).get_user_id_by_threepid("email", email) if existing_user_id is not None: if self.hs.config.request_token_inhibit_3pid_errors: # Make the client think the operation succeeded. See the rationale in the # comments for request_token_inhibit_3pid_errors. # Also wait for some random amount of time between 100ms and 1s to make it # look like we did something. await self.hs.get_clock().sleep(random.randint(1, 10) / 10) return 200, {"sid": random_string(16)} raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE) if self.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE: assert self.hs.config.account_threepid_delegate_email # Have the configured identity server handle the request ret = await self.identity_handler.requestEmailToken( self.hs.config.account_threepid_delegate_email, email, client_secret, send_attempt, next_link, ) else: # Send registration emails from Synapse sid = await self.identity_handler.send_threepid_validation( email, client_secret, send_attempt, self.mailer.send_registration_mail, next_link, ) # Wrap the session id in a JSON object ret = {"sid": sid} threepid_send_requests.labels(type="email", reason="register").observe(send_attempt) return 200, ret
async def on_POST(self, request, room_identifier): requester = await self.auth.get_user_by_req(request) await assert_user_is_admin(self.auth, requester.user) content = parse_json_object_from_request(request) assert_params_in_dict(content, ["user_id"]) target_user = UserID.from_string(content["user_id"]) if not self.hs.is_mine(target_user): raise SynapseError( 400, "This endpoint can only be used with local users") if not await self.admin_handler.get_user(target_user): raise NotFoundError("User not found") if RoomID.is_valid(room_identifier): room_id = room_identifier try: remote_room_hosts = [ x.decode("ascii") for x in request.args[b"server_name"] ] # type: Optional[List[str]] except Exception: remote_room_hosts = None elif RoomAlias.is_valid(room_identifier): handler = self.room_member_handler room_alias = RoomAlias.from_string(room_identifier) room_id, remote_room_hosts = await handler.lookup_room_alias( room_alias) room_id = room_id.to_string() else: raise SynapseError( 400, "%s was not legal room ID or room alias" % (room_identifier, )) fake_requester = create_requester(target_user) # send invite if room has "JoinRules.INVITE" room_state = await self.state_handler.get_current_state(room_id) join_rules_event = room_state.get((EventTypes.JoinRules, "")) if join_rules_event: if not (join_rules_event.content.get("join_rule") == JoinRules.PUBLIC): await self.room_member_handler.update_membership( requester=requester, target=fake_requester.user, room_id=room_id, action="invite", remote_room_hosts=remote_room_hosts, ratelimit=False, ) await self.room_member_handler.update_membership( requester=fake_requester, target=fake_requester.user, room_id=room_id, action="join", remote_room_hosts=remote_room_hosts, ratelimit=False, ) return 200, {"room_id": room_id}
async def on_POST( self, request: SynapseRequest, room_id: str, membership_action: str, txn_id: Optional[str] = None, ) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request, allow_guest=True) if requester.is_guest and membership_action not in { Membership.JOIN, Membership.LEAVE, }: raise AuthError(403, "Guest access not allowed") try: content = parse_json_object_from_request(request) except Exception: # Turns out we used to ignore the body entirely, and some clients # cheekily send invalid bodies. content = {} if membership_action == "invite" and self._has_3pid_invite_keys( content): try: await self.room_member_handler.do_3pid_invite( room_id, requester.user, content["medium"], content["address"], content["id_server"], requester, txn_id, content.get("id_access_token"), ) except ShadowBanError: # Pretend the request succeeded. pass return 200, {} target = requester.user if membership_action in ["invite", "ban", "unban", "kick"]: assert_params_in_dict(content, ["user_id"]) target = UserID.from_string(content["user_id"]) event_content = None if "reason" in content: event_content = {"reason": content["reason"]} try: await self.room_member_handler.update_membership( requester=requester, target=target, room_id=room_id, action=membership_action, txn_id=txn_id, third_party_signed=content.get("third_party_signed", None), content=event_content, ) except ShadowBanError: # Pretend the request succeeded. pass return_value = {} if membership_action == "join": return_value["room_id"] = room_id return 200, return_value
def on_POST(self, request): body = parse_json_object_from_request(request) kind = b"user" if b"kind" in request.args: kind = request.args[b"kind"][0] if kind == b"guest": ret = yield self._do_guest_registration(body) defer.returnValue(ret) return elif kind != b"user": raise UnrecognizedRequestError( "Do not understand membership kind: %s" % (kind,) ) # we do basic sanity checks here because the auth layer will store these # in sessions. Pull out the username/password provided to us. desired_password = None if 'password' in body: if (not isinstance(body['password'], string_types) or len(body['password']) > 512): raise SynapseError(400, "Invalid password") desired_password = body["password"] desired_username = None if 'username' in body: if (not isinstance(body['username'], string_types) or len(body['username']) > 512): raise SynapseError(400, "Invalid username") desired_username = body['username'] appservice = None if self.auth.has_access_token(request): appservice = yield self.auth.get_appservice_by_req(request) # fork off as soon as possible for ASes and shared secret auth which # have completely different registration flows to normal users # == Application Service Registration == if appservice: # Set the desired user according to the AS API (which uses the # 'user' key not 'username'). Since this is a new addition, we'll # fallback to 'username' if they gave one. desired_username = body.get("user", desired_username) # XXX we should check that desired_username is valid. Currently # we give appservices carte blanche for any insanity in mxids, # because the IRC bridges rely on being able to register stupid # IDs. access_token = self.auth.get_access_token_from_request(request) if isinstance(desired_username, string_types): result = yield self._do_appservice_registration( desired_username, access_token, body ) defer.returnValue((200, result)) # we throw for non 200 responses return # for either shared secret or regular registration, downcase the # provided username before attempting to register it. This should mean # that people who try to register with upper-case in their usernames # don't get a nasty surprise. (Note that we treat username # case-insenstively in login, so they are free to carry on imagining # that their username is CrAzYh4cKeR if that keeps them happy) if desired_username is not None: desired_username = desired_username.lower() # == Shared Secret Registration == (e.g. create new user scripts) if 'mac' in body: # FIXME: Should we really be determining if this is shared secret # auth based purely on the 'mac' key? result = yield self._do_shared_secret_registration( desired_username, desired_password, body ) defer.returnValue((200, result)) # we throw for non 200 responses return # == Normal User Registration == (everyone else) if not self.hs.config.enable_registration: raise SynapseError(403, "Registration has been disabled") guest_access_token = body.get("guest_access_token", None) if ( 'initial_device_display_name' in body and 'password' not in body ): # ignore 'initial_device_display_name' if sent without # a password to work around a client bug where it sent # the 'initial_device_display_name' param alone, wiping out # the original registration params logger.warn("Ignoring initial_device_display_name without password") del body['initial_device_display_name'] session_id = self.auth_handler.get_session_id(body) registered_user_id = None if session_id: # if we get a registered user id out of here, it means we previously # registered a user for this session, so we could just return the # user here. We carry on and go through the auth checks though, # for paranoia. registered_user_id = self.auth_handler.get_session_data( session_id, "registered_user_id", None ) if desired_username is not None: yield self.registration_handler.check_username( desired_username, guest_access_token=guest_access_token, assigned_user_id=registered_user_id, ) # Only give msisdn flows if the x_show_msisdn flag is given: # this is a hack to work around the fact that clients were shipped # that use fallback registration if they see any flows that they don't # recognise, which means we break registration for these clients if we # advertise msisdn flows. Once usage of Riot iOS <=0.3.9 and Riot # Android <=0.6.9 have fallen below an acceptable threshold, this # parameter should go away and we should always advertise msisdn flows. show_msisdn = False if 'x_show_msisdn' in body and body['x_show_msisdn']: show_msisdn = True # FIXME: need a better error than "no auth flow found" for scenarios # where we required 3PID for registration but the user didn't give one require_email = 'email' in self.hs.config.registrations_require_3pid require_msisdn = 'msisdn' in self.hs.config.registrations_require_3pid flows = [] if self.hs.config.enable_registration_captcha: # only support 3PIDless registration if no 3PIDs are required if not require_email and not require_msisdn: flows.extend([[LoginType.RECAPTCHA]]) # only support the email-only flow if we don't require MSISDN 3PIDs if not require_msisdn: flows.extend([[LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA]]) if show_msisdn: # only support the MSISDN-only flow if we don't require email 3PIDs if not require_email: flows.extend([[LoginType.MSISDN, LoginType.RECAPTCHA]]) # always let users provide both MSISDN & email flows.extend([ [LoginType.MSISDN, LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA], ]) else: # only support 3PIDless registration if no 3PIDs are required if not require_email and not require_msisdn: flows.extend([[LoginType.DUMMY]]) # only support the email-only flow if we don't require MSISDN 3PIDs if not require_msisdn: flows.extend([[LoginType.EMAIL_IDENTITY]]) if show_msisdn: # only support the MSISDN-only flow if we don't require email 3PIDs if not require_email or require_msisdn: flows.extend([[LoginType.MSISDN]]) # always let users provide both MSISDN & email flows.extend([ [LoginType.MSISDN, LoginType.EMAIL_IDENTITY] ]) # Append m.login.terms to all flows if we're requiring consent if self.hs.config.user_consent_at_registration: new_flows = [] for flow in flows: flow.append(LoginType.TERMS) flows.extend(new_flows) auth_result, params, session_id = yield self.auth_handler.check_auth( flows, body, self.hs.get_ip_from_request(request) ) # Check that we're not trying to register a denied 3pid. # # the user-facing checks will probably already have happened in # /register/email/requestToken when we requested a 3pid, but that's not # guaranteed. if auth_result: for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]: if login_type in auth_result: medium = auth_result[login_type]['medium'] address = auth_result[login_type]['address'] if not check_3pid_allowed(self.hs, medium, address): raise SynapseError( 403, "Third party identifiers (email/phone numbers)" + " are not authorized on this server", Codes.THREEPID_DENIED, ) if registered_user_id is not None: logger.info( "Already registered user ID %r for this session", registered_user_id ) # don't re-register the threepids add_email = False add_msisdn = False else: # NB: This may be from the auth handler and NOT from the POST assert_params_in_dict(params, ["password"]) desired_username = params.get("username", None) guest_access_token = params.get("guest_access_token", None) new_password = params.get("password", None) if desired_username is not None: desired_username = desired_username.lower() threepid = None if auth_result: threepid = auth_result.get(LoginType.EMAIL_IDENTITY) (registered_user_id, _) = yield self.registration_handler.register( localpart=desired_username, password=new_password, guest_access_token=guest_access_token, generate_token=False, threepid=threepid, ) # Necessary due to auth checks prior to the threepid being # written to the db if is_threepid_reserved(self.hs.config, threepid): yield self.store.upsert_monthly_active_user(registered_user_id) # remember that we've now registered that user account, and with # what user ID (since the user may not have specified) self.auth_handler.set_session_data( session_id, "registered_user_id", registered_user_id ) add_email = True add_msisdn = True return_dict = yield self._create_registration_details( registered_user_id, params ) if add_email and auth_result and LoginType.EMAIL_IDENTITY in auth_result: threepid = auth_result[LoginType.EMAIL_IDENTITY] yield self._register_email_threepid( registered_user_id, threepid, return_dict["access_token"], params.get("bind_email") ) if add_msisdn and auth_result and LoginType.MSISDN in auth_result: threepid = auth_result[LoginType.MSISDN] yield self._register_msisdn_threepid( registered_user_id, threepid, return_dict["access_token"], params.get("bind_msisdn") ) if auth_result and LoginType.TERMS in auth_result: logger.info("%s has consented to the privacy policy" % registered_user_id) yield self.store.user_set_consent_version( registered_user_id, self.hs.config.user_consent_version, ) yield self.registration_handler.post_consent_actions(registered_user_id) defer.returnValue((200, return_dict))
async def on_PUT(self, request, user_id): requester = await self.auth.get_user_by_req(request) await assert_user_is_admin(self.auth, requester.user) target_user = UserID.from_string(user_id) body = parse_json_object_from_request(request) if not self.hs.is_mine(target_user): raise SynapseError(400, "This endpoint can only be used with local users") user = await self.admin_handler.get_user(target_user) user_id = target_user.to_string() if user: # modify user if "displayname" in body: await self.profile_handler.set_displayname( target_user, requester, body["displayname"], True ) if "threepids" in body: # check for required parameters for each threepid for threepid in body["threepids"]: assert_params_in_dict(threepid, ["medium", "address"]) # remove old threepids from user threepids = await self.store.user_get_threepids(user_id) for threepid in threepids: try: await self.auth_handler.delete_threepid( user_id, threepid["medium"], threepid["address"], None ) except Exception: logger.exception("Failed to remove threepids") raise SynapseError(500, "Failed to remove threepids") # add new threepids to user current_time = self.hs.get_clock().time_msec() for threepid in body["threepids"]: await self.auth_handler.add_threepid( user_id, threepid["medium"], threepid["address"], current_time ) if "avatar_url" in body and type(body["avatar_url"]) == str: await self.profile_handler.set_avatar_url( target_user, requester, body["avatar_url"], True ) if "admin" in body: set_admin_to = bool(body["admin"]) if set_admin_to != user["admin"]: auth_user = requester.user if target_user == auth_user and not set_admin_to: raise SynapseError(400, "You may not demote yourself.") await self.store.set_server_admin(target_user, set_admin_to) if "password" in body: if ( not isinstance(body["password"], text_type) or len(body["password"]) > 512 ): raise SynapseError(400, "Invalid password") else: new_password = body["password"] logout_devices = True new_password_hash = await self.auth_handler.hash(new_password) await self.set_password_handler.set_password( target_user.to_string(), new_password_hash, logout_devices, requester, ) if "deactivated" in body: deactivate = body["deactivated"] if not isinstance(deactivate, bool): raise SynapseError( 400, "'deactivated' parameter is not of type boolean" ) if deactivate and not user["deactivated"]: await self.deactivate_account_handler.deactivate_account( target_user.to_string(), False ) user = await self.admin_handler.get_user(target_user) return 200, user else: # create user password = body.get("password") password_hash = None if password is not None: if not isinstance(password, text_type) or len(password) > 512: raise SynapseError(400, "Invalid password") password_hash = await self.auth_handler.hash(password) admin = body.get("admin", None) user_type = body.get("user_type", None) displayname = body.get("displayname", None) threepids = body.get("threepids", None) if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES: raise SynapseError(400, "Invalid user type") user_id = await self.registration_handler.register_user( localpart=target_user.localpart, password_hash=password_hash, admin=bool(admin), default_display_name=displayname, user_type=user_type, ) if "threepids" in body: # check for required parameters for each threepid for threepid in body["threepids"]: assert_params_in_dict(threepid, ["medium", "address"]) current_time = self.hs.get_clock().time_msec() for threepid in body["threepids"]: await self.auth_handler.add_threepid( user_id, threepid["medium"], threepid["address"], current_time ) if "avatar_url" in body and type(body["avatar_url"]) == str: await self.profile_handler.set_avatar_url( user_id, requester, body["avatar_url"], True ) ret = await self.admin_handler.get_user(target_user) return 201, ret
async def persist_historical_events( self, events_to_create: List[JsonDict], room_id: str, inherited_depth: int, initial_state_event_ids: List[str], app_service_requester: Requester, ) -> List[str]: """Create and persists all events provided sequentially. Handles the complexity of creating events in chronological order so they can reference each other by prev_event but still persists in reverse-chronoloical order so they have the correct (topological_ordering, stream_ordering) and sort correctly from /messages. Args: events_to_create: List of historical events to create in JSON dictionary format. room_id: Room where you want the events persisted in. inherited_depth: The depth to create the events at (you will probably by calling inherit_depth_from_prev_ids(...)). initial_state_event_ids: This is used to set explicit state for the insertion event at the start of the historical batch since it's floating with no prev_events to derive state from automatically. app_service_requester: The requester of an application service. Returns: List of persisted event IDs """ assert app_service_requester.app_service # We expect the first event in a historical batch to be an insertion event assert events_to_create[0]["type"] == EventTypes.MSC2716_INSERTION # We expect the last event in a historical batch to be an batch event assert events_to_create[-1]["type"] == EventTypes.MSC2716_BATCH # Make the historical event chain float off on its own by specifying no # prev_events for the first event in the chain which causes the HS to # ask for the state at the start of the batch later. prev_event_ids: List[str] = [] event_ids = [] events_to_persist = [] for index, ev in enumerate(events_to_create): assert_params_in_dict( ev, ["type", "origin_server_ts", "content", "sender"]) assert self.hs.is_mine_id( ev["sender"]), "User must be our own: %s" % (ev["sender"], ) event_dict = { "type": ev["type"], "origin_server_ts": ev["origin_server_ts"], "content": ev["content"], "room_id": room_id, "sender": ev["sender"], # requester.user.to_string(), "prev_events": prev_event_ids.copy(), } # Mark all events as historical event_dict["content"][EventContentFields.MSC2716_HISTORICAL] = True event, context = await self.event_creation_handler.create_event( await self.create_requester_for_user_id_from_app_service( ev["sender"], app_service_requester.app_service), event_dict, # Only the first event (which is the insertion event) in the # chain should be floating. The rest should hang off each other # in a chain. allow_no_prev_events=index == 0, prev_event_ids=event_dict.get("prev_events"), # Since the first event (which is the insertion event) in the # chain is floating with no `prev_events`, it can't derive state # from anywhere automatically. So we need to set some state # explicitly. state_event_ids=initial_state_event_ids if index == 0 else None, historical=True, depth=inherited_depth, ) assert context._state_group # Normally this is done when persisting the event but we have to # pre-emptively do it here because we create all the events first, # then persist them in another pass below. And we want to share # state_groups across the whole batch so this lookup needs to work # for the next event in the batch in this loop. await self.store.store_state_group_id_for_event_id( event_id=event.event_id, state_group_id=context._state_group, ) logger.debug( "RoomBatchSendEventRestServlet inserting event=%s, prev_event_ids=%s", event, prev_event_ids, ) events_to_persist.append((event, context)) event_id = event.event_id event_ids.append(event_id) prev_event_ids = [event_id] # Persist events in reverse-chronological order so they have the # correct stream_ordering as they are backfilled (which decrements). # Events are sorted by (topological_ordering, stream_ordering) # where topological_ordering is just depth. for (event, context) in reversed(events_to_persist): await self.event_creation_handler.handle_new_client_event( await self.create_requester_for_user_id_from_app_service( event.sender, app_service_requester.app_service), event=event, context=context, ) return event_ids
async def on_POST(self, request): requester = await self.auth.get_user_by_req(request) user = requester.user content = parse_json_object_from_request(request) if ("pushkey" in content and "app_id" in content and "kind" in content and content["kind"] is None): await self.pusher_pool.remove_pusher(content["app_id"], content["pushkey"], user_id=user.to_string()) return 200, {} assert_params_in_dict( content, [ "kind", "app_id", "app_display_name", "device_display_name", "pushkey", "lang", "data", ], ) logger.debug("set pushkey %s to kind %s", content["pushkey"], content["kind"]) logger.debug("Got pushers request with body: %r", content) append = False if "append" in content: append = content["append"] if not append: await self.pusher_pool.remove_pushers_by_app_id_and_pushkey_not_user( app_id=content["app_id"], pushkey=content["pushkey"], not_user_id=user.to_string(), ) try: await self.pusher_pool.add_pusher( user_id=user.to_string(), access_token=requester.access_token_id, kind=content["kind"], app_id=content["app_id"], app_display_name=content["app_display_name"], device_display_name=content["device_display_name"], pushkey=content["pushkey"], lang=content["lang"], data=content["data"], profile_tag=content.get("profile_tag", ""), ) except PusherConfigException as pce: raise SynapseError(400, "Config Error: " + str(pce), errcode=Codes.MISSING_PARAM) self.notifier.on_new_replication_data() return 200, {}
async def persist_historical_events( self, events_to_create: List[JsonDict], room_id: str, initial_prev_event_ids: List[str], inherited_depth: int, auth_event_ids: List[str], app_service_requester: Requester, ) -> List[str]: """Create and persists all events provided sequentially. Handles the complexity of creating events in chronological order so they can reference each other by prev_event but still persists in reverse-chronoloical order so they have the correct (topological_ordering, stream_ordering) and sort correctly from /messages. Args: events_to_create: List of historical events to create in JSON dictionary format. room_id: Room where you want the events persisted in. initial_prev_event_ids: These will be the prev_events for the first event created. Each event created afterwards will point to the previous event created. inherited_depth: The depth to create the events at (you will probably by calling inherit_depth_from_prev_ids(...)). auth_event_ids: Define which events allow you to create the given event in the room. app_service_requester: The requester of an application service. Returns: List of persisted event IDs """ assert app_service_requester.app_service prev_event_ids = initial_prev_event_ids.copy() event_ids = [] events_to_persist = [] for ev in events_to_create: assert_params_in_dict( ev, ["type", "origin_server_ts", "content", "sender"]) assert self.hs.is_mine_id( ev["sender"]), "User must be our own: %s" % (ev["sender"], ) event_dict = { "type": ev["type"], "origin_server_ts": ev["origin_server_ts"], "content": ev["content"], "room_id": room_id, "sender": ev["sender"], # requester.user.to_string(), "prev_events": prev_event_ids.copy(), } # Mark all events as historical event_dict["content"][EventContentFields.MSC2716_HISTORICAL] = True event, context = await self.event_creation_handler.create_event( await self.create_requester_for_user_id_from_app_service( ev["sender"], app_service_requester.app_service), event_dict, prev_event_ids=event_dict.get("prev_events"), auth_event_ids=auth_event_ids, historical=True, depth=inherited_depth, ) assert context._state_group # Normally this is done when persisting the event but we have to # pre-emptively do it here because we create all the events first, # then persist them in another pass below. And we want to share # state_groups across the whole batch so this lookup needs to work # for the next event in the batch in this loop. await self.store.store_state_group_id_for_event_id( event_id=event.event_id, state_group_id=context._state_group, ) logger.debug( "RoomBatchSendEventRestServlet inserting event=%s, prev_event_ids=%s, auth_event_ids=%s", event, prev_event_ids, auth_event_ids, ) events_to_persist.append((event, context)) event_id = event.event_id event_ids.append(event_id) prev_event_ids = [event_id] # Persist events in reverse-chronological order so they have the # correct stream_ordering as they are backfilled (which decrements). # Events are sorted by (topological_ordering, stream_ordering) # where topological_ordering is just depth. for (event, context) in reversed(events_to_persist): await self.event_creation_handler.handle_new_client_event( await self.create_requester_for_user_id_from_app_service( event.sender, app_service_requester.app_service), event=event, context=context, ) return event_ids
async def on_POST(self, request: SynapseRequest, room_identifier: str) -> Tuple[int, JsonDict]: # This will always be set by the time Twisted calls us. assert request.args is not None requester = await self.auth.get_user_by_req(request) await assert_user_is_admin(self.auth, requester.user) content = parse_json_object_from_request(request) assert_params_in_dict(content, ["user_id"]) target_user = UserID.from_string(content["user_id"]) if not self.hs.is_mine(target_user): raise SynapseError( 400, "This endpoint can only be used with local users") if not await self.admin_handler.get_user(target_user): raise NotFoundError("User not found") # Get the room ID from the identifier. try: remote_room_hosts: Optional[List[str]] = [ x.decode("ascii") for x in request.args[b"server_name"] ] except Exception: remote_room_hosts = None room_id, remote_room_hosts = await self.resolve_room_id( room_identifier, remote_room_hosts) fake_requester = create_requester( target_user, authenticated_entity=requester.authenticated_entity) # send invite if room has "JoinRules.INVITE" room_state = await self.state_handler.get_current_state(room_id) join_rules_event = room_state.get((EventTypes.JoinRules, "")) if join_rules_event: if not (join_rules_event.content.get("join_rule") == JoinRules.PUBLIC): # update_membership with an action of "invite" can raise a # ShadowBanError. This is not handled since it is assumed that # an admin isn't going to call this API with a shadow-banned user. await self.room_member_handler.update_membership( requester=requester, target=fake_requester.user, room_id=room_id, action="invite", remote_room_hosts=remote_room_hosts, ratelimit=False, ) await self.room_member_handler.update_membership( requester=fake_requester, target=fake_requester.user, room_id=room_id, action="join", remote_room_hosts=remote_room_hosts, ratelimit=False, ) return 200, {"room_id": room_id}
async def on_PUT(self, request: SynapseRequest, user_id: str) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request) await assert_user_is_admin(self.auth, requester.user) target_user = UserID.from_string(user_id) body = parse_json_object_from_request(request) if not self.hs.is_mine(target_user): raise SynapseError( HTTPStatus.BAD_REQUEST, "This endpoint can only be used with local users", ) user = await self.admin_handler.get_user(target_user) user_id = target_user.to_string() # check for required parameters for each threepid threepids = body.get("threepids") if threepids is not None: for threepid in threepids: assert_params_in_dict(threepid, ["medium", "address"]) # check for required parameters for each external_id external_ids = body.get("external_ids") if external_ids is not None: for external_id in external_ids: assert_params_in_dict(external_id, ["auth_provider", "external_id"]) user_type = body.get("user_type", None) if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES: raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid user type") set_admin_to = body.get("admin", False) if not isinstance(set_admin_to, bool): raise SynapseError( HTTPStatus.BAD_REQUEST, "Param 'admin' must be a boolean, if given", Codes.BAD_JSON, ) password = body.get("password", None) if password is not None: if not isinstance(password, str) or len(password) > 512: raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid password") deactivate = body.get("deactivated", False) if not isinstance(deactivate, bool): raise SynapseError( HTTPStatus.BAD_REQUEST, "'deactivated' parameter is not of type boolean") # convert List[Dict[str, str]] into List[Tuple[str, str]] if external_ids is not None: new_external_ids = [(external_id["auth_provider"], external_id["external_id"]) for external_id in external_ids] # convert List[Dict[str, str]] into Set[Tuple[str, str]] if threepids is not None: new_threepids = {(threepid["medium"], threepid["address"]) for threepid in threepids} if user: # modify user if "displayname" in body: await self.profile_handler.set_displayname( target_user, requester, body["displayname"], True) if threepids is not None: # get changed threepids (added and removed) # convert List[Dict[str, Any]] into Set[Tuple[str, str]] cur_threepids = {(threepid["medium"], threepid["address"]) for threepid in await self.store.user_get_threepids(user_id)} add_threepids = new_threepids - cur_threepids del_threepids = cur_threepids - new_threepids # remove old threepids for medium, address in del_threepids: try: await self.auth_handler.delete_threepid( user_id, medium, address, None) except Exception: logger.exception("Failed to remove threepids") raise SynapseError(500, "Failed to remove threepids") # add new threepids current_time = self.hs.get_clock().time_msec() for medium, address in add_threepids: await self.auth_handler.add_threepid( user_id, medium, address, current_time) if external_ids is not None: try: await self.store.replace_user_external_id( new_external_ids, user_id, ) except ExternalIDReuseException: raise SynapseError(HTTPStatus.CONFLICT, "External id is already in use.") if "avatar_url" in body and isinstance(body["avatar_url"], str): await self.profile_handler.set_avatar_url( target_user, requester, body["avatar_url"], True) if "admin" in body: if set_admin_to != user["admin"]: auth_user = requester.user if target_user == auth_user and not set_admin_to: raise SynapseError(HTTPStatus.BAD_REQUEST, "You may not demote yourself.") await self.store.set_server_admin(target_user, set_admin_to) if password is not None: logout_devices = True new_password_hash = await self.auth_handler.hash(password) await self.set_password_handler.set_password( target_user.to_string(), new_password_hash, logout_devices, requester, ) if "deactivated" in body: if deactivate and not user["deactivated"]: await self.deactivate_account_handler.deactivate_account( target_user.to_string(), False, requester, by_admin=True) elif not deactivate and user["deactivated"]: if ("password" not in body and self.auth_handler.can_change_password()): raise SynapseError( HTTPStatus.BAD_REQUEST, "Must provide a password to re-activate an account.", ) await self.deactivate_account_handler.activate_account( target_user.to_string()) if "user_type" in body: await self.store.set_user_type(target_user, user_type) user = await self.admin_handler.get_user(target_user) assert user is not None return HTTPStatus.OK, user else: # create user displayname = body.get("displayname", None) password_hash = None if password is not None: password_hash = await self.auth_handler.hash(password) user_id = await self.registration_handler.register_user( localpart=target_user.localpart, password_hash=password_hash, admin=set_admin_to, default_display_name=displayname, user_type=user_type, by_admin=True, ) if threepids is not None: current_time = self.hs.get_clock().time_msec() for medium, address in new_threepids: await self.auth_handler.add_threepid( user_id, medium, address, current_time) if (self.hs.config.email.email_enable_notifs and self.hs.config.email.email_notif_for_new_users): await self.pusher_pool.add_pusher( user_id=user_id, access_token=None, kind="email", app_id="m.email", app_display_name="Email Notifications", device_display_name=address, pushkey=address, lang=None, # We don't know a user's language here data={}, ) if external_ids is not None: try: for auth_provider, external_id in new_external_ids: await self.store.record_user_external_id( auth_provider, external_id, user_id, ) except ExternalIDReuseException: raise SynapseError(HTTPStatus.CONFLICT, "External id is already in use.") if "avatar_url" in body and isinstance(body["avatar_url"], str): await self.profile_handler.set_avatar_url( target_user, requester, body["avatar_url"], True) user = await self.admin_handler.get_user(target_user) assert user is not None return 201, user
async def on_POST(self, request): if self.config.threepid_behaviour_email == ThreepidBehaviour.OFF: if self.config.local_threepid_handling_disabled_due_to_email_config: logger.warning( "User password resets have been disabled due to lack of email config" ) raise SynapseError( 400, "Email-based password resets have been disabled on this server" ) body = parse_json_object_from_request(request) assert_params_in_dict(body, ["client_secret", "email", "send_attempt"]) # Extract params from body client_secret = body["client_secret"] assert_valid_client_secret(client_secret) # Canonicalise the email address. The addresses are all stored canonicalised # in the database. This allows the user to reset his password without having to # know the exact spelling (eg. upper and lower case) of address in the database. # Stored in the database "*****@*****.**" # User requests with "*****@*****.**" would raise a Not Found error try: email = canonicalise_email(body["email"]) except ValueError as e: raise SynapseError(400, str(e)) send_attempt = body["send_attempt"] next_link = body.get("next_link") # Optional param if next_link: # Raise if the provided next_link value isn't valid assert_valid_next_link(self.hs, next_link) # The email will be sent to the stored address. # This avoids a potential account hijack by requesting a password reset to # an email address which is controlled by the attacker but which, after # canonicalisation, matches the one in our database. existing_user_id = await self.hs.get_datastore( ).get_user_id_by_threepid("email", email) if existing_user_id is None: if self.config.request_token_inhibit_3pid_errors: # Make the client think the operation succeeded. See the rationale in the # comments for request_token_inhibit_3pid_errors. # Also wait for some random amount of time between 100ms and 1s to make it # look like we did something. await self.hs.get_clock().sleep(random.randint(1, 10) / 10) return 200, {"sid": random_string(16)} raise SynapseError(400, "Email not found", Codes.THREEPID_NOT_FOUND) if self.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE: assert self.hs.config.account_threepid_delegate_email # Have the configured identity server handle the request ret = await self.identity_handler.requestEmailToken( self.hs.config.account_threepid_delegate_email, email, client_secret, send_attempt, next_link, ) else: # Send password reset emails from Synapse sid = await self.identity_handler.send_threepid_validation( email, client_secret, send_attempt, self.mailer.send_password_reset_mail, next_link, ) # Wrap the session id in a JSON object ret = {"sid": sid} threepid_send_requests.labels( type="email", reason="password_reset").observe(send_attempt) return 200, ret
def on_POST(self, request, room_id): requester = yield self.auth.get_user_by_req(request) yield assert_user_is_admin(self.auth, requester.user) content = parse_json_object_from_request(request) assert_params_in_dict(content, ["new_room_user_id"]) new_room_user_id = content["new_room_user_id"] room_creator_requester = create_requester(new_room_user_id) message = content.get("message", self.DEFAULT_MESSAGE) room_name = content.get("room_name", "Content Violation Notification") info = yield self._room_creation_handler.create_room( room_creator_requester, config={ "preset": "public_chat", "name": room_name, "power_level_content_override": { "users_default": -10, }, }, ratelimit=False, ) new_room_id = info["room_id"] requester_user_id = requester.user.to_string() logger.info( "Shutting down room %r, joining to new room: %r", room_id, new_room_id, ) # This will work even if the room is already blocked, but that is # desirable in case the first attempt at blocking the room failed below. yield self.store.block_room(room_id, requester_user_id) users = yield self.state.get_current_users_in_room(room_id) kicked_users = [] failed_to_kick_users = [] for user_id in users: if not self.hs.is_mine_id(user_id): continue logger.info("Kicking %r from %r...", user_id, room_id) try: target_requester = create_requester(user_id) yield self.room_member_handler.update_membership( requester=target_requester, target=target_requester.user, room_id=room_id, action=Membership.LEAVE, content={}, ratelimit=False, require_consent=False, ) yield self.room_member_handler.forget(target_requester.user, room_id) yield self.room_member_handler.update_membership( requester=target_requester, target=target_requester.user, room_id=new_room_id, action=Membership.JOIN, content={}, ratelimit=False, require_consent=False, ) kicked_users.append(user_id) except Exception: logger.exception( "Failed to leave old room and join new room for %r", user_id, ) failed_to_kick_users.append(user_id) yield self.event_creation_handler.create_and_send_nonmember_event( room_creator_requester, { "type": "m.room.message", "content": {"body": message, "msgtype": "m.text"}, "room_id": new_room_id, "sender": new_room_user_id, }, ratelimit=False, ) aliases_for_room = yield self.store.get_aliases_for_room(room_id) yield self.store.update_aliases_for_room( room_id, new_room_id, requester_user_id ) defer.returnValue((200, { "kicked_users": kicked_users, "failed_to_kick_users": failed_to_kick_users, "local_aliases": aliases_for_room, "new_room_id": new_room_id, }))
async def on_POST(self, request: SynapseRequest, room_id: str) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request, allow_guest=False) if not requester.app_service: raise AuthError( HTTPStatus.FORBIDDEN, "Only application services can use the /batchsend endpoint", ) body = parse_json_object_from_request(request) assert_params_in_dict(body, ["state_events_at_start", "events"]) assert request.args is not None prev_event_ids_from_query = parse_strings_from_args( request.args, "prev_event_id") batch_id_from_query = parse_string(request, "batch_id") if prev_event_ids_from_query is None: raise SynapseError( HTTPStatus.BAD_REQUEST, "prev_event query parameter is required when inserting historical messages back in time", errcode=Codes.MISSING_PARAM, ) # Verify the batch_id_from_query corresponds to an actual insertion event # and have the batch connected. if batch_id_from_query: corresponding_insertion_event_id = ( await self.store.get_insertion_event_id_by_batch_id( room_id, batch_id_from_query)) if corresponding_insertion_event_id is None: raise SynapseError( HTTPStatus.BAD_REQUEST, "No insertion event corresponds to the given ?batch_id", errcode=Codes.INVALID_PARAM, ) # For the event we are inserting next to (`prev_event_ids_from_query`), # find the most recent auth events (derived from state events) that # allowed that message to be sent. We will use that as a base # to auth our historical messages against. auth_event_ids = await self.room_batch_handler.get_most_recent_auth_event_ids_from_event_id_list( prev_event_ids_from_query) state_event_ids_at_start = [] # Create and persist all of the state events that float off on their own # before the batch. These will most likely be all of the invite/member # state events used to auth the upcoming historical messages. if body["state_events_at_start"]: state_event_ids_at_start = ( await self.room_batch_handler.persist_state_events_at_start( state_events_at_start=body["state_events_at_start"], room_id=room_id, initial_auth_event_ids=auth_event_ids, app_service_requester=requester, )) # Update our ongoing auth event ID list with all of the new state we # just created auth_event_ids.extend(state_event_ids_at_start) inherited_depth = await self.room_batch_handler.inherit_depth_from_prev_ids( prev_event_ids_from_query) events_to_create = body["events"] # Figure out which batch to connect to. If they passed in # batch_id_from_query let's use it. The batch ID passed in comes # from the batch_id in the "insertion" event from the previous batch. last_event_in_batch = events_to_create[-1] base_insertion_event = None if batch_id_from_query: batch_id_to_connect_to = batch_id_from_query # Otherwise, create an insertion event to act as a starting point. # # We don't always have an insertion event to start hanging more history # off of (ideally there would be one in the main DAG, but that's not the # case if we're wanting to add history to e.g. existing rooms without # an insertion event), in which case we just create a new insertion event # that can then get pointed to by a "marker" event later. else: base_insertion_event_dict = ( self.room_batch_handler.create_insertion_event_dict( sender=requester.user.to_string(), room_id=room_id, origin_server_ts=last_event_in_batch["origin_server_ts"], )) base_insertion_event_dict[ "prev_events"] = prev_event_ids_from_query.copy() ( base_insertion_event, _, ) = await self.event_creation_handler.create_and_send_nonmember_event( await self.room_batch_handler. create_requester_for_user_id_from_app_service( base_insertion_event_dict["sender"], requester.app_service, ), base_insertion_event_dict, prev_event_ids=base_insertion_event_dict.get("prev_events"), auth_event_ids=auth_event_ids, historical=True, depth=inherited_depth, ) batch_id_to_connect_to = base_insertion_event.content[ EventContentFields.MSC2716_NEXT_BATCH_ID] # Also connect the historical event chain to the end of the floating # state chain, which causes the HS to ask for the state at the start of # the batch later. If there is no state chain to connect to, just make # the insertion event float itself. prev_event_ids = [] if len(state_event_ids_at_start): prev_event_ids = [state_event_ids_at_start[-1]] # Create and persist all of the historical events as well as insertion # and batch meta events to make the batch navigable in the DAG. event_ids, next_batch_id = await self.room_batch_handler.handle_batch_of_events( events_to_create=events_to_create, room_id=room_id, batch_id_to_connect_to=batch_id_to_connect_to, initial_prev_event_ids=prev_event_ids, inherited_depth=inherited_depth, auth_event_ids=auth_event_ids, app_service_requester=requester, ) insertion_event_id = event_ids[0] batch_event_id = event_ids[-1] historical_event_ids = event_ids[1:-1] response_dict = { "state_event_ids": state_event_ids_at_start, "event_ids": historical_event_ids, "next_batch_id": next_batch_id, "insertion_event_id": insertion_event_id, "batch_event_id": batch_event_id, } if base_insertion_event is not None: response_dict[ "base_insertion_event_id"] = base_insertion_event.event_id return HTTPStatus.OK, response_dict
async def on_POST(self, request): if self.hs.config.threepid_behaviour_email == ThreepidBehaviour.OFF: if self.hs.config.local_threepid_handling_disabled_due_to_email_config: logger.warning( "Email registration has been disabled due to lack of email config" ) raise SynapseError( 400, "Email-based registration has been disabled on this server") body = parse_json_object_from_request(request) assert_params_in_dict(body, ["client_secret", "email", "send_attempt"]) # Extract params from body client_secret = body["client_secret"] assert_valid_client_secret(client_secret) email = body["email"] send_attempt = body["send_attempt"] next_link = body.get("next_link") # Optional param if not check_3pid_allowed(self.hs, "email", email): raise SynapseError( 403, "Your email domain is not authorized to register on this server", Codes.THREEPID_DENIED, ) existing_user_id = await self.hs.get_datastore( ).get_user_id_by_threepid("email", body["email"]) if existing_user_id is not None: if self.hs.config.request_token_inhibit_3pid_errors: # Make the client think the operation succeeded. See the rationale in the # comments for request_token_inhibit_3pid_errors. return 200, {"sid": random_string(16)} raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE) if self.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE: assert self.hs.config.account_threepid_delegate_email # Have the configured identity server handle the request ret = await self.identity_handler.requestEmailToken( self.hs.config.account_threepid_delegate_email, email, client_secret, send_attempt, next_link, ) else: # Send registration emails from Synapse sid = await self.identity_handler.send_threepid_validation( email, client_secret, send_attempt, self.mailer.send_registration_mail, next_link, ) # Wrap the session id in a JSON object ret = {"sid": sid} return 200, ret
def on_POST(self, request): body = parse_json_object_from_request(request) client_addr = request.getClientIP() time_now = self.clock.time() allowed, time_allowed = self.ratelimiter.can_do_action( client_addr, time_now_s=time_now, rate_hz=self.hs.config.rc_registration.per_second, burst_count=self.hs.config.rc_registration.burst_count, update=False, ) if not allowed: raise LimitExceededError( retry_after_ms=int(1000 * (time_allowed - time_now))) kind = b"user" if b"kind" in request.args: kind = request.args[b"kind"][0] if kind == b"guest": ret = yield self._do_guest_registration(body, address=client_addr) defer.returnValue(ret) return elif kind != b"user": raise UnrecognizedRequestError( "Do not understand membership kind: %s" % (kind, )) # we do basic sanity checks here because the auth layer will store these # in sessions. Pull out the username/password provided to us. desired_password = None if "password" in body: if (not isinstance(body["password"], string_types) or len(body["password"]) > 512): raise SynapseError(400, "Invalid password") desired_password = body["password"] desired_username = None if "username" in body: if (not isinstance(body["username"], string_types) or len(body["username"]) > 512): raise SynapseError(400, "Invalid username") desired_username = body["username"] appservice = None if self.auth.has_access_token(request): appservice = yield self.auth.get_appservice_by_req(request) # fork off as soon as possible for ASes and shared secret auth which # have completely different registration flows to normal users # == Application Service Registration == if appservice: # Set the desired user according to the AS API (which uses the # 'user' key not 'username'). Since this is a new addition, we'll # fallback to 'username' if they gave one. desired_username = body.get("user", desired_username) # XXX we should check that desired_username is valid. Currently # we give appservices carte blanche for any insanity in mxids, # because the IRC bridges rely on being able to register stupid # IDs. access_token = self.auth.get_access_token_from_request(request) if isinstance(desired_username, string_types): result = yield self._do_appservice_registration( desired_username, access_token, body) defer.returnValue((200, result)) # we throw for non 200 responses return # for either shared secret or regular registration, downcase the # provided username before attempting to register it. This should mean # that people who try to register with upper-case in their usernames # don't get a nasty surprise. (Note that we treat username # case-insenstively in login, so they are free to carry on imagining # that their username is CrAzYh4cKeR if that keeps them happy) if desired_username is not None: desired_username = desired_username.lower() # == Shared Secret Registration == (e.g. create new user scripts) if "mac" in body: # FIXME: Should we really be determining if this is shared secret # auth based purely on the 'mac' key? result = yield self._do_shared_secret_registration( desired_username, desired_password, body) defer.returnValue((200, result)) # we throw for non 200 responses return # == Normal User Registration == (everyone else) if not self.hs.config.enable_registration: raise SynapseError(403, "Registration has been disabled") guest_access_token = body.get("guest_access_token", None) if "initial_device_display_name" in body and "password" not in body: # ignore 'initial_device_display_name' if sent without # a password to work around a client bug where it sent # the 'initial_device_display_name' param alone, wiping out # the original registration params logger.warn( "Ignoring initial_device_display_name without password") del body["initial_device_display_name"] session_id = self.auth_handler.get_session_id(body) registered_user_id = None if session_id: # if we get a registered user id out of here, it means we previously # registered a user for this session, so we could just return the # user here. We carry on and go through the auth checks though, # for paranoia. registered_user_id = self.auth_handler.get_session_data( session_id, "registered_user_id", None) if desired_username is not None: yield self.registration_handler.check_username( desired_username, guest_access_token=guest_access_token, assigned_user_id=registered_user_id, ) # FIXME: need a better error than "no auth flow found" for scenarios # where we required 3PID for registration but the user didn't give one require_email = "email" in self.hs.config.registrations_require_3pid require_msisdn = "msisdn" in self.hs.config.registrations_require_3pid show_msisdn = True if self.hs.config.disable_msisdn_registration: show_msisdn = False require_msisdn = False flows = [] if self.hs.config.enable_registration_captcha: # only support 3PIDless registration if no 3PIDs are required if not require_email and not require_msisdn: # Also add a dummy flow here, otherwise if a client completes # recaptcha first we'll assume they were going for this flow # and complete the request, when they could have been trying to # complete one of the flows with email/msisdn auth. flows.extend([[LoginType.RECAPTCHA, LoginType.DUMMY]]) # only support the email-only flow if we don't require MSISDN 3PIDs if not require_msisdn: flows.extend([[LoginType.RECAPTCHA, LoginType.EMAIL_IDENTITY]]) if show_msisdn: # only support the MSISDN-only flow if we don't require email 3PIDs if not require_email: flows.extend([[LoginType.RECAPTCHA, LoginType.MSISDN]]) # always let users provide both MSISDN & email flows.extend([[ LoginType.RECAPTCHA, LoginType.MSISDN, LoginType.EMAIL_IDENTITY ]]) else: # only support 3PIDless registration if no 3PIDs are required if not require_email and not require_msisdn: flows.extend([[LoginType.DUMMY]]) # only support the email-only flow if we don't require MSISDN 3PIDs if not require_msisdn: flows.extend([[LoginType.EMAIL_IDENTITY]]) if show_msisdn: # only support the MSISDN-only flow if we don't require email 3PIDs if not require_email or require_msisdn: flows.extend([[LoginType.MSISDN]]) # always let users provide both MSISDN & email flows.extend([[LoginType.MSISDN, LoginType.EMAIL_IDENTITY]]) # Append m.login.terms to all flows if we're requiring consent if self.hs.config.user_consent_at_registration: new_flows = [] for flow in flows: inserted = False # m.login.terms should go near the end but before msisdn or email auth for i, stage in enumerate(flow): if stage == LoginType.EMAIL_IDENTITY or stage == LoginType.MSISDN: flow.insert(i, LoginType.TERMS) inserted = True break if not inserted: flow.append(LoginType.TERMS) flows.extend(new_flows) auth_result, params, session_id = yield self.auth_handler.check_auth( flows, body, self.hs.get_ip_from_request(request)) # Check that we're not trying to register a denied 3pid. # # the user-facing checks will probably already have happened in # /register/email/requestToken when we requested a 3pid, but that's not # guaranteed. if auth_result: for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]: if login_type in auth_result: medium = auth_result[login_type]["medium"] address = auth_result[login_type]["address"] if not check_3pid_allowed(self.hs, medium, address): raise SynapseError( 403, "Third party identifiers (email/phone numbers)" + " are not authorized on this server", Codes.THREEPID_DENIED, ) if registered_user_id is not None: logger.info("Already registered user ID %r for this session", registered_user_id) # don't re-register the threepids registered = False else: # NB: This may be from the auth handler and NOT from the POST assert_params_in_dict(params, ["password"]) desired_username = params.get("username", None) guest_access_token = params.get("guest_access_token", None) new_password = params.get("password", None) if desired_username is not None: desired_username = desired_username.lower() threepid = None if auth_result: threepid = auth_result.get(LoginType.EMAIL_IDENTITY) # Also check that we're not trying to register a 3pid that's already # been registered. # # This has probably happened in /register/email/requestToken as well, # but if a user hits this endpoint twice then clicks on each link from # the two activation emails, they would register the same 3pid twice. for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]: if login_type in auth_result: medium = auth_result[login_type]["medium"] address = auth_result[login_type]["address"] existingUid = yield self.store.get_user_id_by_threepid( medium, address) if existingUid is not None: raise SynapseError( 400, "%s is already in use" % medium, Codes.THREEPID_IN_USE, ) (registered_user_id, _) = yield self.registration_handler.register( localpart=desired_username, password=new_password, guest_access_token=guest_access_token, generate_token=False, threepid=threepid, address=client_addr, ) # Necessary due to auth checks prior to the threepid being # written to the db if threepid: if is_threepid_reserved( self.hs.config.mau_limits_reserved_threepids, threepid): yield self.store.upsert_monthly_active_user( registered_user_id) # remember that we've now registered that user account, and with # what user ID (since the user may not have specified) self.auth_handler.set_session_data(session_id, "registered_user_id", registered_user_id) registered = True return_dict = yield self._create_registration_details( registered_user_id, params) if registered: yield self.registration_handler.post_registration_actions( user_id=registered_user_id, auth_result=auth_result, access_token=return_dict.get("access_token"), bind_email=params.get("bind_email"), bind_msisdn=params.get("bind_msisdn"), ) defer.returnValue((200, return_dict))