Added Roles and Permissions. Some minor UI fixes

This commit is contained in:
2026-02-18 13:12:55 +02:00
parent f54cdd525d
commit dbd15c00f8
31 changed files with 1825 additions and 331 deletions

View File

@@ -1,7 +1,7 @@
from fastapi import APIRouter, Depends, Query, WebSocket, WebSocketDisconnect
from typing import Optional
from auth.models import TokenPayload
from auth.dependencies import require_device_access, require_viewer
from auth.dependencies import require_permission
from mqtt.models import (
MqttCommandRequest, CommandSendResponse, MqttStatusResponse,
DeviceMqttStatus, LogListResponse, HeartbeatListResponse,
@@ -16,7 +16,7 @@ router = APIRouter(prefix="/api/mqtt", tags=["mqtt"])
@router.get("/status", response_model=MqttStatusResponse)
async def get_all_device_status(
_user: TokenPayload = Depends(require_viewer),
_user: TokenPayload = Depends(require_permission("mqtt", "view")),
):
heartbeats = await db.get_latest_heartbeats()
now = datetime.now(timezone.utc)
@@ -47,7 +47,7 @@ async def get_all_device_status(
async def send_command(
device_serial: str,
body: MqttCommandRequest,
_user: TokenPayload = Depends(require_device_access),
_user: TokenPayload = Depends(require_permission("mqtt", "view")),
):
command_id = await db.insert_command(
device_serial=device_serial,
@@ -84,7 +84,7 @@ async def get_device_logs(
search: Optional[str] = Query(None),
limit: int = Query(100, ge=1, le=1000),
offset: int = Query(0, ge=0),
_user: TokenPayload = Depends(require_viewer),
_user: TokenPayload = Depends(require_permission("mqtt", "view")),
):
logs, total = await db.get_logs(
device_serial, level=level, search=search,
@@ -98,7 +98,7 @@ async def get_device_heartbeats(
device_serial: str,
limit: int = Query(100, ge=1, le=1000),
offset: int = Query(0, ge=0),
_user: TokenPayload = Depends(require_viewer),
_user: TokenPayload = Depends(require_permission("mqtt", "view")),
):
heartbeats, total = await db.get_heartbeats(
device_serial, limit=limit, offset=offset,
@@ -111,7 +111,7 @@ async def get_device_commands(
device_serial: str,
limit: int = Query(100, ge=1, le=1000),
offset: int = Query(0, ge=0),
_user: TokenPayload = Depends(require_viewer),
_user: TokenPayload = Depends(require_permission("mqtt", "view")),
):
commands, total = await db.get_commands(
device_serial, limit=limit, offset=offset,
@@ -129,12 +129,28 @@ async def mqtt_websocket(websocket: WebSocket):
try:
from auth.utils import decode_access_token
from shared.firebase import get_db
payload = decode_access_token(token)
role = payload.get("role", "")
allowed = {"superadmin", "device_manager", "viewer"}
if role not in allowed:
await websocket.close(code=4003, reason="Insufficient permissions")
return
# sysadmin and admin always have MQTT access
if role not in ("sysadmin", "admin"):
# Check MQTT permission for editor/user
user_sub = payload.get("sub", "")
db_inst = get_db()
if db_inst:
doc = db_inst.collection("admin_users").document(user_sub).get()
if doc.exists:
perms = doc.to_dict().get("permissions", {})
if not perms.get("mqtt", False):
await websocket.close(code=4003, reason="MQTT access denied")
return
else:
await websocket.close(code=4003, reason="User not found")
return
else:
await websocket.close(code=4003, reason="Service unavailable")
return
except Exception:
await websocket.close(code=4001, reason="Invalid token")
return