diff --git a/app/tasks/email_templates.py b/app/tasks/email_templates.py
index 8521ba7..94cb494 100644
--- a/app/tasks/email_templates.py
+++ b/app/tasks/email_templates.py
@@ -8,7 +8,7 @@ from app.config import settings
def create_registration_confirmation_template(
username: str,
email_to: EmailStr,
- confirmation_code: str,
+ confirmation_link: str,
):
email = EmailMessage()
@@ -19,7 +19,7 @@ def create_registration_confirmation_template(
email.set_content(
f"""
{username}, лови аптечку
- {confirmation_code}
+ {confirmation_link}
""",
subtype="html"
)
diff --git a/app/tasks/tasks.py b/app/tasks/tasks.py
index a3eb5c6..bf55721 100644
--- a/app/tasks/tasks.py
+++ b/app/tasks/tasks.py
@@ -1,7 +1,9 @@
+import json
import smtplib
import random
import string
+from cryptography.fernet import Fernet
from pydantic import EmailStr
@@ -19,15 +21,25 @@ def generate_confirmation_code(length=6):
@celery.task
def send_registration_confirmation_email(
+ user_id: int,
username: str,
email_to: EmailStr,
MODE: str
):
confirmation_code = generate_confirmation_code()
+
if MODE == 'TEST':
return confirmation_code
+
+ load = {"user_id": user_id, "username": username, "email": email_to, "confirmation_code": confirmation_code}
+ str_load = json.dumps(load)
+ cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
+ invitation_token = cipher_suite.encrypt(str_load.encode())
+
+ confirmation_link = settings.INVITATION_LINK_HOST + '/api/users/email_verification/' + invitation_token.decode()
+
msg_content = create_registration_confirmation_template(
- username=username, email_to=email_to, confirmation_code=confirmation_code
+ username=username, email_to=email_to, confirmation_link=confirmation_link
)
with smtplib.SMTP_SSL(settings.SMTP_HOST, settings.SMTP_PORT) as server:
diff --git a/app/users/dao.py b/app/users/dao.py
index 63de6e7..7468ebc 100644
--- a/app/users/dao.py
+++ b/app/users/dao.py
@@ -150,7 +150,7 @@ class UserCodesDAO(BaseDAO):
AND now() - usersverificationcodes.date_of_creation < INTERVAL '30 minutes'
"""
query = (select(UsersVerificationCodes.__table__.columns)
- .where((func.now() - UsersVerificationCodes.date_of_creation) < text("INTERVAL '30 minutes'"))
+ .where((func.now() - UsersVerificationCodes.date_of_creation) < text("INTERVAL '10 minutes'"))
.filter_by(**filter_by))
async with async_session_maker() as session:
diff --git a/app/users/router.py b/app/users/router.py
index 5255579..632a09b 100644
--- a/app/users/router.py
+++ b/app/users/router.py
@@ -1,3 +1,6 @@
+import json
+
+from cryptography.fernet import Fernet
from fastapi import APIRouter, Response, Depends, status, Request
from fastapi.responses import RedirectResponse
@@ -50,7 +53,10 @@ async def check_existing_email(email: SEmail):
@router.post("/register", response_model=SUserToken, status_code=status.HTTP_201_CREATED)
async def register_user(response: Response, user_data: SUserRegister):
+ if user_data.password != user_data.password2:
+ raise PasswordsMismatchException
await check_existing_user(user_data)
+
hashed_password = get_password_hash(user_data.password)
user_id = await UserDAO.add(
email=user_data.email,
@@ -60,7 +66,7 @@ async def register_user(response: Response, user_data: SUserRegister):
)
result = send_registration_confirmation_email.delay(
- username=user_data.username, email_to=user_data.email, MODE=settings.MODE
+ user_id=user_id, username=user_data.username, email_to=user_data.email, MODE=settings.MODE
)
result = result.get()
@@ -73,12 +79,16 @@ async def register_user(response: Response, user_data: SUserRegister):
return {"access_token": access_token}
-@router.post('/email_verification', status_code=status.HTTP_200_OK, response_model=SEmailVerification)
-async def email_verification(user_code: SUserCode, user: Users = Depends(get_current_user)):
+@router.get('/email_verification/{user_code}', status_code=status.HTTP_200_OK, response_model=SEmailVerification)
+async def email_verification(user_code: str):
+ invitation_token = user_code.encode()
+ cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
+ user_data = cipher_suite.decrypt(invitation_token).decode("utf-8")
+ user_data = json.loads(user_data)
user_codes = await UserCodesDAO.get_user_codes(
- user_id=user.id, description="Код подтверждения почты", code=user_code.user_code
+ user_id=user_data["user_id"], description="Код подтверждения почты", code=user_data["confirmation_code"]
)
- if not user_codes or not await UserDAO.change_data(user_id=user.id, role=VERIFICATED_USER):
+ if not user_codes or not await UserDAO.change_data(user_id=user_data["user_id"], role=VERIFICATED_USER):
raise WrongCodeException
return {'email_verification': True}
@@ -127,12 +137,12 @@ async def change_avatar(user_data: SUserAvatar, current_user: Users = Depends(ge
@router.patch("/change_password", status_code=status.HTTP_200_OK)
async def change_password(new_password: SUserPassword, current_user: Users = Depends(get_current_user)):
+ if new_password.new_password != new_password.new_password2:
+ raise PasswordsMismatchException
await check_verificated_user_with_exc(user_id=current_user.id)
existing_user = await UserDAO.find_one_or_none(id=current_user.id)
if not verify_password(new_password.current_password, existing_user.hashed_password):
raise IncorrectPasswordException
- if new_password.new_password != new_password.new_password2:
- raise PasswordsMismatchException
hashed_password = get_password_hash(new_password.new_password)
await UserDAO.change_data(current_user.id, hashed_password=hashed_password)
send_password_change_email.delay(current_user.username, current_user.email, MODE=settings.MODE)
diff --git a/app/users/schemas.py b/app/users/schemas.py
index 787d018..91d6c10 100644
--- a/app/users/schemas.py
+++ b/app/users/schemas.py
@@ -15,6 +15,7 @@ class SUserRegister(BaseModel):
email: EmailStr
username: str = Query(None, min_length=2, max_length=30)
password: str = Query(None, min_length=8)
+ password2: str = Query(None, min_length=8)
date_of_birth: date
@field_validator("date_of_birth")