-
-
Notifications
You must be signed in to change notification settings - Fork 144
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Feature Request Type
- Core functionality
- Alteration (enhancement/optimization) of existing feature(s)
- New behavior
Description
In Django it is possible to combine different permissions to manage access to objects more flexibly. In one of my projects I implemented a similar mechanism, which looks like this:
from django.conf import settings
User = settings.AUTH_USER_MODEL
class AND:
def __init__(self, op1, op2):
self.op1 = op1
self.op2 = op2
def has_permission(self, user: User) -> bool:
return self.op1.has_permission(user=user) and self.op2.has_permission(user=user)
class OR:
def __init__(self, op1, op2):
self.op1 = op1
self.op2 = op2
def has_permission(self, user: User) -> bool:
return self.op1.has_permission(user=user) or self.op2.has_permission(user=user)
class OperationHolderMixin:
def __and__(self, other):
return OperandHolder(AND, self, other)
def __or__(self, other):
return OperandHolder(OR, self, other)
class OperandHolder(OperationHolderMixin):
def __init__(self, operator_class, op1_class, op2_class):
self.operator_class = operator_class
self.op1_class = op1_class
self.op2_class = op2_class
def __call__(self, *args, **kwargs):
op1 = self.op1_class(*args, **kwargs)
op2 = self.op2_class(*args, **kwargs)
return self.operator_class(op1, op2)
def __eq__(self, other):
return (
isinstance(other, OperandHolder)
and self.operator_class == other.operator_class
and self.op1_class == other.op1_class
and self.op2_class == other.op2_class
)
def __hash__(self):
return hash((self.operator_class, self.op1_class, self.op2_class))
class BasePermissionMetaclass(OperationHolderMixin, type):
pass
class BasePermission(metaclass=BasePermissionMetaclass):
def has_permission(self, user: User) -> bool:
return True
class HasPermissions(DjangoPermissionExtension):
def __init__(self, *args, permission_classes: Iterable, **kwargs):
super().__init__(*args, **kwargs)
self.permission_classes = permission_classes
def resolve_for_user( # pragma: no cover
self,
resolver: Callable,
user: UserType | None,
*,
info: Info,
source: Any,
) -> AwaitableOrValue[Any]:
if not user.is_authenticated or not user.is_active:
raise DjangoNoPermission
permissions = [permission() for permission in self.permission_classes]
for permission in permissions:
if not permission.has_permission(user=user):
raise DjangoNoPermission
return resolver()
class HasPerm1(BasePermission):
def has_permission(self, user: User):
return user.has_perm_1
class HasPerm2(BasePermission):
def has_permission(self, user: User):
return user.has_perm_2
Usage example:
@strawberry_django.mutation(
extensions=[HasPermissions(permission_classes=(HasPerm1 | HasPerm2,))]
)
...
What do you think about this?
If you are interested in this idea, let me know and I can try to contribute myself.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request