fix:[FL-220][菜单禁用访问控制]
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
+34
-8
@@ -2,7 +2,13 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, get_current_user, require_any_permission, require_permission
|
||||
from ...core.dependencies import (
|
||||
CurrentUser,
|
||||
get_current_user,
|
||||
require_any_permission,
|
||||
require_enabled_menu_route,
|
||||
require_permission,
|
||||
)
|
||||
from ...exceptions.menu_exceptions import MenuValidationError
|
||||
from ...schemas.admin import (
|
||||
AuditLogListResponse,
|
||||
@@ -44,7 +50,11 @@ from ...services.seed_service import seed_defaults
|
||||
router = APIRouter(prefix="/admin", tags=["admin"])
|
||||
|
||||
|
||||
@router.get("/roles", response_model=RoleListResponse)
|
||||
@router.get(
|
||||
"/roles",
|
||||
response_model=RoleListResponse,
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
def get_roles(
|
||||
keyword: str | None = Query(default=None),
|
||||
limit: int = Query(default=50, ge=1, le=200),
|
||||
@@ -55,7 +65,11 @@ def get_roles(
|
||||
return list_roles(db, keyword=keyword, limit=limit, offset=offset)
|
||||
|
||||
|
||||
@router.get("/roles-with-menus", response_model=RolesWithMenusResponse)
|
||||
@router.get(
|
||||
"/roles-with-menus",
|
||||
response_model=RolesWithMenusResponse,
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
def get_roles_with_menus(
|
||||
keyword: str | None = Query(default=None),
|
||||
limit: int = Query(default=50, ge=1, le=200),
|
||||
@@ -66,7 +80,11 @@ def get_roles_with_menus(
|
||||
return list_roles_with_menus(db, keyword=keyword, limit=limit, offset=offset)
|
||||
|
||||
|
||||
@router.post("/roles", response_model=RolePublic)
|
||||
@router.post(
|
||||
"/roles",
|
||||
response_model=RolePublic,
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
def create_role_endpoint(
|
||||
payload: RoleCreateRequest,
|
||||
current_user: CurrentUser = Depends(require_permission("role.manage")),
|
||||
@@ -78,7 +96,11 @@ def create_role_endpoint(
|
||||
return created
|
||||
|
||||
|
||||
@router.patch("/roles/{role_id}", response_model=RolePublic)
|
||||
@router.patch(
|
||||
"/roles/{role_id}",
|
||||
response_model=RolePublic,
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
def update_role_endpoint(
|
||||
role_id: str,
|
||||
payload: RoleUpdateRequest,
|
||||
@@ -91,7 +113,7 @@ def update_role_endpoint(
|
||||
return updated
|
||||
|
||||
|
||||
@router.delete("/roles/{role_id}")
|
||||
@router.delete("/roles/{role_id}", dependencies=[Depends(require_enabled_menu_route)])
|
||||
def delete_role_endpoint(
|
||||
role_id: str,
|
||||
current_user: CurrentUser = Depends(require_permission("role.manage")),
|
||||
@@ -103,7 +125,7 @@ def delete_role_endpoint(
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.get("/roles/{role_id}/menus")
|
||||
@router.get("/roles/{role_id}/menus", dependencies=[Depends(require_enabled_menu_route)])
|
||||
def get_role_menus(
|
||||
role_id: str,
|
||||
_: CurrentUser = Depends(require_any_permission("role.read", "role.manage")),
|
||||
@@ -115,7 +137,11 @@ def get_role_menus(
|
||||
return {"menu_ids": menu_ids or []}
|
||||
|
||||
|
||||
@router.put("/roles/{role_id}/menus", response_model=RolePublic)
|
||||
@router.put(
|
||||
"/roles/{role_id}/menus",
|
||||
response_model=RolePublic,
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
def replace_role_menus_endpoint(
|
||||
role_id: str,
|
||||
payload: RoleMenuUpdateRequest,
|
||||
|
||||
@@ -3,7 +3,7 @@ from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.file_storage import (
|
||||
FileCreateDirectoryRequest,
|
||||
FileDeleteRequest,
|
||||
@@ -23,7 +23,7 @@ from ...services.file_service import (
|
||||
upload_file_to_path,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/admin/files", tags=["admin-files"])
|
||||
router = APIRouter(prefix="/admin/files", tags=["admin-files"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("", response_model=FileListResponse)
|
||||
|
||||
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Upload
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.atp_asset import (
|
||||
AtpAssetCreateRequest,
|
||||
AtpAssetDetail,
|
||||
@@ -42,7 +42,7 @@ from ...services.atp_asset_service import (
|
||||
)
|
||||
from ...services.atp_model_service import get_engine_status
|
||||
|
||||
router = APIRouter(prefix="/atp", tags=["atp-assets"])
|
||||
router = APIRouter(prefix="/atp", tags=["atp-assets"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("/engine/status", response_model=AtpEngineStatusResponse)
|
||||
|
||||
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.atp_model import (
|
||||
AtpEngineStatusResponse,
|
||||
AtpModelCreateRequest,
|
||||
@@ -38,7 +38,7 @@ from ...services.atp_model_service import (
|
||||
update_model_version,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/atp/models", tags=["atp-models"])
|
||||
router = APIRouter(prefix="/atp/models", tags=["atp-models"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("/engine/status", response_model=AtpEngineStatusResponse)
|
||||
|
||||
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Respon
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.elevation import (
|
||||
ElevationApplyJobCreateRequest,
|
||||
ElevationApplyJobCreateResponse,
|
||||
@@ -75,7 +75,7 @@ from ...services.elevation_file_record_service import (
|
||||
update_file_record,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/elevation", tags=["elevation"])
|
||||
router = APIRouter(prefix="/elevation", tags=["elevation"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile, status
|
||||
|
||||
from ...core.dependencies import CurrentUser, require_any_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route
|
||||
from ...schemas.fault_recurrence import (
|
||||
FaultRecurrenceAnalyzeResponse,
|
||||
FaultRecurrenceStrokeMode,
|
||||
@@ -10,7 +10,11 @@ from ...schemas.fault_recurrence import (
|
||||
from ...services.fault_recurrence_service import build_fault_recurrence_report
|
||||
|
||||
|
||||
router = APIRouter(prefix="/fault-recurrence", tags=["fault-recurrence"])
|
||||
router = APIRouter(
|
||||
prefix="/fault-recurrence",
|
||||
tags=["fault-recurrence"],
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
|
||||
|
||||
@router.post("/analyze", response_model=FaultRecurrenceAnalyzeResponse)
|
||||
|
||||
@@ -5,7 +5,7 @@ from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route
|
||||
from ...schemas.fl_analysis import (
|
||||
FlAnalysisJobCreateRequest,
|
||||
FlAnalysisJobCreateResponse,
|
||||
@@ -28,7 +28,7 @@ from ...services.fl_analysis_service import (
|
||||
from ...services.tower_profile_service import get_tower_profile_detail, upsert_tower_profile
|
||||
from ...services.tower_topology import TowerGeometryValidationError
|
||||
|
||||
router = APIRouter(prefix="/fl-analysis", tags=["fl-analysis"])
|
||||
router = APIRouter(prefix="/fl-analysis", tags=["fl-analysis"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("/tower-profiles/{tower_id}", response_model=TowerProfileDetail)
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
|
||||
from ...core.dependencies import CurrentUser, require_any_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route
|
||||
from ...schemas.flower_monitor import (
|
||||
FlowerWorkerTaskOverviewResponse,
|
||||
FlowerWorkersOverviewResponse,
|
||||
@@ -12,7 +12,7 @@ from ...services.flower_monitor_service import (
|
||||
build_workers_overview,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/admin/flower", tags=["admin-flower"])
|
||||
router = APIRouter(prefix="/admin/flower", tags=["admin-flower"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("/workers", response_model=FlowerWorkersOverviewResponse)
|
||||
|
||||
@@ -6,7 +6,7 @@ from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Upload
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.lightning import (
|
||||
LightningCurrentEventListResponse,
|
||||
LightningCurrentEventSummary,
|
||||
@@ -45,7 +45,11 @@ from ...services.lightning_service import (
|
||||
update_lightning_event,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/lightning-currents", tags=["lightning-currents"])
|
||||
router = APIRouter(
|
||||
prefix="/lightning-currents",
|
||||
tags=["lightning-currents"],
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=LightningCurrentEventListResponse)
|
||||
|
||||
@@ -5,7 +5,7 @@ from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.line import (
|
||||
LineCreateRequest,
|
||||
LineListResponse,
|
||||
@@ -34,7 +34,7 @@ from ...services.line_service import (
|
||||
update_line_tower,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/lines", tags=["lines"])
|
||||
router = APIRouter(prefix="/lines", tags=["lines"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("", response_model=LineListResponse)
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.scheduled_task import (
|
||||
ScheduledTaskCreateRequest,
|
||||
ScheduledTaskListResponse,
|
||||
@@ -19,7 +19,11 @@ from ...services.scheduled_task_service import (
|
||||
update_scheduled_task,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/admin/scheduled-tasks", tags=["admin-scheduled-tasks"])
|
||||
router = APIRouter(
|
||||
prefix="/admin/scheduled-tasks",
|
||||
tags=["admin-scheduled-tasks"],
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=ScheduledTaskListResponse)
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, get_current_user, require_permission
|
||||
from ...core.dependencies import CurrentUser, get_current_user, require_enabled_menu_route, require_permission
|
||||
from ...schemas.auth import MessageResponse
|
||||
from ...schemas.system_message import (
|
||||
SystemMessageCreateRequest,
|
||||
@@ -22,7 +22,12 @@ from ...services.system_message_service import (
|
||||
router = APIRouter(prefix="/system-messages", tags=["system_messages"])
|
||||
|
||||
|
||||
@router.post("", response_model=SystemMessagePublic, status_code=status.HTTP_201_CREATED)
|
||||
@router.post(
|
||||
"",
|
||||
response_model=SystemMessagePublic,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
def create_message(
|
||||
payload: SystemMessageCreateRequest,
|
||||
_: CurrentUser = Depends(require_permission("admin.system_message")),
|
||||
@@ -79,7 +84,7 @@ def mark_my_messages_read(
|
||||
return {"affected": affected}
|
||||
|
||||
|
||||
@router.delete("/{message_id}", response_model=MessageResponse)
|
||||
@router.delete("/{message_id}", response_model=MessageResponse, dependencies=[Depends(require_enabled_menu_route)])
|
||||
def remove_system_message(
|
||||
message_id: str,
|
||||
_: CurrentUser = Depends(require_permission("admin.system_message")),
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.system_param import (
|
||||
SystemParamCreateRequest,
|
||||
SystemParamListResponse,
|
||||
@@ -18,7 +18,11 @@ from ...services.system_param_service import (
|
||||
update_system_param,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/admin/system-params", tags=["admin-system-params"])
|
||||
router = APIRouter(
|
||||
prefix="/admin/system-params",
|
||||
tags=["admin-system-params"],
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=SystemParamListResponse)
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
|
||||
from ...core.dependencies import CurrentUser, require_any_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route
|
||||
from ...schemas.task_monitor import TaskMonitorOverviewResponse
|
||||
from ...services.task_monitor_service import build_task_monitor_overview
|
||||
|
||||
router = APIRouter(prefix="/admin/task-monitor", tags=["admin-task-monitor"])
|
||||
router = APIRouter(
|
||||
prefix="/admin/task-monitor",
|
||||
tags=["admin-task-monitor"],
|
||||
dependencies=[Depends(require_enabled_menu_route)],
|
||||
)
|
||||
|
||||
|
||||
@router.get("/overview", response_model=TaskMonitorOverviewResponse)
|
||||
|
||||
@@ -5,7 +5,7 @@ from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.tower_model import (
|
||||
TowerModelCreateRequest,
|
||||
TowerModelImageUploadResponse,
|
||||
@@ -28,7 +28,7 @@ from ...services.tower_model_service import (
|
||||
upload_tower_model_image,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/tower-models", tags=["tower-models"])
|
||||
router = APIRouter(prefix="/tower-models", tags=["tower-models"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("", response_model=TowerModelListResponse)
|
||||
|
||||
@@ -4,12 +4,12 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.tower_profile import TowerProfileDetail, TowerProfileUpsertRequest
|
||||
from ...services.tower_profile_service import get_tower_profile_detail, upsert_tower_profile
|
||||
from ...services.tower_topology import TowerGeometryValidationError
|
||||
|
||||
router = APIRouter(prefix="/tower-profiles", tags=["tower-profiles"])
|
||||
router = APIRouter(prefix="/tower-profiles", tags=["tower-profiles"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("/{tower_id}", response_model=TowerProfileDetail)
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, get_current_user, require_permission
|
||||
from ...core.dependencies import CurrentUser, get_current_user, require_enabled_menu_route, require_permission
|
||||
from ...schemas.auth import MessageResponse
|
||||
from ...schemas.user import (
|
||||
UserCreateRequest,
|
||||
@@ -27,7 +27,7 @@ from ...services.user_service import (
|
||||
update_user,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/users", tags=["users"])
|
||||
router = APIRouter(prefix="/users", tags=["users"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("/check-id/{user_id}", response_model=UserIdCheckResponse)
|
||||
|
||||
@@ -4,12 +4,12 @@ from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...core.database import get_db
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_permission
|
||||
from ...core.dependencies import CurrentUser, require_any_permission, require_enabled_menu_route, require_permission
|
||||
from ...schemas.wine import WineRunDetail, WineRunListResponse, WineRunRequest, WineStatusResponse
|
||||
from ...services.wine_service import create_run, get_run_detail, get_wine_status, list_runs
|
||||
|
||||
|
||||
router = APIRouter(prefix="/wine", tags=["wine"])
|
||||
router = APIRouter(prefix="/wine", tags=["wine"], dependencies=[Depends(require_enabled_menu_route)])
|
||||
|
||||
|
||||
@router.get("/status", response_model=WineStatusResponse)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from dataclasses import dataclass
|
||||
from collections.abc import Callable
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi import Depends, HTTPException, Request, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..models.menu import Menu
|
||||
from ..models.user import User
|
||||
from ..services.legacy_authz_service import get_user_authorization, is_user_enabled
|
||||
from .database import get_db
|
||||
@@ -21,11 +23,78 @@ class CurrentUser:
|
||||
permission_codes: set[str]
|
||||
|
||||
|
||||
MENU_ROUTE_PREFIXES: tuple[tuple[str, str], ...] = (
|
||||
("/api/v1/users", "/admin/users"),
|
||||
("/api/v1/admin/roles-with-menus", "/admin/roles"),
|
||||
("/api/v1/admin/roles", "/admin/roles"),
|
||||
("/api/v1/admin/system-params", "/admin/system-params"),
|
||||
("/api/v1/system-messages", "/admin/system-messages"),
|
||||
("/api/v1/lines", "/admin/power-lines"),
|
||||
("/api/v1/tower-profiles", "/admin/power-lines"),
|
||||
("/api/v1/fl-analysis", "/admin/fl-analysis"),
|
||||
("/api/v1/fault-recurrence", "/admin/fault-recurrence"),
|
||||
("/api/v1/lightning-currents/stats/distribution", "/admin/lightning-distribution"),
|
||||
("/api/v1/lightning-currents/import-distribution", "/admin/lightning-distribution"),
|
||||
("/api/v1/lightning-currents", "/admin/lightning-currents"),
|
||||
("/api/v1/admin/flower", "/admin/workers"),
|
||||
("/api/v1/admin/task-monitor", "/admin/task-monitor"),
|
||||
("/api/v1/admin/scheduled-tasks", "/admin/scheduled-tasks"),
|
||||
("/api/v1/atp", "/admin/atp-models"),
|
||||
("/api/v1/tower-models", "/admin/tower-models"),
|
||||
("/api/v1/admin/files", "/admin/files"),
|
||||
("/api/v1/elevation", "/admin/elevation"),
|
||||
("/api/v1/wine", "/admin/wine-runner"),
|
||||
)
|
||||
|
||||
|
||||
def _load_user_with_rbac(db: Session, user_id: str) -> User | None:
|
||||
stmt = select(User).where(User.id == user_id)
|
||||
return db.execute(stmt).unique().scalar_one_or_none()
|
||||
|
||||
|
||||
def _normalize_path(path: str | None) -> str | None:
|
||||
normalized = (path or "").strip()
|
||||
if not normalized:
|
||||
return None
|
||||
if not normalized.startswith("/"):
|
||||
normalized = f"/{normalized}"
|
||||
if normalized != "/":
|
||||
normalized = normalized.rstrip("/")
|
||||
return normalized
|
||||
|
||||
|
||||
def _path_matches_prefix(path: str, prefix: str) -> bool:
|
||||
return path == prefix or path.startswith(f"{prefix}/")
|
||||
|
||||
|
||||
def _menu_path_for_api_path(api_path: str) -> str | None:
|
||||
normalized_api_path = _normalize_path(api_path)
|
||||
if not normalized_api_path:
|
||||
return None
|
||||
for api_prefix, menu_path in MENU_ROUTE_PREFIXES:
|
||||
if _path_matches_prefix(normalized_api_path, api_prefix):
|
||||
return menu_path
|
||||
return None
|
||||
|
||||
|
||||
def _enabled_menu_path_exists(db: Session, menu_path: str) -> bool:
|
||||
normalized_menu_path = _normalize_path(menu_path)
|
||||
if not normalized_menu_path:
|
||||
return True
|
||||
try:
|
||||
menu = db.execute(
|
||||
select(Menu.id, Menu.status, Menu.visible)
|
||||
.where(Menu.path == normalized_menu_path)
|
||||
.limit(1)
|
||||
).first()
|
||||
except SQLAlchemyError:
|
||||
db.rollback()
|
||||
return True
|
||||
if not menu:
|
||||
return True
|
||||
return menu.status == "enabled" and bool(menu.visible)
|
||||
|
||||
|
||||
def get_current_user(
|
||||
db: Session = Depends(get_db),
|
||||
token: str = Depends(oauth2_scheme),
|
||||
@@ -81,3 +150,17 @@ def require_any_permission(*permission_codes: str) -> Callable[[CurrentUser], Cu
|
||||
)
|
||||
|
||||
return dependency
|
||||
|
||||
|
||||
def require_enabled_menu_route(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
) -> CurrentUser:
|
||||
menu_path = _menu_path_for_api_path(request.url.path)
|
||||
if menu_path and not _enabled_menu_path_exists(db, menu_path):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Menu is disabled",
|
||||
)
|
||||
return current_user
|
||||
|
||||
@@ -455,9 +455,10 @@ def build_menu_tree(db: Session, *, role_codes: set[str] | None = None) -> list[
|
||||
|
||||
menus = db.execute(_menu_stmt().order_by(Menu.sort_order.asc(), Menu.id.asc())).scalars().all()
|
||||
menus = [menu for menu in menus if not _is_removed_menu_code(menu.code)]
|
||||
menus = [menu for menu in menus if menu.status == "enabled" and menu.visible]
|
||||
if role_codes is not None and "admin" not in role_codes:
|
||||
allowed_ids = _get_allowed_menu_ids(db, role_codes)
|
||||
menus = [menu for menu in menus if menu.id in allowed_ids and menu.status == "enabled" and menu.visible]
|
||||
menus = [menu for menu in menus if menu.id in allowed_ids]
|
||||
|
||||
return _to_tree(menus)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user