Files
bakery-ia/services/auth/app/schemas/auth.py

196 lines
6.0 KiB
Python
Raw Normal View History

2025-07-20 08:33:23 +02:00
# ================================================================
# services/auth/app/schemas/auth.py - COMPLETE SCHEMAS
# ================================================================
"""
2025-07-20 08:33:23 +02:00
Pydantic schemas for authentication service
"""
2025-07-20 08:33:23 +02:00
from pydantic import BaseModel, EmailStr, validator
from typing import Optional, List, Dict, Any
from datetime import datetime
2025-07-20 08:33:23 +02:00
import re
# ================================================================
# REQUEST SCHEMAS
# ================================================================
class UserRegistration(BaseModel):
"""User registration schema"""
email: EmailStr
2025-07-20 08:33:23 +02:00
password: str
full_name: str
@validator('password')
def validate_password(cls, v):
2025-07-19 17:49:03 +02:00
if len(v) < 8:
2025-07-20 08:33:23 +02:00
raise ValueError('Password must be at least 8 characters long')
if not re.search(r'[A-Z]', v):
raise ValueError('Password must contain at least one uppercase letter')
if not re.search(r'[a-z]', v):
raise ValueError('Password must contain at least one lowercase letter')
if not re.search(r'\d', v):
raise ValueError('Password must contain at least one number')
return v
2025-07-20 08:33:23 +02:00
@validator('full_name')
def validate_full_name(cls, v):
if len(v.strip()) < 2:
raise ValueError('Full name must be at least 2 characters long')
return v.strip()
class UserLogin(BaseModel):
"""User login schema"""
email: EmailStr
password: str
2025-07-20 08:33:23 +02:00
class RefreshTokenRequest(BaseModel):
"""Refresh token request schema"""
refresh_token: str
class PasswordChange(BaseModel):
"""Password change schema"""
current_password: str
new_password: str
@validator('new_password')
def validate_new_password(cls, v):
if len(v) < 8:
raise ValueError('New password must be at least 8 characters long')
if not re.search(r'[A-Z]', v):
raise ValueError('New password must contain at least one uppercase letter')
if not re.search(r'[a-z]', v):
raise ValueError('New password must contain at least one lowercase letter')
if not re.search(r'\d', v):
raise ValueError('New password must contain at least one number')
return v
class PasswordReset(BaseModel):
"""Password reset request schema"""
email: EmailStr
class PasswordResetConfirm(BaseModel):
"""Password reset confirmation schema"""
token: str
new_password: str
@validator('new_password')
def validate_new_password(cls, v):
if len(v) < 8:
raise ValueError('New password must be at least 8 characters long')
if not re.search(r'[A-Z]', v):
raise ValueError('New password must contain at least one uppercase letter')
if not re.search(r'[a-z]', v):
raise ValueError('New password must contain at least one lowercase letter')
if not re.search(r'\d', v):
raise ValueError('New password must contain at least one number')
return v
# ================================================================
# RESPONSE SCHEMAS
# ================================================================
class TenantMembership(BaseModel):
"""Tenant membership information"""
tenant_id: str
tenant_name: str
role: str
is_active: bool
class TokenResponse(BaseModel):
"""Token response schema"""
access_token: str
refresh_token: str
token_type: str = "bearer"
2025-07-20 08:33:23 +02:00
expires_in: int = 1800 # 30 minutes
user: Dict[str, Any]
tenants: List[TenantMembership] = []
class UserResponse(BaseModel):
"""User response schema"""
id: str
email: str
full_name: str
is_active: bool
is_verified: bool
2025-07-19 17:49:03 +02:00
created_at: datetime
2025-07-20 08:33:23 +02:00
last_login: Optional[datetime] = None
class Config:
from_attributes = True
class TokenVerification(BaseModel):
"""Token verification response"""
valid: bool
user_id: Optional[str] = None
email: Optional[str] = None
full_name: Optional[str] = None
tenants: List[Dict[str, Any]] = []
expires_at: Optional[datetime] = None
2025-07-20 08:33:23 +02:00
class UserProfile(BaseModel):
"""Extended user profile"""
id: str
email: str
full_name: str
is_active: bool
is_verified: bool
created_at: datetime
last_login: Optional[datetime] = None
tenants: List[TenantMembership] = []
preferences: Dict[str, Any] = {}
2025-07-19 17:49:03 +02:00
class Config:
from_attributes = True
2025-07-20 08:33:23 +02:00
# ================================================================
# UPDATE SCHEMAS
# ================================================================
class UserProfileUpdate(BaseModel):
"""User profile update schema"""
full_name: Optional[str] = None
preferences: Optional[Dict[str, Any]] = None
2025-07-20 08:33:23 +02:00
@validator('full_name')
def validate_full_name(cls, v):
if v is not None and len(v.strip()) < 2:
raise ValueError('Full name must be at least 2 characters long')
return v.strip() if v else v
# ================================================================
# ERROR SCHEMAS
# ================================================================
class ErrorResponse(BaseModel):
"""Error response schema"""
error: str
detail: str
status_code: int
2025-07-20 08:33:23 +02:00
class ValidationErrorResponse(BaseModel):
"""Validation error response schema"""
error: str = "validation_error"
detail: str
status_code: int = 422
errors: List[Dict[str, Any]] = []
# ================================================================
# INTERNAL SCHEMAS (for service-to-service communication)
# ================================================================
class UserServiceRequest(BaseModel):
"""Internal user service request"""
2025-07-19 17:49:03 +02:00
user_id: str
2025-07-20 08:33:23 +02:00
action: str
data: Optional[Dict[str, Any]] = None
class TenantAccessRequest(BaseModel):
"""Tenant access verification request"""
user_id: str
tenant_id: str
class TenantAccessResponse(BaseModel):
"""Tenant access verification response"""
has_access: bool
role: Optional[str] = None
tenant_name: Optional[str] = None