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: