"""移动市场接口的严格菜单权限守卫。""" from typing import Callable, Iterable from fastapi import Depends, HTTPException from sqlalchemy.orm import Session from app.core.auth import get_current_user from app.core.database import get_db from app.services.web_auth_roles import ( iter_user_menu_perm_codes, resolve_web_role_for_user, ) def require_market_menu_permission( *, user_id: str, tenant_no: str, required: str, granted: Iterable[str], ) -> bool: """严格判断当前租户用户是否拥有指定移动市场权限码。""" uid = (user_id or "").strip() tenant = (tenant_no or "").strip() code = (required or "").strip() if not uid or not tenant or not code: return False return code in {(item or "").strip() for item in granted if item} def user_has_tenant_menu_perm( db: Session, *, user_id: str, tenant_no: str, perm_code: str, ) -> bool: """按显式租户读取用户权限,不回退默认租户。""" tenant = (tenant_no or "").strip() uid = (user_id or "").strip() if not tenant or not uid: return False granted = iter_user_menu_perm_codes(db, uid, tenant_no=tenant) return require_market_menu_permission( user_id=uid, tenant_no=tenant, required=perm_code, granted=granted, ) def require_market_menu_code(menu_code: str) -> Callable: """为移动市场 API 提供租户化、精确匹配的菜单权限依赖。""" required = (menu_code or "").strip() async def dependency( user: dict = Depends(get_current_user), db: Session = Depends(get_db), ) -> dict: tenant_no = str(user.get("tenant_no") or "").strip() user_id = str(user.get("user_id") or "").strip() if not tenant_no or not user_id: raise HTTPException(status_code=403, detail="用户租户或身份信息缺失") tenant_role = resolve_web_role_for_user( db, user_id, tenant_no=tenant_no ) if tenant_role != "super_admin" and not user_has_tenant_menu_perm( db, user_id=user_id, tenant_no=tenant_no, perm_code=required, ): raise HTTPException(status_code=403, detail="无移动端菜单权限") return user return dependency