5๐
I think you might be able to use django-rules
library here. Link
It is a rule based engine very similar to decision trees and it can be easily integrated with permissions_class framework of DRF.
The best part is you can perform set 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 pretty much anything with the given arguments, but must always return True if the condition they check is true, False otherwise.
Now combining 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 method of permission class.
60๐
Now DRF allows permissions to be composed using bitwise operators: & -and- and | -or-.
Provided they inherit from
rest_framework.permissions.BasePermission
, permissions can be composed using standard Python bitwise 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: Please note there is a comma after IsAuthenticated|ReadOnly
.
- [Django]-Accessing the user's request in a post_save signal
- [Django]-How to aggregate (min/max etc.) over Django JSONField data?
- [Django]-Django: Populate user ID when saving a model
2๐
You need to build your own custom http://www.django-rest-framework.org/api-guide/permissions/#custom-permissions as described in the docs.
Something like:
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 view:
permission_classes = (IsAdminOrStaff,)
- [Django]-How to write django test meant to fail?
- [Django]-Are Django SECRET_KEY's per instance or per app?
- [Django]-CORS error while consuming calling REST API with React
2๐
Aside from the custom permission which is simpler approach mentioned in the earlier answer, you can also look for an existing 3rd party that handle a much complex permission handling if necessary.
As of Feb 2016, those handling complex condition permission includes:
- [Django]-Django Rest Framework with ChoiceField
- [Django]-How to store a dictionary on a Django Model?
- [Django]-Django 1.4 timezone.now() vs datetime.datetime.now()
2๐
One way would be to add another permission class which combines existing classes the way you want it, e.g.:
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))
- [Django]-Django: show a ManyToManyField in a template?
- [Django]-How to use the 'reverse' of a Django ManyToMany relationship?
- [Django]-AttributeError: 'module' object has no attribute 'tests'
1๐
Here is a generic 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
...
- [Django]-Django: Record with max element
- [Django]-How to get POST request values in Django?
- [Django]-STATIC_ROOT vs STATIC_URL in Django
0๐
The easiest way would be to separate them out with |
in permission_classes
attribute or get_permissions
method, but if you have complex rules, you can specify those rules in the check_permissions
method in the viewsets class that you are defining. Something like this:
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = ProfileSerializerBase
def create(self, request, *args, **kwargs):
# Create rules here
def get_permissions(self):
if self.action == "destroy":
# Only Super User or Org Admin can delete record
permission_classes = [SAPermission, OAPermission]
def check_permissions(self, request):
"""
Original check_permissions denies access if any one of the permission
classes returns False, changing it so that it would deny access only if
all classes returns False
"""
all_permissions = []
messages = []
code = []
for permission in self.get_permissions():
all_permissions.append(permission.has_permission(request, self))
messages.append(getattr(permission, "message", None))
code.append(getattr(permission, "code", None))
if True in all_permissions:
return
message = ",".join(i for i in messages if i)
self.permission_denied(
request,
message=message if message else None,
code=code[0],
)
- [Django]-Django model inheritance: create sub-instance of existing instance (downcast)?
- [Django]-The best/recommended way to translate Django database values
- [Django]-Matching query does not exist Error in Django