Authentication to create route Django - 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()

Related

How to use swagger_auto_schema in a class based view with no serializer_class and how to add custom authentication permission to it?

I have a class based view as:
class ClassBasedView(GenericAPIView):
#swagger_auto_schema(responses={201: 'Created'})
#authorize(myCustomPermission)
def post(self, *args, **kwargs) -> Response:
// code.....
return Response(status=HTTPStatus.CREATED)
First:
The use of swagger_auto_schema without any serializer is throwing error as:
AssertionError: DetailView should either include a serializer_class attribute, or override the get_serializer_class() method.
And I don't want to use serializer for this endpoint as I don't need that.
But the swagger_auto_schema keeps on throwing this error.
I want to know whether there is any way to avoid the use of serializer and get the swagger documentation of this endpoint.
Second:
I want to add my custom authorisation permission of this endpoint in the doc.
There is a security field in swagger_auto_schema, but don't know how to make it to use for my custom permission class ie myCustomPermission
Thanks.

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

Custom authorization in Django Rest

I am just a newbie with Django, python. I try to build 1 simple API Collection include CRUD basic, and authentication, authorization.
I have 1 simple Views like:
#api_view(['GET'])
#permission_classes([IsUser])
def get_test(request : Request):
return JsonResponse({"message": "success"}, status = status.HTTP_200_OK)
and IsUser is:
class IsUser(IsAuthenticated):
def has_permission(self, request : Request, view):
token = request.query_params.get('token')
if token:
role = jwt.decode(token.split(" ").__getitem__(1), key="secret_key",algorithms="HS256").get('role')
if role == 'User':
return True
else:
return False
return False
My purpose wants to parse the JWT token and authorization based on that. I wonder don't know if my way is really correct? Can anyone give me some comments as well as documentation to better understand this issue? I don't use any other lib because I want to code by myself at first to understand the flow.
Thanks for helping me.
If you are using the default JWT authentication of DRF then permission class IsAuthenticated will verify the the token for you and instead of specifying token in query parameter specify in headers.
However, if you want to allow a specific type(role) of user to access your endpoint. Then create a subclass of BasePermission class as show in the example.
Then simply add IsAuthenticated in #permission_classes([IsUser & IsAuthenticated]) to make it work.
Again, this would work only if you are using the default Group model.
Here is how you can import it from django.contrib.auth.models import Group
from rest_framework.permissions import BasePermission
USER = 'USER'
class IsUser(BasePermission):
def has_permission(self, request, view, user=None):
if not user:
user = request.user
return USER in [role.name for role in user.groups.filter()]

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

Key based access for the Django Rest Framework

Lets say the API endpoint to GET a list of users is this
/api_auth/user/
But I want to restrict access to this list only to people with an api_key
/api_auth/user/?access_key=$omeRandomHash3252532
How do I implement such an access system using the Django Rest Framework?
Should I use Permissions to implement this?
This is not supported out of the box for django-rest-framework, however it can easily be implemented:
If you take a look at http://www.django-rest-framework.org/api-guide/authentication/ you'll see an Example of a custom authentication method. Baased on that, you'll need to implement something like this:
from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions
class APIKeyAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
api_key = request.GET.get('api_key')
if not api_key:
return None
try:
user = get_user_from_api_key(api_key)
except User.DoesNotExist:
raise exceptions.AuthenticationFailed('No user for API KEY')
return (user, None)
The APIKeyAuthentication should be put on an authentication.py module and be configured with REST_FRAMEWORK setting on settings.py, like this
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'my_custom_package.authentication.APIKeyAuthentication',
)
}
Now, what the above does is that it checks if the api_key parameter is present (if not it will return None to check if the request can be authenticated differently -- if you don't want to check any other authentication classes then just raise an exceptions.AuthenticationFailed exception like we do when we dont find the user below. Now, we need to implement a get_user_from_api_key function that will return a User instance from an API_KEY. If the user that is correlated with the passed api_key is found then it will be returned, if not an exceptions.AuthenticationFailedexception will be thrown.
Concerning the get_user_from_api_key function, its implementation depends on your requirements. For instance, if you want to create a new api key for each user, you should create an APIKey model that will have an api_key CharField and a ForeignKey to the User that has this api_key. The get_user_from_api_key function then will query the APIKey model to get the user with the provided api_key.
Update
If you want to use the django-rest-framework permissions instead of the authentication, you may create an APIKeyPermission class like this:
from rest_framework import permissions
class APIKeyPermission(permissions.BasePermission):
def has_permission(self, request, view):
api_key = request.GET.get('api_key')
return check_permission(api_key, request)
Where the check_permission function will check if the api_key passed has permissions for that specific request. Please check the examples on http://www.django-rest-framework.org/api-guide/permissions/ for more info - you may instead choose to implement has_object_permission to implement object-level permissions instead of view-level permissions.
If you are able to set a header in your request you can use Rest Framework's Token Authentication.
Otherwise, if you need to put it in the URL as a GET-paramter you could make your own custom authentication class:
from rest_framework.authentication import TokenAuthentication
class MyAuthentication(TokenAuthentication):
def authenticate(self, request):
token = request.GET.get('api-key', None)
if token is None:
return None
return self.authenticate_credentials(token)