infrasynth-backend-kit/infrasynth/security/services.py
2026-08-28 14:38:47 -05:00

69 lines
2.5 KiB
Python

from django.db.models import Q
from django.utils import timezone
from .models import Grant, Revoke
class AuthorizationService:
"""Singleton service for permission resolution."""
def _get_system_user_scopes(self, user) -> set[str] | None:
from .auth.api_keys import SystemUser
if isinstance(user, SystemUser):
return set(user.scopes)
return None
def has_permission(self, user, codename: str) -> bool:
if not user or not user.is_authenticated:
return False
if user.is_superuser:
return True
system_scopes = self._get_system_user_scopes(user)
if system_scopes is not None:
return codename in system_scopes
if Revoke.objects.filter(user=user, codename=codename).exists():
return False
if (
Grant.objects.filter(user=user, codename=codename)
.filter(Q(expires_at__isnull=True) | Q(expires_at__gt=timezone.now()))
.exists()
):
return True
user_roles = self._get_role_permission_lists(user)
for perm_list in user_roles:
if codename in (perm_list or []):
return True
return False
def get_effective_permissions(self, user) -> set[str]:
if not user or not user.is_authenticated:
return set()
if user.is_superuser:
return {"*"}
system_scopes = self._get_system_user_scopes(user)
if system_scopes is not None:
return system_scopes
revoked = set(Revoke.objects.filter(user=user).values_list("codename", flat=True))
granted = set(
Grant.objects.filter(user=user)
.filter(Q(expires_at__isnull=True) | Q(expires_at__gt=timezone.now()))
.values_list("codename", flat=True)
)
role_perms = set()
for perm_list in self._get_role_permission_lists(user):
role_perms.update(perm_list or [])
return (granted | role_perms) - revoked
@staticmethod
def _get_role_permission_lists(user) -> list[list[str]]:
roles = getattr(user, "roles", None)
if roles is None:
return []
return list(roles.values_list("permissions", flat=True))
def has_all_permissions(self, user, codenames: list[str]) -> bool:
return all(self.has_permission(user, c) for c in codenames)
def has_any_permission(self, user, codenames: list[str]) -> bool:
return any(self.has_permission(user, c) for c in codenames)