Adding IsAuthenticatedOrReadOnly permission in django rest framework - python

Suppose I have the following model -
class Person(models.Model):
name = models.CharField(max_length=200)
clubs = models.ManyToManyField(Club,related_name = 'people')
date = models.DateTimeField(default=datetime.now)
def __str__(self):
return self.name
used to create a rest api.
views.py
class PersonDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = PersonSerializer
def get_object(self):
person_id = self.kwargs.get('pk',None)
return Person.objects.get(pk=person_id)
How do I add permissions so that only authenticated user can add,update delete or retrieve objects from the person list in the api. And read-only permissions for non authorized users. I tried going through the docs but it is all very confusing. Can someone explain?

You need to add IsAuthenticatedOrReadOnly permission class to PersonDetail view.
From the DRF Docs:
The IsAuthenticatedOrReadOnly will allow authenticated users to perform any request. Requests for unauthorised users will only be permitted if the request
method is one of the "safe" methods; GET, HEAD or OPTIONS.
from rest_framework.permissions import IsAuthenticatedOrReadOnly
class PersonDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = PersonSerializer
permission_classes = (IsAuthenticatedOrReadOnly,) # specify the permission class in your view
def get_object(self):
person_id = self.kwargs.get('pk',None)
return Person.objects.get(pk=person_id)

Related

Edit permissions for Django Details View for api calls

I am very new to django and currently trying to generate api calls to localhost:8000/stateapi/id where id is an id for a single "State" object in a json (like 1, 2, etc).
It is using token authentication by passing a username and password to the "get-token" endpoint and then using that token for calls to the stateapi endpoint.
I mostly followed this tutorial https://scotch.io/tutorials/build-a-rest-api-with-django-a-test-driven-approach-part-2
and keep getting a "detail": "You do not have permission to perform this action."
Here are the views where CreateView handles the "localhost:8000/stateapi" endpoint and DetailsView handles the localhost:8000/stateapi/id endpoint.
class CreateView(generics.ListCreateAPIView):
queryset = State.objects.all()
serializer_class = StateSerializer
permission_classes = (permissions.IsAuthenticated,IsOwner)
def perform_create(self, serializer):
"""Save the post data when creating a new State."""
serializer.save(owner=self.request.user)
class DetailsView(generics.RetrieveUpdateDestroyAPIView):
"""This class handles the http GET, PUT and DELETE requests."""
queryset = State.objects.all()
serializer_class = StateSerializer
permission_classes = (permissions.IsAuthenticated,IsOwner)
I can't seem to figure out why the authenticated user has permission to access information from CreateView but not DetailsView.
Here is the permissions code:
class IsOwner(BasePermission):
"""Custom permission class to allow only bucketlist owners to edit them."""
def has_object_permission(self, request, view, obj):
# Our problem is that we do not have a owner property for the object
"""Return True if permission is granted to the bucketlist owner."""
return obj.owner == request.user
Upon testing what happens when DetailsView is called, i've found that obj.owner is "None" when DetailsView is called and obj.owner is correctly equal to request.user whenever CreateView is called which would explain why the authenticated user can make get requests to the endpoint without an id while it cannot for the endpoint with an id.
Are there an suggestions as to how I could either:
a) make sure obj has the correct "owner" property (something that CreateView is doing but DetailsView is not)
b) change my permissions in some way
c) something else I cannot think of.
Thanks!
Can you share your State model and StateSerializer – Iain Shelvington Jun 18 at 3:26
State Model:
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from django.dispatch import receiver
# Create your models here.
# 1 is /, 2 is -, 3 is (, 4 is ), 5 is .
class State(models.Model):
STATE = models.CharField(max_length=30,blank=True,null=True)
Team_Contact = models.CharField(max_length=100,blank=True,null=True)
CONTACT_INFORMATION = models.TextField(blank=True,null=True)
LEGISLATION1EXECUTIVE_ORDER = models.TextField(blank=True,null=True)
TESTING = models.TextField(blank=True,null=True)
TESTING1DEPLOYMENT_REQUIREMENTS_3SELF_CERTIFICATION4 = models.TextField(blank=True,null=True)
PRE2EMPTION = models.TextField(blank=True,null=True)
owner = models.ForeignKey('auth.User', related_name='statelists', on_delete=models.CASCADE,blank=True,null=True)
OVERSIGHT_DEPARTMENT = models.TextField(blank=True,null=True)
INFRASTRUCTURE_DEVELOPMENTS = models.TextField(blank=True,null=True)
CRASHES1SAFETY_INCIDENTS = models.TextField(blank=True,null=True)
DATA1PRIVACY_CONCERNS = models.TextField(blank=True,null=True)
PUBLIC_EDUCATION_FOR_AVS = models.TextField(blank=True,null=True)
LIABILITY1INSURANCE_REQUIREMENTS = models.TextField(blank=True,null=True)
HEALTH1EQUITY_CONCERNS = models.TextField(blank=True,null=True)
MISC5 = models.TextField(blank=True,null=True)
def __str__(self):
"""Return a human readable representation of the model instance."""
return "{}".format(self.STATE)
# This receiver handles token creation immediately a new user is created.
#receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
Serializers:
from rest_framework import serializers
from .models import State
from django.contrib.auth.models import User
class StateSerializer(serializers.ModelSerializer):
"""Serializer to map the Model instance into JSON format."""
# understand exactly what this line does
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
"""Meta class to map serializer's fields with the model fields."""
model = State
fields = ('id','STATE','Team_Contact','CONTACT_INFORMATION','LEGISLATION1EXECUTIVE_ORDER','TESTING',
'TESTING1DEPLOYMENT_REQUIREMENTS_3SELF_CERTIFICATION4','PRE2EMPTION','OVERSIGHT_DEPARTMENT','INFRASTRUCTURE_DEVELOPMENTS',
'CRASHES1SAFETY_INCIDENTS','DATA1PRIVACY_CONCERNS','PUBLIC_EDUCATION_FOR_AVS','LIABILITY1INSURANCE_REQUIREMENTS',
'HEALTH1EQUITY_CONCERNS','MISC5', 'owner')
read_only_fields = ('STATE', 'Team_Contact','CONTACT_INFORMATION')
class UserSerializer(serializers.ModelSerializer):
"""A user serializer to aid in authentication and authorization."""
states = serializers.PrimaryKeyRelatedField(
many=True, queryset=State.objects.all())
class Meta:
"""Map this serializer to the default django user model."""
model = User
fields = ('id', 'username', 'states')
All things here seem to be normal. I think the problem comes from other parts of your code? Or your checked object actually doesn't have any owner linked to it?

Django Restframework has_object_permission() function is not working for object permission

I'm in the process of debugging my custom permissions class and returning a value of False for my has_object_permission() function, but my I'm still able to access my API (GET request), via Restframework's API browser without authenticating and I can't understand why. Any help would be greatly appreciated. Please see code below. for whatever reasons, it appears that my has_object_permission function is not executing. Please Help
urls.py
router = BulkRouter()
router.register(r'api1', SimpleViewSet1)
urlpatterns = [
url(r'^test/', include(router.urls, namespace='api1')),
]
views.py
class SimpleViewSet1(generics.BulkModelViewSet):
queryset = Barcode.objects.all()
permission_classes = (MyUserPermission,)
serializer_class = SimpleSerializer1
def get_queryset(self):
user = User.objects.get(pk=2)
return Barcode.objects.filter(owner = user)
def get_object(self):
obj = get_object_or_404(self.get_queryset())
self.check_object_permissions(self.request, obj)
return obj
permissions.py
class MyUserPermission(BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return False
serializer.py
class SimpleSerializer1(BulkSerializerMixin, # only required in DRF3
ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta(object):
model = Barcode
# only required in DRF3
list_serializer_class = BulkListSerializer
fields = ('barcode_number', 'barcode_type', 'owner')
models.py
#python_2_unicode_compatible
class Barcode(models.Model):
owner = models.ForeignKey('auth.User', related_name = 'barcodes')
barcode_number = models.CharField(max_length=200)
barcode_type = models.CharField(max_length=200)
def __str__(self):
return self.barcode_number
Django Rest API Guide says:
Also note that the generic views will only check the object-level permissions for views that retrieve a single model instance. If you require object-level filtering of list views, you'll need to filter the queryset separately. See the filtering documentation for more details.
rest_framework.generics.BulkModelViewSet, as it's name suggests,does bulk operations. It means that you have to use object-level filtering as proposed in the docs.
You should be looking especially under this section. Pay close attention to the example and make use of the code. You should also read about the DjangoModelPermissions to understand how does the example in the link above works.

Django Rest Framework permission_class

I am new to DRF, and I had came across the following problem when I trying to customize the Permission in DRF.
Suppose I had the following code in my permissions.py file:
class GetPermission(BasePermission):
obj_attr = 'POITS'
def has_permission(self, request, view):
user = request.user
employee = Employee.objects.get(user=user)
# Return a dict which indicates whether the request user has the corresponding permissions
permissions = get_permission(employee.id)
return permissions[GetPermission.obj_attr]
And in my view, I want to override the static variable in the GetPermission class:
class AssignmentList(generics.ListCreateAPIView):
GetPermission.obj_attr = 'ASSIGNMENT'
permission_classes = (IsAuthenticated, IsStaff, GetPermission)
queryset = Assignment.objects.all()
serializer_class = AssignmentSerializer
pagination_class = LargeResultsSetPagination
def perform_create(self, serializer):
employee = Employee.objects.get(user=self.request.user)
serializer.save(sender=employee, status=0, operatable=0)
However, as the documentation of DRF points out:
Permission checks are always run at the very start of the view, before any other code is allowed to proceed.
So how am I supposed to do, thanks in advance, any ideas will be welcomed, since I am a new fish to DRF.
You need to create subclasses of your permission for each attribute, and use self.obj_attr inside the has_permission method.
class DefaultPermission(GetPermission):
obj_attr = 'POITS'
class AssignmentPermission(GetPermission):
obj_attr = 'ASSIGNMENT'

Sending patch request to Django Rest Framework API with Foreign Key

I need to send put request to API for which I know only the foreign key.
How am supposed to do this.
models.py
class Company(models.Model):
name = models.CharField(max_length = 100)
user = models.OneToOneField(settings.AUTH_USER_MODEL, unique = True)
serializer.py
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ('id', 'name','user')
views.py
class Company(object):
permission_classes = (IsAuthenticated,IsUserThenReadPatch)
serializer_class = CompanySerializer
def get_queryset(self):
user = self.request.user
return Company.objects.filter(user = user)
class CompanyDetails(Company, RetrieveUpdateAPIView, APIView):
pass
urls.py
url(r'^company/$',views.CompanyDetails.as_view()),
In order to enable all CRUD operations in DRF you probably want to use ViewSet instead of View:
# views.py
class CompanyViewSet(ViewSet):
permission_classes = (IsAuthenticated,IsUserThenReadPatch)
serializer_class = CompanySerializer
def get_queryset(self):
user = self.request.user
return Company.objects.filter(user = user)
# urls.py
router = routers.SimpleRouter()
router.register(r'company', CompanyViewSet)
urlpatterns = router.urls
The above will allow you do send all CRUD REST requests:
GET /company - list all companies
POST /company - create a company
GET /company/:id - get a single company
PUT/POST /company/:id - update a company
PATCH /company/:id - partially update a company
DELETE /company/:id - delete a company
You can read more in the DRF docs - viewsets and routers

Basic Django REST framework - Write only resource

I am trying to create a simple service which allows anonymous users to submit their name and email. I want to AllowAny on adding their info, and IsAuthenticated on everything else. I'm having trouble getting this granularity.
models.py
from django.db import models
class Invitee(models.Model):
name = models.CharField(max_length=255)
email = models.EmailField(max_length=70,blank=True)
modified = models.DateTimeField(auto_now=True)
serializers.py
class InviteeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Invitee
fields = ('name', 'email')
def create(self, validated_data):
return Invitee(**validated_data)
views.py
class InviteeViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Invitee.objects.all()
serializer_class = InviteeSerializer
What and where should I put to make it so users can submit their name and email, but only admins can read, update, delete? Thanks any help.
The easiest and safest way to do this is with multiple serializers, one of each user class you need. You will also need to use custom permissions to enforce the read/write difference between authenticated and anonymous users.
class InviteeSerializer(serializers.HyperlinkedModelSerializer):
def create(self, validated_data):
return Invitee(**validated_data)
class LimitedInviteeSerializer(InviteeSerializer):
class Meta:
model = Invitee
fields = ('name', 'email', ) # a limited subset of the fields
class FullInviteeSerializer(InviteeSerializer):
class Meta:
model = Invitee
fields = ('name', 'email', "modified", ) # all of the fields
While right now it only looks like you need read/write, if you need full read/write/delete permissions I would recommend reading this Stack Overflow question.
You will also need to control what serializer is being used on the view level. This needs to be done using a combination of permissions and overridden view methods.
class InviteeViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Invitee.objects.all()
serializer_class = LimitedInviteeSerializer
def get_serializer_class(self):
if self.request.user.is_active:
return FullInviteeSerializer
return super(InviteeViewSet, self).get_serializer_class()
On the view you will need to override get_serializer_class to determine what serializer should be used based on if the user is active. Anonymous users should never be marked as active, so this is the best way to check while excluding deactivated accounts.
You will also need to create a custom permissions class that will do the opposite of the built-in IsAuthenticatedOrReadOnly permission. You are looking for authenticated users to do everything, and anonymous users to only be write-only. I've called this class IsAuthenticatedOrWriteOnly to match the other permission class.
class IsAuthenticatedOrWriteOnly(BasePermission):
"""
The request is authenticated as a user, or is a write-only request.
"""
def has_permission(self, request, view):
WRITE_METHODS = ["POST", ]
return (
request.method in WRITE_METHODS or
request.user and
request.user.is_authenticated()
)
You just need to add this your existing list of permission classes, or override it manually it on the view using permission_classes.

Categories

Resources