Coverage for src/lexigram/admin/auth/types.py: 100%

105 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-24 23:39 +0800

1"""Admin authentication domain types and enumerations.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass, field 

6from datetime import datetime 

7from enum import Enum 

8 

9 

10class AdminSecurityEventType(str, Enum): 

11 """Security audit event types tracked for admin authentication.""" 

12 

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" 

49 

50 

51class AdminLockoutStatus(str, Enum): 

52 """Account lockout status levels.""" 

53 

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 

58 

59 

60class AdminPasswordRule(str, Enum): 

61 """Password policy rules that can be violated.""" 

62 

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" 

71 

72 

73@dataclass(frozen=True) 

74class AdminAuthResult: 

75 """Result of a successful authentication. 

76 

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 """ 

87 

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 

95 

96 

97@dataclass(frozen=True) 

98class AdminLockoutInfo: 

99 """Information about an account's lockout state. 

100 

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 """ 

108 

109 status: AdminLockoutStatus 

110 consecutive_failures: int 

111 locked_at: datetime | None = None 

112 unlock_at: datetime | None = None 

113 is_permanent: bool = False 

114 

115 

116@dataclass(frozen=True) 

117class AdminPasswordViolation: 

118 """A single password policy violation. 

119 

120 Attributes: 

121 rule: The rule that was violated. 

122 message: Human-readable description of the violation. 

123 """ 

124 

125 rule: AdminPasswordRule 

126 message: str 

127 

128 

129@dataclass(frozen=True) 

130class AdminPasswordValidationResult: 

131 """Result of password policy validation. 

132 

133 Attributes: 

134 is_valid: Whether the password passes all policy rules. 

135 violations: List of all violated rules (empty when valid). 

136 """ 

137 

138 is_valid: bool 

139 violations: list[AdminPasswordViolation] = field(default_factory=list) 

140 

141 

142@dataclass(frozen=True) 

143class AdminLoginAttempt: 

144 """Record of a single login attempt. 

145 

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 """ 

155 

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 

163 

164 

165@dataclass(frozen=True) 

166class AdminSecurityEvent: 

167 """A security audit event record. 

168 

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 """ 

179 

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 

188 

189 

190@dataclass(frozen=True) 

191class AdminPasswordResetToken: 

192 """Persisted password reset token record (sha256 of the raw token). 

193 

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 """ 

201 

202 email: str 

203 token_hash: str 

204 expires_at: datetime 

205 consumed_at: datetime | None = None 

206 

207 

208__all__ = [ 

209 "AdminAuthResult", 

210 "AdminLockoutInfo", 

211 "AdminLockoutStatus", 

212 "AdminLoginAttempt", 

213 "AdminPasswordResetToken", 

214 "AdminPasswordRule", 

215 "AdminPasswordValidationResult", 

216 "AdminPasswordViolation", 

217 "AdminSecurityEvent", 

218 "AdminSecurityEventType", 

219]