Files
secure-online-shop/app/schemas/auth.py
T

86 lines
2.0 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Annotated, Literal
from pydantic import BaseModel, ConfigDict, StrictStr, StringConstraints, field_validator
from app.models.user import RoleEnum
UsernameField = Annotated[
StrictStr,
StringConstraints(
strip_whitespace=True,
min_length=3,
max_length=50,
),
]
PasswordField = Annotated[
StrictStr,
StringConstraints(
min_length=8,
max_length=72,
),
]
StrongPasswordField = Annotated[
StrictStr,
StringConstraints(
min_length=12,
max_length=72,
),
]
class UserCreate(BaseModel):
model_config = ConfigDict(extra="forbid")
username: UsernameField
password: StrongPasswordField
@field_validator("username")
@classmethod
def normalize_username(cls, value: str) -> str:
return value.lower()
@field_validator("password")
@classmethod
def validate_password_strength(cls, value: str) -> str:
has_upper = any(char.isupper() for char in value)
has_lower = any(char.islower() for char in value)
has_digit = any(char.isdigit() for char in value)
has_special = any(not char.isalnum() for char in value)
if not all((has_upper, has_lower, has_digit, has_special)):
raise ValueError(
"Password must include upper, lower, digit, and special characters"
)
return value
class LoginRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
username: UsernameField
password: PasswordField
@field_validator("username")
@classmethod
def normalize_username(cls, value: str) -> str:
return value.lower()
class UserResponse(BaseModel):
model_config = ConfigDict(from_attributes=True, extra="forbid")
id: int
username: str
role: RoleEnum
is_active: bool
created_at: datetime
class TokenResponse(BaseModel):
model_config = ConfigDict(extra="forbid")
access_token: str
token_type: Literal["bearer"] = "bearer"