How to get OR permissions instead of AND in REST framework

It seems that the permission classes are ANDed when the REST framework checks the permissions. This means that for each permission, you must return True for the permission. It does things like "if you are a superuser, you can access anything, but if you are a regular user, you need explicit permissions" that are difficult to implement, you cannot just return False, this will crash the entire stack. Is there a way to resolve a short circuit? Something like "if this permission is granted, stop checking?" or some other way to deal with such cases?

+11
source share
6 answers

I think you could use the django-rules library here. Link

This is a rule-based mechanism, very similar to decision trees, and can be easily integrated with the frameworksclass framework DRF.

The best part is that you can perform operations on simple permissions and create complex permissions from them.

Example

 >>> @rules.predicate >>> def is_admin(user): ... return user.is_staff ... >>> @rules.predicate >>> def is_object_owner(user, object): return object.owner == user 

Predicates can do almost anything with given arguments, but should always return True if the condition they are checking is true, False otherwise. Now combine these two predicates.

 is_object_editable = is_object_owner | is_admin 

You can use this new predicate rule is_object_editable inside your has_permissions permission class method.

+3
source

Now DRF allows you to compose permissions using bitwise operators: & -and- and | -or-.

From the docs :

Provided that they inherit from rest_framework.permissions.BasePermission , permissions can be compiled using standard Python rest_framework.permissions.BasePermission operators. For example, IsAuthenticatedOrReadOnly could be written:

 from rest_framework.permissions import BasePermission, IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView class ReadOnly(BasePermission): def has_permission(self, request, view): return request.method in SAFE_METHODS class ExampleView(APIView): permission_classes = (IsAuthenticated|ReadOnly,) def get(self, request, format=None): content = { 'status': 'request was permitted' } return Response(content) 

Edited: note that there is a comma after IsAuthenticated|ReadOnly .

+7
source

You need to create your own custom http://www.django-rest-framework.org/api-guide/permissions/#custom-permissions , as described in the docs.

Sort of:

 from rest_framework import permissions class IsAdminOrStaff(permissions.BasePermission): message = 'None of permissions requirements fulfilled.' def has_permission(self, request, view): return request.user.is_admin() or request.user.is_staff() 

Then, in your opinion:

 permission_classes = (IsAdminOrStaff,) 
+3
source

Besides the user permission, which is the simpler approach mentioned in the previous answer, you can also find an existing third party that handle very complex permission processing if necessary.

As of February 2016, those handling the resolution of a complex condition include:

+2
source

One way would be to add another class that combines the existing classes the way you want, for example:

 class IsAdmin(BasePermission): """Allow access to admins""" def has_object_permission(self, request, view, obj): return request.user.is_admin() class IsOwner(BasePermission): """Allow access to owners""" def has_object_permission(self, request, view, obj): request.user.is_owner(obj) class IsAdminOrOwner(BasePermission): """Allow access to admins and owners""" def has_object_permission(*args): return (IsAdmin.has_object_permission(*args) or IsOwner.has_object_permission(*args)) 
+2
source

Here is a general solution:

 from functools import reduce from rest_framework.decorators import permission_classes from rest_framework.permissions import BasePermission def any_of(*perm_classes): """Returns permission class that allows access for one of permission classes provided in perm_classes""" class Or(BasePermission): def has_permission(*args): allowed = [p.has_permission(*args) for p in perm_classes] return reduce(lambda x, y: x or y, allowed) return Or class IsAdmin(BasePermission): """Allow access to admins""" def has_object_permission(self, request, view, obj): return request.user.is_admin() class IsOwner(BasePermission): """Allow access to owners""" def has_object_permission(self, request, view, obj): request.user.is_owner(obj) """Allow access to admins and owners""" @permission_classes((any_of(IsAdmin, IsOwner),)) def you_function(request): # Your logic ... 
0
source

Source: https://habr.com/ru/post/1243586/


All Articles