fix: address auth subsystem issues from ws-debug review

- lib/auth: make RateLimiter.is_allowed read-only (no dict mutation on read)
- daemon/server: add periodic blacklist_expired cleanup to poll loop (60s interval)
- daemon/server: negotiate only matched Bearer subprotocol on WebSocket connect
- webui/server: rewrite _is_personal_auth with path-prefix matching, cover WebAuthn register routes
- daemon/handlers/auth: eliminate redundant get_user call in auth_update_user
This commit is contained in:
2026-08-12 17:51:09 +00:00
parent 0889ef0d08
commit 85d8770ba6
4 changed files with 42 additions and 23 deletions
+13 -10
View File
@@ -128,14 +128,18 @@ _AUTH_EXEMPT = {
# ── Personal auth routes (operates on own account, no subsystem permission needed) ──
# These routes require a valid JWT but do NOT require an "auth" permission entry.
# A user with only "firewall:read" can still view session, change password, logout, etc.
_AUTH_PERSONAL = {
("GET", "/api/auth/session"),
("POST", "/api/auth/password"),
("POST", "/api/auth/logout"),
("GET", "/api/auth/webauthn/credentials"),
}
# Method-agnostic — covers all HTTP methods for future-proofing.
_AUTH_PERSONAL_PATHS = (
"/api/auth/session",
"/api/auth/password",
"/api/auth/logout",
)
# Pattern: DELETE /api/auth/webauthn/creds/<id> — match prefix only
_AUTH_PERSONAL_PREFIXES = (
"/api/auth/webauthn/register-",
"/api/auth/webauthn/credentials",
"/api/auth/webauthn/creds/",
)
def _subsystem_from_path(path: str) -> str | None:
@@ -150,10 +154,9 @@ def _subsystem_from_path(path: str) -> str | None:
def _is_personal_auth(method: str, path: str) -> bool:
"""Check if route is a personal auth operation (no subsystem permission needed)."""
if (method, path) in _AUTH_PERSONAL:
if path in _AUTH_PERSONAL_PATHS:
return True
# Personal credential deletion: DELETE /api/auth/webauthn/creds/<id>
return method == "DELETE" and path.startswith("/api/auth/webauthn/creds/")
return any(path.startswith(prefix) for prefix in _AUTH_PERSONAL_PREFIXES)
def _has_permission(perms: dict, subsystem: str, method: str) -> bool: