예제 #1
0
파일: login.py 프로젝트: yvwvnacb/synapse
    async def on_POST(self, request: SynapseRequest):
        login_submission = parse_json_object_from_request(request)

        try:
            if login_submission["type"] == LoginRestServlet.APPSERVICE_TYPE:
                appservice = self.auth.get_appservice_by_req(request)

                if appservice.is_rate_limited():
                    self._address_ratelimiter.ratelimit(request.getClientIP())

                result = await self._do_appservice_login(
                    login_submission, appservice)
            elif self.jwt_enabled and (
                    login_submission["type"] == LoginRestServlet.JWT_TYPE
                    or login_submission["type"]
                    == LoginRestServlet.JWT_TYPE_DEPRECATED):
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_jwt_login(login_submission)
            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_token_login(login_submission)
            else:
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_other_login(login_submission)
        except KeyError:
            raise SynapseError(400, "Missing JSON keys.")

        well_known_data = self._well_known_builder.get_well_known()
        if well_known_data:
            result["well_known"] = well_known_data
        return 200, result
예제 #2
0
    async def on_POST(self,
                      request: SynapseRequest) -> Tuple[int, LoginResponse]:
        login_submission = parse_json_object_from_request(request)

        # Check to see if the client requested a refresh token.
        client_requested_refresh_token = login_submission.get(
            LoginRestServlet.REFRESH_TOKEN_PARAM, False)
        if not isinstance(client_requested_refresh_token, bool):
            raise SynapseError(400, "`refresh_token` should be true or false.")

        should_issue_refresh_token = (self._refresh_tokens_enabled
                                      and client_requested_refresh_token)

        try:
            if login_submission["type"] in (
                    LoginRestServlet.APPSERVICE_TYPE,
                    LoginRestServlet.APPSERVICE_TYPE_UNSTABLE,
            ):
                appservice = self.auth.get_appservice_by_req(request)

                if appservice.is_rate_limited():
                    await self._address_ratelimiter.ratelimit(
                        None, request.getClientIP())

                result = await self._do_appservice_login(
                    login_submission,
                    appservice,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            elif self.jwt_enabled and (
                    login_submission["type"] == LoginRestServlet.JWT_TYPE
                    or login_submission["type"]
                    == LoginRestServlet.JWT_TYPE_DEPRECATED):
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_jwt_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_token_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            else:
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_other_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
        except KeyError:
            raise SynapseError(400, "Missing JSON keys.")

        well_known_data = self._well_known_builder.get_well_known()
        if well_known_data:
            result["well_known"] = well_known_data
        return 200, result
예제 #3
0
파일: login.py 프로젝트: samuel-p/synapse
    async def on_POST(self,
                      request: SynapseRequest) -> Tuple[int, LoginResponse]:
        login_submission = parse_json_object_from_request(request)

        if self._msc2918_enabled:
            # Check if this login should also issue a refresh token, as per
            # MSC2918
            should_issue_refresh_token = parse_boolean(
                request,
                name=LoginRestServlet.REFRESH_TOKEN_PARAM,
                default=False)
        else:
            should_issue_refresh_token = False

        try:
            if login_submission["type"] == LoginRestServlet.APPSERVICE_TYPE:
                appservice = self.auth.get_appservice_by_req(request)

                if appservice.is_rate_limited():
                    await self._address_ratelimiter.ratelimit(
                        None, request.getClientIP())

                result = await self._do_appservice_login(
                    login_submission,
                    appservice,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            elif self.jwt_enabled and (
                    login_submission["type"] == LoginRestServlet.JWT_TYPE
                    or login_submission["type"]
                    == LoginRestServlet.JWT_TYPE_DEPRECATED):
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_jwt_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_token_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
            else:
                await self._address_ratelimiter.ratelimit(
                    None, request.getClientIP())
                result = await self._do_other_login(
                    login_submission,
                    should_issue_refresh_token=should_issue_refresh_token,
                )
        except KeyError:
            raise SynapseError(400, "Missing JSON keys.")

        well_known_data = self._well_known_builder.get_well_known()
        if well_known_data:
            result["well_known"] = well_known_data
        return 200, result
예제 #4
0
    async def on_POST(self, request: SynapseRequest):
        self._address_ratelimiter.ratelimit(request.getClientIP())

        params = parse_json_object_from_request(request)
        logger.info("----lookup bind--------param:%s" % (str(params)))

        bind_type = params["bind_type"]
        if bind_type is None:
            raise LoginError(410,
                             "bind_type field for bind openid is missing",
                             errcode=Codes.FORBIDDEN)

        requester = await self.auth.get_user_by_req(request)
        logger.info('------requester: %s' % (requester, ))
        user_id = requester.user
        logger.info('------user: %s' % (user_id, ))

        #complete unbind
        openid = await self.hs.get_datastore(
        ).get_external_id_for_user_provider(
            bind_type,
            str(user_id),
        )
        if openid is None:
            raise LoginError(400,
                             "openid not bind",
                             errcode=Codes.OPENID_NOT_BIND)

        return 200, {}
예제 #5
0
    async def on_POST(self, request: SynapseRequest):
        login_submission = parse_json_object_from_request(request)
        logger.info('login type------%s' % (login_submission["type"], ))
        try:
            if login_submission["type"] == LoginRestServlet.APPSERVICE_TYPE:
                appservice = self.auth.get_appservice_by_req(request)

                if appservice.is_rate_limited():
                    self._address_ratelimiter.ratelimit(request.getClientIP())

                result = await self._do_appservice_login(
                    login_submission, appservice)
            elif self.jwt_enabled and (
                    login_submission["type"] == LoginRestServlet.JWT_TYPE
                    or login_submission["type"]
                    == LoginRestServlet.JWT_TYPE_DEPRECATED):
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_jwt_login(login_submission)
            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_token_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.VER_CODE_EMAIL_TYPE:  # Email verification code to login
                result = await self._do_ver_code_email_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.VER_CODE_MSISDN_TYPE:  # Msisdn verification code to login
                result = await self._do_ver_code_msisdn_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.OAUTH2_ALIPAY_TYPE:  # OAuth2 of alipay login
                result = await self._do_oauth2_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.OAUTH2_WEIXIN_TYPE:  # OAuth2 of weixin login
                result = await self._do_oauth2_login(login_submission)
            elif login_submission[
                    "type"] == LoginRestServlet.SSO_LDAP_TYPE:  # SSO of ldap login
                result = await self._do_sso_ldap_login(login_submission)
            else:
                self._address_ratelimiter.ratelimit(request.getClientIP())
                result = await self._do_other_login(login_submission)
        except KeyError:
            raise SynapseError(400, "Missing JSON keys.")

        well_known_data = self._well_known_builder.get_well_known()
        if well_known_data:
            result["well_known"] = well_known_data
        return 200, result
예제 #6
0
파일: sso.py 프로젝트: yvwvnacb/synapse
    async def handle_submit_username_request(self, request: SynapseRequest,
                                             localpart: str,
                                             session_id: str) -> None:
        """Handle a request to the username-picker 'submit' endpoint

        Will serve an HTTP response to the request.

        Args:
            request: HTTP request
            localpart: localpart requested by the user
            session_id: ID of the username mapping session, extracted from a cookie
        """
        self._expire_old_sessions()
        session = self._username_mapping_sessions.get(session_id)
        if not session:
            logger.info("Couldn't find session id %s", session_id)
            raise SynapseError(400, "unknown session")

        logger.info("[session %s] Registering localpart %s", session_id,
                    localpart)

        attributes = UserAttributes(
            localpart=localpart,
            display_name=session.display_name,
            emails=session.emails,
        )

        # the following will raise a 400 error if the username has been taken in the
        # meantime.
        user_id = await self._register_mapped_user(
            attributes,
            session.auth_provider_id,
            session.remote_user_id,
            get_request_user_agent(request),
            request.getClientIP(),
        )

        logger.info("[session %s] Registered userid %s", session_id, user_id)

        # delete the mapping session and the cookie
        del self._username_mapping_sessions[session_id]

        # delete the cookie
        request.addCookie(
            USERNAME_MAPPING_SESSION_COOKIE_NAME,
            b"",
            expires=b"Thu, 01 Jan 1970 00:00:00 GMT",
            path=b"/",
        )

        await self._auth_handler.complete_sso_login(
            user_id,
            request,
            session.client_redirect_url,
            session.extra_login_attributes,
        )
예제 #7
0
    async def on_POST(self, request: SynapseRequest):
        self._address_ratelimiter.ratelimit(request.getClientIP())

        params = parse_json_object_from_request(request)
        medium = params["medium"]
        address = params["address"]
        if medium is None:
            raise LoginError(410,
                             "medium field for get_ver_code is missing",
                             errcode=Codes.FORBIDDEN)
        if address is None:
            raise LoginError(410,
                             "address field for get_ver_code is missing",
                             errcode=Codes.FORBIDDEN)
        if medium not in ("email", "msisdn"):
            raise LoginError(411, "no support medium", errcode=Codes.FORBIDDEN)
        # verify medium and send to email
        existing_user_id = await self.hs.get_datastore(
        ).get_user_id_by_threepid(medium, address)
        if existing_user_id is None:
            if medium == "msisdn":
                raise SynapseError(400, "msisdn not bind",
                                   Codes.TEMPORARY_NOT_BIND_MSISDN)
            else:
                raise SynapseError(400, "email not bind", Codes.EMAIL_NOT_BIND)

        # call IS for verify email ver_code
        # identity_handler = self.hs.get_identity_handler()
        # result = await identity_handler.request_get_threepid_ver_code(self.hs.config.account_threepid_delegate_email, "email", email)
        # logger.info("result:%s" % (str(result)))
        # self.get_ver_code_cache.setdefault(email, result["verCode"])
        # ver_code_service_host = "192.168.15.4"
        # ver_code_service_port = "8080"
        # ver_code_service_send_api = "/api/services/auth/v1/code"
        sendSmsType = medium
        if sendSmsType == "msisdn":
            sendSmsType = "mobile"
        params = {"value": address, "type": sendSmsType}
        try:
            result = await self.http_client.post_json_get_json(
                self.hs.config.auth_baseurl + self.hs.config.auth_get_vercode,
                params,
            )
            logger.info("result: %s" % (str(result)))
            if result["code"] != 200:
                raise SynapseError(500, result["message"])
        except HttpResponseException as e:
            logger.info("Proxied getvercode failed: %r", e)
            raise e.to_synapse_error()
        except RequestTimedOutError:
            raise SynapseError(
                500,
                "Timed out contacting extral server:ver_code_send_service")
        return 200, {}
예제 #8
0
    def ratelimit_request_token_requests(
        self,
        request: SynapseRequest,
        medium: str,
        address: str,
    ):
        """Used to ratelimit requests to `/requestToken` by IP and address.

        Args:
            request: The associated request
            medium: The type of threepid, e.g. "msisdn" or "email"
            address: The actual threepid ID, e.g. the phone number or email address
        """

        self._3pid_validation_ratelimiter_ip.ratelimit(
            (medium, request.getClientIP()))
        self._3pid_validation_ratelimiter_address.ratelimit((medium, address))
예제 #9
0
    async def get_user_by_req(
        self,
        request: SynapseRequest,
        allow_guest: bool = False,
        rights: str = "access",
        allow_expired: bool = False,
    ) -> Requester:
        """Get a registered user's ID.

        Args:
            request: An HTTP request with an access_token query parameter.
            allow_guest: If False, will raise an AuthError if the user making the
                request is a guest.
            rights: The operation being performed; the access token must allow this
            allow_expired: If True, allow the request through even if the account
                is expired, or session token lifetime has ended. Note that
                /login will deliver access tokens regardless of expiration.

        Returns:
            Resolves to the requester
        Raises:
            InvalidClientCredentialsError if no user by that token exists or the token
                is invalid.
            AuthError if access is denied for the user in the access token
        """
        try:
            ip_addr = request.getClientIP()
            user_agent = get_request_user_agent(request)

            access_token = self.get_access_token_from_request(request)

            user_id, app_service = await self._get_appservice_user_id(request)
            if user_id and app_service:
                if ip_addr and self._track_appservice_user_ips:
                    await self.store.insert_client_ip(
                        user_id=user_id,
                        access_token=access_token,
                        ip=ip_addr,
                        user_agent=user_agent,
                        device_id="dummy-device",  # stubbed
                    )

                requester = create_requester(user_id, app_service=app_service)

                request.requester = user_id
                if user_id in self._force_tracing_for_users:
                    opentracing.set_tag(opentracing.tags.SAMPLING_PRIORITY, 1)
                opentracing.set_tag("authenticated_entity", user_id)
                opentracing.set_tag("user_id", user_id)
                opentracing.set_tag("appservice_id", app_service.id)

                return requester

            user_info = await self.get_user_by_access_token(
                access_token, rights, allow_expired=allow_expired)
            token_id = user_info.token_id
            is_guest = user_info.is_guest
            shadow_banned = user_info.shadow_banned

            # Deny the request if the user account has expired.
            if self._account_validity_enabled and not allow_expired:
                if await self.store.is_account_expired(user_info.user_id,
                                                       self.clock.time_msec()):
                    raise AuthError(403,
                                    "User account has expired",
                                    errcode=Codes.EXPIRED_ACCOUNT)

            device_id = user_info.device_id

            if access_token and ip_addr:
                await self.store.insert_client_ip(
                    user_id=user_info.token_owner,
                    access_token=access_token,
                    ip=ip_addr,
                    user_agent=user_agent,
                    device_id=device_id,
                )

            if is_guest and not allow_guest:
                raise AuthError(
                    403,
                    "Guest access not allowed",
                    errcode=Codes.GUEST_ACCESS_FORBIDDEN,
                )

            requester = create_requester(
                user_info.user_id,
                token_id,
                is_guest,
                shadow_banned,
                device_id,
                app_service=app_service,
                authenticated_entity=user_info.token_owner,
            )

            request.requester = requester
            if user_info.token_owner in self._force_tracing_for_users:
                opentracing.set_tag(opentracing.tags.SAMPLING_PRIORITY, 1)
            opentracing.set_tag("authenticated_entity", user_info.token_owner)
            opentracing.set_tag("user_id", user_info.user_id)
            if device_id:
                opentracing.set_tag("device_id", device_id)

            return requester
        except KeyError:
            raise MissingClientTokenError()
예제 #10
0
파일: register.py 프로젝트: Fnux/synapse
    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        body = parse_json_object_from_request(request)

        client_addr = request.getClientIP()

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

        kind = parse_string(request, "kind", default="user")

        if kind == "guest":
            ret = await self._do_guest_registration(body, address=client_addr)
            return ret
        elif kind != "user":
            raise UnrecognizedRequestError(
                f"Do not understand membership kind: {kind}", )

        # Check if the clients wishes for this registration to issue a refresh
        # token.
        client_requested_refresh_tokens = body.get("refresh_token", False)
        if not isinstance(client_requested_refresh_tokens, bool):
            raise SynapseError(400, "`refresh_token` should be true or false.")

        should_issue_refresh_token = (self._refresh_tokens_enabled
                                      and client_requested_refresh_tokens)

        # 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.server.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
예제 #11
0
파일: sso.py 프로젝트: yvwvnacb/synapse
    async def complete_sso_login_request(
        self,
        auth_provider_id: str,
        remote_user_id: str,
        request: SynapseRequest,
        client_redirect_url: str,
        sso_to_matrix_id_mapper: Callable[[int], Awaitable[UserAttributes]],
        grandfather_existing_users: Callable[[], Awaitable[Optional[str]]],
        extra_login_attributes: Optional[JsonDict] = None,
    ) -> None:
        """
        Given an SSO ID, retrieve the user ID for it and possibly register the user.

        This first checks if the SSO ID has previously been linked to a matrix ID,
        if it has that matrix ID is returned regardless of the current mapping
        logic.

        If a callable is provided for grandfathering users, it is called and can
        potentially return a matrix ID to use. If it does, the SSO ID is linked to
        this matrix ID for subsequent calls.

        The mapping function is called (potentially multiple times) to generate
        a localpart for the user.

        If an unused localpart is generated, the user is registered from the
        given user-agent and IP address and the SSO ID is linked to this matrix
        ID for subsequent calls.

        Finally, we generate a redirect to the supplied redirect uri, with a login token

        Args:
            auth_provider_id: A unique identifier for this SSO provider, e.g.
                "oidc" or "saml".

            remote_user_id: The unique identifier from the SSO provider.

            request: The request to respond to

            client_redirect_url: The redirect URL passed in by the client.

            sso_to_matrix_id_mapper: A callable to generate the user attributes.
                The only parameter is an integer which represents the amount of
                times the returned mxid localpart mapping has failed.

                It is expected that the mapper can raise two exceptions, which
                will get passed through to the caller:

                    MappingException if there was a problem mapping the response
                        to the user.
                    RedirectException to redirect to an additional page (e.g.
                        to prompt the user for more information).

            grandfather_existing_users: A callable which can return an previously
                existing matrix ID. The SSO ID is then linked to the returned
                matrix ID.

            extra_login_attributes: An optional dictionary of extra
                attributes to be provided to the client in the login response.

        Raises:
            MappingException if there was a problem mapping the response to a user.
            RedirectException: if the mapping provider needs to redirect the user
                to an additional page. (e.g. to prompt for more information)

        """
        # grab a lock while we try to find a mapping for this user. This seems...
        # optimistic, especially for implementations that end up redirecting to
        # interstitial pages.
        with await self._mapping_lock.queue(auth_provider_id):
            # first of all, check if we already have a mapping for this user
            user_id = await self.get_sso_user_by_remote_user_id(
                auth_provider_id,
                remote_user_id,
            )

            # Check for grandfathering of users.
            if not user_id:
                user_id = await grandfather_existing_users()
                if user_id:
                    # Future logins should also match this user ID.
                    await self._store.record_user_external_id(
                        auth_provider_id, remote_user_id, user_id)

            # Otherwise, generate a new user.
            if not user_id:
                attributes = await self._call_attribute_mapper(
                    sso_to_matrix_id_mapper)

                if attributes.localpart is None:
                    # the mapper doesn't return a username. bail out with a redirect to
                    # the username picker.
                    await self._redirect_to_username_picker(
                        auth_provider_id,
                        remote_user_id,
                        attributes,
                        client_redirect_url,
                        extra_login_attributes,
                    )

                user_id = await self._register_mapped_user(
                    attributes,
                    auth_provider_id,
                    remote_user_id,
                    get_request_user_agent(request),
                    request.getClientIP(),
                )

        await self._auth_handler.complete_sso_login(user_id, request,
                                                    client_redirect_url,
                                                    extra_login_attributes)
예제 #12
0
    async def on_POST(self, request: SynapseRequest):
        self._address_ratelimiter.ratelimit(request.getClientIP())

        params = parse_json_object_from_request(request)
        logger.info("------------param:%s" % (str(params)))
        bind_type = params["bind_type"]
        auth_code = params["auth_code"]
        if bind_type is None:
            raise LoginError(410,
                             "bind_type field for bind openid is missing",
                             errcode=Codes.FORBIDDEN)
        if auth_code is None:
            raise LoginError(410,
                             "auth_code field for bind openid is missing",
                             errcode=Codes.FORBIDDEN)
        requester = await self.auth.get_user_by_req(request)
        logger.info('------requester: %s' % (requester, ))
        user_id = requester.user
        logger.info('------user: %s' % (user_id, ))

        openid = await self.hs.get_datastore(
        ).get_external_id_for_user_provider(bind_type, str(user_id))
        if openid is not None:
            raise LoginError(416,
                             "openid is binded,not support reeapt bind",
                             errcode=Codes.OPENID_BINDED)

        #first get openid from cache with auth_code
        if self._cache.__contains__(auth_code):
            remote_user_id = self._cache[auth_code]

            logger.info('--cache----remote_user_id: %s' % (remote_user_id, ))
            if remote_user_id is not None:
                # complete bind
                await self.hs.get_datastore().record_user_external_id(
                    bind_type,
                    remote_user_id,
                    str(user_id),
                )
                self._cache.pop(auth_code, None)

                return 200, {}
        else:
            logger.info('-no-cache-by-auth_code--')

        # first get openid from cache with auth_code
        app_type = ''
        if bind_type == 'alipay':
            app_type = 'zhifubao'
        elif bind_type == 'weixin':
            app_type = 'weixin'
            # Desttop handler, only weixin
            if params.__contains__("device_type"):
                device_type = params["device_type"]
                if device_type:
                    app_type = 'weixin-' + device_type

        # call get_openid from authserver
        remote_user = await self.get_openid_by_code(app_type, auth_code)

        if remote_user["code"] == 502:
            #get openid from cache,if exists then complete bind

            raise SynapseError(502, remote_user["message"])

        if remote_user["code"] != 200 & remote_user["code"] != 502:
            raise SynapseError(500, remote_user["message"])

        remote_user_id = remote_user['obj']
        if remote_user_id is None:
            raise LoginError(414,
                             "auth_code invalid",
                             errcode=Codes.INVALID_AUTH_CODE)
        logger.info("======bind_type: %s   remote_user_id: %s  user_id: %s" %
                    (bind_type, remote_user_id, str(user_id)))

        #complete bind
        await self.hs.get_datastore().record_user_external_id(
            bind_type,
            remote_user_id,
            str(user_id),
        )

        return 200, {}
예제 #13
0
    async def _wrapped_get_user_by_req(
        self,
        request: SynapseRequest,
        allow_guest: bool,
        rights: str,
        allow_expired: bool,
    ) -> Requester:
        """Helper for get_user_by_req

        Once get_user_by_req has set up the opentracing span, this does the actual work.
        """
        try:
            ip_addr = request.getClientIP()
            user_agent = get_request_user_agent(request)

            access_token = self.get_access_token_from_request(request)

            (
                user_id,
                device_id,
                app_service,
            ) = await self._get_appservice_user_id_and_device_id(request)
            if user_id and app_service:
                if ip_addr and self._track_appservice_user_ips:
                    await self.store.insert_client_ip(
                        user_id=user_id,
                        access_token=access_token,
                        ip=ip_addr,
                        user_agent=user_agent,
                        device_id="dummy-device"
                        if device_id is None else device_id,  # stubbed
                    )

                requester = create_requester(user_id,
                                             app_service=app_service,
                                             device_id=device_id)

                request.requester = user_id
                return requester

            user_info = await self.get_user_by_access_token(
                access_token, rights, allow_expired=allow_expired)
            token_id = user_info.token_id
            is_guest = user_info.is_guest
            shadow_banned = user_info.shadow_banned

            # Deny the request if the user account has expired.
            if not allow_expired:
                if await self._account_validity_handler.is_user_expired(
                        user_info.user_id):
                    # Raise the error if either an account validity module has determined
                    # the account has expired, or the legacy account validity
                    # implementation is enabled and determined the account has expired
                    raise AuthError(
                        403,
                        "User account has expired",
                        errcode=Codes.EXPIRED_ACCOUNT,
                    )

            device_id = user_info.device_id

            if access_token and ip_addr:
                await self.store.insert_client_ip(
                    user_id=user_info.token_owner,
                    access_token=access_token,
                    ip=ip_addr,
                    user_agent=user_agent,
                    device_id=device_id,
                )
                # Track also the puppeted user client IP if enabled and the user is puppeting
                if (user_info.user_id != user_info.token_owner
                        and self._track_puppeted_user_ips):
                    await self.store.insert_client_ip(
                        user_id=user_info.user_id,
                        access_token=access_token,
                        ip=ip_addr,
                        user_agent=user_agent,
                        device_id=device_id,
                    )

            if is_guest and not allow_guest:
                raise AuthError(
                    403,
                    "Guest access not allowed",
                    errcode=Codes.GUEST_ACCESS_FORBIDDEN,
                )

            # Mark the token as used. This is used to invalidate old refresh
            # tokens after some time.
            if not user_info.token_used and token_id is not None:
                await self.store.mark_access_token_as_used(token_id)

            requester = create_requester(
                user_info.user_id,
                token_id,
                is_guest,
                shadow_banned,
                device_id,
                app_service=app_service,
                authenticated_entity=user_info.token_owner,
            )

            request.requester = requester
            return requester
        except KeyError:
            raise MissingClientTokenError()
예제 #14
0
        async def new_func(request: SynapseRequest, *args: Any,
                           **kwargs: str) -> Optional[Tuple[int, Any]]:
            """A callback which can be passed to HttpServer.RegisterPaths

            Args:
                request:
                *args: unused?
                **kwargs: the dict mapping keys to path components as specified
                    in the path match regexp.

            Returns:
                (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: Optional[
                    str] = 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 None
                        response = await func(origin, content, request.args,
                                              *args, **kwargs)
                else:
                    response = await func(origin, content, request.args, *args,
                                          **kwargs)

            return response