From 68e73d60ec88675c2432600151a19e9373bff775 Mon Sep 17 00:00:00 2001 From: Jakub Miazek Date: Fri, 29 Mar 2024 14:12:32 +0100 Subject: [PATCH] refactor: introduce pydantic SecretStr to protect sensitive information like user password --- app/models/user.py | 10 ++++++---- app/schemas/user.py | 14 +++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/models/user.py b/app/models/user.py index 34e213a..34bab01 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -3,6 +3,7 @@ from typing import Any import bcrypt from passlib.context import CryptContext +from pydantic import SecretStr from sqlalchemy import String, LargeBinary, select from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.ext.asyncio import AsyncSession @@ -25,11 +26,12 @@ class User(Base): return self._password.decode("utf-8") @password.setter - def password(self, password: str): - self._password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()) + def password(self, password: SecretStr): + _password_string = password.get_secret_value() + self._password = bcrypt.hashpw(_password_string.encode("utf-8"), bcrypt.gensalt()) - def check_password(self, password: str): - return pwd_context.verify(password, self.password) + def check_password(self, password: SecretStr): + return pwd_context.verify(password.get_secret_value(), self.password) @classmethod async def find(cls, database_session: AsyncSession, where_conditions: list[Any]): diff --git a/app/schemas/user.py b/app/schemas/user.py index 0d5ed7b..d7a8dd3 100644 --- a/app/schemas/user.py +++ b/app/schemas/user.py @@ -1,6 +1,6 @@ from uuid import UUID -from pydantic import BaseModel, Field, EmailStr, ConfigDict +from pydantic import BaseModel, Field, EmailStr, ConfigDict, SecretStr config = ConfigDict(from_attributes=True) @@ -8,10 +8,10 @@ config = ConfigDict(from_attributes=True) # TODO: add pydantic field validator for strong password class UserSchema(BaseModel): model_config = config - email: EmailStr = Field(title="User’s email", description="User’s email") - first_name: str = Field(title="User’s first name", description="User’s first name") - last_name: str = Field(title="User’s last name", description="User’s last name") - password: str = Field(title="User’s password", description="User’s password") + email: EmailStr = Field(title="User’s email", description="User’s email", examples=["john@domain.com"]) + first_name: str = Field(title="User’s first name", description="User’s first name", examples=["John"]) + last_name: str = Field(title="User’s last name", description="User’s last name", examples=["Doe"]) + password: SecretStr = Field(title="User’s password", description="User’s password", examples=["@SuperSecret123"]) class UserResponse(BaseModel): @@ -29,5 +29,5 @@ class TokenResponse(BaseModel): class UserLogin(BaseModel): model_config = config - email: EmailStr = Field(title="User’s email", description="User’s email") - password: str = Field(title="User’s password", description="User’s password") + email: EmailStr = Field(title="User’s email", description="User’s email", examples=["john@domain.com"]) + password: SecretStr = Field(title="User’s password", description="User’s password", examples=["@SuperSecret123"])