security: rate limiter, is_admin flag, password policy, log masking
M-3: Role.name == 'Admin' string comparison → Role.is_admin boolean flag.
Admin role created with is_admin=True. has_permission checks getattr(role,
'is_admin', False) instead of comparing role names.
M-2: In-memory rate limiter on /api/auth/login. Tracks failed attempts
per IP (10 in 5 min window, 15 min ban). Thread-safe via threading.Lock.
M-5: Password policy — UserCreate.password: min_length=8, must include
uppercase, lowercase, and digit.
L-1: log_security_event details dict now masks sensitive fields:
password, secret, token, api_key, authorization. Recursive masking
for nested dicts. Sensitive field names matched via regex.
OTHER:
- 4 rate limiter tests (under limit, ban, success clears, IP isolation)
- 4 password policy tests (too short, no upper, no digit, valid)
- 2 log masking tests (flat keys, nested)