Override the authToken views in Django Rest - python

I am using the token based Authentication in Django and need to add User object in addition to token being returned.
How do I override this class view ? Where do I need add this class and make the changes ? Currently this is found in the rest_framework package and I don't want to modify the library .
from rest_framework import parsers, renderers
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework.response import Response
from rest_framework.views import APIView
class ObtainAuthToken(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
print "dasdsa"
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
obtain_auth_token = ObtainAuthToken.as_view()

From docs.
First that you need is to extend the ObtainAuthToken class.
# views.py
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email
})
And after this add the CustomAuthToken class to your urls.py like view
# urls.py
from django.urls import path
from . import views
urlpatterns += [
path(r'api-token-auth/', views.CustomAuthToken.as_view())
]

You should extend your CustomClass from AuthToken, the route default url to your CustomClass:
from rest_framework_jwt.views import ObtainJSONWebToken
class JSONWebTokenAPIOverride(ObtainJSONWebToken):
"""
Override JWT
"""
def post(self, request):
# Do whatever you want
Then in your urls.py:
url(
r'^api-auth$',
cache_page(0)(views.JSONWebTokenAPIOverride.as_view())
)
I hope it helps

I wanted to override some default CRSF functionality and used the following approach:
from rest_framework.authentication import SessionAuthentication
class SessionCsrfExemptAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
# Do not perform a csrf check
return False
Then in my settings file I referenced it in the following way:
'DEFAULT_AUTHENTICATION_CLASSES': (
'myapp.utils.authenticate.SessionCsrfExemptAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'oauth2_provider.ext.rest_framework.OAuth2Authentication',
'rest_framework_social_oauth2.authentication.SocialAuthentication',
),
This allowed me to import the existing functionality, override it and reference it in the settings file. I think you can use a similar approach here.

I use the option JWT_RESPONSE_PAYLOAD_HANDLER.
In the response I include the token, expiration timestamp and the user.
In settings.py add:
JWT_AUTH = {
...
'JWT_RESPONSE_PAYLOAD_HANDLER':'<app_name>.functions.custom_jwt_response',
}
Then in functions.py add the following
def custom_jwt_response(token, user=None, request=None):
import jwt
jwt = jwt.decode(token, verify=False)
return {
'token': token,
'token_exp': jwt['exp'],
'user': UserSerializer(user, context={'request': request}).data
}

The answers here are good but in my opinion they don't make full use of inheritance. When we inherit a class, we shouldn't just try to reinvent the wheel and instead make use of the super() keyword. Here is my code example, where I want to turn the username argument into lowercase before performing the authentication request:
class GetAuthToken(ObtainAuthToken):
"""
Override Django's ObtainAuthToken to provide custom way of authenticating user for token
"""
def post(self, request, *args, **kwargs):
#-- turn username to lowercase
if ('username' in request.data):
request.data['username'] = request.data['username'].lower()
#-- perform normal function
return super().post(request, *args, **kwargs)

Related

Return a Http Response In get_queryset

Im using get_queryset, in ListAPIView
I want to check the user's access token, before providing the list, I done the below but the issue is that get_query set does not return a Response, is there a way to return a response, or I should use an alternative :
this my class in the views.py :
class ListProductsOfCategory(generics.ListAPIView):
serializer_class = ProductSerializer
lookup_url_kwarg = 'category_id'
def get_queryset(self):
# I get the token here from the headers
token = self.request.META.get("HTTP_TOKEN", "")
if not token:
return Response(
data={
"message": "no token!"
},
status=status.HTTP_400_BAD_REQUEST
)
if not UserAccess.objects.filter(accessToken=token).exists():
return Response(
data={
"message": "invalid token!"
},
status=status.HTTP_400_BAD_REQUEST
)
category_id = self.kwargs.get(self.lookup_url_kwarg)
return Product.objects.filter(category_id=category_id)
note that everything is working perfect If I removed the token related part.
thanks in advance.
after last update this is the repsonse :
I'd suggest you to move check token logic into dispatch() method. It's a better place than get_queryset. Or even better to write your own authentication class in order to share it between views.
With some fixes (see updated get_queryset()) it can be:
UPDATE
I think you can go with built-in restframework.exceptions.AuthenticationFailed.
If you are not satisfied with default DRF exceptions you can create your own custom exceptions. For example somewhere in exceptions.py:
from rest_framework.exceptions import APIException
class MissingTokenException(APIException):
status_code = 400
default_detail = 'Your request does not contain token'
default_code = 'no_token'
class InvalidTokenException(APIException):
status_code = 400
default_detail = 'Your request contain invalid token'
default_code = 'invalid_token'
Then you can use them in views.py:
from rest_framework import serializers
from .exceptions import MissingTokenException, InvalidTokenException
class ListProductsOfCategory(generics.ListAPIView):
serializer_class = ProductSerializer
lookup_url_kwarg = 'category_id'
def dispatch(self, *args, **kwargs):
token = self.request.META.get("HTTP_TOKEN", "")
if not token:
raise MissingTokenException
if not UserAccess.objects.filter(accessToken=token).exists():
raise InvalidTokenException
return super().dispatch(*args, **kwargs)
def get_queryset(self):
qs = super().get_queryset()
category_id = self.kwargs.get(self.lookup_url_kwarg)
return qs.filter(category_id=category_id)
I'm not 100% if I'm getting this right, but I believe you can just use the regular authentication mechanisms that DRF provides. In this particular example, I think this section of the DRF docs should show you how to do it the "DRF" way: Setting Authentication Scheme
If you add the TokenAuthentication scheme to your application, you don't need to verify the token in your get_queryset method, but you can just use decorators to restrict access for function-based views or permission_classes for class-based views:
View-based
I guess this is what you'd be most interested in.
class ListProductsOfCategory(generics.ListAPIView):
serializer_class = ProductSerializer
lookup_url_kwarg = 'category_id'
authentication_classes = (TokenAuthentication, ) # Add others if desired
permission_classes = (IsAuthenticated,)
Route-based
If you only want to restrict access for some of your routes (e.g. only post or detail views), then you can write your own permission class. For example, see this question here: How to add django rest framework permissions on specific method only ?

Empty routes in Django Rest Framework

I'm trying to register a simple method into a DRF and I'm having some problems. The route is not showing in API Explorer.
It's probably something simple that I'm missing..
How can I get the register route to show in the API?
Resuts (empty)
GET /api/
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{}
Urls
from django.conf.urls import url, include
from django.contrib import admin
from rest_framework import routers
from rest_framework_jwt.views import obtain_jwt_token
from reservations.views.homepage import HomepageView
from users.views import UserViewSet
""" API routes
"""
router = routers.DefaultRouter()
router.register(r'test', UserViewSet, base_name='users')
""" Route patterns
"""
urlpatterns = [
url(r'^$', HomepageView.as_view(), name='homepage'),
url(r'^api/', include(router.urls)),
url(r'^api-token-auth/', obtain_jwt_token),
url(r'^admin/', admin.site.urls),
]
Viewset
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from rest_framework import viewsets
from rest_framework.response import Response
class UserViewSet(viewsets.ViewSet):
def register(self, request):
return Response({
'status': 'User registered'
})
ViewSet has some specific methods for every method (GET, POST, PUT etc) like list, detail, create, update etc. You should use that methods. Api explorer decides on the basis of these methods that which method is allowed by your view. You can see these methods here.
In your case, I suppose you want to create new user. So you should use create method like this.
class UserViewSet(viewsets.ViewSet):
def create(self, request, *args, **kwargs):
return Response({
'status': 'User registered'
})
If you want to work only with then list, create method you must have in it. Please check below example of my code. please check this link for more information http://www.django-rest-framework.org/api-guide/viewsets/
class UserViewSet(viewsets.ViewSet):
def list(self, request):
queryset = crm_models.EmployeeLeaveApp.objects.all()
serializer = serializers.EmployeeVisitSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = crm_models.EmployeeLeaveApp.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = serializers.EmployeeVisitSerializer(user)
return Response(serializer.data)
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
Use #list_route for this. marking extra actions for routing
...
#list_route(methods=['post'])
def register(self, request):
return Response({
'status': 'User registered'
})

How to route an url to an specific method of a class Django and DRF

I am very new in the python world and now I building an application with Django 1.8 with the Rest Framework and I want to create a class view to DRY my code.
For example I want to have a class view for the students in my system
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class SnippetList(APIView):
def getWorstStudents(self, request, format=None):
# Logic here
How can I assign a specified URL in urls.py to hit this method?
And also I have implemented REST framework JWT Auth
http://getblimp.github.io/django-rest-framework-jwt/ for token auth.
How can I restrict the access to allow only authenticated users can access to this url?
Thank you in advance!
Use routers with viewsets.
First, subclass your view from ModelViewSet instead of from APIView. Second, use the #list_route decorator in your getWorstStudents method. Third, tie everything up in your urls.py with a router.
It should look like (I have not tested the code):
views.py
class StudentsViewSet(viewsets.ViewSet):
#list_route(methods=['get'], permission_classes=(IsAuthenticated,)) # you can define who can access this view here
def getWorstStudents(self, request, format=None):
# Logic here
# routers.py
router = routers.DefaultRouter()
router.register(r'students', views.StudentsViewSet, base_name='student')
# urls.py
import .routers import router
urlpatterns = [
url(r'^', include(router.urls)),
]
The router will generate a view with the name student-getWorstStudents accessible from the students/getWorstStudents url.
You can set urls like any other Django app, documented here
# urls.py
from django.conf.urls import url
from somewhere import SnippetList
urlpatterns = [
url(r'^your/url/$', SnippetList.as_view()),
]
About DRY with your method, you can define the method you want to response to, and call the getWorstStudents (btw by convention I would call it get_worst_students). Let's say you want to response the post method:
# views.py
from rest_framework.response import Response
def getWorstStudents(params)
class SnippetList(APIView):
def post(self, request, *args, **kwargs):
# call getWorstStudents method here and response a Response Object
You can define getWorstStudents inside SnippetList class or in other file to import wherever you need it.
Finally, about authentication, DRF provides classes for this, documented here.
From docs, you need define this in your settings.py file:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
And use it in your views:
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
def get(self, request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
You can also define your own authentication class and set it in authentication_classes tuple. Custom authentication classes documented here.

Django Rest Framework complaining about CSRF

I have developed a simple webservice, but failed to use post with Django Rest Framework as it complains about CSRF:
"detail": "CSRF Failed: CSRF cookie not set."
Removing the api_view decorator does stop the message from appearing but then I won't be able to access the request.data. I think that the api_view does check CSRF although I added the csrf_exempt decorator.
This is my view:
#permission_classes((IsAuthenticated, ))
#csrf_exempt
#api_view(['POST'])
def get_stats(request):
"""
Returns the stats available.
"""
user = request.user
if request.method == 'POST':
serializer = StatsRequestSerializer(data=request.data)
stats_request = serializer.data
return JSONResponse(stats_request)
#serializer = QuizSerializer(user.quizes.all(), many=True)
#return JSONResponse(serializer.data)
response = ActionResponse(status='error', error='Invalid request')
serializer = ActionResponseSerializer(response)
return JSONResponse(serializer.data, status=400)
This is my model:
class StatsRequest(models.Model):
"""
A model which describes a request for some stats for specific users.
"""
start_date = models.DateField()
end_date = models.DateField()
and this is my request POST:
{"start_date" : "1992-01-15", "end_date" : "1992-01-15" }
Any ideas?
More info:
AUTHENTICATION_BACKENDS = (
'social.backends.facebook.FacebookOAuth2',
'social.backends.google.GoogleOAuth2',
'django.contrib.auth.backends.ModelBackend'
)
So, after trying to figure this out for a couple of hours I finally did it.
Tracing the source code of DRF and Django lead me to believe that I need to find a workaround for this as the CSRF verification is made explicitly even if turned off, probably the CSRF check is being made at the api_view decorator. So I simply created my own decorator:
from functools import wraps
from django.utils.decorators import available_attrs, decorator_from_middleware
def csrf_clear(view_func):
"""
Skips the CSRF checks by setting the 'csrf_processing_done' to true.
"""
def wrapped_view(*args, **kwargs):
request = args[0]
request.csrf_processing_done = True
return view_func(*args, **kwargs)
return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
and my view with the new decorator:
#csrf_clear
#api_view(['POST'])
#permission_classes((IsAuthenticated, ))
def get_stats(request):
"""
Returns the stats available.
"""
user = request.user
if request.method == 'POST':
serializer = StatsRequestSerializer(data=request.data)
if serializer.is_valid():
stats_request = serializer.data
return JSONResponse(stats_request)
#serializer = QuizSerializer(user.quizes.all(), many=True)
#return JSONResponse(serializer.data)
response = ActionResponse(status='error', error='Invalid request')
serializer = ActionResponseSerializer(response)
return JSONResponse(serializer.data, status=400)
urls.py
from django.views.decorators.csrf import csrf_exempt
urlpatterns = [
url(r'^snippets/$', views.SnippetList.as_view()),
url(r'^snippets/(?P<pk>[0-9]+)/$', csrf_exempt(views.SnippetDetail.as_view())),
]
views.py
from django.views.decorators.csrf import csrf_exempt
from rest_framework.views import APIView
class SnippetList(APIView):
#csrf_exempt
#need_post_parameters([PARAM_MESSAGE_OBJ])
def post(self, request, *args, **kwargs):
data = request.POST.get(PARAM_MESSAGE_OBJ)
try:
message_obj = json.loads(data)
except Exception as e:
return HttpResponseBadRequest(error_json("Could not parse JSON"))
http://www.chenxm.cc/post/509.html

DjangoRestFramework upload file 'CSRF Verification Failed'

I am using DjangoRestFramework to make an API. Currently I have OAuth2 authentication working which means I can generate a valid access token to use the API.
How do I upload a user file? I need to upload a file and relate it to the user who uploaded it.
I am currently trying to do it this way
api/views.py:
class FileUploadView(APIView):
parser_classes = (FileUploadParser,)
def put(self, request, filename, format=None):
file_obj = request.FILES['file']
# do stuff
return Response(status=204)
api/urls.py contains this line:
url(r'^files/', 'api.views.FileUploadView'),
but when I try to upload a file I get an error stating that:
'CSRF verification failed. Request aborted'
'Reason given for failure: CSRF cookie not set'
when I try this curl command:
curl -XPUT http://localhost:8000/files/ -H 'Authorization: Bearer some_access_token' -F filedata=#localfile.txt
Here are my REST_FRAMEWORK defaults:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.OAuth2Authentication',
)
}
1) In my original code I was expecting a filename parameter to come into my view but I was not parsing it out of the url in urls.py. It should have looked something like this:
url(r'^uploads/(?P<filename>[\w.]{0,256})/$', views.UploadsView.as_view()),
2) I was also not providing the APIView as a view. If you notice above I am specifically calling the .as_view() method.
With the above fixed and implementing POST instead of PUT my code works as expected. Here is my current APIView:
class UploadsView(APIView):
parser_classes = (FileUploadParser,)
permission_classes = (permissions.IsAuthenticated,)
def post(self, request, format=None):
file_obj = request.FILES['file']
print("FILE OBJ: ", file_obj)
return Response(status=201)
Per the Django REST Framework Documentation, "If you're using SessionAuthentication you'll need to include valid CSRF tokens for any POST, PUT, PATCH or DELETE operations.
In order to make AJAX requests, you need to include CSRF token in the HTTP header, as described in the Django documentation."
Alternatively, you can attempt to make this view CSRF Exempt:
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
class FileUploadView(APIView):
parser_classes = (FileUploadParser,)
#method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(FileUploadView, self).dispatch(request, *args, **kwargs)
def put(self, request, filename, format=None):
file_obj = request.FILES['file']
# do stuff
return Response(status=204)

Categories

Resources