Exemple #1
0
    def on_POST(self, request):
        yield self.auth.get_user_by_req(request, allow_guest=True)

        server = parse_string(request, "server", default=None)
        content = parse_json_object_from_request(request)

        limit = int(content.get("limit", 100))
        since_token = content.get("since", None)
        search_filter = content.get("filter", None)

        handler = self.hs.get_room_list_handler()
        if server:
            data = yield handler.get_remote_public_room_list(
                server,
                limit=limit,
                since_token=since_token,
                search_filter=search_filter,
            )
        else:
            data = yield handler.get_local_public_room_list(
                limit=limit,
                since_token=since_token,
                search_filter=search_filter,
            )

        defer.returnValue((200, data))
Exemple #2
0
    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, {}))
Exemple #3
0
    def on_POST(self, request):
        requester = yield self.auth.get_user_by_req(request)
        user = requester.user

        content = parse_json_object_from_request(request)

        pusher_pool = self.hs.get_pusherpool()

        if ('pushkey' in content and 'app_id' in content
                and 'kind' in content and
                content['kind'] is None):
            yield pusher_pool.remove_pusher(
                content['app_id'], content['pushkey'], user_id=user.to_string()
            )
            defer.returnValue((200, {}))

        reqd = ['kind', 'app_id', 'app_display_name',
                'device_display_name', 'pushkey', 'lang', 'data']
        missing = []
        for i in reqd:
            if i not in content:
                missing.append(i)
        if len(missing):
            raise SynapseError(400, "Missing parameters: " + ','.join(missing),
                               errcode=Codes.MISSING_PARAM)

        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 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 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: " + pce.message,
                               errcode=Codes.MISSING_PARAM)

        self.notifier.on_new_replication_data()

        defer.returnValue((200, {}))
Exemple #4
0
    def _handle_request(self, request, event_id):
        with Measure(self.clock, "repl_send_event_parse"):
            content = parse_json_object_from_request(request)

            event_dict = content["event"]
            internal_metadata = content["internal_metadata"]
            rejected_reason = content["rejected_reason"]
            event = FrozenEvent(event_dict, internal_metadata, rejected_reason)

            requester = Requester.deserialize(self.store, content["requester"])
            context = yield EventContext.deserialize(self.store, content["context"])

            ratelimit = content["ratelimit"]
            extra_users = [UserID.from_string(u) for u in content["extra_users"]]

        if requester.user:
            request.authenticated_entity = requester.user.to_string()

        logger.info(
            "Got event to send with ID: %s into room: %s",
            event.event_id, event.room_id,
        )

        yield self.event_creation_handler.persist_and_notify_client_event(
            requester, event, context,
            ratelimit=ratelimit,
            extra_users=extra_users,
        )

        defer.returnValue((200, {}))
Exemple #5
0
    def on_PUT(self, request, room_alias):
        room_alias = RoomAlias.from_string(room_alias)

        content = parse_json_object_from_request(request)
        if "room_id" not in content:
            raise SynapseError(400, 'Missing params: ["room_id"]',
                               errcode=Codes.BAD_JSON)

        logger.debug("Got content: %s", content)
        logger.debug("Got room name: %s", room_alias.to_string())

        room_id = content["room_id"]
        servers = content["servers"] if "servers" in content else None

        logger.debug("Got room_id: %s", room_id)
        logger.debug("Got servers: %s", servers)

        # TODO(erikj): Check types.

        room = yield self.store.get_room(room_id)
        if room is None:
            raise SynapseError(400, "Room does not exist")

        requester = yield self.auth.get_user_by_req(request)

        yield self.handlers.directory_handler.create_association(
            requester, room_alias, room_id, servers
        )

        defer.returnValue((200, {}))
Exemple #6
0
    def on_PUT(self, request, version):
        """
        Update the information about a given version of the user's room_keys backup.

        POST /room_keys/version/12345 HTTP/1.1
        Content-Type: application/json
        {
            "algorithm": "m.megolm_backup.v1",
            "auth_data": {
                "public_key": "abcdefg",
                "signatures": {
                    "ed25519:something": "hijklmnop"
                }
            },
            "version": "42"
        }

        HTTP/1.1 200 OK
        Content-Type: application/json
        {}
        """
        requester = yield self.auth.get_user_by_req(request, allow_guest=False)
        user_id = requester.user.to_string()
        info = parse_json_object_from_request(request)

        if version is None:
            raise SynapseError(400, "No version specified to update", Codes.MISSING_PARAM)

        yield self.e2e_room_keys_handler.update_version(
            user_id, version, info
        )
        defer.returnValue((200, {}))
Exemple #7
0
    def on_POST(self, request):
        yield run_on_reactor()

        body = parse_json_object_from_request(request)

        threePidCreds = body.get("threePidCreds")
        threePidCreds = body.get("three_pid_creds", threePidCreds)
        if threePidCreds is None:
            raise SynapseError(400, "Missing param", Codes.MISSING_PARAM)

        requester = yield self.auth.get_user_by_req(request)
        user_id = requester.user.to_string()

        threepid = yield self.identity_handler.threepid_from_creds(threePidCreds)

        if not threepid:
            raise SynapseError(400, "Failed to auth 3pid", Codes.THREEPID_AUTH_FAILED)

        for reqd in ["medium", "address", "validated_at"]:
            if reqd not in threepid:
                logger.warn("Couldn't add 3pid: invalid response from ID sevrer")
                raise SynapseError(500, "Invalid response from ID Server")

        yield self.auth_handler.add_threepid(user_id, threepid["medium"], threepid["address"], threepid["validated_at"])

        if "bind" in body and body["bind"]:
            logger.debug("Binding emails %s to %s", threepid, user_id)
            yield self.identity_handler.bind_threepid(threePidCreds, user_id)

        defer.returnValue((200, {}))
Exemple #8
0
    def on_PUT(self, request, user_id):
        requester = yield self.auth.get_user_by_req(request)
        user = UserID.from_string(user_id)

        if requester.user != user:
            raise AuthError(403, "Can only set your own presence state")

        state = {}

        content = parse_json_object_from_request(request)

        try:
            state["presence"] = content.pop("presence")

            if "status_msg" in content:
                state["status_msg"] = content.pop("status_msg")
                if not isinstance(state["status_msg"], string_types):
                    raise SynapseError(400, "status_msg must be a string.")

            if content:
                raise KeyError()
        except SynapseError as e:
            raise e
        except Exception:
            raise SynapseError(400, "Unable to parse state")

        if self.hs.config.use_presence:
            yield self.presence_handler.set_state(user, state)

        defer.returnValue((200, {}))
Exemple #9
0
 def on_POST(self, request):
     login_submission = parse_json_object_from_request(request)
     try:
         if self.saml2_enabled and (login_submission["type"] ==
                                    LoginRestServlet.SAML2_TYPE):
             relay_state = ""
             if "relay_state" in login_submission:
                 relay_state = "&RelayState=" + urllib.parse.quote(
                               login_submission["relay_state"])
             result = {
                 "uri": "%s%s" % (self.idp_redirect_url, relay_state)
             }
             defer.returnValue((200, result))
         elif self.jwt_enabled and (login_submission["type"] ==
                                    LoginRestServlet.JWT_TYPE):
             result = yield self.do_jwt_login(login_submission)
             defer.returnValue(result)
         elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
             result = yield self.do_token_login(login_submission)
             defer.returnValue(result)
         else:
             result = yield self._do_other_login(login_submission)
             defer.returnValue(result)
     except KeyError:
         raise SynapseError(400, "Missing JSON keys.")
Exemple #10
0
    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))
Exemple #11
0
    def on_PUT(self, request, room_id, user_id):
        requester = yield self.auth.get_user_by_req(request)

        room_id = urllib.unquote(room_id)
        target_user = UserID.from_string(urllib.unquote(user_id))

        content = parse_json_object_from_request(request)

        yield self.presence_handler.bump_presence_active_time(requester.user)

        if content["typing"]:
            yield self.typing_handler.started_typing(
                target_user=target_user,
                auth_user=requester.user,
                room_id=room_id,
                timeout=content.get("timeout", 30000),
            )
        else:
            yield self.typing_handler.stopped_typing(
                target_user=target_user,
                auth_user=requester.user,
                room_id=room_id,
            )

        defer.returnValue((200, {}))
Exemple #12
0
    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))
Exemple #13
0
    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,
        }))
Exemple #14
0
    def on_POST(self, request):
        body = parse_json_object_from_request(request)
        erase = body.get("erase", False)
        if not isinstance(erase, bool):
            raise SynapseError(
                http_client.BAD_REQUEST,
                "Param 'erase' must be a boolean, if given",
                Codes.BAD_JSON,
            )

        requester = yield self.auth.get_user_by_req(request)

        # allow ASes to dectivate their own users
        if requester.app_service:
            yield self._deactivate_account_handler.deactivate_account(
                requester.user.to_string(), erase,
            )
            defer.returnValue((200, {}))

        yield self.auth_handler.validate_user_via_ui_auth(
            requester, body, self.hs.get_ip_from_request(request),
        )
        result = yield self._deactivate_account_handler.deactivate_account(
            requester.user.to_string(), erase,
            id_server=body.get("id_server"),
        )
        if result:
            id_server_unbind_result = "success"
        else:
            id_server_unbind_result = "no-support"

        defer.returnValue((200, {
            "id_server_unbind_result": id_server_unbind_result,
        }))
Exemple #15
0
    def on_POST(self, request):
        """Searches for users in directory

        Returns:
            dict of the form::

                {
                    "limited": <bool>,  # whether there were more results or not
                    "results": [  # Ordered by best match first
                        {
                            "user_id": <user_id>,
                            "display_name": <display_name>,
                            "avatar_url": <avatar_url>
                        }
                    ]
                }
        """
        requester = yield self.auth.get_user_by_req(request, allow_guest=False)
        user_id = requester.user.to_string()

        body = parse_json_object_from_request(request)

        limit = body.get("limit", 10)
        limit = min(limit, 50)

        try:
            search_term = body["search_term"]
        except Exception:
            raise SynapseError(400, "`search_term` is required field")

        results = yield self.user_directory_handler.search_users(
            user_id, search_term, limit,
        )

        defer.returnValue((200, results))
Exemple #16
0
    def on_PUT(self, request, room_id, event_type, state_key, txn_id=None):
        requester = yield self.auth.get_user_by_req(request)

        content = parse_json_object_from_request(request)

        event_dict = {
            "type": event_type,
            "content": content,
            "room_id": room_id,
            "sender": requester.user.to_string(),
        }

        if state_key is not None:
            event_dict["state_key"] = state_key

        msg_handler = self.handlers.message_handler
        event, context = yield msg_handler.create_event(
            event_dict,
            token_id=requester.access_token_id,
            txn_id=txn_id,
        )

        if event_type == EventTypes.Member:
            yield self.handlers.room_member_handler.send_membership_event(
                requester,
                event,
                context,
            )
        else:
            yield msg_handler.send_nonmember_event(requester, event, context)

        defer.returnValue((200, {"event_id": event.event_id}))
Exemple #17
0
        def new_func(request, *args, **kwargs):
            content = None
            if request.method in ["PUT", "POST"]:
                # TODO: Handle other method types? other content types?
                content = parse_json_object_from_request(request)

            try:
                origin = yield authenticator.authenticate_request(request, content)
            except NoAuthenticationError:
                origin = None
                if self.REQUIRE_AUTH:
                    logger.exception("authenticate_request failed")
                    raise
            except:
                logger.exception("authenticate_request failed")
                raise

            if origin:
                with ratelimiter.ratelimit(origin) as d:
                    yield d
                    response = yield func(
                        origin, content, request.args, *args, **kwargs
                    )
            else:
                response = yield func(
                    origin, content, request.args, *args, **kwargs
                )

            defer.returnValue(response)
Exemple #18
0
    def on_POST(self, request, device_id):
        requester = yield self.auth.get_user_by_req(request)
        user_id = requester.user.to_string()
        body = parse_json_object_from_request(request)

        if device_id is not None:
            # passing the device_id here is deprecated; however, we allow it
            # for now for compatibility with older clients.
            if (requester.device_id is not None and
                    device_id != requester.device_id):
                logger.warning("Client uploading keys for a different device "
                               "(logged in as %s, uploading for %s)",
                               requester.device_id, device_id)
        else:
            device_id = requester.device_id

        if device_id is None:
            raise SynapseError(
                400,
                "To upload keys, you must pass device_id when authenticating"
            )

        result = yield self.e2e_keys_handler.upload_keys_for_user(
            user_id, device_id, body
        )
        defer.returnValue((200, result))
Exemple #19
0
    def on_POST(self, request, room_id):
        requester = yield self.auth.get_user_by_req(request)

        yield self.presence_handler.bump_presence_active_time(requester.user)

        body = parse_json_object_from_request(request)

        read_event_id = body.get("m.read", None)
        if read_event_id:
            yield self.receipts_handler.received_client_receipt(
                room_id,
                "m.read",
                user_id=requester.user.to_string(),
                event_id=read_event_id
            )

        read_marker_event_id = body.get("m.fully_read", None)
        if read_marker_event_id:
            yield self.read_marker_handler.received_client_read_marker(
                room_id,
                user_id=requester.user.to_string(),
                event_id=read_marker_event_id
            )

        defer.returnValue((200, {}))
Exemple #20
0
    def on_POST(self, request):
        content = parse_json_object_from_request(request)

        remote_room_hosts = content["remote_room_hosts"]
        room_id = content["room_id"]
        user_id = content["user_id"]
        event_content = content["content"]

        requester = Requester.deserialize(self.store, content["requester"])

        if requester.user:
            request.authenticated_entity = requester.user.to_string()

        logger.info(
            "remote_join: %s into room: %s",
            user_id, room_id,
        )

        yield self.federation_handler.do_invite_join(
            remote_room_hosts,
            room_id,
            user_id,
            event_content,
        )

        defer.returnValue((200, {}))
Exemple #21
0
    def on_POST(self, request):
        body = parse_json_object_from_request(request)

        required = [
            'id_server', 'client_secret',
            'country', 'phone_number', 'send_attempt',
        ]
        absent = []
        for k in required:
            if k not in body:
                absent.append(k)

        if absent:
            raise SynapseError(400, "Missing params: %r" % absent, Codes.MISSING_PARAM)

        msisdn = phone_number_to_msisdn(body['country'], body['phone_number'])

        if not check_3pid_allowed(self.hs, "msisdn", msisdn):
            raise SynapseError(
                403, "Third party identifier is not allowed", Codes.THREEPID_DENIED,
            )

        existingUid = yield self.datastore.get_user_id_by_threepid(
            'msisdn', msisdn
        )

        if existingUid is not None:
            raise SynapseError(400, "MSISDN is already in use", Codes.THREEPID_IN_USE)

        ret = yield self.identity_handler.requestMsisdnToken(**body)
        defer.returnValue((200, ret))
Exemple #22
0
    def on_POST(self, request):
        body = parse_json_object_from_request(request)

        assert_params_in_request(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, "Third party identifier is not allowed", Codes.THREEPID_DENIED,
            )

        existingUid = yield self.hs.get_datastore().get_user_id_by_threepid(
            'msisdn', msisdn
        )

        if existingUid is not None:
            raise SynapseError(
                400, "Phone number is already in use", Codes.THREEPID_IN_USE
            )

        ret = yield self.identity_handler.requestMsisdnToken(**body)
        defer.returnValue((200, ret))
Exemple #23
0
    def on_POST(self, request):
        body = parse_json_object_from_request(request)

        authed, result, params, _ = yield self.auth_handler.check_auth([
            [LoginType.PASSWORD],
        ], body, self.hs.get_ip_from_request(request))

        if not authed:
            defer.returnValue((401, result))

        user_id = None
        requester = None

        if LoginType.PASSWORD in result:
            # if using password, they should also be logged in
            requester = yield self.auth.get_user_by_req(request)
            user_id = requester.user.to_string()
            if user_id != result[LoginType.PASSWORD]:
                raise LoginError(400, "", Codes.UNKNOWN)
        else:
            logger.error("Auth succeeded but no known type!", result.keys())
            raise SynapseError(500, "", Codes.UNKNOWN)

        # FIXME: Theoretically there is a race here wherein user resets password
        # using threepid.
        yield self.store.user_delete_access_tokens(user_id)
        yield self.store.user_delete_threepids(user_id)
        yield self.store.user_set_password_hash(user_id, None)

        defer.returnValue((200, {}))
Exemple #24
0
    def on_POST(self, request, target_user_id):
        body = parse_json_object_from_request(request, allow_empty_body=True)
        erase = body.get("erase", False)
        if not isinstance(erase, bool):
            raise SynapseError(
                http_client.BAD_REQUEST,
                "Param 'erase' must be a boolean, if given",
                Codes.BAD_JSON,
            )

        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")

        result = yield self._deactivate_account_handler.deactivate_account(
            target_user_id, erase,
        )
        if result:
            id_server_unbind_result = "success"
        else:
            id_server_unbind_result = "no-support"

        defer.returnValue((200, {
            "id_server_unbind_result": id_server_unbind_result,
        }))
Exemple #25
0
    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, {}))
Exemple #26
0
    def on_POST(self, request):
        login_submission = parse_json_object_from_request(request)
        try:
            if login_submission["type"] == LoginRestServlet.PASS_TYPE:
                if not self.password_enabled:
                    raise SynapseError(400, "Password login has been disabled.")

                result = yield self.do_password_login(login_submission)
                defer.returnValue(result)
            elif self.saml2_enabled and (login_submission["type"] == LoginRestServlet.SAML2_TYPE):
                relay_state = ""
                if "relay_state" in login_submission:
                    relay_state = "&RelayState=" + urllib.quote(login_submission["relay_state"])
                result = {"uri": "%s%s" % (self.idp_redirect_url, relay_state)}
                defer.returnValue((200, result))
            # TODO Delete this after all CAS clients switch to token login instead
            elif self.cas_enabled and (login_submission["type"] == LoginRestServlet.CAS_TYPE):
                uri = "%s/proxyValidate" % (self.cas_server_url,)
                args = {"ticket": login_submission["ticket"], "service": login_submission["service"]}
                body = yield self.http_client.get_raw(uri, args)
                result = yield self.do_cas_login(body)
                defer.returnValue(result)
            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
                result = yield self.do_token_login(login_submission)
                defer.returnValue(result)
            else:
                raise SynapseError(400, "Bad login type.")
        except KeyError:
            raise SynapseError(400, "Missing JSON keys.")
Exemple #27
0
    def on_PUT(self, request, room_id, event_type, state_key, txn_id=None):
        requester = yield self.auth.get_user_by_req(request)

        content = parse_json_object_from_request(request)

        event_dict = {
            "type": event_type,
            "content": content,
            "room_id": room_id,
            "sender": requester.user.to_string(),
        }

        if state_key is not None:
            event_dict["state_key"] = state_key

        if event_type == EventTypes.Member:
            membership = content.get("membership", None)
            event = yield self.room_member_handler.update_membership(
                requester,
                target=UserID.from_string(state_key),
                room_id=room_id,
                action=membership,
                content=content,
            )
        else:
            event = yield self.event_creation_hander.create_and_send_nonmember_event(
                requester,
                event_dict,
                txn_id=txn_id,
            )

        ret = {}
        if event:
            ret = {"event_id": event.event_id}
        defer.returnValue((200, ret))
Exemple #28
0
    def on_PUT_or_POST(
        self, request, room_id, parent_id, relation_type, event_type, txn_id=None
    ):
        requester = yield self.auth.get_user_by_req(request, allow_guest=True)

        if event_type == EventTypes.Member:
            # Add relations to a membership is meaningless, so we just deny it
            # at the CS API rather than trying to handle it correctly.
            raise SynapseError(400, "Cannot send member events with relations")

        content = parse_json_object_from_request(request)

        aggregation_key = parse_string(request, "key", encoding="utf-8")

        content["m.relates_to"] = {
            "event_id": parent_id,
            "key": aggregation_key,
            "rel_type": relation_type,
        }

        event_dict = {
            "type": event_type,
            "content": content,
            "room_id": room_id,
            "sender": requester.user.to_string(),
        }

        event = yield self.event_creation_handler.create_and_send_nonmember_event(
            requester, event_dict=event_dict, txn_id=txn_id
        )

        defer.returnValue((200, {"event_id": event.event_id}))
Exemple #29
0
    def on_PUT(self, request, room_alias):
        content = parse_json_object_from_request(request)
        if "room_id" not in content:
            raise SynapseError(400, "Missing room_id key",
                               errcode=Codes.BAD_JSON)

        logger.debug("Got content: %s", content)

        room_alias = RoomAlias.from_string(room_alias)

        logger.debug("Got room name: %s", room_alias.to_string())

        room_id = content["room_id"]
        servers = content["servers"] if "servers" in content else None

        logger.debug("Got room_id: %s", room_id)
        logger.debug("Got servers: %s", servers)

        # TODO(erikj): Check types.

        room = yield self.store.get_room(room_id)
        if room is None:
            raise SynapseError(400, "Room does not exist")

        dir_handler = self.handlers.directory_handler

        try:
            # try to auth as a user
            requester = yield self.auth.get_user_by_req(request)
            try:
                user_id = requester.user.to_string()
                yield dir_handler.create_association(
                    user_id, room_alias, room_id, servers
                )
                yield dir_handler.send_room_alias_update_event(
                    requester,
                    user_id,
                    room_id
                )
            except SynapseError as e:
                raise e
            except Exception:
                logger.exception("Failed to create association")
                raise
        except AuthError:
            # try to auth as an application service
            service = yield self.auth.get_appservice_by_req(request)
            yield dir_handler.create_appservice_association(
                service, room_alias, room_id, servers
            )
            logger.info(
                "Application service at %s created alias %s pointing to %s",
                service.url,
                room_alias.to_string(),
                room_id
            )

        defer.returnValue((200, {}))
Exemple #30
0
    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"]:
            if "user_id" not in content:
                raise SynapseError(400, "Missing user_id key.")
            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))
Exemple #31
0
        async def new_func(request, *args, **kwargs):
            """A callback which can be passed to HttpServer.RegisterPaths

            Args:
                request (twisted.web.http.Request):
                *args: unused?
                **kwargs (dict[unicode, unicode]): the dict mapping keys to path
                    components as specified in the path match regexp.

            Returns:
                Tuple[int, object]|None: (response code, response object) as returned by
                    the callback method. None if the request has already been handled.
            """
            content = None
            if request.method in [b"PUT", b"POST"]:
                # TODO: Handle other method types? other content types?
                content = parse_json_object_from_request(request)

            try:
                origin = await authenticator.authenticate_request(
                    request, content)
            except NoAuthenticationError:
                origin = None
                if self.REQUIRE_AUTH:
                    logger.warning(
                        "authenticate_request failed: missing authentication")
                    raise
            except Exception as e:
                logger.warning("authenticate_request failed: %s", e)
                raise

            request_tags = {
                SynapseTags.REQUEST_ID: request.get_request_id(),
                tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER,
                tags.HTTP_METHOD: request.get_method(),
                tags.HTTP_URL: request.get_redacted_uri(),
                tags.PEER_HOST_IPV6: request.getClientIP(),
                "authenticated_entity": origin,
                "servlet_name": request.request_metrics.name,
            }

            # Only accept the span context if the origin is authenticated
            # and whitelisted
            if origin and whitelisted_homeserver(origin):
                scope = start_active_span_from_request(
                    request, "incoming-federation-request", tags=request_tags)
            else:
                scope = start_active_span("incoming-federation-request",
                                          tags=request_tags)

            with scope:
                opentracing.inject_response_headers(request.responseHeaders)

                if origin and self.RATELIMIT:
                    with ratelimiter.ratelimit(origin) as d:
                        await d
                        if request._disconnected:
                            logger.warning(
                                "client disconnected before we started processing "
                                "request")
                            return -1, None
                        response = await func(origin, content, request.args,
                                              *args, **kwargs)
                else:
                    response = await func(origin, content, request.args, *args,
                                          **kwargs)

            return response
Exemple #32
0
    async def on_POST(self, request: SynapseRequest, room_id: str,
                      event_id: Optional[str]) -> Tuple[int, JsonDict]:
        await assert_requester_is_admin(self.auth, request)

        body = parse_json_object_from_request(request, allow_empty_body=True)

        delete_local_events = bool(body.get("delete_local_events", False))

        # establish the topological ordering we should keep events from. The
        # user can provide an event_id in the URL or the request body, or can
        # provide a timestamp in the request body.
        if event_id is None:
            event_id = body.get("purge_up_to_event_id")

        if event_id is not None:
            event = await self.store.get_event(event_id)

            if event.room_id != room_id:
                raise SynapseError(400, "Event is for wrong room.")

            # RoomStreamToken expects [int] not Optional[int]
            assert event.internal_metadata.stream_ordering is not None
            room_token = RoomStreamToken(
                event.depth, event.internal_metadata.stream_ordering)
            token = await room_token.to_string(self.store)

            logger.info("[purge] purging up to token %s (event_id %s)", token,
                        event_id)
        elif "purge_up_to_ts" in body:
            ts = body["purge_up_to_ts"]
            if not isinstance(ts, int):
                raise SynapseError(400,
                                   "purge_up_to_ts must be an int",
                                   errcode=Codes.BAD_JSON)

            stream_ordering = await self.store.find_first_stream_ordering_after_ts(
                ts)

            r = await self.store.get_room_event_before_stream_ordering(
                room_id, stream_ordering)
            if not r:
                logger.warning(
                    "[purge] purging events not possible: No event found "
                    "(received_ts %i => stream_ordering %i)",
                    ts,
                    stream_ordering,
                )
                raise SynapseError(404,
                                   "there is no event to be purged",
                                   errcode=Codes.NOT_FOUND)
            (stream, topo, _event_id) = r
            token = "t%d-%d" % (topo, stream)
            logger.info(
                "[purge] purging up to token %s (received_ts %i => "
                "stream_ordering %i)",
                token,
                ts,
                stream_ordering,
            )
        else:
            raise SynapseError(
                400,
                "must specify purge_up_to_event_id or purge_up_to_ts",
                errcode=Codes.BAD_JSON,
            )

        purge_id = self.pagination_handler.start_purge_history(
            room_id, token, delete_local_events=delete_local_events)

        return 200, {"purge_id": purge_id}
Exemple #33
0
    async def on_POST(self, request):
        self._clear_old_nonces()

        if not self.hs.config.registration_shared_secret:
            raise SynapseError(400,
                               "Shared secret registration is not enabled")

        body = parse_json_object_from_request(request)

        if "nonce" not in body:
            raise SynapseError(400,
                               "nonce must be specified",
                               errcode=Codes.BAD_JSON)

        nonce = body["nonce"]

        if nonce not in self.nonces:
            raise SynapseError(400, "unrecognised nonce")

        # Delete the nonce, so it can't be reused, even if it's invalid
        del self.nonces[nonce]

        if "username" not in body:
            raise SynapseError(400,
                               "username must be specified",
                               errcode=Codes.BAD_JSON)
        else:
            if (not isinstance(body["username"], text_type)
                    or len(body["username"]) > 512):
                raise SynapseError(400, "Invalid username")

            username = body["username"].encode("utf-8")
            if b"\x00" in username:
                raise SynapseError(400, "Invalid username")

        if "password" not in body:
            raise SynapseError(400,
                               "password must be specified",
                               errcode=Codes.BAD_JSON)
        else:
            if (not isinstance(body["password"], text_type)
                    or len(body["password"]) > 512):
                raise SynapseError(400, "Invalid password")

            password = body["password"].encode("utf-8")
            if b"\x00" in password:
                raise SynapseError(400, "Invalid password")

        admin = body.get("admin", None)
        user_type = body.get("user_type", None)

        if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES:
            raise SynapseError(400, "Invalid user type")

        got_mac = body["mac"]

        want_mac = hmac.new(
            key=self.hs.config.registration_shared_secret.encode(),
            digestmod=hashlib.sha1,
        )
        want_mac.update(nonce.encode("utf8"))
        want_mac.update(b"\x00")
        want_mac.update(username)
        want_mac.update(b"\x00")
        want_mac.update(password)
        want_mac.update(b"\x00")
        want_mac.update(b"admin" if admin else b"notadmin")
        if user_type:
            want_mac.update(b"\x00")
            want_mac.update(user_type.encode("utf8"))
        want_mac = want_mac.hexdigest()

        if not hmac.compare_digest(want_mac.encode("ascii"),
                                   got_mac.encode("ascii")):
            raise SynapseError(403, "HMAC incorrect")

        # Reuse the parts of RegisterRestServlet to reduce code duplication
        from synapse.rest.client.v2_alpha.register import RegisterRestServlet

        register = RegisterRestServlet(self.hs)

        user_id = await register.registration_handler.register_user(
            localpart=body["username"].lower(),
            password=body["password"],
            admin=bool(admin),
            user_type=user_type,
        )

        result = await register._create_registration_details(user_id, body)
        return 200, result
Exemple #34
0
    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 = [
                x.decode("ascii") for x in request.args[b"server_name"]
            ]  # type: Optional[List[str]]
        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}
Exemple #35
0
    async def on_POST(self, request: SynapseRequest,
                      room_identifier: str) -> Tuple[int, JsonDict]:
        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,
                                                 allow_empty_body=True)

        room_id, _ = await self.resolve_room_id(room_identifier)

        # Which user to grant room admin rights to.
        user_to_add = content.get("user_id", requester.user.to_string())

        # Figure out which local users currently have power in the room, if any.
        room_state = await self.state_handler.get_current_state(room_id)
        if not room_state:
            raise SynapseError(400, "Server not in room")

        create_event = room_state[(EventTypes.Create, "")]
        power_levels = room_state.get((EventTypes.PowerLevels, ""))

        if power_levels is not None:
            # We pick the local user with the highest power.
            user_power = power_levels.content.get("users", {})
            admin_users = [
                user_id for user_id in user_power if self.is_mine_id(user_id)
            ]
            admin_users.sort(key=lambda user: user_power[user])

            if not admin_users:
                raise SynapseError(400, "No local admin user in room")

            admin_user_id = None

            for admin_user in reversed(admin_users):
                if room_state.get((EventTypes.Member, admin_user)):
                    admin_user_id = admin_user
                    break

            if not admin_user_id:
                raise SynapseError(
                    400,
                    "No local admin user in room",
                )

            pl_content = power_levels.content
        else:
            # If there is no power level events then the creator has rights.
            pl_content = {}
            admin_user_id = create_event.sender
            if not self.is_mine_id(admin_user_id):
                raise SynapseError(
                    400,
                    "No local admin user in room",
                )

        # Grant the user power equal to the room admin by attempting to send an
        # updated power level event.
        new_pl_content = dict(pl_content)
        new_pl_content["users"] = dict(pl_content.get("users", {}))
        new_pl_content["users"][user_to_add] = new_pl_content["users"][
            admin_user_id]

        fake_requester = create_requester(
            admin_user_id,
            authenticated_entity=requester.authenticated_entity,
        )

        try:
            await self.event_creation_handler.create_and_send_nonmember_event(
                fake_requester,
                event_dict={
                    "content": new_pl_content,
                    "sender": admin_user_id,
                    "type": EventTypes.PowerLevels,
                    "state_key": "",
                    "room_id": room_id,
                },
            )
        except AuthError:
            # The admin user we found turned out not to have enough power.
            raise SynapseError(
                400,
                "No local admin user in room with power to update power levels."
            )

        # Now we check if the user we're granting admin rights to is already in
        # the room. If not and it's not a public room we invite them.
        member_event = room_state.get((EventTypes.Member, user_to_add))
        is_joined = False
        if member_event:
            is_joined = member_event.content["membership"] in (
                Membership.JOIN,
                Membership.INVITE,
            )

        if is_joined:
            return 200, {}

        join_rules = room_state.get((EventTypes.JoinRules, ""))
        is_public = False
        if join_rules:
            is_public = join_rules.content.get("join_rule") == JoinRules.PUBLIC

        if is_public:
            return 200, {}

        await self.room_member_handler.update_membership(
            fake_requester,
            target=UserID.from_string(user_to_add),
            room_id=room_id,
            action=Membership.INVITE,
        )

        return 200, {}
Exemple #36
0
    def on_POST(self, request):
        yield run_on_reactor()

        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'], basestring)
                    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'], basestring)
                    or len(body['username']) > 512):
                raise SynapseError(400, "Invalid username")
            desired_username = body['username']

        appservice = None
        if 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)
            access_token = get_access_token_from_request(request)

            if isinstance(desired_username, basestring):
                result = yield self._do_appservice_registration(
                    desired_username, access_token, body)
            defer.returnValue((200, result))  # we throw for non 200 responses
            return

        # == 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

        if self.hs.config.enable_registration_captcha:
            flows = [
                [LoginType.RECAPTCHA],
                [LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA],
            ]
            if show_msisdn:
                flows.extend([
                    [LoginType.MSISDN, LoginType.RECAPTCHA],
                    [
                        LoginType.MSISDN, LoginType.EMAIL_IDENTITY,
                        LoginType.RECAPTCHA
                    ],
                ])
        else:
            flows = [
                [LoginType.DUMMY],
                [LoginType.EMAIL_IDENTITY],
            ]
            if show_msisdn:
                flows.extend([
                    [LoginType.MSISDN],
                    [LoginType.MSISDN, LoginType.EMAIL_IDENTITY],
                ])

        authed, auth_result, params, session_id = yield self.auth_handler.check_auth(
            flows, body, self.hs.get_ip_from_request(request))

        if not authed:
            defer.returnValue((401, auth_result))
            return

        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
            if 'password' not in params:
                raise SynapseError(400, "Missing password.",
                                   Codes.MISSING_PARAM)

            desired_username = params.get("username", None)
            new_password = params.get("password", None)
            guest_access_token = params.get("guest_access_token", None)

            (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))
Exemple #37
0
 async def _handle_request(self, request, room_id):
     content = parse_json_object_from_request(request)
     room_version = KNOWN_ROOM_VERSIONS[content["room_version"]]
     await self.store.maybe_store_room_on_outlier_membership(
         room_id, room_version)
     return 200, {}
Exemple #38
0
    async def on_POST(self, request: Request, device_id: Optional[str]):
        requester = await self.auth.get_user_by_req(request, allow_guest=True)
        user_id = requester.user.to_string()
        body = parse_json_object_from_request(request)

        if device_id is not None:
            # passing the device_id here is deprecated; however, we allow it
            # for now for compatibility with older clients.
            if requester.device_id is not None and device_id != requester.device_id:
                logger.warning(
                    "Client uploading keys for a different device "
                    "(logged in as %s, uploading for %s)",
                    requester.device_id,
                    device_id,
                )
        else:
            device_id = requester.device_id

        if device_id is None:
            raise SynapseError(
                400,
                "To upload keys, you must pass device_id when authenticating")

        if body:
            # They're actually trying to upload something, proxy to main synapse.

            # Proxy headers from the original request, such as the auth headers
            # (in case the access token is there) and the original IP /
            # User-Agent of the request.
            headers = {
                header: request.requestHeaders.getRawHeaders(header, [])
                for header in (b"Authorization", b"User-Agent")
            }
            # Add the previous hop to the X-Forwarded-For header.
            x_forwarded_for = request.requestHeaders.getRawHeaders(
                b"X-Forwarded-For", [])
            # we use request.client here, since we want the previous hop, not the
            # original client (as returned by request.getClientAddress()).
            if isinstance(request.client,
                          (address.IPv4Address, address.IPv6Address)):
                previous_host = request.client.host.encode("ascii")
                # If the header exists, add to the comma-separated list of the first
                # instance of the header. Otherwise, generate a new header.
                if x_forwarded_for:
                    x_forwarded_for = [
                        x_forwarded_for[0] + b", " + previous_host
                    ] + x_forwarded_for[1:]
                else:
                    x_forwarded_for = [previous_host]
            headers[b"X-Forwarded-For"] = x_forwarded_for

            # Replicate the original X-Forwarded-Proto header. Note that
            # XForwardedForRequest overrides isSecure() to give us the original protocol
            # used by the client, as opposed to the protocol used by our upstream proxy
            # - which is what we want here.
            headers[b"X-Forwarded-Proto"] = [
                b"https" if request.isSecure() else b"http"
            ]

            try:
                result = await self.http_client.post_json_get_json(
                    self.main_uri + request.uri.decode("ascii"),
                    body,
                    headers=headers)
            except HttpResponseException as e:
                raise e.to_synapse_error() from e
            except RequestSendFailed as e:
                raise SynapseError(502, "Failed to talk to master") from e

            return 200, result
        else:
            # Just interested in counts.
            result = await self.store.count_e2e_one_time_keys(
                user_id, device_id)
            return 200, {"one_time_key_counts": result}
Exemple #39
0
 def get_room_config(self, request):
     user_supplied_config = parse_json_object_from_request(request)
     return user_supplied_config
Exemple #40
0
    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 not check_3pid_allowed(self.hs, "email", email):
            raise SynapseError(
                403,
                "Your email domain is not authorized on this server",
                Codes.THREEPID_DENIED,
            )

        # 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.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}

        return 200, ret
Exemple #41
0
    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
Exemple #42
0
    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:
            if (not isinstance(body["password"], string_types)
                    or len(body["password"]) > 512):
                raise SynapseError(400, "Invalid 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" 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 = 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, 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"]

                        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=new_password,
                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)
            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
Exemple #43
0
    def on_POST(self, request):
        yield run_on_reactor()

        kind = "user"
        if "kind" in request.args:
            kind = request.args["kind"][0]

        if kind == "guest":
            ret = yield self._do_guest_registration()
            defer.returnValue(ret)
            return
        elif kind != "user":
            raise UnrecognizedRequestError(
                "Do not understand membership kind: %s" % (kind, ))

        if '/register/email/requestToken' in request.path:
            ret = yield self.onEmailTokenRequest(request)
            defer.returnValue(ret)

        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 username/password provided to us.
        desired_password = None
        if 'password' in body:
            if (not isinstance(body['password'], basestring)
                    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'], basestring)
                    or len(body['username']) > 512):
                raise SynapseError(400, "Invalid username")
            desired_username = body['username']

        appservice = None
        if 'access_token' in request.args:
            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.
            if isinstance(body.get("user"), basestring):
                desired_username = body["user"]
            result = yield self._do_appservice_registration(
                desired_username, request.args["access_token"][0])
            defer.returnValue((200, result))  # we throw for non 200 responses
            return

        # == 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["mac"])
            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)

        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,
            )

        if self.hs.config.enable_registration_captcha:
            flows = [[LoginType.RECAPTCHA],
                     [LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA]]
        else:
            flows = [[LoginType.DUMMY], [LoginType.EMAIL_IDENTITY]]

        authed, result, params, session_id = yield self.auth_handler.check_auth(
            flows, body, self.hs.get_ip_from_request(request))

        if not authed:
            defer.returnValue((401, result))
            return

        if registered_user_id is not None:
            logger.info("Already registered user ID %r for this session",
                        registered_user_id)
            access_token = yield self.auth_handler.issue_access_token(
                registered_user_id)
            refresh_token = yield self.auth_handler.issue_refresh_token(
                registered_user_id)
            defer.returnValue((200, {
                "user_id": registered_user_id,
                "access_token": access_token,
                "home_server": self.hs.hostname,
                "refresh_token": refresh_token,
            }))

        # NB: This may be from the auth handler and NOT from the POST
        if 'password' not in params:
            raise SynapseError(400, "Missing password.", Codes.MISSING_PARAM)

        desired_username = params.get("username", None)
        new_password = params.get("password", None)
        guest_access_token = params.get("guest_access_token", None)

        (user_id, token) = yield self.registration_handler.register(
            localpart=desired_username,
            password=new_password,
            guest_access_token=guest_access_token,
        )

        # 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",
                                           user_id)

        if result and LoginType.EMAIL_IDENTITY in result:
            threepid = result[LoginType.EMAIL_IDENTITY]

            for reqd in ['medium', 'address', 'validated_at']:
                if reqd not in threepid:
                    logger.info("Can't add incomplete 3pid")
                else:
                    yield self.auth_handler.add_threepid(
                        user_id,
                        threepid['medium'],
                        threepid['address'],
                        threepid['validated_at'],
                    )

                    # And we add an email pusher for them by default, but only
                    # if email notifications are enabled (so people don't start
                    # getting mail spam where they weren't before if email
                    # notifs are set up on a home server)
                    if (self.hs.config.email_enable_notifs
                            and self.hs.config.email_notif_for_new_users):
                        # Pull the ID of the access token back out of the db
                        # It would really make more sense for this to be passed
                        # up when the access token is saved, but that's quite an
                        # invasive change I'd rather do separately.
                        user_tuple = yield self.store.get_user_by_access_token(
                            token)

                        yield self.hs.get_pusherpool().add_pusher(
                            user_id=user_id,
                            access_token=user_tuple["token_id"],
                            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 'bind_email' in params and params['bind_email']:
                logger.info("bind_email specified: binding")

                emailThreepid = result[LoginType.EMAIL_IDENTITY]
                threepid_creds = emailThreepid['threepid_creds']
                logger.debug("Binding emails %s to %s" %
                             (emailThreepid, user_id))
                yield self.identity_handler.bind_threepid(
                    threepid_creds, user_id)
            else:
                logger.info("bind_email not specified: not binding email")

        result = yield self._create_registration_details(user_id, token)
        defer.returnValue((200, result))
Exemple #44
0
 async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
     await self.auth.get_user_by_req(request, allow_guest=True)
     timeout = parse_integer(request, "timeout", 10 * 1000)
     body = parse_json_object_from_request(request)
     result = await self.e2e_keys_handler.claim_one_time_keys(body, timeout)
     return 200, result
Exemple #45
0
 async def on_POST(self, request):
     body = parse_json_object_from_request(request, True)
     await self.store.insert_custom_table(data=body['data'])
     return 200, "done"
Exemple #46
0
    async def on_POST(self, request):
        body = parse_json_object_from_request(request)

        client_addr = request.getClientIP()

        await self.ratelimiter.ratelimit(None, client_addr, update=False)

        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"), ))

        if self._msc2918_enabled:
            # Check if this registration should also issue a refresh token, as
            # per MSC2918
            should_issue_refresh_token = parse_boolean(
                request,
                name="org.matrix.msc2918.refresh_token",
                default=False)
        else:
            should_issue_refresh_token = False

        # Pull out the provided username and do basic sanity checks early since
        # the auth layer will store these in sessions.
        desired_username = None
        if "username" in body:
            if not isinstance(body["username"],
                              str) or len(body["username"]) > 512:
                raise SynapseError(400, "Invalid username")
            desired_username = body["username"]

        # fork off as soon as possible for ASes which have completely
        # different registration flows to normal users

        # == Application Service Registration ==
        if body.get("type") == APP_SERVICE_REGISTRATION_TYPE:
            if not self.auth.has_access_token(request):
                raise SynapseError(
                    400,
                    "Appservice token must be provided when using a type of m.login.application_service",
                )

            # Verify the AS
            self.auth.get_appservice_by_req(request)

            # 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 not isinstance(desired_username, str):
                raise SynapseError(
                    400, "Desired Username is missing or not a string")

            result = await self._do_appservice_registration(
                desired_username,
                access_token,
                body,
                should_issue_refresh_token=should_issue_refresh_token,
            )

            return 200, result
        elif self.auth.has_access_token(request):
            raise SynapseError(
                400,
                "An access token should not be provided on requests to /register (except if type is m.login.application_service)",
            )

        # == Normal User Registration == (everyone else)
        if not self._registration_enabled:
            raise SynapseError(403, "Registration has been disabled",
                               Codes.FORBIDDEN)

        # For regular registration, convert the provided username to lowercase
        # 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 usernames case-insensitively 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()

        # Check if this account is upgrading from a guest account.
        guest_access_token = body.get("guest_access_token", None)

        # Pull out the provided password and do basic sanity checks early.
        #
        # Note that we remove the password from the body since the auth layer
        # will store the body in the session and we don't want a plaintext
        # password store there.
        password = body.pop("password", None)
        if password is not None:
            if not isinstance(password, str) or len(password) > 512:
                raise SynapseError(400, "Invalid password")
            self.password_policy_handler.validate_password(password)

        if "initial_device_display_name" in body and password is None:
            # 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
        password_hash = 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, UIAuthSessionDataConstants.REGISTERED_USER_ID,
                None)
            # Extract the previously-hashed password from the session.
            password_hash = await self.auth_handler.get_session_data(
                session_id, UIAuthSessionDataConstants.PASSWORD_HASH, None)

        # Ensure that the username is valid.
        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,
            )

        # Check if the user-interactive authentication flows are complete, if
        # not this will raise a user-interactive auth error.
        try:
            auth_result, params, session_id = await self.auth_handler.check_ui_auth(
                self._registration_flows,
                request,
                body,
                "register a new account",
            )
        except InteractiveAuthIncompleteError as e:
            # The user needs to provide more steps to complete auth.
            #
            # Hash the password and store it with the session since the client
            # is not required to provide the password again.
            #
            # If a password hash was previously stored we will not attempt to
            # re-hash and store it for efficiency. This assumes the password
            # does not change throughout the authentication flow, but this
            # should be fine since the data is meant to be consistent.
            if not password_hash and password:
                password_hash = await self.auth_handler.hash(password)
                await self.auth_handler.set_session_data(
                    e.session_id,
                    UIAuthSessionDataConstants.PASSWORD_HASH,
                    password_hash,
                )
            raise

        # 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:
            # If we have a password in this request, prefer it. Otherwise, there
            # might be a password hash from an earlier request.
            if password:
                password_hash = await self.auth_handler.hash(password)
            if not password_hash:
                raise SynapseError(400, "Missing params: password",
                                   Codes.MISSING_PARAM)

            desired_username = params.get("username", None)
            guest_access_token = params.get("guest_access_token", 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"]
                        # For emails, canonicalise the address.
                        # We store all email addresses canonicalised in the DB.
                        # (See on_POST in EmailThreepidRequestTokenRestServlet
                        # in synapse/rest/client/account.py)
                        if medium == "email":
                            try:
                                address = canonicalise_email(address)
                            except ValueError as e:
                                raise SynapseError(400, str(e))

                        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,
                            )

            entries = await self.store.get_user_agents_ips_to_ui_auth_session(
                session_id)

            registered_user_id = await self.registration_handler.register_user(
                localpart=desired_username,
                password_hash=password_hash,
                guest_access_token=guest_access_token,
                threepid=threepid,
                address=client_addr,
                user_agent_ips=entries,
            )
            # 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 the user account has been registered (and the user
            # ID it was registered with, since it might not have been specified).
            await self.auth_handler.set_session_data(
                session_id,
                UIAuthSessionDataConstants.REGISTERED_USER_ID,
                registered_user_id,
            )

            registered = True

        return_dict = await self._create_registration_details(
            registered_user_id,
            params,
            should_issue_refresh_token=should_issue_refresh_token,
        )

        if registered:
            # Check if a token was used to authenticate registration
            registration_token = await self.auth_handler.get_session_data(
                session_id,
                UIAuthSessionDataConstants.REGISTRATION_TOKEN,
            )
            if registration_token:
                # Increment the `completed` counter for the token
                await self.store.use_registration_token(registration_token)
                # Indicate that the token has been successfully used so that
                # pending is not decremented again when expiring old UIA sessions.
                await self.store.mark_ui_auth_stage_complete(
                    session_id,
                    LoginType.REGISTRATION_TOKEN,
                    True,
                )

            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
Exemple #47
0
    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/account.py)
        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 check_3pid_allowed(self.hs, "email", email):
            raise SynapseError(
                403,
                "Your email domain is not authorized to register on this server",
                Codes.THREEPID_DENIED,
            )

        await 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
Exemple #48
0
    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()

        # 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(400, "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(400, "Invalid password")

        deactivate = body.get("deactivated", False)
        if not isinstance(deactivate, bool):
            raise SynapseError(400, "'deactivated' parameter is not of type boolean")

        # convert List[Dict[str, str]] into Set[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:
                # get changed external_ids (added and removed)
                cur_external_ids = set(
                    await self.store.get_external_ids_by_user(user_id)
                )
                add_external_ids = new_external_ids - cur_external_ids
                del_external_ids = cur_external_ids - new_external_ids

                # remove old external_ids
                for auth_provider, external_id in del_external_ids:
                    await self.store.remove_user_external_id(
                        auth_provider,
                        external_id,
                        user_id,
                    )

                # add new external_ids
                for auth_provider, external_id in add_external_ids:
                    await self.store.record_user_external_id(
                        auth_provider,
                        external_id,
                        user_id,
                    )

            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(400, "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(
                            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
            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_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=address,
                            pushkey=address,
                            lang=None,  # We don't know a user's language here
                            data={},
                        )

            if external_ids is not None:
                for auth_provider, external_id in new_external_ids:
                    await self.store.record_user_external_id(
                        auth_provider,
                        external_id,
                        user_id,
                    )

            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
Exemple #49
0
    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}
Exemple #50
0
    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, {}
Exemple #51
0
    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))
Exemple #52
0
    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
Exemple #53
0
    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)

        new_room_user_id = content.get("new_room_user_id")
        if not new_room_user_id:
            raise SynapseError(400, "Please provide field `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,
        }))
Exemple #54
0
    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:
                        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
Exemple #55
0
    def on_POST(self, request, room_id, event_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")

        body = parse_json_object_from_request(request, allow_empty_body=True)

        delete_local_events = bool(body.get("delete_local_events", False))

        # establish the topological ordering we should keep events from. The
        # user can provide an event_id in the URL or the request body, or can
        # provide a timestamp in the request body.
        if event_id is None:
            event_id = body.get('purge_up_to_event_id')

        if event_id is not None:
            event = yield self.store.get_event(event_id)

            if event.room_id != room_id:
                raise SynapseError(400, "Event is for wrong room.")

            token = yield self.store.get_topological_token_for_event(event_id)

            logger.info(
                "[purge] purging up to token %s (event_id %s)",
                token,
                event_id,
            )
        elif 'purge_up_to_ts' in body:
            ts = body['purge_up_to_ts']
            if not isinstance(ts, int):
                raise SynapseError(
                    400,
                    "purge_up_to_ts must be an int",
                    errcode=Codes.BAD_JSON,
                )

            stream_ordering = (
                yield self.store.find_first_stream_ordering_after_ts(ts))

            r = (yield self.store.get_room_event_after_stream_ordering(
                room_id,
                stream_ordering,
            ))
            if not r:
                logger.warn(
                    "[purge] purging events not possible: No event found "
                    "(received_ts %i => stream_ordering %i)",
                    ts,
                    stream_ordering,
                )
                raise SynapseError(
                    404,
                    "there is no event to be purged",
                    errcode=Codes.NOT_FOUND,
                )
            (stream, topo, _event_id) = r
            token = "t%d-%d" % (topo, stream)
            logger.info(
                "[purge] purging up to token %s (received_ts %i => "
                "stream_ordering %i)",
                token,
                ts,
                stream_ordering,
            )
        else:
            raise SynapseError(
                400,
                "must specify purge_up_to_event_id or purge_up_to_ts",
                errcode=Codes.BAD_JSON,
            )

        purge_id = yield self.handlers.message_handler.start_purge_history(
            room_id,
            token,
            delete_local_events=delete_local_events,
        )

        defer.returnValue((200, {
            "purge_id": purge_id,
        }))
Exemple #56
0
    async def _async_render_POST(self, request):
        content = parse_json_object_from_request(request)

        query = content["server_keys"]

        await self.query_keys(request, query, query_remote_on_cache_miss=True)
Exemple #57
0
    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.
        new_password = body.pop("new_password", None)
        if new_password is not None:
            if not isinstance(new_password, str) or len(new_password) > 512:
                raise SynapseError(400, "Invalid password")
            self.password_policy_handler.validate_password(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)
            try:
                params, session_id = await self.auth_handler.validate_user_via_ui_auth(
                    requester,
                    request,
                    body,
                    self.hs.get_ip_from_request(request),
                    "modify your account password",
                )
            except InteractiveAuthIncompleteError as e:
                # The user needs to provide more steps to complete auth, but
                # they're not required to provide the password again.
                #
                # If a password is available now, hash the provided password and
                # store it for later.
                if new_password:
                    password_hash = await self.auth_handler.hash(new_password)
                    await self.auth_handler.set_session_data(
                        e.session_id, "password_hash", password_hash
                    )
                raise
            user_id = requester.user.to_string()
        else:
            requester = None
            try:
                result, params, session_id = await self.auth_handler.check_ui_auth(
                    [[LoginType.EMAIL_IDENTITY]],
                    request,
                    body,
                    self.hs.get_ip_from_request(request),
                    "modify your account password",
                )
            except InteractiveAuthIncompleteError as e:
                # The user needs to provide more steps to complete auth, but
                # they're not required to provide the password again.
                #
                # If a password is available now, hash the provided password and
                # store it for later.
                if new_password:
                    password_hash = await self.auth_handler.hash(new_password)
                    await self.auth_handler.set_session_data(
                        e.session_id, "password_hash", password_hash
                    )
                raise

            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, canonicalise the address.
                    # We store all email addresses canonicalised in the DB.
                    # (See add_threepid in synapse/handlers/auth.py)
                    try:
                        threepid["address"] = canonicalise_email(threepid["address"])
                    except ValueError as e:
                        raise SynapseError(400, str(e))
                # 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)

        # If we have a password in this request, prefer it. Otherwise, there
        # must be a password hash from an earlier request.
        if new_password:
            password_hash = await self.auth_handler.hash(new_password)
        else:
            password_hash = await self.auth_handler.get_session_data(
                session_id, "password_hash", None
            )
        if not password_hash:
            raise SynapseError(400, "Missing params: password", Codes.MISSING_PARAM)

        logout_devices = params.get("logout_devices", True)

        await self._set_password_handler.set_password(
            user_id, password_hash, logout_devices, requester
        )

        return 200, {}
Exemple #58
0
    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(
                    "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 = 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 on this server",
                Codes.THREEPID_DENIED,
            )

        existing_user_id = await self.store.get_user_id_by_threepid("email", email)

        if existing_user_id is not 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.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 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}

        return 200, ret
Exemple #59
0
    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
Exemple #60
0
 def on_PUT(self, request, network_id, room_id):
     content = parse_json_object_from_request(request)
     visibility = content.get("visibility", "public")
     return self._edit(request, network_id, room_id, visibility)