I'm building a Django API view by extending the rest_framework.views.APIView class.
I have successfully built many APIs that are only callable by an authenticated user. I have done this by adding: permission_classes = [permissions.IsAuthenticated,]
There are some APIs that I only want unauthenticated users to call. Such as "ForgotPassword". Basically, I want to ensure that the API caller doesn't send in the JWT Token in the request header. How can I enforce that? There is no permissions.IsUnAuthenticated.
you can easily create your own IsNotAuthenticated class
something like this:
from rest_framework.permissions import BasePermission
class IsNotAuthenticated(BasePermission):
"""
Allows access only to non authenticated users.
"""
def has_permission(self, request, view):
return not request.user.is_authenticated()
then: permission_classes = (myapp.permissions.IsNotAuthenticated,)
regards.
In case you are using function based view then it would be good if you use the following.
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(lambda u: not u.is_authenticated())
Or you can do it in permissions.py like this (For who were getting bool object error)
from rest_framework import permissions
class IsNotAuthenticated(permissions.BasePermission):
def has_permission(self, request, view):
return not request.user.is_authenticated
And in the main view
from .permissions import IsNotAuthenticated
permission_classes = [IsNotAuthenticated]
The following answer is for Django not Django REST Framework
For a Class-Based View, make a custom mixin like this
class IsNotAuthenticatedMixin(UserPassesTestMixin):
"""
Allows access only to non authenticated users.
"""
def test_func(self):
return not self.request.user.is_authenticated
def handle_no_permission(self):
return redirect('home')
You can inherit any Class based view from IsNotAuthenticatedMixin
Related
I am working with django rest, I however have an issue in one of my views because, i want to allow both authenticated users and unauthenticated users access the view then check if the user is authenticated then there are some special events to be done by the celery tasks, however, whenever i add the decorator for the authentication_classes, unauthenticated users can no longer visit the page even after setting the permission_classes to allow all
simply my code is here, hope someone can know what i need to add or remove
#api_view(['GET'])
#permission_classes([AllowAny])
#authentication_classes([TokenAuthentication])
def item_details(request, pk):
if request.user.is_authenticated:
#here some tasks
the main issue is that it seams that TokenAuthentication just nullify AllowAny and takes over the checking of the permisssion class
or is there something am doing wrong?
Overriding the authenticate method of TokenAuthentication
myauth.py
from rest_framework.authentication import TokenAuthentication
class TokenAuthenticationSafe(TokenAuthentication):
def authenticate(self, request):
try:
return super().authenticate(request=request)
except:
return None
views.py
from myauth import TokenAuthenticationSafe
#api_view(['GET'])
#permission_classes([AllowAny])
#authentication_classes([TokenAuthenticationSafe])
def item_details(request, pk):
if request.user.is_authenticated:
#here some tasks
I have used ListCreateAPIView and RetrieveUpdateDestroyAPIView for a model. Now I want to add JWT authentication to only the Update and Destroy part in the RetrieveUpdateDestroyAPIView. How can I do that?
Let me make my question a bit more clear. I have a model named Post. Now All users are allowed to view the post but update, delete is only available to the user who created it. And I want to use JWT Authentication.
You can write custom permission class for this:
from rest_framework import permissions
class CustomPermission(permissions.BasePermission):
def has_permission(self, request, view):
if view.action in ('update', 'destroy'):
return request.user.is_authenticated
return True
And use in in your view:
class ExampleView(RetrieveUpdateDestroyAPIView):
permission_classes = (CustomPermission,)
we can override the method get_authenticators and don't forget to add authentication_classes to api view.
def get_authenticators(self):
if self.request.method in ['PUT', 'DELETE']:
return [auth() for auth in self.authentication_classes]
else:
return []
For your question update we need to add object level permissions like below
class OwnerRequiredPermission(object):
def has_object_permission(self, request, obj):
return obj.created_by == request.user
add above permission class to permission_classes
I am building a Restapi using Django and Rest framework and mongoengine, so far all requests require a user to be authenticated and check against a token.
But now I need to allow different actions to different users. I don't know where to begin. Any guidelines ?
For example I want only the admin to be able to write and read users objects:
class UsersViewSet(ModelViewSet):
queryset = Users.objects.all()
serializer_class = UsersSerializer
def me(self, request, *args, **kwargs):
serializer = self.serializer_class(request.user)
return Response(serializer.data)
Read the chapter on custom permisssion. You will want to extend permissions.BasePermission and provide the authentication logic inside has_permission.
from rest_framework import permissions
class CustomUserPermission(permissions.BasePermission):
def has_permission(self, request, view):
# return True if user has permission
pass
Then inside your view.
class UsersViewSet(ModelViewSet):
permission_classes = (CustomUserPermission,)
I am trying to implement TokenAuthentication using the Rest Framework, but it seems that I can't add my own custom decorators to my ViewSets because they are evaluated BEFORE the authentication. Consider this:
from django.utils.decorators import method_decorator
from django.http.response import HttpResponseForbidden
def require_staff(View):
def staffOnly(function):
def wrap(request, *args, **kwargs):
if request.user.is_active and request.user.is_staff:
return function(request, *args, **kwargs)
else:
return HttpResponseForbidden()
return wrap
View.dispatch = method_decorator(staffOnly)(View.dispatch)
return View
When I try to implement this, it seems the decorator code fires first, so the authentication is never run.
#require_staff
class CustomerViewSet(ModelViewSet):
model = Customer
filter_class = CustomerFilter
filter_backends = (DjangoFilterBackend,)
Since request.user is never set, introducing the decorator breaks authentication.
I think the issue is that Authentication is occuring the rest_frameworks dispatch() function and it is not clear to me how I could add additional (say) custom security if authentication is done that late in the game.
Am I missing something here, or what is the proper way to implement this customization?
Someone suggested using Permissions for this instead. I assume they mean custom DRF Permissions, right?
Everything you need to know is DRF permissions is here: http://www.django-rest-framework.org/api-guide/permissions
DRF provides a built in permission that is similar to yours, called IsAdminUser
The IsAdminUser permission class will deny permission to any user,
unless user.is_staff is True in which case permission will be allowed.
This permission is suitable is you want your API to only be accessible
to a subset of trusted administrators.
To use this permission in a Class Based View:
class ExampleView(APIView):
permission_classes = (IsAdminUser,)
Now you have two options to do an extra check for user.is_active.
The first is override the IsAdminUser permission, like so:
from rest_framework import permissions
class IsActiveAndAdminUser(permissions.IsAdminUser):
"""Only allow a user who is Admin and Active to view this endpoint. """
def has_permission(self, request, view):
is_admin = super(IsAdminAndActiveUser, self).has_permission(request, view)
return request.user.is_active and is_admin
The second is to create an IsActiveUser permission, and chain them in your view.
IsActiveUser Permission:
from rest_framework import permissions
class IsActiveUser(permissions.BasePermission):
""" Only Active Users have permission """
def has_permission(self, request, view):
return request.user.is_active
Your new permission list in your class based view:
class ExampleView(APIView):
permission_classes = (IsActiveUser, IsAdminUser,)
I am trying to use Django with allauth trying to create a login page.
I am able to login and redirect the user to LOGIN_REDIRECT_URL (= "/users/{id}/mypage") page successfully. I have a separate view for /users/{id}/mypage called the UserHomePageView as defined in views.py:
from django.views.generic import TemplateView
class UserHomePageView(TemplateView):
template_name = "user_homepage.html"
I want a security measure to prevent anyone (basically non-loggedin-users) to navigating to "/users/{id}/mypage" (e.g /users/5/mypage). e.i If unauthenticated user tries to navigate to /users/5/mypage, he/she should get redirected to signin page/homepage.
Basically, how do i present a unauthenticated user to view pages that are meant for authenticated users (I don't want to use template tags users.authenitcated, but wish to override the TemplateView)
Another solution which is generally used is to decorate the dispatch method with the login_required decorator:
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
class UserHomePageView(TemplateView):
template_name = "user_homepage.html"
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(UserHomePageView, self).dispatch(*args, **kwargs)
This has the advantage that it works with all methods (get, post, put, etc.), and that using the login_required decorator is more DRY than explicitly checking request.user.is_authenticated and redirecting.
I found the answer...
By overriding get() and using self.request.user.is_authenticated()
class UserHomePageView(TemplateView):
template_name = "user_homepage.html"
redirect_field_name = "next"
def get(self, request, *args, **kwargs):
'''
Overriding TemplateView.get() in order to
prevent unauthorized user to view UserHomePageView
'''
# Redirect to Homepage if user is not signed in
if not self.request.user.is_authenticated():
return redirect(self.get_redirect_url())
# Process normally if User is signed in
context = self.get_context_data(**kwargs)
return self.render_to_response(context)