Coverage for src/lexigram/admin/auth/types.py: 100%
105 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 14:26 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 14:26 +0800
1"""Admin authentication domain types and enumerations."""
3from __future__ import annotations
5from dataclasses import dataclass, field
6from datetime import datetime
7from enum import Enum
10class AdminSecurityEventType(str, Enum):
11 """Security audit event types tracked for admin authentication."""
13 LOGIN_SUCCESS = "login_success"
14 LOGIN_FAILURE = "login_failure"
15 LOGIN_BLOCKED_IP = "login_blocked_ip"
16 LOGIN_BLOCKED_LOCKOUT = "login_blocked_lockout"
17 LOGOUT = "logout"
18 SESSION_CREATED = "session_created"
19 SESSION_EXPIRED = "session_expired"
20 SESSION_REVOKED = "session_revoked"
21 SESSION_ROTATED = "session_rotated"
22 ACCOUNT_LOCKED = "account_locked"
23 ACCOUNT_UNLOCKED = "account_unlocked"
24 PASSWORD_CHANGED = "password_changed" # noqa: S105 # event type name, not a credential
25 PASSWORD_RESET_REQUESTED = "password_reset_requested" # noqa: S105 # event type name, not a credential
26 SETUP_COMPLETED = "setup_completed"
27 SETUP_BLOCKED = "setup_blocked"
28 SETUP_TOKEN_USED = "setup_token_used" # noqa: S105 # event type name, not a credential
29 CSRF_VIOLATION = "csrf_violation"
30 PERMISSION_DENIED = "permission_denied"
31 SUSPICIOUS_ACTIVITY = "suspicious_activity"
32 ADMIN_UNLOCK = "admin_unlock"
33 SETTINGS_UPDATED = "settings_updated"
34 MFA_CHALLENGE_ISSUED = "mfa_challenge_issued"
35 MFA_CHALLENGE_FAILED = "mfa_challenge_failed"
36 MFA_VERIFIED = "mfa_verified"
37 MFA_ENABLED = "mfa_enabled"
38 MFA_DISABLED = "mfa_disabled"
39 EMAIL_VERIFICATION_SENT = "email_verification_sent"
40 EMAIL_VERIFIED = "email_verified"
41 EMAIL_VERIFICATION_FAILED = "email_verification_failed"
42 EMAIL_OTP_SENT = "email_otp_sent"
43 EMAIL_OTP_FAILED = "email_otp_failed"
44 ROLE_CREATED = "role_created"
45 ROLE_UPDATED = "role_updated"
46 ROLE_DELETED = "role_deleted"
47 USER_REGISTERED = "user_registered"
48 TENANT_SWITCHED = "tenant_switched"
51class AdminLockoutStatus(str, Enum):
52 """Account lockout status levels."""
54 NONE = "none"
55 SOFT = "soft" # Delay added but not blocked
56 LOCKED = "locked" # Temporarily locked, auto-unlocks
57 PERMANENT = "permanent" # Requires manual admin unlock
60class AdminPasswordRule(str, Enum):
61 """Password policy rules that can be violated."""
63 TOO_SHORT = "too_short"
64 TOO_LONG = "too_long"
65 MISSING_UPPERCASE = "missing_uppercase"
66 MISSING_LOWERCASE = "missing_lowercase"
67 MISSING_DIGIT = "missing_digit"
68 MISSING_SPECIAL = "missing_special"
69 COMMON_PASSWORD = "common_password" # noqa: S105 # policy rule name, not a credential
70 CONTAINS_EMAIL = "contains_email"
73@dataclass(frozen=True)
74class AdminAuthResult:
75 """Result of a successful authentication.
77 Attributes:
78 session_id: Newly created session identifier.
79 user_id: Authenticated admin user's UUID.
80 email: Authenticated admin user's email.
81 roles: List of role names assigned to the user.
82 expires_at: Absolute session expiry timestamp.
83 mfa_required: True when the user must complete a 2FA challenge.
84 email_verification_required: True when login is blocked until the
85 email is verified.
86 """
88 session_id: str
89 user_id: str
90 email: str
91 roles: list[str]
92 expires_at: datetime
93 mfa_required: bool = False
94 email_verification_required: bool = False
97@dataclass(frozen=True)
98class AdminLockoutInfo:
99 """Information about an account's lockout state.
101 Attributes:
102 status: Current lockout status.
103 consecutive_failures: Total consecutive failed attempts.
104 locked_at: When the lockout was applied (None if not locked).
105 unlock_at: When the lockout will auto-expire (None if permanent/not locked).
106 is_permanent: Whether the lockout requires manual admin intervention.
107 """
109 status: AdminLockoutStatus
110 consecutive_failures: int
111 locked_at: datetime | None = None
112 unlock_at: datetime | None = None
113 is_permanent: bool = False
116@dataclass(frozen=True)
117class AdminPasswordViolation:
118 """A single password policy violation.
120 Attributes:
121 rule: The rule that was violated.
122 message: Human-readable description of the violation.
123 """
125 rule: AdminPasswordRule
126 message: str
129@dataclass(frozen=True)
130class AdminPasswordValidationResult:
131 """Result of password policy validation.
133 Attributes:
134 is_valid: Whether the password passes all policy rules.
135 violations: List of all violated rules (empty when valid).
136 """
138 is_valid: bool
139 violations: list[AdminPasswordViolation] = field(default_factory=list)
142@dataclass(frozen=True)
143class AdminLoginAttempt:
144 """Record of a single login attempt.
146 Attributes:
147 id: Unique UUID for this attempt record.
148 email: Email address used in the attempt.
149 ip_address: Client IP address.
150 user_agent: Client user agent string.
151 success: Whether the attempt succeeded.
152 failure_reason: Short failure code (None on success).
153 attempted_at: When the attempt occurred.
154 """
156 id: str
157 email: str
158 ip_address: str
159 user_agent: str
160 success: bool
161 failure_reason: str | None
162 attempted_at: datetime
165@dataclass(frozen=True)
166class AdminSecurityEvent:
167 """A security audit event record.
169 Attributes:
170 id: Unique UUID for this event.
171 event_type: Type of security event.
172 admin_user_id: Associated admin user (None for pre-auth events).
173 ip_address: Client IP address.
174 user_agent: Client user agent string.
175 success: Whether the operation succeeded.
176 metadata: Structured additional context.
177 created_at: When the event occurred.
178 """
180 id: str
181 event_type: AdminSecurityEventType
182 admin_user_id: str | None
183 ip_address: str
184 user_agent: str
185 success: bool
186 metadata: dict[str, str | int | bool | None]
187 created_at: datetime
190@dataclass(frozen=True)
191class AdminPasswordResetToken:
192 """Persisted password reset token record (sha256 of the raw token).
194 Attributes:
195 email: Email the reset token was issued for.
196 token_hash: sha256 hex digest of the raw token — the raw token
197 itself is never persisted.
198 expires_at: UTC expiry timestamp.
199 consumed_at: UTC consumption timestamp; ``None`` while unused.
200 """
202 email: str
203 token_hash: str
204 expires_at: datetime
205 consumed_at: datetime | None = None
208__all__ = [
209 "AdminAuthResult",
210 "AdminLockoutInfo",
211 "AdminLockoutStatus",
212 "AdminLoginAttempt",
213 "AdminPasswordResetToken",
214 "AdminPasswordRule",
215 "AdminPasswordValidationResult",
216 "AdminPasswordViolation",
217 "AdminSecurityEvent",
218 "AdminSecurityEventType",
219]