fix: harden agent startup and websocket auth

This commit is contained in:
root
2026-07-27 11:49:18 +03:00
parent a386a1fd5c
commit 3fd8525c4e
10 changed files with 283 additions and 29 deletions

View File

@@ -20,7 +20,6 @@ import asyncio
from contextlib import asynccontextmanager
import os
from pathlib import Path
import sys
import uuid
# project_root is used for static files mounting
@@ -669,28 +668,52 @@ def _authorize_websocket(websocket: WebSocket, resource: str, action: str) -> bo
try:
from .core.auth.jwt import decode_token
from .core.database import SessionLocal
from .models.auth import User, Role
from .models.auth import User
payload = decode_token(ws_token)
username = payload.get("sub")
if not isinstance(username, str) or not username:
logger.explore(
"WebSocket authorization — token missing 'sub' claim",
payload={"resource": resource, "action": action},
error="JWT payload has no valid subject",
)
return False
db = SessionLocal()
try:
user = db.query(User).filter(User.username == username).first()
if not user:
logger.explore(
"WebSocket authorization — user not found in database",
payload={"resource": resource, "action": action, "username": username},
error="Authenticated user missing from local DB",
)
return False
if not getattr(user, "is_active", True):
logger.explore(
"WebSocket authorization — user is inactive",
payload={"resource": resource, "action": action, "username": username},
error="User account is disabled",
)
return False
# Admin bypass via is_admin flag
# is_admin is the sole administrative authority. The database migration
# backfills the legacy Admin role before this authorization path runs.
for role in user.roles:
if getattr(role, "is_admin", False):
return True
for perm in role.permissions:
if perm.resource == resource and perm.action == action:
return True
logger.explore(
"WebSocket authorization denied — no matching permission or admin role",
payload={"resource": resource, "action": action, "user": username,
"roles": [r.name for r in user.roles],
"is_active": getattr(user, "is_active", True)},
error="No matching RBAC permission",
)
return False
finally:
db.close()