- Shipped
- October 14, 2026 at 4:30 PM UTC
- Author
- Kamo
- Commit
- c6ec95e
SuspiciousDetectionService was injected into SecurityController and evaluate() was never called, so every rule an organization configured on the Security screen (failed-login threshold, geo anomaly, rapid IP switching, off-hours access, concurrent sessions) was stored and inert. Two responses only logged (LOCK_ACCOUNT, NOTIFY_ADMIN_EMAIL), and FORCE_LOGOUT / CONCURRENT_SESSIONS walked `KEYS ****` reading sessions as hashes - they are JSON strings, so neither ever matched. Wiring (SignInDetection, @Async, swallows everything): - Both password sign-in success points (host-resolved login and completeSignIn) and the failed-credential point now hand the sign-in to the rules, then write the access-log row via logEventSync with isSuspicious / suspiciousReason / detectionRuleId filled in. Evaluate before logging, so the geo rule does not see the sign-in in its own history. It runs after the session exists and off the request thread: nothing can delay or refuse a sign-in, and any Redis / database / EmailService failure is "not suspicious". - PROGRESSIVE_LOGIN_LOCKOUT is unchanged: still enforced inline by LoginThrottleGate before the password compare, still skipped here. Responses: - LOCK_ACCOUNT: users.is_account_locked (the platform's existing lock, refused by UserAuthenticationService, cleared on the Users screen), in one UPDATE guarded by security_provider_id = the evaluating org, then all the account's sessions end. Another org may not write the account, so there this membership's sessions end and the address gets a TEMP_BLOCK access rule. A failed sign-in names no account; its lock is the TEMP_BLOCK. ACCOUNT_LOCKED / FORCE_LOGOUT rows are logged. - FORCE_LOGOUT: this membership's sessions, via the per-user session index **************** - CONCURRENT_SESSIONS counts through the same index (new **************** no KEYS scan. - NOTIFY_ADMIN_EMAIL: the new SUSPICIOUS_SIGNIN_DETECTED letter (kamo-shared-library 6fce139a) to the rule's own addresses, at most once an hour per rule and subject so an attack cannot flood the inbox. - One TEMP_BLOCK however many responses ask for it. Every rule configured in production today is LOG_ONLY, so this deploy marks access-log rows and locks or blocks nobody.
