fix: harden dashboard auth and terminal flows
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 3m29s
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:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user