dashboard-nanobot/backend/bootstrap/auth_access.py

61 lines
1.8 KiB
Python

from __future__ import annotations
import re
from enum import Enum
from typing import Optional
class RouteAccessMode(str, Enum):
PUBLIC = "public"
PANEL_ONLY = "panel_only"
BOT_OR_PANEL = "bot_or_panel"
PUBLIC_BOT_OR_PANEL = "public_bot_or_panel"
_BOT_ID_API_RE = re.compile(r"^/api/bots/([^/]+)(?:/.*)?$")
_BOT_ID_PUBLIC_RE = re.compile(r"^/public/bots/([^/]+)(?:/.*)?$")
_BOT_PANEL_ONLY_ROUTE_METHODS = [
(re.compile(r"^/api/bots/[^/]+$"), {"DELETE"}),
(re.compile(r"^/api/bots/[^/]+/(?:enable|disable|deactivate)$"), {"POST"}),
]
_PUBLIC_PATHS = {
"/api/panel/auth/status",
"/api/panel/auth/login",
"/api/panel/auth/logout",
"/api/health",
"/api/health/cache",
"/api/system/defaults",
}
_BOT_PUBLIC_AUTH_RE = re.compile(r"^/api/bots/[^/]+/auth/(?:login|logout|status)$")
def extract_bot_id(path: str) -> Optional[str]:
raw = str(path or "").strip()
match = _BOT_ID_API_RE.match(raw) or _BOT_ID_PUBLIC_RE.match(raw)
if not match or not match.group(1):
return None
return match.group(1).strip() or None
def resolve_route_access_mode(path: str, method: str) -> RouteAccessMode:
raw_path = str(path or "").strip()
verb = str(method or "GET").strip().upper()
if raw_path in _PUBLIC_PATHS or _BOT_PUBLIC_AUTH_RE.fullmatch(raw_path):
return RouteAccessMode.PUBLIC
if raw_path.startswith("/public/bots/"):
return RouteAccessMode.PUBLIC_BOT_OR_PANEL
if _BOT_ID_API_RE.fullmatch(raw_path):
if any(pattern.fullmatch(raw_path) and verb in methods for pattern, methods in _BOT_PANEL_ONLY_ROUTE_METHODS):
return RouteAccessMode.PANEL_ONLY
return RouteAccessMode.BOT_OR_PANEL
if raw_path.startswith("/api/"):
return RouteAccessMode.PANEL_ONLY
return RouteAccessMode.PUBLIC