Beispiel #1
0
    async def change_username(self, id: int, username: str) -> None:
        new_username = self._validate_user_model(UserInChangeUsername, {
            "username": username
        }).username

        item = await self._repo.get(id)
        old_username = item.get("username")
        if old_username == new_username:
            raise HTTPException(400,
                                detail=get_error_message(
                                    "username change same", self._language))

        existing_user = await self._repo.get_by_username(new_username)

        if existing_user is not None:
            raise HTTPException(400,
                                detail=get_error_message(
                                    "existing username", self._language))

        logger.info(
            f"change_username id={id} old_username={old_username} new_username={new_username}"
        )
        await self._repo.change_username(id, new_username)
        logger.info(f"change_username id={id} success")
        return None
Beispiel #2
0
    async def register(self, data: dict) -> Dict[str, str]:
        """POST /register

        Args:
            data: email, username, password1, password2.

        Returns:
            Access and refresh tokens.

        Raises:
            HTTPException:
                400 - validation error.

        """
        if not self._debug:
            captcha = data.get("captcha")
            if not await validate_captcha(captcha, self._recaptcha_secret):
                raise HTTPException(400,
                                    detail=get_error_message(
                                        "captcha", self._language))

        # try:
        #     user = UserInRegister(**data)
        # except ValidationError as e:
        #     message = get_error_message(e.errors()[0].get("msg"), self._language)
        #     raise HTTPException(400, detail=message)
        user = self._validate_user_model(UserInRegister, data)

        if await self._email_exists(user.email):
            raise HTTPException(400,
                                detail=get_error_message(
                                    "existing email", self._language))

        if await self._username_exists(user.username):
            raise HTTPException(400,
                                detail=get_error_message(
                                    "existing username", self._language))

        new_user = UserInCreate(**user.dict(),
                                password=get_password_hash(
                                    user.password1)).dict()

        try:
            validate_email(new_user.get("email"), timeout=5)
        except EmailNotValidError:
            raise HTTPException(400,
                                detail=get_error_message(
                                    "try another email", self._language))

        new_user_id = await self._repo.create(new_user)

        asyncio.create_task(
            self._request_email_confirmation(new_user.get("email")))

        payload = UserPayload(id=new_user_id, username=user.username).dict()
        return self._auth_backend.create_tokens(payload)
Beispiel #3
0
    async def forgot_password(self, data: dict, ip: str) -> None:
        """POST /forgot_password

        Only for accounts with password.

        Args:
            data: {email: "*****@*****.**"}
            ip: ip from request

        Returns:
            None

        Raises:
            HTTPException:
                400 - validation or timeout.
                404 - email not found.
        """
        try:
            email = UserInForgotPassword(**data).email
        except ValidationError:
            raise HTTPException(400,
                                detail=get_error_message(
                                    "validation", self._language))

        item = await self._repo.get_by_email(email)

        if item is None:
            raise HTTPException(404,
                                detail=get_error_message(
                                    "email not found", self._language))

        if item.get("password") is None:
            raise HTTPException(406)

        id = item.get("id")

        if not await self._repo.is_password_reset_available(id):
            raise HTTPException(400,
                                detail=get_error_message(
                                    "reset before", self._language))
        logger.info(f"forgot_password ip={ip} email={email}")

        token = create_random_string()
        token_hash = hash_string(token)

        await self._repo.set_password_reset_token(id, token_hash)

        # if not self._debug:  # TODO
        email_client = self._create_email_client()
        await email_client.send_forgot_password_email(email, token)

        return None
Beispiel #4
0
 def _validate_user_model(self, model, data: dict):
     try:
         user = model(**data)
         return user
     except ValidationError as e:
         # for error in e.errors():
         msg = e.errors()[0].get("msg")
         raise HTTPException(400,
                             detail=get_error_message(msg, self._language))
Beispiel #5
0
 async def password_set(self, data: dict) -> None:
     item = await self._repo.get(self._user.id)
     if item.get("provider") is not None and item.get("password") is None:
         user_model = self._validate_user_model(UserInSetPassword, data)
         password_hash = get_password_hash(user_model.password1)
         await self._repo.set_password(self._user.id, password_hash)
         return None
     else:
         raise HTTPException(
             400,
             get_error_message("password already exists", self._language))
Beispiel #6
0
    async def password_change(self, data: dict) -> None:
        user_model = self._validate_user_model(UserInChangePassword, data)
        item = await self._repo.get(self._user.id)

        if not verify_password(user_model.old_password, item.get("password")):
            raise HTTPException(400,
                                detail=get_error_message(
                                    "password invalid", self._language))

        password_hash = get_password_hash(user_model.password1)
        await self._repo.set_password(self._user.id, password_hash)
        return None
Beispiel #7
0
    async def login(self, data: dict, ip: str) -> Dict[str, str]:
        """POST /login

        Args:
            data: login, password.
            ip: for bruteforce check.

        Returns:
            Access and refresh tokens.

        Raises:
            HTTPException:
                400 - validation error or ban.
                404 - user doesn't exist.
                429 - bruteforce attempt.
        """
        try:
            user = UserInLogin(**data)
        except ValidationError:
            raise HTTPException(400)

        if await self._is_bruteforce(ip, user.login):
            raise HTTPException(429, detail="Too many requests")

        item = await self._repo.get_by_login(user.login)

        if item is None:
            raise HTTPException(404)

        if not item.get("active"):
            raise HTTPException(400,
                                detail=get_error_message(
                                    "ban", self._language))

        if not verify_password(user.password, item.get("password")):
            raise HTTPException(401)

        await self._update_last_login(item.get("id"))

        payload = UserPayload(**item).dict()
        tokens = self._auth_backend.create_tokens(payload)

        return tokens