From c2224ae9c17b72d374db218be61cd9dcaecd9e0c Mon Sep 17 00:00:00 2001 From: urec56 Date: Wed, 12 Jun 2024 22:05:10 +0500 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8=D0=BB=20?= =?UTF-8?q?=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/dao/user.py | 17 +++++++++++++++-- app/tasks/email_templates.py | 29 +++++++++------------------- app/tasks/tasks.py | 37 +++++++++++------------------------- app/users/router.py | 20 +++++++++++-------- app/utils/auth.py | 33 +++++++------------------------- 5 files changed, 54 insertions(+), 82 deletions(-) diff --git a/app/dao/user.py b/app/dao/user.py index 7c17b45..326d868 100644 --- a/app/dao/user.py +++ b/app/dao/user.py @@ -1,5 +1,5 @@ -from pydantic import HttpUrl -from sqlalchemy import update, select, insert, func +from pydantic import HttpUrl, EmailStr +from sqlalchemy import update, select, insert, func, or_ from sqlalchemy.exc import MultipleResultsFound, IntegrityError, NoResultFound from app.chat.shemas import SAllowedChats @@ -41,6 +41,19 @@ class UserDAO(BaseDAO): except NoResultFound: raise UserNotFoundException + async def find_one_by_username_or_email(self, username: str, email: EmailStr) -> SUser: + try: + query = select(Users.__table__.columns).where( + or_(Users.username == username, Users.email == email) + ) + result = await self.session.execute(query) + result = result.mappings().one() + return SUser.model_validate(result) + except MultipleResultsFound: + raise IncorrectDataException + except NoResultFound: + raise UserNotFoundException + async def find_all(self) -> SUsers: query = ( select( diff --git a/app/tasks/email_templates.py b/app/tasks/email_templates.py index 1987a3d..51c18e1 100644 --- a/app/tasks/email_templates.py +++ b/app/tasks/email_templates.py @@ -5,11 +5,7 @@ from pydantic import EmailStr from app.config import settings -def create_registration_confirmation_template( - username: str, - email_to: EmailStr, - confirmation_link: str, -): +def create_registration_confirmation_template(username: str, email_to: EmailStr, confirmation_link: str): email = EmailMessage() email["Subject"] = "Подтверждение регистрации" @@ -26,41 +22,34 @@ def create_registration_confirmation_template( return email -def create_password_change_confirmation_template( - username: str, - email_to: EmailStr, -): +def create_data_change_confirmation_email(username: str, email_to: EmailStr, confirmation_code: str): email = EmailMessage() - email["Subject"] = "Смена пароля к аккаунту" + email["Subject"] = "Подтверждение изменения данных" email["From"] = settings.SMTP_USER email["To"] = email_to email.set_content( f""" -

{username}, ты менял пароль?

-

Если нет, то пидора ответ

+

{username}, лови аптечку

+ {confirmation_code} """, subtype="html", ) return email -def create_password_recover_template( - username: str, - email_to: EmailStr, - confirmation_code: str, -): +def create_data_change_email(username: str, email_to: EmailStr): email = EmailMessage() - email["Subject"] = "Восстановление пароля" + email["Subject"] = "Изменение данных" email["From"] = settings.SMTP_USER email["To"] = email_to email.set_content( f""" -

{username}, ты тут хотел восстановить пароль?

-

{confirmation_code}

+

{username}, данные менял?

+ Вот то-то и оно. """, subtype="html", ) diff --git a/app/tasks/tasks.py b/app/tasks/tasks.py index 3e4ec0a..a0a025d 100644 --- a/app/tasks/tasks.py +++ b/app/tasks/tasks.py @@ -9,8 +9,8 @@ from app.config import settings from app.tasks.celery import celery from app.tasks.email_templates import ( create_registration_confirmation_template, - create_password_change_confirmation_template, - create_password_recover_template, + create_data_change_confirmation_email, + create_data_change_email, ) from app.utils.auth import encode_confirmation_token from app.users.schemas import SConfirmationData @@ -23,7 +23,7 @@ def generate_confirmation_code(length=6) -> str: @celery.task -def send_registration_confirmation_email(user_data: dict): +def send_registration_confirmation_email(user_data: dict) -> None: user_data = SConfirmationData.model_validate(user_data) invitation_token = encode_confirmation_token(user_data) @@ -39,37 +39,22 @@ def send_registration_confirmation_email(user_data: dict): @celery.task -def send_password_change_email(username: str, email_to: EmailStr, MODE: str): - if MODE == "TEST": - return True - msg_content = create_password_change_confirmation_template(username=username, email_to=email_to) +def send_data_change_confirmation_email(user_data: dict) -> None: + user_data = SConfirmationData.model_validate(user_data) + + msg_content = create_data_change_confirmation_email( + username=user_data.username, email_to=user_data.email_to, confirmation_code=user_data.confirmation_code + ) with smtplib.SMTP_SSL(settings.SMTP_HOST, settings.SMTP_PORT) as server: server.login(settings.SMTP_USER, settings.SMTP_PASS) server.send_message(msg_content) - return True - @celery.task -def send_password_recover_email(username: str, email_to: EmailStr, MODE: str): - confirmation_code = generate_confirmation_code() - if MODE == "TEST": - return confirmation_code - msg_content = create_password_recover_template(username=username, email_to=email_to, confirmation_code=confirmation_code) +def send_data_change_email(username: str, email_to: EmailStr) -> None: + msg_content = create_data_change_email(username=username, email_to=email_to) with smtplib.SMTP_SSL(settings.SMTP_HOST, settings.SMTP_PORT) as server: server.login(settings.SMTP_USER, settings.SMTP_PASS) server.send_message(msg_content) - - return confirmation_code - - -@celery.task -def send_data_change_confirmation_email(username: str, email_to: EmailStr, MODE: str): - pass - - -@celery.task -def send_data_change_email(username: str, email_to: EmailStr, MODE: str): - pass diff --git a/app/users/router.py b/app/users/router.py index c4af888..ef587a6 100644 --- a/app/users/router.py +++ b/app/users/router.py @@ -35,7 +35,8 @@ from app.users.schemas import ( from app.tasks.tasks import ( send_registration_confirmation_email, send_data_change_confirmation_email, - generate_confirmation_code + generate_confirmation_code, + send_data_change_email ) router = APIRouter(prefix="/users", tags=["Пользователи"]) @@ -152,13 +153,15 @@ async def get_user_avatars_history(user=Depends(get_current_user), uow=Depends(U ) async def send_confirmation_code(user_data: SUserSendConfirmationCode, user: SUser = Depends(get_current_user)): redis_session = get_redis_session() - if verify_password(user_data.current_password, user.hashed_password): - verification_code = send_data_change_confirmation_email.delay( - username=user.username, email_to=user_data.email, MODE=settings.MODE - ) - verification_code = verification_code.get() - await RedisService.set_verification_code(redis=redis_session, user_id=user.id, verification_code=verification_code) - raise IncorrectAuthDataException + if not verify_password(user_data.current_password, user.hashed_password): + raise IncorrectAuthDataException + + user_code = generate_confirmation_code() + user_mail_data = SConfirmationData.model_validate( + {"user_id": user.id, "username": user_data.username, "email_to": user_data.email, "confirmation_code": user_code} + ) + send_data_change_confirmation_email.delay(user_mail_data) + await RedisService.set_verification_code(redis=redis_session, user_id=user.id, verification_code=user_code) @router.post( @@ -185,6 +188,7 @@ async def change_user_data(user_data: SUserChangeData, user=Depends(get_current_ ) await uow.user.add_user_avatar(user_id=user.id, avatar=user_data.avatar_url) await uow.commit() + send_data_change_email.delay(user_data.username, user_data.email) await RedisService.delete_verification_code(redis=redis_session, user_id=user.id) diff --git a/app/utils/auth.py b/app/utils/auth.py index 4218274..06b6df9 100644 --- a/app/utils/auth.py +++ b/app/utils/auth.py @@ -59,36 +59,17 @@ def decode_confirmation_token(invitation_token: str) -> SConfirmationData: class AuthService: - @staticmethod - async def authenticate_user_by_email(uow: UnitOfWork, email: EmailStr, password: str) -> SUser | None: - try: - async with uow: - user = await uow.user.find_one(email=email) - if not verify_password(password, user.hashed_password): - return - return user - except UserNotFoundException: - return - - @staticmethod - async def authenticate_user_by_username(uow: UnitOfWork, username: str, password: str) -> SUser | None: - try: - async with uow: - user = await uow.user.find_one(username=username) - if not verify_password(password, user.hashed_password): - return - return user - except UserNotFoundException: - return @classmethod async def authenticate_user(cls, uow: UnitOfWork, email_or_username: str, password: str) -> SUser: - user = await cls.authenticate_user_by_email(uow, email_or_username, password) - if not user: - user = await cls.authenticate_user_by_username(uow, email_or_username, password) - if not user: + try: + async with uow: + user = await uow.user.find_one_by_username_or_email(username=email_or_username, email=email_or_username) + if not verify_password(password, user.hashed_password): + raise IncorrectAuthDataException + return user + except UserNotFoundException: raise IncorrectAuthDataException - return user @staticmethod async def check_verificated_user(uow: UnitOfWork, user_id: int) -> bool: