69 lines
2.5 KiB
Python
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)
|