- Added RoleRepository and UserRepository for managing roles and users. - Implemented methods for creating, retrieving, and assigning roles to users. - Introduced functions to ensure default roles and an admin user exist in the system. - Updated UnitOfWork to include user and role repositories. - Created new security module for password hashing and JWT token management. - Added tests for authentication flows, including registration, login, and password reset. - Enhanced HTML templates for user registration, login, and password management with error handling. - Added a logo image to the static assets.
68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator
|
|
|
|
|
|
class FormModel(BaseModel):
|
|
"""Base Pydantic model for HTML form submissions."""
|
|
|
|
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
|
|
|
|
class RegistrationForm(FormModel):
|
|
username: str = Field(min_length=3, max_length=128)
|
|
email: str = Field(min_length=5, max_length=255)
|
|
password: str = Field(min_length=8, max_length=256)
|
|
confirm_password: str
|
|
|
|
@field_validator("email")
|
|
@classmethod
|
|
def validate_email(cls, value: str) -> str:
|
|
if "@" not in value or value.startswith("@") or value.endswith("@"):
|
|
raise ValueError("Invalid email address.")
|
|
local, domain = value.split("@", 1)
|
|
if not local or "." not in domain:
|
|
raise ValueError("Invalid email address.")
|
|
return value.lower()
|
|
|
|
@field_validator("confirm_password")
|
|
@classmethod
|
|
def passwords_match(cls, value: str, info: ValidationInfo) -> str:
|
|
password = info.data.get("password")
|
|
if password != value:
|
|
raise ValueError("Passwords do not match.")
|
|
return value
|
|
|
|
|
|
class LoginForm(FormModel):
|
|
username: str = Field(min_length=1, max_length=255)
|
|
password: str = Field(min_length=1, max_length=256)
|
|
|
|
|
|
class PasswordResetRequestForm(FormModel):
|
|
email: str = Field(min_length=5, max_length=255)
|
|
|
|
@field_validator("email")
|
|
@classmethod
|
|
def validate_email(cls, value: str) -> str:
|
|
if "@" not in value or value.startswith("@") or value.endswith("@"):
|
|
raise ValueError("Invalid email address.")
|
|
local, domain = value.split("@", 1)
|
|
if not local or "." not in domain:
|
|
raise ValueError("Invalid email address.")
|
|
return value.lower()
|
|
|
|
|
|
class PasswordResetForm(FormModel):
|
|
token: str = Field(min_length=1)
|
|
password: str = Field(min_length=8, max_length=256)
|
|
confirm_password: str
|
|
|
|
@field_validator("confirm_password")
|
|
@classmethod
|
|
def reset_passwords_match(cls, value: str, info: ValidationInfo) -> str:
|
|
password = info.data.get("password")
|
|
if password != value:
|
|
raise ValueError("Passwords do not match.")
|
|
return value
|