Beispiel #1
0
    def _check_local_password(self, user_id, password):
        """Authenticate a user against the local password database.

        user_id is checked case insensitively, but will throw if there are
        multiple inexact matches.

        Args:
            user_id (str): complete @user:id
        Returns:
            (str) the canonical_user_id
        Raises:
            LoginError if the password was incorrect
        """
        user_id, password_hash = yield self._find_user_id_and_pwd_hash(user_id)
        result = self.validate_hash(password, password_hash)
        if not result:
            logger.warn("Failed password login for user %s", user_id)
            raise LoginError(403, "", errcode=Codes.FORBIDDEN)
        defer.returnValue(user_id)
Beispiel #2
0
    def on_POST(self, request):
        yield run_on_reactor()

        body = parse_json_object_from_request(request)

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

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

        user_id = 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)
        elif 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 using email, we must know about the email they're authing with!
            threepid_user_id = yield self.hs.get_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!", result.keys())
            raise SynapseError(500, "", Codes.UNKNOWN)

        if 'new_password' not in params:
            raise SynapseError(400, "", Codes.MISSING_PARAM)
        new_password = params['new_password']

        yield self.auth_handler.set_password(user_id, new_password, requester)

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

        # if the caller provides an access token, it ought to be valid.
        requester = None
        if has_access_token(request):
            requester = yield self.auth.get_user_by_req(
                request,
            )  # type: synapse.types.Requester

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

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

        if LoginType.PASSWORD in result:
            user_id = result[LoginType.PASSWORD]
            # if using password, they should also be logged in
            if requester is None:
                raise SynapseError(
                    400,
                    "Deactivate account requires an access_token",
                    errcode=Codes.MISSING_TOKEN
                )
            if requester.user.to_string() != user_id:
                raise LoginError(400, "", Codes.UNKNOWN)
        else:
            logger.error("Auth succeeded but no known type!", result.keys())
            raise SynapseError(500, "", Codes.UNKNOWN)

        yield self.auth_handler.deactivate_account(user_id)
        defer.returnValue((200, {}))
Beispiel #4
0
    async def _do_appservice_login(
        self,
        login_submission: JsonDict,
        appservice: ApplicationService,
        should_issue_refresh_token: bool = False,
    ) -> LoginResponse:
        identifier = login_submission.get("identifier")
        logger.info("Got appservice login request with identifier: %r",
                    identifier)

        if not isinstance(identifier, dict):
            raise SynapseError(400, "Invalid identifier in login submission",
                               Codes.INVALID_PARAM)

        # this login flow only supports identifiers of type "m.id.user".
        if identifier.get("type") != "m.id.user":
            raise SynapseError(400, "Unknown login identifier type",
                               Codes.INVALID_PARAM)

        user = identifier.get("user")
        if not isinstance(user, str):
            raise SynapseError(400, "Invalid user in identifier",
                               Codes.INVALID_PARAM)

        if user.startswith("@"):
            qualified_user_id = user
        else:
            qualified_user_id = UserID(user, self.hs.hostname).to_string()

        if not appservice.is_interested_in_user(qualified_user_id):
            raise LoginError(403,
                             "Invalid access_token",
                             errcode=Codes.FORBIDDEN)

        return await self._complete_login(
            qualified_user_id,
            login_submission,
            ratelimit=appservice.is_rate_limited(),
            should_issue_refresh_token=should_issue_refresh_token,
        )
Beispiel #5
0
    async def on_POST(self, request: SynapseRequest):
        self._address_ratelimiter.ratelimit(request.getClientIP())

        params = parse_json_object_from_request(request)
        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
        await self.hs.get_datastore(
        ).delete_external_id_with_user_and_provider(
            bind_type,
            str(user_id),
        )

        return 200, {}
Beispiel #6
0
    def do_password_login(self, login_submission):
        if 'medium' in login_submission and 'address' in login_submission:
            address = login_submission['address']
            if login_submission['medium'] == 'email':
                # For emails, transform the address to lowercase.
                # We store all email addreses as lowercase in the DB.
                # (See add_threepid in synapse/handlers/auth.py)
                address = address.lower()
            user_id = yield self.hs.get_datastore().get_user_id_by_threepid(
                login_submission['medium'], address)
            if not user_id:
                raise LoginError(403, "", errcode=Codes.FORBIDDEN)
        else:
            user_id = login_submission['user']

        if not user_id.startswith('@'):
            user_id = UserID.create(user_id, self.hs.hostname).to_string()

        auth_handler = self.auth_handler
        user_id = yield auth_handler.validate_password_login(
            user_id=user_id,
            password=login_submission["password"],
        )
        device_id = yield self._register_device(user_id, login_submission)
        access_token = yield auth_handler.get_access_token_for_user_id(
            user_id,
            device_id,
            login_submission.get("initial_device_display_name"),
        )
        result = {
            "user_id": user_id,  # may have changed
            "access_token": access_token,
            "home_server": self.hs.hostname,
            "device_id": device_id,
        }

        defer.returnValue((200, result))
Beispiel #7
0
    def do_password_login(self, login_submission):
        if 'medium' in login_submission and 'address' in login_submission:
            user_id = yield self.hs.get_datastore().get_user_id_by_threepid(
                login_submission['medium'], login_submission['address'])
            if not user_id:
                raise LoginError(403, "", errcode=Codes.FORBIDDEN)
        else:
            user_id = login_submission['user']

        if not user_id.startswith('@'):
            user_id = UserID.create(user_id, self.hs.hostname).to_string()

        auth_handler = self.handlers.auth_handler
        user_id, access_token, refresh_token = yield auth_handler.login_with_password(
            user_id=user_id, password=login_submission["password"])

        result = {
            "user_id": user_id,  # may have changed
            "access_token": access_token,
            "refresh_token": refresh_token,
            "home_server": self.hs.hostname,
        }

        defer.returnValue((200, result))
Beispiel #8
0
    def do_password_login(self, login_submission):
        if 'medium' in login_submission and 'address' in login_submission:
            user_id = yield self.hs.get_datastore().get_user_id_by_threepid(
                login_submission['medium'], login_submission['address']
            )
            if not user_id:
                raise LoginError(403, "", errcode=Codes.FORBIDDEN)
        else:
            user_id = login_submission['user']

        if not user_id.startswith('@'):
            user_id = UserID.create(
                user_id, self.hs.hostname
            ).to_string()

        auth_handler = self.auth_handler
        user_id = yield auth_handler.validate_password_login(
            user_id=user_id,
            password=login_submission["password"],
        )
        device_id = yield self._register_device(user_id, login_submission)
        access_token, refresh_token = (
            yield auth_handler.get_login_tuple_for_user_id(
                user_id, device_id,
                login_submission.get("initial_device_display_name")
            )
        )
        result = {
            "user_id": user_id,  # may have changed
            "access_token": access_token,
            "refresh_token": refresh_token,
            "home_server": self.hs.hostname,
            "device_id": device_id,
        }

        defer.returnValue((200, result))
Beispiel #9
0
    async def _complete_login(
        self,
        user_id: str,
        login_submission: JsonDict,
        callback: Optional[Callable[[LoginResponse], Awaitable[None]]] = None,
        create_non_existent_users: bool = False,
        ratelimit: bool = True,
        auth_provider_id: Optional[str] = None,
        should_issue_refresh_token: bool = False,
        auth_provider_session_id: Optional[str] = None,
    ) -> LoginResponse:
        """Called when we've successfully authed the user and now need to
        actually login them in (e.g. create devices). This gets called on
        all successful logins.

        Applies the ratelimiting for successful login attempts against an
        account.

        Args:
            user_id: ID of the user to register.
            login_submission: Dictionary of login information.
            callback: Callback function to run after login.
            create_non_existent_users: Whether to create the user if they don't
                exist. Defaults to False.
            ratelimit: Whether to ratelimit the login request.
            auth_provider_id: The SSO IdP the user used, if any.
            should_issue_refresh_token: True if this login should issue
                a refresh token alongside the access token.
            auth_provider_session_id: The session ID got during login from the SSO IdP.

        Returns:
            result: Dictionary of account information after successful login.
        """

        # Before we actually log them in we check if they've already logged in
        # too often. This happens here rather than before as we don't
        # necessarily know the user before now.
        if ratelimit:
            await self._account_ratelimiter.ratelimit(None, user_id.lower())

        if create_non_existent_users:
            canonical_uid = await self.auth_handler.check_user_exists(user_id)
            if not canonical_uid:
                canonical_uid = await self.registration_handler.register_user(
                    localpart=UserID.from_string(user_id).localpart)
            user_id = canonical_uid

        device_id = login_submission.get("device_id")

        # If device_id is present, check that device_id is not longer than a reasonable 512 characters
        if device_id and len(device_id) > 512:
            raise LoginError(
                400,
                "device_id cannot be longer than 512 characters.",
                errcode=Codes.INVALID_PARAM,
            )

        initial_display_name = login_submission.get(
            "initial_device_display_name")
        (
            device_id,
            access_token,
            valid_until_ms,
            refresh_token,
        ) = await self.registration_handler.register_device(
            user_id,
            device_id,
            initial_display_name,
            auth_provider_id=auth_provider_id,
            should_issue_refresh_token=should_issue_refresh_token,
            auth_provider_session_id=auth_provider_session_id,
        )

        result = LoginResponse(
            user_id=user_id,
            access_token=access_token,
            home_server=self.hs.hostname,
            device_id=device_id,
        )

        if valid_until_ms is not None:
            expires_in_ms = valid_until_ms - self.clock.time_msec()
            result["expires_in_ms"] = expires_in_ms

        if refresh_token is not None:
            result["refresh_token"] = refresh_token

        if callback is not None:
            await callback(result)

        return result
Beispiel #10
0
    def _do_other_login(self, login_submission):
        """Handle non-token/saml/jwt logins

        Args:
            login_submission:

        Returns:
            (int, object): HTTP code/response
        """
        # Log the request we got, but only certain fields to minimise the chance of
        # logging someone's password (even if they accidentally put it in the wrong
        # field)
        logger.info(
            "Got login request with identifier: %r, medium: %r, address: %r, user: %r",
            login_submission.get('identifier'),
            login_submission.get('medium'),
            login_submission.get('address'),
            login_submission.get('user'),
        )
        login_submission_legacy_convert(login_submission)

        if "identifier" not in login_submission:
            raise SynapseError(400, "Missing param: identifier")

        identifier = login_submission["identifier"]
        if "type" not in identifier:
            raise SynapseError(400, "Login identifier has no type")

        # convert phone type identifiers to generic threepids
        if identifier["type"] == "m.id.phone":
            identifier = login_id_thirdparty_from_phone(identifier)

        # convert threepid identifiers to user IDs
        if identifier["type"] == "m.id.thirdparty":
            address = identifier.get('address')
            medium = identifier.get('medium')

            if medium is None or address is None:
                raise SynapseError(400, "Invalid thirdparty identifier")

            if medium == 'email':
                # For emails, transform the address to lowercase.
                # We store all email addreses as lowercase in the DB.
                # (See add_threepid in synapse/handlers/auth.py)
                address = address.lower()
            user_id = yield self.hs.get_datastore().get_user_id_by_threepid(
                medium, address,
            )
            if not user_id:
                logger.warn(
                    "unknown 3pid identifier medium %s, address %r",
                    medium, address,
                )
                raise LoginError(403, "", errcode=Codes.FORBIDDEN)

            identifier = {
                "type": "m.id.user",
                "user": user_id,
            }

        # by this point, the identifier should be an m.id.user: if it's anything
        # else, we haven't understood it.
        if identifier["type"] != "m.id.user":
            raise SynapseError(400, "Unknown login identifier type")
        if "user" not in identifier:
            raise SynapseError(400, "User identifier is missing 'user' key")

        auth_handler = self.auth_handler
        canonical_user_id, callback = yield auth_handler.validate_login(
            identifier["user"],
            login_submission,
        )

        device_id = yield self._register_device(
            canonical_user_id, login_submission,
        )
        access_token = yield auth_handler.get_access_token_for_user_id(
            canonical_user_id, device_id,
        )

        result = {
            "user_id": canonical_user_id,
            "access_token": access_token,
            "home_server": self.hs.hostname,
            "device_id": device_id,
        }

        if callback is not None:
            yield callback(result)

        defer.returnValue((200, result))
    async def async_render_POST(self, request: Request):
        session_id = request.getCookie(SESSION_COOKIE_NAME)
        if not session_id:
            _return_html_error(400, "missing session_id", request)
            return

        session_id = session_id.decode("ascii", errors="replace")
        session = get_mapping_session(session_id)
        if not session:
            logger.info("Session ID %s not found", session_id)
            _return_html_error(403, "Unknown session", request)
            return

        # we don't clear the session from the dict until the ID is successfully
        # registered, so the user can go round and have another go if need be.
        #
        # this means there's theoretically a race where a single user can register
        # two accounts. I'm going to assume that's not a dealbreaker.

        if b"username" not in request.args:
            _return_html_error(400, "missing username", request)
            return
        localpart = request.args[b"username"][0].decode("utf-8",
                                                        errors="replace")

        if b"password" not in request.args:
            logger.info("Registering username %s", localpart)
            try:
                registered_user_id = await self._module_api.register_user(
                    localpart=localpart, displayname=localpart)
            except SynapseError as e:
                logger.warning("Error during registration: %s", e)
                _return_html_error(e.code, e.msg, request)
                return
        else:
            password = request.args[b"password"][0].decode("utf-8",
                                                           errors="replace")
            if localpart.startswith("@"):
                registered_user_id = localpart
            else:
                registered_user_id = UserID(
                    localpart, self._module_api._hs.hostname).to_string()

            success = False
            try:
                passwd_response = await self._module_api._auth_handler._check_local_password(
                    registered_user_id, password)
                if passwd_response != registered_user_id:
                    raise LoginError(403,
                                     "Invalid password",
                                     errcode=Codes.FORBIDDEN)
            except Exception as e:
                logger.warning("Error checking credentials of %s: %s %s" %
                               (localpart, type(e), e))
                _return_html_error(e.code, e.msg, request)
                return

        await self._module_api.record_user_external_id("saml",
                                                       session.remote_user_id,
                                                       registered_user_id)

        del username_mapping_sessions[session_id]

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

        await self._module_api.complete_sso_login_async(
            registered_user_id,
            request,
            session.client_redirect_url,
        )
Beispiel #12
0
    def do_jwt_login(self, login_submission):
        token = login_submission.get("token", None)
        if token is None:
            raise LoginError(401,
                             "Token field for JWT is missing",
                             errcode=Codes.UNAUTHORIZED)

        import jwt
        from jwt.exceptions import InvalidTokenError

        try:
            payload = jwt.decode(token,
                                 self.jwt_secret,
                                 algorithms=[self.jwt_algorithm])
        except jwt.ExpiredSignatureError:
            raise LoginError(401, "JWT expired", errcode=Codes.UNAUTHORIZED)
        except InvalidTokenError:
            raise LoginError(401, "Invalid JWT", errcode=Codes.UNAUTHORIZED)

        user = payload.get("sub", None)
        if user is None:
            raise LoginError(401, "Invalid JWT", errcode=Codes.UNAUTHORIZED)

        user_id = UserID(user, self.hs.hostname).to_string()

        auth_handler = self.auth_handler
        registered_user_id = yield auth_handler.check_user_exists(user_id)
        if registered_user_id:
            device_id = login_submission.get("device_id")
            initial_display_name = login_submission.get(
                "initial_device_display_name")
            device_id, access_token = yield self.registration_handler.register_device(
                registered_user_id,
                device_id,
                initial_display_name,
            )

            result = {
                "user_id": registered_user_id,
                "access_token": access_token,
                "home_server": self.hs.hostname,
            }
        else:
            user_id, access_token = (
                yield
                self.handlers.registration_handler.register(localpart=user))

            device_id = login_submission.get("device_id")
            initial_display_name = login_submission.get(
                "initial_device_display_name")
            device_id, access_token = yield self.registration_handler.register_device(
                registered_user_id,
                device_id,
                initial_display_name,
            )

            result = {
                "user_id": user_id,  # may have changed
                "access_token": access_token,
                "home_server": self.hs.hostname,
            }

        defer.returnValue(result)
Beispiel #13
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, {}
Beispiel #14
0
    def check_auth(self, flows, clientdict, clientip):
        """
        Takes a dictionary sent by the client in the login / registration
        protocol and handles the login flow.

        As a side effect, this function fills in the 'creds' key on the user's
        session with a map, which maps each auth-type (str) to the relevant
        identity authenticated by that auth-type (mostly str, but for captcha, bool).

        Args:
            flows (list): A list of login flows. Each flow is an ordered list of
                          strings representing auth-types. At least one full
                          flow must be completed in order for auth to be successful.
            clientdict: The dictionary from the client root level, not the
                        'auth' key: this method prompts for auth if none is sent.
            clientip (str): The IP address of the client.
        Returns:
            A tuple of (authed, dict, dict, session_id) where authed is true if
            the client has successfully completed an auth flow. If it is true
            the first dict contains the authenticated credentials of each stage.

            If authed is false, the first dictionary is the server response to
            the login request and should be passed back to the client.

            In either case, the second dict contains the parameters for this
            request (which may have been given only in a previous call).

            session_id is the ID of this session, either passed in by the client
            or assigned by the call to check_auth
        """

        authdict = None
        sid = None
        if clientdict and 'auth' in clientdict:
            authdict = clientdict['auth']
            del clientdict['auth']
            if 'session' in authdict:
                sid = authdict['session']
        session = self._get_session_info(sid)

        if len(clientdict) > 0:
            # This was designed to allow the client to omit the parameters
            # and just supply the session in subsequent calls so it split
            # auth between devices by just sharing the session, (eg. so you
            # could continue registration from your phone having clicked the
            # email auth link on there). It's probably too open to abuse
            # because it lets unauthenticated clients store arbitrary objects
            # on a home server.
            # Revisit: Assumimg the REST APIs do sensible validation, the data
            # isn't arbintrary.
            session['clientdict'] = clientdict
            self._save_session(session)
        elif 'clientdict' in session:
            clientdict = session['clientdict']

        if not authdict:
            defer.returnValue(
                (False, self._auth_dict_for_flows(flows, session), clientdict,
                 session['id']))

        if 'creds' not in session:
            session['creds'] = {}
        creds = session['creds']

        # check auth type currently being presented
        errordict = {}
        if 'type' in authdict:
            login_type = authdict['type']
            if login_type not in self.checkers:
                raise LoginError(400, "", Codes.UNRECOGNIZED)
            try:
                result = yield self.checkers[login_type](authdict, clientip)
                if result:
                    creds[login_type] = result
                    self._save_session(session)
            except LoginError, e:
                if login_type == LoginType.EMAIL_IDENTITY:
                    # riot used to have a bug where it would request a new
                    # validation token (thus sending a new email) each time it
                    # got a 401 with a 'flows' field.
                    # (https://github.com/vector-im/vector-web/issues/2447).
                    #
                    # Grandfather in the old behaviour for now to avoid
                    # breaking old riot deployments.
                    raise e

                # this step failed. Merge the error dict into the response
                # so that the client can have another go.
                errordict = e.error_dict()
Beispiel #15
0
    def validate_login(self, username, login_submission):
        """Authenticates the user for the /login API

        Also used by the user-interactive auth flow to validate
        m.login.password auth types.

        Args:
            username (str): username supplied by the user
            login_submission (dict): the whole of the login submission
                (including 'type' and other relevant fields)
        Returns:
            Deferred[str, func]: canonical user id, and optional callback
                to be called once the access token and device id are issued
        Raises:
            StoreError if there was a problem accessing the database
            SynapseError if there was a problem with the request
            LoginError if there was an authentication problem.
        """

        if username.startswith("@"):
            qualified_user_id = username
        else:
            qualified_user_id = UserID(username, self.hs.hostname).to_string()

        login_type = login_submission.get("type")
        known_login_type = False

        # special case to check for "password" for the check_password interface
        # for the auth providers
        password = login_submission.get("password")

        if login_type == LoginType.PASSWORD:
            if not self._password_enabled:
                raise SynapseError(400, "Password login has been disabled.")
            if not password:
                raise SynapseError(400, "Missing parameter: password")

        for provider in self.password_providers:
            if hasattr(provider,
                       "check_password") and login_type == LoginType.PASSWORD:
                known_login_type = True
                is_valid = yield provider.check_password(
                    qualified_user_id, password)
                if is_valid:
                    return qualified_user_id, None

            if not hasattr(provider,
                           "get_supported_login_types") or not hasattr(
                               provider, "check_auth"):
                # this password provider doesn't understand custom login types
                continue

            supported_login_types = provider.get_supported_login_types()
            if login_type not in supported_login_types:
                # this password provider doesn't understand this login type
                continue

            known_login_type = True
            login_fields = supported_login_types[login_type]

            missing_fields = []
            login_dict = {}
            for f in login_fields:
                if f not in login_submission:
                    missing_fields.append(f)
                else:
                    login_dict[f] = login_submission[f]
            if missing_fields:
                raise SynapseError(
                    400,
                    "Missing parameters for login type %s: %s" %
                    (login_type, missing_fields),
                )

            result = yield provider.check_auth(username, login_type,
                                               login_dict)
            if result:
                if isinstance(result, str):
                    result = (result, None)
                return result

        if login_type == LoginType.PASSWORD and self.hs.config.password_localdb_enabled:
            known_login_type = True

            canonical_user_id = yield self._check_local_password(
                qualified_user_id, password)

            if canonical_user_id:
                return canonical_user_id, None

        if not known_login_type:
            raise SynapseError(400, "Unknown login type %s" % login_type)

        # We raise a 403 here, but note that if we're doing user-interactive
        # login, it turns all LoginErrors into a 401 anyway.
        raise LoginError(403, "Invalid password", errcode=Codes.FORBIDDEN)
Beispiel #16
0
    def check_auth(self, flows, clientdict, clientip=None):
        """
        Takes a dictionary sent by the client in the login / registration
        protocol and handles the login flow.

        Args:
            flows: list of list of stages
            authdict: The dictionary from the client root level, not the
                      'auth' key: this method prompts for auth if none is sent.
        Returns:
            A tuple of authed, dict, dict where authed is true if the client
            has successfully completed an auth flow. If it is true, the first
            dict contains the authenticated credentials of each stage.

            If authed is false, the first dictionary is the server response to
            the login request and should be passed back to the client.

            In either case, the second dict contains the parameters for this
            request (which may have been given only in a previous call).
        """

        authdict = None
        sid = None
        if clientdict and 'auth' in clientdict:
            authdict = clientdict['auth']
            del clientdict['auth']
            if 'session' in authdict:
                sid = authdict['session']
        sess = self._get_session_info(sid)

        if len(clientdict) > 0:
            # This was designed to allow the client to omit the parameters
            # and just supply the session in subsequent calls so it split
            # auth between devices by just sharing the session, (eg. so you
            # could continue registration from your phone having clicked the
            # email auth link on there). It's probably too open to abuse
            # because it lets unauthenticated clients store arbitrary objects
            # on a home server.
            # sess['clientdict'] = clientdict
            # self._save_session(sess)
            pass
        elif 'clientdict' in sess:
            clientdict = sess['clientdict']

        if not authdict:
            defer.returnValue(
                (False, self._auth_dict_for_flows(flows, sess), clientdict)
            )

        if 'creds' not in sess:
            sess['creds'] = {}
        creds = sess['creds']

        # check auth type currently being presented
        if 'type' in authdict:
            if authdict['type'] not in self.checkers:
                raise LoginError(400, "", Codes.UNRECOGNIZED)
            result = yield self.checkers[authdict['type']](authdict, clientip)
            if result:
                creds[authdict['type']] = result
                self._save_session(sess)

        for f in flows:
            if len(set(f) - set(creds.keys())) == 0:
                logger.info("Auth completed with creds: %r", creds)
                self._remove_session(sess)
                defer.returnValue((True, creds, clientdict))

        ret = self._auth_dict_for_flows(flows, sess)
        ret['completed'] = creds.keys()
        defer.returnValue((False, ret, clientdict))
Beispiel #17
0
 def _check_password(self, user_id, password, stored_hash):
     """Checks that user_id has passed password, raises LoginError if not."""
     if not bcrypt.checkpw(password, stored_hash):
         logger.warn("Failed password login for user %s", user_id)
         raise LoginError(403, "", errcode=Codes.FORBIDDEN)
Beispiel #18
0
    def do_password_login(self, login_submission):
        if "password" not in login_submission:
            raise SynapseError(400, "Missing parameter: password")

        login_submission_legacy_convert(login_submission)

        if "identifier" not in login_submission:
            raise SynapseError(400, "Missing param: identifier")

        identifier = login_submission["identifier"]
        if "type" not in identifier:
            raise SynapseError(400, "Login identifier has no type")

        # convert phone type identifiers to generic threepids
        if identifier["type"] == "m.id.phone":
            identifier = login_id_thirdparty_from_phone(identifier)

        # convert threepid identifiers to user IDs
        if identifier["type"] == "m.id.thirdparty":
            if 'medium' not in identifier or 'address' not in identifier:
                raise SynapseError(400, "Invalid thirdparty identifier")

            address = identifier['address']
            if identifier['medium'] == 'email':
                # For emails, transform the address to lowercase.
                # We store all email addreses as lowercase in the DB.
                # (See add_threepid in synapse/handlers/auth.py)
                address = address.lower()
            user_id = yield self.hs.get_datastore().get_user_id_by_threepid(
                identifier['medium'], address)
            if not user_id:
                raise LoginError(403, "", errcode=Codes.FORBIDDEN)

            identifier = {
                "type": "m.id.user",
                "user": user_id,
            }

        # by this point, the identifier should be an m.id.user: if it's anything
        # else, we haven't understood it.
        if identifier["type"] != "m.id.user":
            raise SynapseError(400, "Unknown login identifier type")
        if "user" not in identifier:
            raise SynapseError(400, "User identifier is missing 'user' key")

        user_id = identifier["user"]

        if not user_id.startswith('@'):
            user_id = UserID(user_id, self.hs.hostname).to_string()

        auth_handler = self.auth_handler
        user_id = yield auth_handler.validate_password_login(
            user_id=user_id,
            password=login_submission["password"],
        )
        device_id = yield self._register_device(user_id, login_submission)
        access_token = yield auth_handler.get_access_token_for_user_id(
            user_id,
            device_id,
            login_submission.get("initial_device_display_name"),
        )
        result = {
            "user_id": user_id,  # may have changed
            "access_token": access_token,
            "home_server": self.hs.hostname,
            "device_id": device_id,
        }

        defer.returnValue((200, result))
Beispiel #19
0
    def do_auth0_login(self, login_submission):
        auth0_access_token = login_submission.get("access_token", None)
        auth0_id_token = login_submission.get("id_token", None)
        auth0_tenant_domain = self.hs.config.auth0_tenant_domain
        auth0_client_id = self.hs.config.auth0_client_id
        auth0_username = self.hs.config.auth0_username

        auth0_url = "https://" + auth0_tenant_domain + "/"
        auth0_jwks_url = auth0_url + ".well-known/jwks.json"

        if auth0_access_token is None:
            raise LoginError(401,
                             "Auth0 access_token is required",
                             errcode=Codes.UNAUTHORIZED)
        if auth0_id_token is None:
            raise LoginError(401,
                             "Auth0 id_token is required",
                             errcode=Codes.UNAUTHORIZED)

        try:
            auth0_jwks_json = urllib.request.urlopen(auth0_jwks_url)
            auth0_jwks = json.loads(auth0_jwks_json.read())
        except Exception:
            raise LoginError(401,
                             "Failed to retrieve JWKS from Auth0",
                             errcode=Codes.UNAUTHORIZED)

        try:
            from jose import jwt

            jwks_key = None
            jwks_alg = None
            token_header = jwt.get_unverified_header(auth0_id_token)
            for key in auth0_jwks["keys"]:
                if key["kid"] == token_header["kid"]:
                    # this key id was used to encrypt the token
                    jwks_alg = key["alg"]
                    jwks_key = {
                        "kty": key["kty"],
                        "kid": key["kid"],
                        "use": key["use"],
                        "n": key["n"],
                        "e": key["e"],
                    }

            if jwks_key is None:
                raise LoginError(
                    401,
                    "Unable to locate JWKS 'kid' matching JWT token",
                    errcode=Codes.UNAUTHORIZED)

            payload = jwt.decode(
                auth0_id_token,
                jwks_key,
                algorithms=jwks_alg,
                audience=auth0_client_id,
                issuer=auth0_url,
                access_token=auth0_access_token,
            )

        except jwt.ExpiredSignatureError:
            raise LoginError(401, "Expired JWT", errcode=Codes.UNAUTHORIZED)
        except jwt.JWTClaimsError:
            raise LoginError(401,
                             "Invalid JWT claims",
                             errcode=Codes.UNAUTHORIZED)
        except jwt.JWTError:
            raise LoginError(401, "Invalid JWT", errcode=Codes.UNAUTHORIZED)

        token_user = payload.get(auth0_username, None)
        if token_user is None:
            raise LoginError(
                401,
                "JWT does not contain a username. Make sure 'profile' is included in your JWT request scope.",
                errcode=Codes.UNAUTHORIZED)

        user = UserID(token_user, self.hs.hostname)
        user_id = user.to_string()

        registered_user_id = yield self.auth_handler.check_user_exists(user_id)
        if registered_user_id is None:
            # register a new user
            registered_user_id, _ = yield self.handlers.registration_handler.register(
                localpart=token_user,
                generate_token=False,
            )

            requestor = create_requester(user_id)

            # set initial display name
            displayname = payload.get("name", None)
            if displayname is not None:
                yield self.profile_handler.set_displayname(user,
                                                           requestor,
                                                           displayname,
                                                           by_admin=True)

            # set initial avatar url
            avatar_url = payload.get("picture")
            if avatar_url is not None:
                yield self.profile_handler.set_avatar_url(user,
                                                          requestor,
                                                          avatar_url,
                                                          by_admin=True)

        # get an access token
        device_id = yield self._register_device(registered_user_id,
                                                login_submission)
        access_token = yield self.auth_handler.get_access_token_for_user_id(
            registered_user_id,
            device_id,
        )

        result = {
            "access_token": access_token,
            "device_id": device_id,
            "home_server": self.hs.hostname,
            "user_id": registered_user_id,
        }

        defer.returnValue((200, result))
Beispiel #20
0
    async def _do_other_login(self, login_submission):
        """Handle non-token/saml/jwt logins

        Args:
            login_submission:

        Returns:
            dict: HTTP response
        """
        # Log the request we got, but only certain fields to minimise the chance of
        # logging someone's password (even if they accidentally put it in the wrong
        # field)
        logger.info(
            "Got login request with identifier: %r, medium: %r, address: %r, user: %r",
            login_submission.get("identifier"),
            login_submission.get("medium"),
            login_submission.get("address"),
            login_submission.get("user"),
        )
        login_submission_legacy_convert(login_submission)

        if "identifier" not in login_submission:
            raise SynapseError(400, "Missing param: identifier")

        identifier = login_submission["identifier"]
        if "type" not in identifier:
            raise SynapseError(400, "Login identifier has no type")

        # convert phone type identifiers to generic threepids
        if identifier["type"] == "m.id.phone":
            identifier = login_id_thirdparty_from_phone(identifier)

        # convert threepid identifiers to user IDs
        if identifier["type"] == "m.id.thirdparty":
            address = identifier.get("address")
            medium = identifier.get("medium")

            if medium is None or address is None:
                raise SynapseError(400, "Invalid thirdparty identifier")

            if medium == "email":
                # For emails, transform the address to lowercase.
                # We store all email addreses as lowercase in the DB.
                # (See add_threepid in synapse/handlers/auth.py)
                address = address.lower()

            # We also apply account rate limiting using the 3PID as a key, as
            # otherwise using 3PID bypasses the ratelimiting based on user ID.
            self._failed_attempts_ratelimiter.ratelimit(
                (medium, address),
                time_now_s=self._clock.time(),
                rate_hz=self.hs.config.rc_login_failed_attempts.per_second,
                burst_count=self.hs.config.rc_login_failed_attempts.
                burst_count,
                update=False,
            )

            # Check for login providers that support 3pid login types
            (
                canonical_user_id,
                callback_3pid,
            ) = await self.auth_handler.check_password_provider_3pid(
                medium, address, login_submission["password"])
            if canonical_user_id:
                # Authentication through password provider and 3pid succeeded

                result = await self._complete_login(canonical_user_id,
                                                    login_submission,
                                                    callback_3pid)
                return result

            # No password providers were able to handle this 3pid
            # Check local store
            user_id = await self.hs.get_datastore().get_user_id_by_threepid(
                medium, address)
            if not user_id:
                logger.warning("unknown 3pid identifier medium %s, address %r",
                               medium, address)
                # We mark that we've failed to log in here, as
                # `check_password_provider_3pid` might have returned `None` due
                # to an incorrect password, rather than the account not
                # existing.
                #
                # If it returned None but the 3PID was bound then we won't hit
                # this code path, which is fine as then the per-user ratelimit
                # will kick in below.
                self._failed_attempts_ratelimiter.can_do_action(
                    (medium, address),
                    time_now_s=self._clock.time(),
                    rate_hz=self.hs.config.rc_login_failed_attempts.per_second,
                    burst_count=self.hs.config.rc_login_failed_attempts.
                    burst_count,
                    update=True,
                )
                raise LoginError(403, "", errcode=Codes.FORBIDDEN)

            identifier = {"type": "m.id.user", "user": user_id}

        # by this point, the identifier should be an m.id.user: if it's anything
        # else, we haven't understood it.
        if identifier["type"] != "m.id.user":
            raise SynapseError(400, "Unknown login identifier type")
        if "user" not in identifier:
            raise SynapseError(400, "User identifier is missing 'user' key")

        if identifier["user"].startswith("@"):
            qualified_user_id = identifier["user"]
        else:
            qualified_user_id = UserID(identifier["user"],
                                       self.hs.hostname).to_string()

        # Check if we've hit the failed ratelimit (but don't update it)
        self._failed_attempts_ratelimiter.ratelimit(
            qualified_user_id.lower(),
            time_now_s=self._clock.time(),
            rate_hz=self.hs.config.rc_login_failed_attempts.per_second,
            burst_count=self.hs.config.rc_login_failed_attempts.burst_count,
            update=False,
        )

        try:
            canonical_user_id, callback = await self.auth_handler.validate_login(
                identifier["user"], login_submission)
        except LoginError:
            # The user has failed to log in, so we need to update the rate
            # limiter. Using `can_do_action` avoids us raising a ratelimit
            # exception and masking the LoginError. The actual ratelimiting
            # should have happened above.
            self._failed_attempts_ratelimiter.can_do_action(
                qualified_user_id.lower(),
                time_now_s=self._clock.time(),
                rate_hz=self.hs.config.rc_login_failed_attempts.per_second,
                burst_count=self.hs.config.rc_login_failed_attempts.
                burst_count,
                update=True,
            )
            raise

        result = await self._complete_login(canonical_user_id,
                                            login_submission, callback)
        return result
Beispiel #21
0
    async def _do_jwt_login(
            self,
            login_submission: JsonDict,
            should_issue_refresh_token: bool = False) -> LoginResponse:
        token = login_submission.get("token", None)
        if token is None:
            raise LoginError(403,
                             "Token field for JWT is missing",
                             errcode=Codes.FORBIDDEN)

        from authlib.jose import JsonWebToken, JWTClaims
        from authlib.jose.errors import BadSignatureError, InvalidClaimError, JoseError

        jwt = JsonWebToken([self.jwt_algorithm])
        claim_options = {}
        if self.jwt_issuer is not None:
            claim_options["iss"] = {
                "value": self.jwt_issuer,
                "essential": True
            }
        if self.jwt_audiences is not None:
            claim_options["aud"] = {
                "values": self.jwt_audiences,
                "essential": True
            }

        try:
            claims = jwt.decode(
                token,
                key=self.jwt_secret,
                claims_cls=JWTClaims,
                claims_options=claim_options,
            )
        except BadSignatureError:
            # We handle this case separately to provide a better error message
            raise LoginError(
                403,
                "JWT validation failed: Signature verification failed",
                errcode=Codes.FORBIDDEN,
            )
        except JoseError as e:
            # A JWT error occurred, return some info back to the client.
            raise LoginError(
                403,
                "JWT validation failed: %s" % (str(e), ),
                errcode=Codes.FORBIDDEN,
            )

        try:
            claims.validate(leeway=120)  # allows 2 min of clock skew

            # Enforce the old behavior which is rolled out in productive
            # servers: if the JWT contains an 'aud' claim but none is
            # configured, the login attempt will fail
            if claims.get("aud") is not None:
                if self.jwt_audiences is None or len(self.jwt_audiences) == 0:
                    raise InvalidClaimError("aud")
        except JoseError as e:
            raise LoginError(
                403,
                "JWT validation failed: %s" % (str(e), ),
                errcode=Codes.FORBIDDEN,
            )

        user = claims.get(self.jwt_subject_claim, None)
        if user is None:
            raise LoginError(403, "Invalid JWT", errcode=Codes.FORBIDDEN)

        user_id = UserID(user, self.hs.hostname).to_string()
        result = await self._complete_login(
            user_id,
            login_submission,
            create_non_existent_users=True,
            should_issue_refresh_token=should_issue_refresh_token,
        )
        return result
    def _check_threepid(self, medium, authdict):
        if "threepid_creds" not in authdict:
            raise LoginError(400, "Missing threepid_creds",
                             Codes.MISSING_PARAM)

        threepid_creds = authdict["threepid_creds"]

        identity_handler = self.hs.get_handlers().identity_handler

        logger.info("Getting validated threepid. threepidcreds: %r",
                    (threepid_creds, ))

        # msisdns are currently always ThreepidBehaviour.REMOTE
        if medium == "msisdn":
            if not self.hs.config.account_threepid_delegate_msisdn:
                raise SynapseError(
                    400,
                    "Phone number verification is not enabled on this homeserver"
                )
            threepid = yield identity_handler.threepid_from_creds(
                self.hs.config.account_threepid_delegate_msisdn,
                threepid_creds)
        elif medium == "email":
            if self.hs.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
                assert self.hs.config.account_threepid_delegate_email
                threepid = yield identity_handler.threepid_from_creds(
                    self.hs.config.account_threepid_delegate_email,
                    threepid_creds)
            elif self.hs.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
                threepid = None
                row = yield self.store.get_threepid_validation_session(
                    medium,
                    threepid_creds["client_secret"],
                    sid=threepid_creds["sid"],
                    validated=True,
                )

                if row:
                    threepid = {
                        "medium": row["medium"],
                        "address": row["address"],
                        "validated_at": row["validated_at"],
                    }

                    # Valid threepid returned, delete from the db
                    yield self.store.delete_threepid_session(
                        threepid_creds["sid"])
            else:
                raise SynapseError(
                    400,
                    "Email address verification is not enabled on this homeserver"
                )
        else:
            # this can't happen!
            raise AssertionError("Unrecognized threepid medium: %s" %
                                 (medium, ))

        if not threepid:
            raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)

        if threepid["medium"] != medium:
            raise LoginError(
                401,
                "Expecting threepid of type '%s', got '%s'" %
                (medium, threepid["medium"]),
                errcode=Codes.UNAUTHORIZED,
            )

        threepid["threepid_creds"] = authdict["threepid_creds"]

        return threepid
Beispiel #23
0
    async def on_POST(self, request: SynapseRequest):

        params = parse_json_object_from_request(request)
        logger.info("-----pushgateway-------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
        #     )
        notification = params["notification"]
        if notification is None:
            raise LoginError(410,
                             "notification field for push_gateway is missing",
                             errcode=Codes.FORBIDDEN)
        devices = notification["devices"]
        if devices is None:
            raise LoginError(410,
                             "devices field for push_gateway is missing",
                             errcode=Codes.FORBIDDEN)
        # room_id = notification["room_id"]
        # if room_id is None:
        #     raise LoginError(
        #         410, "room_id field for push_gateway is missing", errcode=Codes.FORBIDDEN
        #     )
        # event_id = notification["event_id"]
        # if event_id is None:
        #     raise LoginError(
        #         410, "event_id field for push_gateway is missing", errcode=Codes.FORBIDDEN
        #     )

        #select recievers pushkey
        if len(devices) == 0:
            raise LoginError(410,
                             "devices field for push_gateway is missing",
                             errcode=Codes.FORBIDDEN)
        logger.info("devices len--------:%s" % (len(devices)))

        reg_ids = []
        jg_ids = []
        huawei_ids = []
        xiaomi_ids = []
        appids = []
        unread_counts = notification['counts']['unread']
        if unread_counts is None:
            unread_counts = 0

        for d in devices:
            logger.info("-----device----%s" % str(d, ))
            reg_ids.append(d["pushkey"])
            appids.append(d['app_id'])

        logger.info("reg_ids len--------:%s" % (len(reg_ids)))
        logger.info("call appids--------:%s" % (str(appids)))
        appid = str(appids[0])

        push_params = {"ids": reg_ids}
        # if appid == 'android_huawei':
        #     push_params = {
        #         "ids": [
        #             {"id": reg_ids[0], "count": unread_counts}
        #         ]
        #     }

        if 'format' not in params or devices[0]['data'][
                'format'] != 'event_id_only':
            if 'content' in notification:
                content = notification['content']

                if content is None:
                    logger.warning('content is None')
                    return

                msg_type = None
                msg_body = ''

                event_type = notification['type']
                msg_type = event_type
                if event_type == 'm.call.invite':
                    # msg_type = event_type
                    offer = content['offer']
                    if offer is None:
                        raise LoginError(
                            410,
                            "offer field for push_gateway is missing",
                            errcode=Codes.FORBIDDEN)
                    sdp = offer['sdp']
                    if sdp is None:
                        raise LoginError(
                            410,
                            "sdp field for push_gateway is missing",
                            errcode=Codes.FORBIDDEN)
                    # video call
                    if 'm=video' in sdp:
                        msg_type = 'call.video'
                    elif 'm=audio' in sdp and 'm=video' not in sdp:
                        msg_type = 'call.audio'

                elif event_type == 'm.room.message':
                    # else:
                    if 'msgtype' not in content:
                        logger.warning('msg_type is None')
                        return
                    msg_type = content['msgtype']
                    msg_body = content['body']
                event_id = notification['event_id']

                sender = notification['sender']

                sender_display_name = notification['sender_display_name']
                room_id = notification['room_id']
                # logger.info("call room_id--------:%s" % (room_id,))
                #look room info
                room = await self.store.get_room_with_stats(room_id)
                logger.info("call room--------:%s" % (str(room)))
                #check room is direct room
                direct_room_count = await self.store.count_direct_room_for_room_id(
                    room_id)
                logger.info("call direct_room_count--------:%s" %
                            (direct_room_count, ))
                group_type = 'group'
                #default room_name,direct room then it is sender_display_name
                group_name = room['name']
                reciever = ''
                # single chat room
                if direct_room_count >= 1:
                    logger.info("the currently room is direct_room--------")
                    group_type = 'single'
                    group_name = sender_display_name

                # select reciever matrixID
                reciever_pusher = await self.store.get_pushers_by_app_id_and_pushkey(
                    appid, reg_ids[0])
                logger.info(
                    "-=-=-=reciever_pushers reciever_pusher--------:%s" %
                    (reciever_pusher, ))
                for row in reciever_pusher:
                    logger.info("-=-=-=reciever_pusher row--------:%s" %
                                (row, ))
                    reciever = row.user_name
                    break

                push_params['msgType'] = msg_type
                push_params['body'] = msg_body
                push_params['fromUserName'] = sender_display_name
                push_params['groupType'] = group_type
                push_params['groupName'] = group_name
                push_params['matrixId'] = reciever
                push_params['contactMatrixId'] = sender
                push_params['unreadCount'] = unread_counts
                push_params['senderDisplayName'] = sender_display_name
                push_params['eventId'] = event_id
            else:
                logger.warning('params:content is missed')
                return

        logger.info(" push_params--------:%s" % (str(push_params)))

        logger.info("call appid(push_type)-------:%s" % (str(appid)))

        push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_jiguang
        if appid.startswith('android_'):
            push_type = appid.split('_', 1)[1]
            if push_type == 'huawei':
                push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_huawei
            elif push_type == 'firebase':
                push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_firebase
            elif push_type == 'xiaomi':
                push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_xiaomi
            elif push_type == 'oppo':
                push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_oppo
            elif push_type == 'vivo':
                push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_vivo
            elif push_type == 'getui':
                push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_getui
            else:
                push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_jiguang
        elif appid == 'ios_apns':
            # push_type = appid.split('_', 1)[1]
            push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_apns
        elif appid == 'ios_apns_sandbox':
            push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_apns_sandbox
            # else:
            #     push_url = self.hs.config.push_baseurl + self.hs.config.push_gateway_jiguang
        logger.info("call push_url-------:%s" % (str(push_url)))

        try:
            result = await self.http_client.post_json_get_json(
                push_url,
                push_params,
            )
            # logger.info("%s get openid from %s: %s" % (str(app_type), self.hs.config.auth_get_vercode, remote_user))
            logger.info("result: %s" % (str(result)))

        except HttpResponseException as e:
            logger.info("Proxied push jiguang gateway failed: %r", e)
            raise e.to_synapse_error()
        except RequestTimedOutError:
            raise SynapseError(
                500, "Timed out contacting extral server:push jiguang gateway")

        return 200, {}
Beispiel #24
0
    def _do_other_login(self, login_submission):
        """Handle non-token/saml/jwt logins

        Args:
            login_submission:

        Returns:
            dict: HTTP response
        """
        # Log the request we got, but only certain fields to minimise the chance of
        # logging someone's password (even if they accidentally put it in the wrong
        # field)
        logger.info(
            "Got login request with identifier: %r, medium: %r, address: %r, user: %r",
            login_submission.get("identifier"),
            login_submission.get("medium"),
            login_submission.get("address"),
            login_submission.get("user"),
        )
        login_submission_legacy_convert(login_submission)

        if "identifier" not in login_submission:
            raise SynapseError(400, "Missing param: identifier")

        identifier = login_submission["identifier"]
        if "type" not in identifier:
            raise SynapseError(400, "Login identifier has no type")

        # convert phone type identifiers to generic threepids
        if identifier["type"] == "m.id.phone":
            identifier = login_id_thirdparty_from_phone(identifier)

        # convert threepid identifiers to user IDs
        if identifier["type"] == "m.id.thirdparty":
            address = identifier.get("address")
            medium = identifier.get("medium")

            if medium is None or address is None:
                raise SynapseError(400, "Invalid thirdparty identifier")

            if medium == "email":
                # For emails, transform the address to lowercase.
                # We store all email addreses as lowercase in the DB.
                # (See add_threepid in synapse/handlers/auth.py)
                address = address.lower()

            # Check for login providers that support 3pid login types
            canonical_user_id, callback_3pid = (
                yield self.auth_handler.check_password_provider_3pid(
                    medium, address, login_submission["password"]))
            if canonical_user_id:
                # Authentication through password provider and 3pid succeeded
                result = yield self._register_device_with_callback(
                    canonical_user_id, login_submission, callback_3pid)
                return result

            # No password providers were able to handle this 3pid
            # Check local store
            user_id = yield self.hs.get_datastore().get_user_id_by_threepid(
                medium, address)
            if not user_id:
                logger.warn("unknown 3pid identifier medium %s, address %r",
                            medium, address)
                raise LoginError(403, "", errcode=Codes.FORBIDDEN)

            identifier = {"type": "m.id.user", "user": user_id}

        # by this point, the identifier should be an m.id.user: if it's anything
        # else, we haven't understood it.
        if identifier["type"] != "m.id.user":
            raise SynapseError(400, "Unknown login identifier type")
        if "user" not in identifier:
            raise SynapseError(400, "User identifier is missing 'user' key")

        canonical_user_id, callback = yield self.auth_handler.validate_login(
            identifier["user"], login_submission)

        result = yield self._register_device_with_callback(
            canonical_user_id, login_submission, callback)
        return result
Beispiel #25
0
    def check_auth(self, flows, clientdict, clientip):
        """
        Takes a dictionary sent by the client in the login / registration
        protocol and handles the login flow.

        As a side effect, this function fills in the 'creds' key on the user's
        session with a map, which maps each auth-type (str) to the relevant
        identity authenticated by that auth-type (mostly str, but for captcha, bool).

        Args:
            flows (list): A list of login flows. Each flow is an ordered list of
                          strings representing auth-types. At least one full
                          flow must be completed in order for auth to be successful.
            clientdict: The dictionary from the client root level, not the
                        'auth' key: this method prompts for auth if none is sent.
            clientip (str): The IP address of the client.
        Returns:
            A tuple of (authed, dict, dict, session_id) where authed is true if
            the client has successfully completed an auth flow. If it is true
            the first dict contains the authenticated credentials of each stage.

            If authed is false, the first dictionary is the server response to
            the login request and should be passed back to the client.

            In either case, the second dict contains the parameters for this
            request (which may have been given only in a previous call).

            session_id is the ID of this session, either passed in by the client
            or assigned by the call to check_auth
        """

        authdict = None
        sid = None
        if clientdict and 'auth' in clientdict:
            authdict = clientdict['auth']
            del clientdict['auth']
            if 'session' in authdict:
                sid = authdict['session']
        session = self._get_session_info(sid)

        if len(clientdict) > 0:
            # This was designed to allow the client to omit the parameters
            # and just supply the session in subsequent calls so it split
            # auth between devices by just sharing the session, (eg. so you
            # could continue registration from your phone having clicked the
            # email auth link on there). It's probably too open to abuse
            # because it lets unauthenticated clients store arbitrary objects
            # on a home server.
            # Revisit: Assumimg the REST APIs do sensible validation, the data
            # isn't arbintrary.
            session['clientdict'] = clientdict
            self._save_session(session)
        elif 'clientdict' in session:
            clientdict = session['clientdict']

        if not authdict:
            defer.returnValue(
                (
                    False, self._auth_dict_for_flows(flows, session),
                    clientdict, session['id']
                )
            )

        if 'creds' not in session:
            session['creds'] = {}
        creds = session['creds']

        # check auth type currently being presented
        if 'type' in authdict:
            if authdict['type'] not in self.checkers:
                raise LoginError(400, "", Codes.UNRECOGNIZED)
            result = yield self.checkers[authdict['type']](authdict, clientip)
            if result:
                creds[authdict['type']] = result
                self._save_session(session)

        for f in flows:
            if len(set(f) - set(creds.keys())) == 0:
                logger.info("Auth completed with creds: %r", creds)
                defer.returnValue((True, creds, clientdict, session['id']))

        ret = self._auth_dict_for_flows(flows, session)
        ret['completed'] = creds.keys()
        defer.returnValue((False, ret, clientdict, session['id']))