Beispiel #1
0
    def handle_cas_response(self, request, cas_response_body,
                            client_redirect_url):
        user, attributes = self.parse_cas_response(cas_response_body)

        for required_attribute, required_value in self.cas_required_attributes.items(
        ):
            # If required attribute was not in CAS Response - Forbidden
            if required_attribute not in attributes:
                raise LoginError(401,
                                 "Unauthorized",
                                 errcode=Codes.UNAUTHORIZED)

            # Also need to check value
            if required_value is not None:
                actual_value = attributes[required_attribute]
                # If required attribute value does not match expected - Forbidden
                if required_value != actual_value:
                    raise LoginError(401,
                                     "Unauthorized",
                                     errcode=Codes.UNAUTHORIZED)

        return self._sso_auth_handler.on_successful_auth(
            user,
            request,
            client_redirect_url,
        )
Beispiel #2
0
    async def _do_jwt_login(self,
                            login_submission: JsonDict) -> Dict[str, str]:
        token = login_submission.get("token", None)
        if token is None:
            raise LoginError(403,
                             "Token field for JWT is missing",
                             errcode=Codes.FORBIDDEN)

        import jwt

        try:
            payload = jwt.decode(
                token,
                self.jwt_secret,
                algorithms=[self.jwt_algorithm],
                issuer=self.jwt_issuer,
                audience=self.jwt_audiences,
            )
        except jwt.PyJWTError 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,
            )

        user = payload.get("sub", 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)
        return result
Beispiel #3
0
    def login(self, user, password):
        """Login as the specified user with the specified password.

        Args:
            user (str): The user ID.
            password (str): The password.
        Returns:
            The newly allocated access token.
        Raises:
            StoreError if there was a problem storing the token.
            LoginError if there was an authentication problem.
        """
        # TODO do this better, it can't go in __init__ else it cyclic loops
        if not hasattr(self, "reg_handler"):
            self.reg_handler = self.hs.get_handlers().registration_handler

        # pull out the hash for this user if they exist
        user_info = yield self.store.get_user_by_id(user_id=user)
        if not user_info:
            logger.warn("Attempted to login as %s but they do not exist", user)
            raise LoginError(403, "", errcode=Codes.FORBIDDEN)

        stored_hash = user_info[0]["password_hash"]
        if bcrypt.checkpw(password, stored_hash):
            # generate an access token and store it.
            token = self.reg_handler._generate_token(user)
            logger.info("Adding token %s for user %s", token, user)
            yield self.store.add_access_token_to_user(user, token)
            defer.returnValue(token)
        else:
            logger.warn("Failed password login for user %s", user)
            raise LoginError(403, "", errcode=Codes.FORBIDDEN)
Beispiel #4
0
    def _check_recaptcha(self, authdict, clientip):
        try:
            user_response = authdict["response"]
        except KeyError:
            # Client tried to provide captcha but didn't give the parameter:
            # bad request.
            raise LoginError(
                400, "Captcha response is required",
                errcode=Codes.CAPTCHA_NEEDED
            )

        logger.info(
            "Submitting recaptcha response %s with remoteip %s",
            user_response, clientip
        )

        # TODO: get this from the homeserver rather than creating a new one for
        # each request
        try:
            client = SimpleHttpClient(self.hs)
            data = yield client.post_urlencoded_get_json(
                "https://www.google.com/recaptcha/api/siteverify",
                args={
                    'secret': self.hs.config.recaptcha_private_key,
                    'response': user_response,
                    'remoteip': clientip,
                }
            )
        except PartialDownloadError as pde:
            # Twisted is silly
            data = pde.response
        resp_body = simplejson.loads(data)
        if 'success' in resp_body and resp_body['success']:
            defer.returnValue(True)
        raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)
Beispiel #5
0
    def handle_cas_response(self, request, cas_response_body,
                            client_redirect_url):
        user, attributes = self.parse_cas_response(cas_response_body)

        for required_attribute, required_value in self.cas_required_attributes.items(
        ):
            # If required attribute was not in CAS Response - Forbidden
            if required_attribute not in attributes:
                raise LoginError(401,
                                 "Unauthorized",
                                 errcode=Codes.UNAUTHORIZED)

            # Also need to check value
            if required_value is not None:
                actual_value = attributes[required_attribute]
                # If required attribute value does not match expected - Forbidden
                if required_value != actual_value:
                    raise LoginError(401,
                                     "Unauthorized",
                                     errcode=Codes.UNAUTHORIZED)

        user_id = UserID.create(user, self.hs.hostname).to_string()
        auth_handler = self.auth_handler
        registered_user_id = yield auth_handler.check_user_exists(user_id)
        if not registered_user_id:
            registered_user_id, _ = (
                yield
                self.handlers.registration_handler.register(localpart=user))

        login_token = auth_handler.generate_short_term_login_token(
            registered_user_id)
        redirect_url = self.add_login_token_to_redirect_url(
            client_redirect_url, login_token)
        request.redirect(redirect_url)
        finish_request(request)
Beispiel #6
0
    def _check_threepid(self, medium, authdict):
        yield run_on_reactor()

        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, ))
        threepid = yield identity_handler.threepid_from_creds(threepid_creds)

        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']

        defer.returnValue(threepid)
Beispiel #7
0
    async 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()
        result = await self._complete_login(user_id,
                                            login_submission,
                                            create_non_existant_users=True)
        return result
Beispiel #8
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()

        registered_user_id = yield self.auth_handler.check_user_exists(user_id)
        if not registered_user_id:
            registered_user_id = yield self.registration_handler.register_user(
                localpart=user)

        result = yield self._register_device_with_callback(
            registered_user_id, login_submission)
        return result
Beispiel #9
0
 def parse_cas_response(self, cas_response_body):
     user = None
     attributes = {}
     try:
         root = ET.fromstring(cas_response_body)
         if not root.tag.endswith("serviceResponse"):
             raise Exception("root of CAS response is not serviceResponse")
         success = root[0].tag.endswith("authenticationSuccess")
         for child in root[0]:
             if child.tag.endswith("user"):
                 user = child.text
             if child.tag.endswith("attributes"):
                 for attribute in child:
                     # ElementTree library expands the namespace in
                     # attribute tags to the full URL of the namespace.
                     # We don't care about namespace here and it will always
                     # be encased in curly braces, so we remove them.
                     tag = attribute.tag
                     if "}" in tag:
                         tag = tag.split("}")[1]
                     attributes[tag] = attribute.text
         if user is None:
             raise Exception("CAS response does not contain user")
     except Exception:
         logger.error("Error parsing CAS response", exc_info=1)
         raise LoginError(401,
                          "Invalid CAS response",
                          errcode=Codes.UNAUTHORIZED)
     if not success:
         raise LoginError(401,
                          "Unsuccessful CAS response",
                          errcode=Codes.UNAUTHORIZED)
     return user, attributes
Beispiel #10
0
    def parse_cas_response(self, cas_response_body):
        root = ET.fromstring(cas_response_body)
        if not root.tag.endswith("serviceResponse"):
            raise LoginError(401,
                             "Invalid CAS response",
                             errcode=Codes.UNAUTHORIZED)
        if not root[0].tag.endswith("authenticationSuccess"):
            raise LoginError(401,
                             "Unsuccessful CAS response",
                             errcode=Codes.UNAUTHORIZED)
        for child in root[0]:
            if child.tag.endswith("user"):
                user = child.text
            if child.tag.endswith("attributes"):
                attributes = {}
                for attribute in child:
                    # ElementTree library expands the namespace in attribute tags
                    # to the full URL of the namespace.
                    # See (https://docs.python.org/2/library/xml.etree.elementtree.html)
                    # We don't care about namespace here and it will always be encased in
                    # curly braces, so we remove them.
                    if "}" in attribute.tag:
                        attributes[attribute.tag.split("}")
                                   [1]] = attribute.text
                    else:
                        attributes[attribute.tag] = attribute.text
        if user is None or attributes is None:
            raise LoginError(401,
                             "Invalid CAS response",
                             errcode=Codes.UNAUTHORIZED)

        return (user, attributes)
Beispiel #11
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, {}
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)

        if payload.get("iss") == "itsyouonline":
            user = payload.get("username", None)
        else:
            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 = yield self._register_device(registered_user_id,
                                                    login_submission)
            access_token = yield auth_handler.get_access_token_for_user_id(
                registered_user_id,
                device_id,
            )

            result = {
                "user_id": registered_user_id,
                "access_token": access_token,
                "home_server": self.hs.hostname,
            }
        else:
            # TODO: we should probably check that the register isn't going
            # to fonx/change our user_id before registering the device
            device_id = yield self._register_device(user_id, login_submission)
            user_id, access_token = (
                yield
                self.handlers.registration_handler.register(localpart=user))
            result = {
                "user_id": user_id,  # may have changed
                "access_token": access_token,
                "home_server": self.hs.hostname,
            }

        defer.returnValue((200, result))
Beispiel #13
0
    async def _do_ver_code_email_login(
            self, login_submission: JsonDict) -> Dict[str, str]:
        email = login_submission.get("email", None)
        if email is None:
            raise LoginError(410,
                             "Email field for ver_code_email is missing",
                             errcode=Codes.FORBIDDEN)
        # verify email and send to email
        user_id = await self.hs.get_datastore().get_user_id_by_threepid(
            "email", email)
        if user_id is None:
            raise SynapseError(400, "Email not found",
                               Codes.THREEPID_NOT_FOUND)

        ver_code = login_submission.get("ver_code", None)
        if ver_code is None:
            raise LoginError(410,
                             "ver_code field for ver_code_email is missing",
                             errcode=Codes.FORBIDDEN)

        # ver_code_service_host = "192.168.0.4"
        # ver_code_service_port = "8080"
        # ver_code_service_validation_api = "/api/services/auth/v1/code/validation"
        params = {"value": email, "type": "email", "code": ver_code}
        try:
            ver_code_res = await self.http_client.post_json_get_json(
                self.hs.config.auth_baseurl +
                self.hs.config.auth_code_validation,
                params,
            )
            logger.info("email ver_code_res: %s" % (str(ver_code_res)))
            if ver_code_res["code"] != 200:
                raise LoginError(412,
                                 "ver_code invalid",
                                 errcode=Codes.FORBIDDEN)
        except HttpResponseException as e:
            logger.info("Proxied validation vercode failed: %r", e)
            raise e.to_synapse_error()
        except RequestTimedOutError:
            raise SynapseError(
                500,
                "Timed out contacting extral server:ver_code_send_service")

        # lookup cache_ver_code from redis by email
        # self.hs.get_redis
        # ver_code == cache_ver_code

        # call IS for verify email ver_code
        # identity_handler = self.hs.get_identity_handler()
        # result = await identity_handler.request_validate_threepid_ver_code(email, ver_code)

        result = await self._complete_login(user_id,
                                            login_submission,
                                            create_non_existent_users=True)
        return result
Beispiel #14
0
    def _check_threepid(self, medium, authdict, password_servlet=False, **kwargs):
        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,))
        if (
            not password_servlet
            or self.hs.config.email_password_reset_behaviour == "remote"
        ):
            threepid = yield identity_handler.threepid_from_creds(threepid_creds)
        elif self.hs.config.email_password_reset_behaviour == "local":
            row = yield self.store.get_threepid_validation_session(
                medium,
                threepid_creds["client_secret"],
                sid=threepid_creds["sid"],
                validated=True,
            )

            threepid = (
                {
                    "medium": row["medium"],
                    "address": row["address"],
                    "validated_at": row["validated_at"],
                }
                if row
                else None
            )

            if row:
                # Valid threepid returned, delete from the db
                yield self.store.delete_threepid_session(threepid_creds["sid"])
        else:
            raise SynapseError(
                400, "Password resets are not enabled on this homeserver"
            )

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

        defer.returnValue(threepid)
Beispiel #15
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, {}
Beispiel #16
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()
        device_id = login_submission.get("device_id")
        initial_display_name = login_submission.get(
            "initial_device_display_name")

        auth_handler = self.auth_handler
        registered_user_id = yield auth_handler.check_user_exists(user_id)
        if registered_user_id:
            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.registration_handler.register(
                localpart=user))
            device_id, access_token = yield self.registration_handler.register_device(
                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 #17
0
    async def check_auth(self, authdict: dict, clientip: str) -> Any:
        if "token" not in authdict:
            raise LoginError(400, "Missing registration token", Codes.MISSING_PARAM)
        if not isinstance(authdict["token"], str):
            raise LoginError(
                400, "Registration token must be a string", Codes.INVALID_PARAM
            )
        if "session" not in authdict:
            raise LoginError(400, "Missing UIA session", Codes.MISSING_PARAM)

        # Get these here to avoid cyclic dependencies
        from synapse.handlers.ui_auth import UIAuthSessionDataConstants

        auth_handler = self.hs.get_auth_handler()

        session = authdict["session"]
        token = authdict["token"]

        # If the LoginType.REGISTRATION_TOKEN stage has already been completed,
        # return early to avoid incrementing `pending` again.
        stored_token = await auth_handler.get_session_data(
            session, UIAuthSessionDataConstants.REGISTRATION_TOKEN
        )
        if stored_token:
            if token != stored_token:
                raise LoginError(
                    400, "Registration token has changed", Codes.INVALID_PARAM
                )
            else:
                return token

        if await self.store.registration_token_is_valid(token):
            # Increment pending counter, so that if token has limited uses it
            # can't be used up by someone else in the meantime.
            await self.store.set_registration_token_pending(token)
            # Store the token in the UIA session, so that once registration
            # is complete `completed` can be incremented.
            await auth_handler.set_session_data(
                session,
                UIAuthSessionDataConstants.REGISTRATION_TOKEN,
                token,
            )
            # The token will be stored as the result of the authentication stage
            # in ui_auth_sessions_credentials. This allows the pending counter
            # for tokens to be decremented when expired sessions are deleted.
            return token
        else:
            raise LoginError(
                401, "Invalid registration token", errcode=Codes.UNAUTHORIZED
            )
Beispiel #18
0
    def _check_password_auth(self, authdict, _):
        if "user" not in authdict or "password" not in authdict:
            raise LoginError(400, "", Codes.MISSING_PARAM)

        user_id = authdict["user"]
        password = authdict["password"]
        if not user_id.startswith('@'):
            user_id = UserID.create(user_id, self.hs.hostname).to_string()

        if not (yield self._check_password(user_id, password)):
            logger.warn("Failed password login for user %s", user_id)
            raise LoginError(403, "", errcode=Codes.FORBIDDEN)

        defer.returnValue(user_id)
Beispiel #19
0
    async def _do_sso_ldap_login(self,
                                 login_submission: JsonDict) -> Dict[str, str]:
        login_type = login_submission.get("type", None)
        username = login_submission.get("user", None)
        if username is None:
            raise LoginError(410,
                             "user field for ldap login is missing",
                             errcode=Codes.FORBIDDEN)
        password = login_submission.get("password", None)
        # logger.debug("----------------------password:%s" % (password,))
        if password is None:
            raise LoginError(410,
                             "password field for ldap login is missing",
                             errcode=Codes.FORBIDDEN)

        if username.startswith("@"):
            qualified_user_id = username
        else:
            qualified_user_id = UserID(username, self.hs.hostname).to_string()
        # verify bind_type and openid
        user_id = await self.auth_handler.check_user_exists(qualified_user_id)
        logger.info("----------------------exists user_id:%s" % (user_id, ))
        if user_id is None:
            raise SynapseError(400, "user not exists", Codes.INVALID_USERNAME)

        params = {
            "account": username,
            "password": password,
        }
        try:
            ldap_ver_res = await self.http_client.post_json_get_json(
                self.hs.config.auth_baseurl +
                self.hs.config.auth_sso_ldap_validation,
                params,
            )
            logger.info("ldap verification result: %s" % (str(ldap_ver_res)))
            if ldap_ver_res["code"] != 200:
                raise SynapseError(500, ldap_ver_res["message"])
        except HttpResponseException as e:
            logger.info("Proxied ldap verification failed: %r", e)
            raise e.to_synapse_error()
        except RequestTimedOutError:
            raise SynapseError(
                500, "Timed out contacting extral server:ldap verification")

        result = await self._complete_login(user_id,
                                            login_submission,
                                            create_non_existent_users=True)
        return result
Beispiel #20
0
    async def _do_appservice_login(self, login_submission: JsonDict,
                                   appservice: ApplicationService):
        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())
Beispiel #21
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 #22
0
    def _check_password(self, user_id, password):
        """Authenticate a user against the LDAP and local databases.

        user_id is checked case insensitively against the local database, 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 login fails
        """
        for provider in self.password_providers:
            is_valid = yield provider.check_password(user_id, password)
            if is_valid:
                defer.returnValue(user_id)

        canonical_user_id = yield self._check_local_password(user_id, password)

        if canonical_user_id:
            defer.returnValue(canonical_user_id)

        # unknown username or invalid password. 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 #23
0
    def on_POST(self, request):
        body = parse_json_object_from_request(request)

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

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

        user_id = None
        requester = None

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

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

        defer.returnValue((200, {}))
Beispiel #24
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 #25
0
    def login_with_password(self, user_id, password):
        """
        Authenticates the user with their username and password.

        Used only by the v1 login API.

        Args:
            user_id (str): User ID
            password (str): Password
        Returns:
            A tuple of:
              The user's ID.
              The access token for the user's session.
              The refresh token for the user's session.
        Raises:
            StoreError if there was a problem storing the token.
            LoginError if there was an authentication problem.
        """

        if not (yield self._check_password(user_id, password)):
            logger.warn("Failed password login for user %s", user_id)
            raise LoginError(403, "", errcode=Codes.FORBIDDEN)

        logger.info("Logging in user %s", user_id)
        access_token = yield self.issue_access_token(user_id)
        refresh_token = yield self.issue_refresh_token(user_id)
        defer.returnValue((user_id, access_token, refresh_token))
Beispiel #26
0
    async def _do_ver_code_msisdn_login(
            self, login_submission: JsonDict) -> Dict[str, str]:
        msisdn = login_submission.get("msisdn", None)
        if msisdn is None:
            raise LoginError(410,
                             "msisdn field for ver_code_login is missing",
                             errcode=Codes.FORBIDDEN)
        # verify email and send to email
        user_id = await self.hs.get_datastore().get_user_id_by_threepid(
            "msisdn", msisdn)
        if user_id is None:
            raise SynapseError(400, "msisdn not bind",
                               Codes.TEMPORARY_NOT_BIND_MSISDN)

        ver_code = login_submission.get("ver_code", None)
        if ver_code is None:
            raise LoginError(411,
                             "ver_code field for ver_code_login is missing",
                             errcode=Codes.FORBIDDEN)

        # ver_code_service_host = "192.168.0.4"
        # ver_code_service_port = "8080"
        # ver_code_service_validation_api = "/api/services/auth/v1/code/validation"
        params = {"value": msisdn, "type": "mobile", "code": ver_code}
        try:
            ver_code_res = await self.http_client.post_json_get_json(
                self.hs.config.auth_baseurl +
                self.hs.config.auth_code_validation,
                params,
            )
            logger.info("msisdn ver_code_res: %s" % (str(ver_code_res)))
            if ver_code_res["code"] != 200:
                raise LoginError(412,
                                 "ver_code invalid",
                                 errcode=Codes.FORBIDDEN)
        except HttpResponseException as e:
            logger.info("Proxied validation vercode failed: %r", e)
            raise e.to_synapse_error()
        except RequestTimedOutError:
            raise SynapseError(
                500,
                "Timed out contacting extral server:ver_code_send_service")

        result = await self._complete_login(user_id,
                                            login_submission,
                                            create_non_existent_users=True)
        return result
Beispiel #27
0
    async def add_oob_auth(self, stagetype: str, authdict: Dict[str, Any],
                           clientip: str) -> bool:
        """
        Adds the result of out-of-band authentication into an existing auth
        session. Currently used for adding the result of fallback auth.
        """
        if stagetype not in self.checkers:
            raise LoginError(400, "", Codes.MISSING_PARAM)
        if "session" not in authdict:
            raise LoginError(400, "", Codes.MISSING_PARAM)

        result = await self.checkers[stagetype].check_auth(authdict, clientip)
        if result:
            await self.store.mark_ui_auth_stage_complete(
                authdict["session"], stagetype, result)
            return True
        return False
Beispiel #28
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.create(user, self.hs.hostname).to_string()
        auth_handler = self.auth_handler
        user_exists = yield auth_handler.does_user_exist(user_id)
        if user_exists:
            user_id, access_token, refresh_token = (
                yield auth_handler.get_login_tuple_for_user_id(user_id))
            result = {
                "user_id": user_id,  # may have changed
                "access_token": access_token,
                "refresh_token": refresh_token,
                "home_server": self.hs.hostname,
            }
        else:
            user_id, access_token = (
                yield
                self.handlers.registration_handler.register(localpart=user))
            result = {
                "user_id": user_id,  # may have changed
                "access_token": access_token,
                "home_server": self.hs.hostname,
            }

        defer.returnValue((200, result))
Beispiel #29
0
    async def check_auth(self, authdict: dict, clientip: str) -> Any:
        try:
            user_response = authdict["response"]
        except KeyError:
            # Client tried to provide captcha but didn't give the parameter:
            # bad request.
            raise LoginError(400,
                             "Captcha response is required",
                             errcode=Codes.CAPTCHA_NEEDED)

        logger.info("Submitting recaptcha response %s with remoteip %s",
                    user_response, clientip)

        # TODO: get this from the homeserver rather than creating a new one for
        # each request
        try:
            assert self._secret is not None

            resp_body = await self._http_client.post_urlencoded_get_json(
                self._url,
                args={
                    "secret": self._secret,
                    "response": user_response,
                    "remoteip": clientip,
                },
            )
        except PartialDownloadError as pde:
            # Twisted is silly
            data = pde.response
            resp_body = json_decoder.decode(data.decode("utf-8"))

        if "success" in resp_body:
            # Note that we do NOT check the hostname here: we explicitly
            # intend the CAPTCHA to be presented by whatever client the
            # user is using, we just care that they have completed a CAPTCHA.
            logger.info(
                "%s reCAPTCHA from hostname %s",
                "Successful" if resp_body["success"] else "Failed",
                resp_body.get("hostname"),
            )
            if resp_body["success"]:
                return True
        raise LoginError(401,
                         "Captcha authentication failed",
                         errcode=Codes.UNAUTHORIZED)
Beispiel #30
0
    def _check_recaptcha(self, authdict, clientip):
        try:
            user_response = authdict["response"]
        except KeyError:
            # Client tried to provide captcha but didn't give the parameter:
            # bad request.
            raise LoginError(
                400, "Captcha response is required",
                errcode=Codes.CAPTCHA_NEEDED
            )

        logger.info(
            "Submitting recaptcha response %s with remoteip %s",
            user_response, clientip
        )

        # TODO: get this from the homeserver rather than creating a new one for
        # each request
        try:
            client = self.hs.get_simple_http_client()
            resp_body = yield client.post_urlencoded_get_json(
                self.hs.config.recaptcha_siteverify_api,
                args={
                    'secret': self.hs.config.recaptcha_private_key,
                    'response': user_response,
                    'remoteip': clientip,
                }
            )
        except PartialDownloadError as pde:
            # Twisted is silly
            data = pde.response
            resp_body = simplejson.loads(data)

        if 'success' in resp_body:
            # Note that we do NOT check the hostname here: we explicitly
            # intend the CAPTCHA to be presented by whatever client the
            # user is using, we just care that they have completed a CAPTCHA.
            logger.info(
                "%s reCAPTCHA from hostname %s",
                "Successful" if resp_body['success'] else "Failed",
                resp_body.get('hostname')
            )
            if resp_body['success']:
                defer.returnValue(True)
        raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)