Eliminated the `RevokedToken` model and associated logic for managing token revocation. Removed unused files, related tests, and outdated dependencies in authentication modules. Simplified token decoding, user validation, and dependency injection by streamlining the flow and enhancing maintainability.
126 lines
3.4 KiB
Python
126 lines
3.4 KiB
Python
# app/schemas/users.py
|
|
import re
|
|
from datetime import datetime
|
|
from typing import Optional, Dict, Any
|
|
from uuid import UUID
|
|
|
|
import pydantic
|
|
from pydantic import BaseModel, EmailStr, field_validator, ConfigDict
|
|
|
|
|
|
class UserBase(BaseModel):
|
|
email: EmailStr
|
|
first_name: str
|
|
last_name: str
|
|
phone_number: Optional[str] = None
|
|
|
|
@field_validator('phone_number')
|
|
@classmethod
|
|
def validate_phone_number(cls, v: Optional[str]) -> Optional[str]:
|
|
if v is None:
|
|
return v
|
|
# Simple regex for phone validation
|
|
if not re.match(r'^\+?[0-9\s\-\(\)]{8,20}$', v):
|
|
raise ValueError('Invalid phone number format')
|
|
return v
|
|
|
|
|
|
class UserCreate(UserBase):
|
|
password: str
|
|
|
|
@field_validator('password')
|
|
@classmethod
|
|
def password_strength(cls, v: str) -> str:
|
|
"""Basic password strength validation"""
|
|
if len(v) < 8:
|
|
raise ValueError('Password must be at least 8 characters')
|
|
if not any(char.isdigit() for char in v):
|
|
raise ValueError('Password must contain at least one digit')
|
|
if not any(char.isupper() for char in v):
|
|
raise ValueError('Password must contain at least one uppercase letter')
|
|
return v
|
|
|
|
|
|
class UserUpdate(BaseModel):
|
|
first_name: Optional[str] = None
|
|
last_name: Optional[str] = None
|
|
phone_number: Optional[str] = None
|
|
preferences: Optional[Dict[str, Any]] = None
|
|
|
|
@field_validator('phone_number')
|
|
@classmethod
|
|
def validate_phone_number(cls, v: Optional[str]) -> Optional[str]:
|
|
if v is None:
|
|
return v
|
|
# Simple regex for phone validation
|
|
if not re.match(r'^\+?[0-9\s\-\(\)]{8,20}$', v):
|
|
raise ValueError('Invalid phone number format')
|
|
return v
|
|
|
|
|
|
class UserInDB(UserBase):
|
|
id: UUID
|
|
is_active: bool
|
|
is_superuser: bool
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class UserResponse(UserBase):
|
|
id: UUID
|
|
is_active: bool
|
|
is_superuser: bool
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class Token(BaseModel):
|
|
access_token: str
|
|
refresh_token: Optional[str] = None
|
|
token_type: str = "bearer"
|
|
|
|
|
|
class TokenPayload(BaseModel):
|
|
sub: str # User ID
|
|
exp: int # Expiration time
|
|
iat: Optional[int] = None # Issued at
|
|
jti: Optional[str] = None # JWT ID
|
|
is_superuser: Optional[bool] = False
|
|
first_name: Optional[str] = None
|
|
email: Optional[str] = None
|
|
type: Optional[str] = None # Token type (access/refresh)
|
|
|
|
|
|
class TokenData(BaseModel):
|
|
user_id: UUID
|
|
is_superuser: bool = False
|
|
|
|
|
|
class PasswordReset(BaseModel):
|
|
token: str
|
|
new_password: str
|
|
|
|
@field_validator('new_password')
|
|
@classmethod
|
|
def password_strength(cls, v: str) -> str:
|
|
"""Basic password strength validation"""
|
|
if len(v) < 8:
|
|
raise ValueError('Password must be at least 8 characters')
|
|
if not any(char.isdigit() for char in v):
|
|
raise ValueError('Password must contain at least one digit')
|
|
if not any(char.isupper() for char in v):
|
|
raise ValueError('Password must contain at least one uppercase letter')
|
|
return v
|
|
|
|
|
|
class LoginRequest(BaseModel):
|
|
email: EmailStr
|
|
password: str
|
|
|
|
|
|
class RefreshTokenRequest(BaseModel):
|
|
refresh_token: str |