fix: harden dashboard auth and terminal flows
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 3m29s

Tighten terminal websocket auth and proxy trust handling while making file-backed auth/RBAC writes atomic to reduce high-impact security and persistence risks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gan, Jimmy
2026-03-06 23:53:58 +08:00
parent c9f06fbbbb
commit 44f0ed5d04
9 changed files with 337 additions and 175 deletions
+29 -6
View File
@@ -1,5 +1,7 @@
import json
import os
import threading
import tempfile
from typing import Optional
from pydantic import BaseModel
from fastapi import HTTPException, Request, status
@@ -7,6 +9,7 @@ from fastapi import HTTPException, Request, status
import config
RBAC_FILE = os.path.join(config.VOLUME_ROOT, "docker/nas-dashboard/rbac.json")
_rbac_lock = threading.RLock()
ROLE_GROUP_MAP = {
"dashboard_admin": "admin",
@@ -41,10 +44,30 @@ def load_rbac() -> dict:
return DEFAULT_RBAC.copy()
def _atomic_json_write(path: str, data: dict):
os.makedirs(os.path.dirname(path), exist_ok=True)
fd, temp_path = tempfile.mkstemp(dir=os.path.dirname(path), prefix=".tmp-rbac-", suffix=".json")
try:
with os.fdopen(fd, "w") as f:
json.dump(data, f, indent=2)
f.flush()
os.fsync(f.fileno())
os.replace(temp_path, path)
finally:
if os.path.exists(temp_path):
os.unlink(temp_path)
def update_rbac(mutator):
with _rbac_lock:
data = load_rbac()
result = mutator(data)
_atomic_json_write(RBAC_FILE, data)
return result
def save_rbac(data: dict):
os.makedirs(os.path.dirname(RBAC_FILE), exist_ok=True)
with open(RBAC_FILE, "w") as f:
json.dump(data, f, indent=2)
_atomic_json_write(RBAC_FILE, data)
def resolve_role(groups: list[str]) -> Optional[str]:
@@ -77,9 +100,9 @@ def get_sidebar_order(username: str) -> dict | None:
def save_sidebar_order(username: str, order: dict):
rbac = load_rbac()
rbac.setdefault("user_overrides", {}).setdefault(username, {})["sidebar_order"] = order
save_rbac(rbac)
def mutator(rbac):
rbac.setdefault("user_overrides", {}).setdefault(username, {})["sidebar_order"] = order
update_rbac(mutator)
def has_page_access(user: User, page: str) -> bool: