fix(audit-2026-06-11/revocation-lag): evict PrincipalCache on credential change
The PrincipalCache (token-hash -> resolved Principal, 60s TTL) had no eviction hook, so deactivation / password change / API-key revocation flipped the DB rows but the cache kept serving the stale principal for up to the TTL window. Closes the audit's PrincipalCache revocation-lag Medium and greens authz::deactivating_user_revokes_their_api_keys. - PrincipalCache::evict_user(user_id) + evict_token(hash). - One shared cache Arc threaded into AuthState / AdminsState / ApiKeysState (a per-state cache would let the middleware's own copy keep authenticating a revoked principal). - Evict on: deactivation, password change (both evict_user), logout (evict_token, precise), single API-key delete (evict_user). - H-E1 note: DB-write invalidation stays best-effort by design (a blip must not undo the rotation); the cache evict is what makes it take effect on the next request. - Renamed bearer_and_cookie_produce_same_principal -> session_token_and_api_key_produce_same_principal (cookie auth was removed in C-1; the test never tested cookies). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -498,12 +498,19 @@ pub async fn build_app(
|
||||
.and_then(|s| s.parse::<usize>().ok())
|
||||
.filter(|n| *n > 0)
|
||||
.unwrap_or(2);
|
||||
// Audit 2026-06-11 (PrincipalCache revocation-lag) — one shared
|
||||
// cache instance threaded into every state that performs auth or
|
||||
// revocation, so an evict on the revocation side is visible to the
|
||||
// middleware's resolve side. A per-state cache would let a revoked
|
||||
// principal keep authenticating from the middleware's own copy.
|
||||
let principal_cache =
|
||||
Arc::new(picloud_manager_core::auth_middleware::PrincipalCache::new());
|
||||
let auth_state = AuthState {
|
||||
users: auth.users.clone(),
|
||||
sessions: auth.sessions.clone(),
|
||||
keys: auth.keys.clone(),
|
||||
ttl: auth.ttl,
|
||||
principal_cache: Arc::new(picloud_manager_core::auth_middleware::PrincipalCache::new()),
|
||||
principal_cache: principal_cache.clone(),
|
||||
login_rate_limiter: Arc::new(
|
||||
picloud_manager_core::login_rate_limit::LoginRateLimiter::new(),
|
||||
),
|
||||
@@ -514,6 +521,7 @@ pub async fn build_app(
|
||||
sessions: auth.sessions,
|
||||
keys: auth.keys.clone(),
|
||||
authz: authz.clone(),
|
||||
principal_cache: principal_cache.clone(),
|
||||
};
|
||||
let app_members_state = AppMembersState {
|
||||
apps: apps_state.apps.clone(),
|
||||
@@ -526,7 +534,10 @@ pub async fn build_app(
|
||||
authz: authz.clone(),
|
||||
users: users.clone(),
|
||||
};
|
||||
let api_keys_state = ApiKeysState { keys: auth.keys };
|
||||
let api_keys_state = ApiKeysState {
|
||||
keys: auth.keys,
|
||||
principal_cache: principal_cache.clone(),
|
||||
};
|
||||
|
||||
// /admin/auth/login + /logout are unguarded by design (login is how
|
||||
// you get in). /admin/auth/me applies the middleware internally so
|
||||
|
||||
Reference in New Issue
Block a user