""" System User model for authentication and authorization. Represents a system user with login credentials and permissions. """ from datetime import datetime from typing import Optional, Dict, Any, List from uuid import UUID from pydantic import BaseModel, Field, EmailStr, model_validator from enum import Enum class UserStatus(str, Enum): """User account status options.""" ACTIVE = "active" INACTIVE = "inactive" SUSPENDED = "suspended" LOCKED = "locked" PENDING_ACTIVATION = "pending_activation" class UserRole(str, Enum): """System user roles.""" SUPER_ADMIN = "super_admin" ADMIN = "admin" MANAGER = "manager" USER = "user" READ_ONLY = "read_only" class LoginAttemptModel(BaseModel): """Login attempt tracking.""" timestamp: datetime = Field(default_factory=datetime.utcnow) ip_address: Optional[str] = Field(None, description="IP address of login attempt") user_agent: Optional[str] = Field(None, description="User agent string") success: bool = Field(..., description="Whether login was successful") failure_reason: Optional[str] = Field(None, description="Reason for failure if unsuccessful") class SecuritySettingsModel(BaseModel): """User security settings.""" require_password_change: bool = Field(default=False, description="Force password change on next login") password_expires_at: Optional[datetime] = Field(None, description="Password expiry date") failed_login_attempts: int = Field(default=0, description="Count of consecutive failed login attempts") last_failed_login: Optional[datetime] = Field(None, description="Timestamp of last failed login") account_locked_until: Optional[datetime] = Field(None, description="Account lock expiry time") last_password_change: Optional[datetime] = Field(None, description="Last password change timestamp") login_attempts: List[LoginAttemptModel] = Field(default_factory=list, description="Recent login attempts (last 10)") password_reset_token: Optional[str] = Field(None, description="Password reset token (stored securely)") password_reset_token_created_at: Optional[datetime] = Field(None, description="When reset token was created") forgot_pwd_otp: Optional[str] = Field(None, description="Temporary OTP for forgot-password flow") forgot_pwd_otp_expires_at: Optional[datetime] = Field(None, description="Expiry of forgot-password OTP") forgot_pwd_otp_attempts: int = Field(default=0, description="Failed OTP verification attempts") class SystemUserModel(BaseModel): """ System User data model for authentication and authorization. Represents the complete user document in MongoDB. """ user_id: UUID = Field(..., description="Unique user identifier (UUID/ULID)") username: str = Field(..., description="Unique username (lowercase alphanumeric)") email: EmailStr = Field(..., description="User email address") merchant_id: str = Field(..., description="User Merchant Information") merchant_type: Optional[str] = Field(None, description="Merchant type (ncnf, cnf, distributor, retail)") # Authentication password_hash: str = Field(..., description="Bcrypt hashed password") # Personal information first_name: Optional[str] = Field(None, description="User first name") last_name: Optional[str] = Field(None, description="User last name") full_name: Optional[str] = Field(None, description="User full name") phone: Optional[str] = Field(None, description="User phone number (E.164 format)") # Authorization role_id: str = Field(default="user", description="User role identifier") permissions: Dict[str, List[str]] = Field(default_factory=dict, description="Grouped permissions by module") # Status and security status: UserStatus = Field(default=UserStatus.PENDING_ACTIVATION, description="Account status") security_settings: SecuritySettingsModel = Field( default_factory=SecuritySettingsModel, description="Security and login settings" ) # Session management last_login_at: Optional[datetime] = Field(None, description="Last successful login timestamp") last_login_ip: Optional[str] = Field(None, description="IP address of last login") current_session_token: Optional[str] = Field(None, description="Current JWT token hash for session management") # Profile information profile_picture_url: Optional[str] = Field(None, description="URL to profile picture") timezone: str = Field(default="UTC", description="User timezone") language: str = Field(default="en", description="Preferred language code") # Audit fields created_by: str = Field(..., description="User ID who created this user account") created_at: datetime = Field(default_factory=datetime.utcnow, description="Account creation timestamp") updated_at: Optional[datetime] = Field(None, description="Last update timestamp") updated_by: Optional[str] = Field(None, description="User ID who last updated this record") # Additional metadata metadata: Optional[Dict[str, Any]] = Field(None, description="Additional metadata (employee_user_id, employee_code, designation, etc.)") class Config: populate_by_name = True json_schema_extra = { "example": { "user_id": "usr_01HZQX5K3N2P8R6T4V9W", "username": "john.doe", "email": "john.doe@company.com", "merchant_id": "mch_retail_001", "merchant_type": "retail", "password_hash": "$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LeVMstdMT6jmDQrji", "first_name": "John", "last_name": "Doe", "phone": "+919876543210", "role_id": "admin", "permissions": { "customers": ["view", "create", "update"], "orders": ["view", "create", "update"], "settings": ["view", "update"] }, "status": "active", "security_settings": { "require_password_change": False, "failed_login_attempts": 0, "login_attempts": [] }, "last_login_at": "2024-11-30T10:30:00Z", "last_login_ip": "192.168.1.100", "timezone": "Asia/Kolkata", "language": "en", "created_by": "system", "created_at": "2024-01-15T08:00:00Z" } }