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

82 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-13 22:14 +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" 

25 PASSWORD_RESET_REQUESTED = "password_reset_requested" 

26 SETUP_COMPLETED = "setup_completed" 

27 SETUP_BLOCKED = "setup_blocked" 

28 SETUP_TOKEN_USED = "setup_token_used" 

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 

35 

36class AdminLockoutStatus(str, Enum): 

37 """Account lockout status levels.""" 

38 

39 NONE = "none" 

40 SOFT = "soft" # Delay added but not blocked 

41 LOCKED = "locked" # Temporarily locked, auto-unlocks 

42 PERMANENT = "permanent" # Requires manual admin unlock 

43 

44 

45class AdminPasswordRule(str, Enum): 

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

47 

48 TOO_SHORT = "too_short" 

49 TOO_LONG = "too_long" 

50 MISSING_UPPERCASE = "missing_uppercase" 

51 MISSING_LOWERCASE = "missing_lowercase" 

52 MISSING_DIGIT = "missing_digit" 

53 MISSING_SPECIAL = "missing_special" 

54 COMMON_PASSWORD = "common_password" 

55 CONTAINS_EMAIL = "contains_email" 

56 

57 

58@dataclass(frozen=True) 

59class AdminAuthResult: 

60 """Result of a successful authentication. 

61 

62 Attributes: 

63 session_id: Newly created session identifier. 

64 user_id: Authenticated admin user's UUID. 

65 email: Authenticated admin user's email. 

66 roles: List of role names assigned to the user. 

67 expires_at: Absolute session expiry timestamp. 

68 """ 

69 

70 session_id: str 

71 user_id: str 

72 email: str 

73 roles: list[str] 

74 expires_at: datetime 

75 

76 

77@dataclass(frozen=True) 

78class AdminLockoutInfo: 

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

80 

81 Attributes: 

82 status: Current lockout status. 

83 consecutive_failures: Total consecutive failed attempts. 

84 locked_at: When the lockout was applied (None if not locked). 

85 unlock_at: When the lockout will auto-expire (None if permanent/not locked). 

86 is_permanent: Whether the lockout requires manual admin intervention. 

87 """ 

88 

89 status: AdminLockoutStatus 

90 consecutive_failures: int 

91 locked_at: datetime | None = None 

92 unlock_at: datetime | None = None 

93 is_permanent: bool = False 

94 

95 

96@dataclass(frozen=True) 

97class AdminPasswordViolation: 

98 """A single password policy violation. 

99 

100 Attributes: 

101 rule: The rule that was violated. 

102 message: Human-readable description of the violation. 

103 """ 

104 

105 rule: AdminPasswordRule 

106 message: str 

107 

108 

109@dataclass(frozen=True) 

110class AdminPasswordValidationResult: 

111 """Result of password policy validation. 

112 

113 Attributes: 

114 is_valid: Whether the password passes all policy rules. 

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

116 """ 

117 

118 is_valid: bool 

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

120 

121 

122@dataclass(frozen=True) 

123class AdminLoginAttempt: 

124 """Record of a single login attempt. 

125 

126 Attributes: 

127 id: Unique UUID for this attempt record. 

128 email: Email address used in the attempt. 

129 ip_address: Client IP address. 

130 user_agent: Client user agent string. 

131 success: Whether the attempt succeeded. 

132 failure_reason: Short failure code (None on success). 

133 attempted_at: When the attempt occurred. 

134 """ 

135 

136 id: str 

137 email: str 

138 ip_address: str 

139 user_agent: str 

140 success: bool 

141 failure_reason: str | None 

142 attempted_at: datetime 

143 

144 

145@dataclass(frozen=True) 

146class AdminSecurityEvent: 

147 """A security audit event record. 

148 

149 Attributes: 

150 id: Unique UUID for this event. 

151 event_type: Type of security event. 

152 admin_user_id: Associated admin user (None for pre-auth events). 

153 ip_address: Client IP address. 

154 user_agent: Client user agent string. 

155 success: Whether the operation succeeded. 

156 metadata: Structured additional context. 

157 created_at: When the event occurred. 

158 """ 

159 

160 id: str 

161 event_type: AdminSecurityEventType 

162 admin_user_id: str | None 

163 ip_address: str 

164 user_agent: str 

165 success: bool 

166 metadata: dict[str, str | int | bool | None] 

167 created_at: datetime 

168 

169 

170__all__ = [ 

171 "AdminAuthResult", 

172 "AdminLockoutInfo", 

173 "AdminLockoutStatus", 

174 "AdminLoginAttempt", 

175 "AdminPasswordRule", 

176 "AdminPasswordValidationResult", 

177 "AdminPasswordViolation", 

178 "AdminSecurityEvent", 

179 "AdminSecurityEventType", 

180]