Get kwargs in Tastypie custom authorization - django

I would like to get kwargs in Tastypie custom authorization. I've to authorize whether user has access to the id in URL kwargs.
Authorization methods doesn't seem to pass kwargs but passes only bundle and object_list.

As you said, a custom Authorization does not has **kwargs in the signature.
But you can access URL parameters (like id) using bundle.request.
This kind of example bellow should work:
class RestrictedIdAuthorization(Authorization):
def read_detail(self, object_list, bundle):
param_id = bundle.request.GET['id']
accepted_ids = [42, 54, 67] # Must be changed, of course.
return param_id in accepted_ids
You can take a look to this post to have another example.

Related

Named url kwargs in authenticate method

I'm trying to create a custom authentication class in the Django rest framework. In the authenticate(self, request) method, at first glance, it seems that there is no way to get named kwarg from the URL. Is there a way to accomplish this?
You can get url via request.get_full_path():
def authenticate(self, request):
...
url = request.get_full_path()
Check django docs in https://docs.djangoproject.com/en/3.2/topics/auth/customizing/#writing-an-authentication-backend you might find it useful

django rest framework - allow only certain methods

I have a resource where i only want to allow a client to do a post request on the resource, thats why i use
class MyViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
in my viewset.
When i do a post request, it works as expected.
When i do a list request, it throws a 405 response, as expected.
When i do a retrieve, put, patch or delete method, it throws a 404 instead of a 405...why?
How can i make every single request return a 405 despite of the post request?
thanks and greetings!
Use http_method_names attribute
class MyViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
http_method_names = ['post']
# your code
There doesn't seem to be any reason to use a ViewSet if you only want to support a single action. Instead, use a CreateApiView with a specific URL pointing to it.
If you viewset doesn't have any detail methods, then the drf SimpleRouter will not create any url route for /api/basename/{id}/
So Django's url dispatcher will not match those urls at all, and returns a 404.
I don't think it makes sense to return 405 for every single method. That status implies that at least one method should be valid for a specific url.
You could add a dummy detail method, but just hand all requests over to the APIView 405 handler.
I think this should force the router to register detail urls for the viewset, and just return 405 for everything (possibly with the exception of OPTIONS).
class MyViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
def retrieve(self, request, *args, **kwargs):
return self.http_method_not_allowed(request, *args, **kwargs)

Unable to enforce authentication requirement in Django Rest Framework

I'm trying to enforce Authentication for access to my API, but the permission_classes = permissions.IsAuthenticated,) attribute of my ViewSet doesn't seem to be working. Because I try to access the User's profile later on when in get_queryset(), I'm getting errors like:
TypeError: 'AnonymousUser' object is not iterable"
I also have authentication set in my rest_framework settings:
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
And I also added to my dispatch method before doing the other business logic checks on the User profile:
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return Response(status=status.HTTP_401_UNAUTHORIZED)
That's not working either:
File "/lib/python3.5/site-packages/rest_framework/response.py", line 57, in rendered_content
assert renderer, ".accepted_renderer not set on Response"
AssertionError: .accepted_renderer not set on Response
Why do none of these authentication controls work?
Edit: here's a simplified version of get_queryset():
kwargs = {}
for k,v in self.request.query_params.items():
if not validate_that_key_is_an_actual_model_field(k):
return Response(status=status.HTTP_400_BAD_REQUEST)
kwargs[k] = v
return Product.objects.filter(fk_client=self.client)\
.filter(**kwargs)\
.order_by('uuid')\
.select_related('fk_user')
It turns out I was doing some other things in dispatch() that were executing before the super class's dispatch was called, and hence accessing the User profile before a Http401 could be returned by the super class. Additionally, because the super class's dispatch hadn't been called yet, the renderer hadn't been initialized properly (depending on the request's content headers) and so that was the source of the AssertionError: accepted_renderer not set on Response.

Django Tastypie Authentication for prepend_urls methods

My application has several user types admin, user and manager.
I have defined an endpoint for a Resource, which has several prepend_urls.
eg: the endpoints would be
/Profile/search/
/Profile/shortview/
/Profile/
How can I limit access to the endpoints such that
/Profile/search/ is accessible to admin, manager
/Profile/shortview/ is accessible to all
/Profile/ is accessible to admin only
I have thought of using my own class but for authentication and authorization but think they are applied to the entire resource not individual prepend_url endpoints.
authorization = MyAuthorization()
authentication= MyAuthentication()
Any help is appreciated
I'm going to suppose you have been configured your prepend_urls and hence you have wrapped a function called dispatch_search, so something like this will raise an exception if user is unauthorized to use the endpoint:
def dispatch_search(self, request, *args, **kwargs):
# check authorization here
self._meta.authorization.is_authorized(request)
Edited from here below
When inheriting from the DjangoAuthorization class, you also can override the methods:
read_detail(self, object_list, bundle)
read_list(self, object_list, bundle)
to raise an exception if user should not be able to read an specific resource or the resource list itself.
And your MyAuthorization class:
from tastypie.exceptions import Unauthorized
from tastypie.authorization import DjangoAuthorization
class MyAuthorization(DjangoAuthorization):
def is_authorized(self, request):
if request.user.is_superuser and 'search' in request.path:
return True
# more business logic here to check the other endpoints
raise Unauthorized('Unauthorized :(')
def read_list(self, object_list, bundle):
self.is_authorized(bundle.request) # call your custom validation
# Fallback to the DjangoAuthorization read_list
return super(MyAuthorization, self).read_list(object_list, bundle)
Refer to the docs for a complete list of functions you can override to add more business logic: http://django-tastypie.readthedocs.org/en/latest/authorization.html#the-authorization-api

Authentication to create route Django

I have a model, called rides, which I want to have access to my custom token authentication. I do not want this to be a made public to the whole viewset.
How can I add the authentication method to the create method? The below throws an error complaining I can't add a list_route to a create method as it exists already.
class RideViewSet(viewsets.ModelViewSet):
# POST /rides/
#list_route(methods=['post'], authentication_classes=[CustomTokenAuth])
def create(self, request, *args, **kwargs):
The decorator won't work on the ViewSet's list / create / ...
You'll need to deal with the authenticate by yourself.
Therefore you need to fill the DRF's request with:
request._authenticator as the auth backend that has been doing the auth
request.user, request.auth as the result of your auth backend's authenticate()