# ================================================================ # services/auth/app/schemas/auth.py - COMPLETE SCHEMAS # ================================================================ """ Pydantic schemas for authentication service """ from pydantic import BaseModel, EmailStr, validator from typing import Optional, List, Dict, Any from datetime import datetime import re # ================================================================ # REQUEST SCHEMAS # ================================================================ class UserRegistration(BaseModel): """User registration schema""" email: EmailStr password: str full_name: str @validator('password') def validate_password(cls, v): if len(v) < 8: 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 @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 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" 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 created_at: datetime 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 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] = {} class Config: from_attributes = True # ================================================================ # UPDATE SCHEMAS # ================================================================ class UserProfileUpdate(BaseModel): """User profile update schema""" full_name: Optional[str] = None preferences: Optional[Dict[str, Any]] = None @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 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""" user_id: str 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