refactor: introduce pydantic SecretStr to protect sensitive information like user password

This commit is contained in:
Jakub Miazek 2024-03-29 14:12:32 +01:00
parent 37c5bb316f
commit 68e73d60ec
2 changed files with 13 additions and 11 deletions

View File

@ -3,6 +3,7 @@ from typing import Any
import bcrypt import bcrypt
from passlib.context import CryptContext from passlib.context import CryptContext
from pydantic import SecretStr
from sqlalchemy import String, LargeBinary, select from sqlalchemy import String, LargeBinary, select
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@ -25,11 +26,12 @@ class User(Base):
return self._password.decode("utf-8") return self._password.decode("utf-8")
@password.setter @password.setter
def password(self, password: str): def password(self, password: SecretStr):
self._password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()) _password_string = password.get_secret_value()
self._password = bcrypt.hashpw(_password_string.encode("utf-8"), bcrypt.gensalt())
def check_password(self, password: str): def check_password(self, password: SecretStr):
return pwd_context.verify(password, self.password) return pwd_context.verify(password.get_secret_value(), self.password)
@classmethod @classmethod
async def find(cls, database_session: AsyncSession, where_conditions: list[Any]): async def find(cls, database_session: AsyncSession, where_conditions: list[Any]):

View File

@ -1,6 +1,6 @@
from uuid import UUID from uuid import UUID
from pydantic import BaseModel, Field, EmailStr, ConfigDict from pydantic import BaseModel, Field, EmailStr, ConfigDict, SecretStr
config = ConfigDict(from_attributes=True) config = ConfigDict(from_attributes=True)
@ -8,10 +8,10 @@ config = ConfigDict(from_attributes=True)
# TODO: add pydantic field validator for strong password # TODO: add pydantic field validator for strong password
class UserSchema(BaseModel): class UserSchema(BaseModel):
model_config = config model_config = config
email: EmailStr = Field(title="Users email", description="Users email") email: EmailStr = Field(title="Users email", description="Users email", examples=["john@domain.com"])
first_name: str = Field(title="Users first name", description="Users first name") first_name: str = Field(title="Users first name", description="Users first name", examples=["John"])
last_name: str = Field(title="Users last name", description="Users last name") last_name: str = Field(title="Users last name", description="Users last name", examples=["Doe"])
password: str = Field(title="Users password", description="Users password") password: SecretStr = Field(title="Users password", description="Users password", examples=["@SuperSecret123"])
class UserResponse(BaseModel): class UserResponse(BaseModel):
@ -29,5 +29,5 @@ class TokenResponse(BaseModel):
class UserLogin(BaseModel): class UserLogin(BaseModel):
model_config = config model_config = config
email: EmailStr = Field(title="Users email", description="Users email") email: EmailStr = Field(title="Users email", description="Users email", examples=["john@domain.com"])
password: str = Field(title="Users password", description="Users password") password: SecretStr = Field(title="Users password", description="Users password", examples=["@SuperSecret123"])