Include Loyalty/Communication/Sports Center backends and registry updates alongside production nginx and compose wiring. Co-authored-by: Cursor <cursoragent@cursor.com>
53 lines
1.7 KiB
Python
53 lines
1.7 KiB
Python
"""Permission enforcement helpers for Loyalty APIs."""
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
|
|
from fastapi import Depends
|
|
|
|
from app.core.config import settings
|
|
from app.core.security import get_current_user
|
|
from shared.exceptions import ForbiddenError
|
|
from shared.security import CurrentUser
|
|
|
|
_ADMIN_ROLES = frozenset(
|
|
{"platform_admin", "tenant_owner", "tenant_admin"}
|
|
)
|
|
|
|
|
|
def user_has_permission(user: CurrentUser, permission: str) -> bool:
|
|
if any(role in _ADMIN_ROLES for role in user.roles):
|
|
return True
|
|
roles = set(user.roles)
|
|
if "loyalty.manage" in roles or permission in roles:
|
|
return True
|
|
# Platform-wide loyalty.view grants any *.view leaf.
|
|
if "loyalty.view" in roles and permission.endswith(".view"):
|
|
return True
|
|
# Resource manage (e.g. loyalty.programs.manage) grants that tree.
|
|
parts = permission.split(".")
|
|
if len(parts) >= 3:
|
|
manage = f"{parts[0]}.{parts[1]}.manage"
|
|
if manage in roles:
|
|
return True
|
|
return False
|
|
|
|
|
|
def require_permissions(*permissions: str) -> Callable:
|
|
"""Deny unless AUTH is off, user is admin, or any listed permission is present."""
|
|
|
|
async def _dependency(
|
|
user: CurrentUser = Depends(get_current_user),
|
|
) -> CurrentUser:
|
|
if not settings.auth_required:
|
|
return user
|
|
if any(user_has_permission(user, perm) for perm in permissions):
|
|
return user
|
|
raise ForbiddenError(
|
|
"دسترسی مجاز نیست",
|
|
error_code="permission_denied",
|
|
details={"required": list(permissions)},
|
|
)
|
|
|
|
return _dependency
|