I have written a custom permission
class IsObjectOwner(permissions.BasePermission):
def has_permission(self, request, view):
if ...
.....
return False
else:
return True
And I am trying to apply this on a few particular view definitions.
URLs.py
url(r'^get_possible_moves/', include('GameView.get_possible_moves')),
url(r'^get_available_pieces/', include('GameView.get_available_pieces')),
url(r'^remove_pieces/', include('GameView.remove_pie
ces')),
views.py
class GameView(viewsets.ModelViewSet):
queryset = GameModel.objects.all()
serializer_class = GameSerializer
def get_available_pieces(self,request,*args,**kwargs):
permission_classes(IsObjectOwner,)
....
def get_possible_moves(self,request,*args,**kwargs):
permission_classes(IsObjectOwner,)
.....
def remove_pieces(self,request,*args,**kwargs):
permission_classes(IsObjectOwner,)
......
.......
But the permission is not working on the view and the permission doesn't seem to be getting invoked. I would like to know how to implement custom permission on view definition, any help is appreciated.
Note: There are other definitions too in this view, on which I do not want to impose the permissions. So, I cannot put the permissions at view level.
And this is not a game of chess.
Set the permissions_classes to the entire viewset and not just to the functions.
class GameView(viewsets.ModelViewSet):
permission_classes = (IsObjectOwner,)
queryset = GameModel.objects.all()
serializer_class = GameSerializer
def get_available_pieces(self,request,*args,**kwargs):
....
return
def get_possible_moves(self,request,*args,**kwargs):
....
return
def remove_pieces(self,request,*args,**kwargs):
....
return
Related
The below is an api that required authen. It works
class some_random_api(generics.GenericAPIView):
permission_classes = (IsAuthenticated, )
def get(self,request):
return HttpResponse("Called successfully")
However, I dont like declaring variable. After looking up this document, https://www.django-rest-framework.org/api-guide/permissions/ . I find an alternative way by using decorator. So I change my code into this.
from rest_framework.decorators import permission_classes
class some_random_api(generics.GenericAPIView):
#permission_classes(IsAuthenticated)
def get(self,request):
return HttpResponse("You call random test api")
Now this API does not check Authen
The decorator #permission_classes is only applicable to function-based API views as documented.
REST framework provides a set of additional decorators which can be added to your views. These must come after (below) the #api_view decorator.
The available decorators are:
...
#permission_classes(...)
...
Also here:
Or, if you're using the #api_view decorator with function based views.
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def example_view(request, format=None):
...
An alternative to defining a fixed list permission_classes = (IsAuthenticated, ) is to override the get_permissions() and assign different permissions for the different HTTP methods as described here.
class some_random_api(generics.GenericAPIView):
def get_permissions(self):
if self.request.method == "GET": # If using viewsets, the self.action can be used e.g. <if self.action == "retrieve":>
permission_classes = [permissions.IsAuthenticated]
else:
permission_classes = [permissions.IsAdminUser]
return [permission() for permission in permission_classes]
def get(self, request):
return HttpResponse("You call random test api")
Related questions:
Django Rest Framework - GenericViewSet with Authentication/Permission decorator
Django REST Framework - Separate permissions per methods
I'm building a DRF API, and I would like to allow staff members (is_staff == True) to access all of the REST endpoints, while still providing custom permission checking per ViewSet. Ideally, this would be a global setting, but I'm not against setting it up per ViewSet.
Here are the things I've tried:
Option 1: Check on every custom permission
from rest_framework import permissions
class SomeModelPermission(permissions.BasePermission):
def has_permission(self, request, view):
if request.user.is_staff:
return True
# other logic
def has_object_permission(self, request, view, obj):
if request.user.is_staff:
return True
# other logic
This works, but I'd rather not repeat so much code.
Option 2: Bitwise operators
I tried removing the is_staff logic from the custom permission above, and adding this to the ViewSet:
from rest_framework import permissions, viewsets
class SomeModelViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAdminUser|SomeModelPermission,)
However, this actually does not enforce permissions as I'd like, because IsAdminUser inherits from BasePermission, which is defined as:
class BasePermission(object):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return True
IsAdminUser doesn't define its own has_object_permission, so it will always return True when checking object permissions, resulting in unintended object access.
Any ideas? I was hoping there would be some way I could set a global permissions check that would return True when the user is a staff member, and defer to the custom permissions otherwise. But reading through how permissions are determined, I'm not sure that this is possible.
Bitwise solution:
How about creating your own IsAdminUser which also defines has_object_permission ? You could just inherit from the existing one:
from rest_framework.permissions import IsAdminUser as BaseIsAdminUser
class IsAdminUser(BaseIsAdminUser):
def has_object_permission(self, request, view, obj):
# Just reuse the same logic as `has_permission`...
return self.has_permission(request, view)
Then you can do what you attempted above, with the bitwise operator:
from rest_framework import permissions, viewsets
from your_own_project.permissions import IsAdminUser
class SomeModelViewSet(viewsets.ModelViewSet):
permission_classes = (IsAdminUser|SomeModelPermission,)
Another solution:
A bit "hacky" in some ways, but you could try to create your own permission types on the fly.
So the end result would look something like:
class SomeModelViewSet(viewsets.ModelViewSet):
permission_classes = skip_for_staff((SomeModelPermission, SomeOtherPermission, ...))
With the implementation something similar to:
class StaffAllowedMixin:
def has_permission(self, request, view):
if request.user.is_staff:
return True
return super().has_permission(request, view)
def has_object_permission(self, request, view, obj):
if request.user.is_staff:
return True
return super().has_object_permission(request, view, obj)
def skip_for_staff(permission_classes):
# You can probably also use a comprehension here, but for clarity:
staff_allowed_classes = []
for permission_class in permissions(
staff_allowed_classes.append(
# Create a new type (class) with name StaffAllowed<...>
type(f"StaffAllowed{permission_class}",
# Inherit from the mixin above, and from the original class
(StaffAllowedMixin, permission_class),
# empty dictionary means you don't want to override any attributes
{})
)
return tuple(staff_allowed_classes)
Essentially, for each permission class, you create a new class with the extra mixin that takes precedence and checks if the user is staff.
But you do that on the fly, where your permissions are used, instead of having to predefine it for every permission.
There is a permission class for admin users.
Here is an example of that:
class deletecompletedreopenjobAPIView(RetrieveUpdateAPIView):
queryset = Job.objects._all()
serializer_class = JobCompletedDeleteStatusSerializer
lookup_field = 'job_id'
permission_classes = [IsOwnerOrReadOnly | **IsAdminUser**]
authentication_classes = (authentication.TokenAuthentication,)
My custom IsOwnerOrReadOnly is as follows:
class IsOwnerOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
return obj.user == request.user
I have defined the following models
class Flight(models.Model):
...
class FlightUpdate(models.Model):
flight = models.ForeignKey('Flight', related_name='updates')
...
and the following viewset using the NestedViewsetMixin in the REST Framework Extensions
class FlightUpdateViewSet(mixins.ListModelMixin,
mixins.CreateModelMixin,
NestedViewSetMixin,
viewsets.GenericViewSet):
"""
API Endpoint for Flight Updates
"""
queryset = FlightUpdate.objects.all()
serializer_class = FlightUpdateSerializer
def create(self, request, *args, **kwargs):
flight = Flight.objects.get(pk=self.get_parents_query_dict()['flight'])
...
So, to access the FlightUpdates associated with a Flight, the URL is /flights/1/updates/.
I want to ensure that people can only create FlightUpdates if they have the permissions to change the Flight object with which the FlightUpdate is associated.
How would I go about performing the extra check when adding a FlightUpdate? I've tried adding something like this in the viewset, but I'm not sure if it's the best way.
if not request.user.has_perm('flights.change_flight', flight):
raise PermissionError()
Note: I'm using django-rules for the object-level permissions implementation.
I solved this problem by implementing a custom permissions class.
from django.core.exceptions import ObjectDoesNotExist
from rest_framework.permissions import BasePermission, SAFE_METHODS
from .models import Flight
class FlightPermission(BasePermission):
def has_permission(self, request, view):
if request.method in SAFE_METHODS:
return True
try:
flight = Flight.objects.get(pk=view.kwargs['parent_lookup_flight'])
except ObjectDoesNotExist:
return False
return request.user.has_perm('flights.change_flight', flight)
It seems that permission classes are ANDed when REST framework checks permissions. That is every permission class needs to return True for permission to be granted. This makes things like "if you are a superuser, you can access anything, but if you are a regular user you need explicit permissions" a bit hard to implement, you cannot just return False, it will fail the whole stack. Is there a way to maybe short-circuit permissions? Something like "if this permission is granted, stop checking?" or some other way to deal with cases like that?
Now DRF allows permissions to be composed using bitwise operators: & -and- and | -or-.
From the docs:
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.
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.
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,)
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:
rest_condition
djangorestframework-composed-permissions
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))
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
...
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],
)
I am using http://www.django-rest-framework.org/
I have the scenario where I want to pass two or more variables based on that I need to fetch data from database. In the following code only pk is there which I want to replace with two other fields in database.
Also please suggest how can I write my urlconfig the same.
Views.py
class ExampleViewSet(viewsets.ReadOnlyModelViewSet):
model = myTable
def list(self, request):
queryset = myTable.objects.all()
serializer = mySerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = myTable.objects.all()
s = get_object_or_404(queryset, pk=pk)
serializer = mySerializer(s)
return Response(serializer.data)
Serializer.py
class Serializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = myTable
fields = ('attr1', 'attr2', 'attr3')
Here is how you would do it with the recent Django REST Framework.
Assuming your variables are in the resource URLs like so:
GET /parent/:id/child/
GET /parent/:id/child/:id/
Then:
urls.py:
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'parent/(?P<parent_id>.+)/child', views.ExampleViewSet)
urlpatterns = router.urls
views.py:
class ExampleViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = Serializer
def get_queryset(self):
parent = self.kwargs['parent']
return myTable.objects.filter(parent=parent)
Where the 'parent' in the queryset part is your parent object. You may need to adjust it a little, of course, but the idea is encapsulated in the kwargs.
This solution will also save you a little code and you can make it into a full blown ModelViewSet just by subclassing it.
Hope that helps.
More here: DRF Filtering against the URL.
Here is an example of how you might implement what you want:
class ExampleViewSet(viewsets.ReadOnlyModelViewSet):
# This code saves you from repeating yourself
queryset = myTable.objects.all()
serializer_class = mySerializer
def list(self, request, *args, **kwargs):
# Get your variables from request
var1 = request.QUERY_DICT.get('var1_name', None) # for GET requests
var2 = request.DATA.get('var2_name', None) # for POST requests
if var1 is not None:
# Get your data according to the variable var1
data = self.get_queryset().filter(var1)
serialized_data = self.get_serializer(data, many=True)
return Response(serialized_data.data)
if var2 is not None:
# Do as you need for var2
return Response(...)
# Default behaviour : call parent
return super(ExampleViewSet, self).list(request, *args, **kwargs)
def retrieve(self, request, *args, **kwargs):
# Same for retrieve
# 1. get your variable xyz from the request
# 2. Get your object based on your variable's value
s = myTable.objects.get(varX=xyz)
# 3. Serialize it and send it as a response
serialized_data = self.get_serializer(s)
return Response(serialized_data.data)
# 4. Don't forget to treat the case when your variable is None (call parent method)
As for the urlconf, it depends on how you want to send your variables (get, post or through the url).
Hope this helps.
urls.py
url(
regex=r'^teach/(?P<pk>\d+?)/(?P<pk1>\d+?)/$',
view=teach_update.as_view(),
name='teach'
)
Templates
<td><a href="/teach/{{tid}}/{{i.id}}"><button type="button" class="btn
btn-warning">Update</button></a></td>
Views.py
class teach_update(view):
def get(self,request,**kwargs):
dist=self.kwargs['pk']
ddd=self.kwargs['pk1']