dashboard-nanobot/backend/bootstrap/auth_access.py

98 lines
2.8 KiB
Python

from __future__ import annotations
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"
_PUBLIC_EXACT_PATHS = {
"/api/health",
"/api/health/cache",
"/api/system/defaults",
}
_PANEL_AUTH_SEGMENTS = ("api", "panel", "auth")
_BOT_PUBLIC_SEGMENTS = ("public", "bots")
_BOT_API_SEGMENTS = ("api", "bots")
_BOT_AUTH_SEGMENT = "auth"
_BOT_PANEL_ONLY_ACTIONS = {"enable", "disable", "deactivate"}
_BOT_PUBLIC_AUTH_ACTIONS = {"login", "logout", "status"}
def _path_segments(path: str) -> list[str]:
raw = str(path or "").strip().strip("/")
if not raw:
return []
return [segment for segment in raw.split("/") if segment]
def extract_bot_id(path: str) -> Optional[str]:
segments = _path_segments(path)
if len(segments) < 3:
return None
if tuple(segments[:2]) not in {_BOT_API_SEGMENTS, _BOT_PUBLIC_SEGMENTS}:
return None
bot_id = str(segments[2] or "").strip()
return bot_id or None
def _is_panel_auth_route(segments: list[str]) -> bool:
return tuple(segments[:3]) == _PANEL_AUTH_SEGMENTS
def _is_public_bot_route(segments: list[str]) -> bool:
return tuple(segments[:2]) == _BOT_PUBLIC_SEGMENTS and len(segments) >= 3
def _is_bot_auth_route(segments: list[str]) -> bool:
return (
tuple(segments[:2]) == _BOT_API_SEGMENTS
and len(segments) >= 5
and segments[3] == _BOT_AUTH_SEGMENT
and segments[4] in _BOT_PUBLIC_AUTH_ACTIONS
)
def _is_panel_only_bot_action(segments: list[str], method: str) -> bool:
if tuple(segments[:2]) != _BOT_API_SEGMENTS or len(segments) < 3:
return False
if len(segments) == 3 and method == "DELETE":
return True
return len(segments) >= 4 and method == "POST" and segments[3] in _BOT_PANEL_ONLY_ACTIONS
def _is_bot_scoped_api_route(segments: list[str]) -> bool:
return tuple(segments[:2]) == _BOT_API_SEGMENTS and len(segments) >= 3
def resolve_route_access_mode(path: str, method: str) -> RouteAccessMode:
raw_path = str(path or "").strip()
verb = str(method or "GET").strip().upper()
segments = _path_segments(raw_path)
if raw_path in _PUBLIC_EXACT_PATHS:
return RouteAccessMode.PUBLIC
if _is_panel_auth_route(segments) or _is_bot_auth_route(segments):
return RouteAccessMode.PUBLIC
if _is_public_bot_route(segments):
return RouteAccessMode.PUBLIC_BOT_OR_PANEL
if _is_panel_only_bot_action(segments, verb):
return RouteAccessMode.PANEL_ONLY
if _is_bot_scoped_api_route(segments):
return RouteAccessMode.BOT_OR_PANEL
if raw_path.startswith("/api/"):
return RouteAccessMode.PANEL_ONLY
return RouteAccessMode.PUBLIC