django rest framework simulate required_permissions - django

often when i define permissions on my django views I would use something like this
#permission_required('comment.add_thread', raise_exception=True)
def save_comment(request, id=None):
""" """
But, when rest framework, how can I tell the API method to check for comment.add_thread permission before performing the operation?

It all depends how you create your api views, if your view is not generic, then you need to create your own permission like this:
from rest_framework import permissions
class AddCommentPermission(permissions.BasePermission):
def has_permission(self, request, view):
return request.user.has_perm('comment.add_thread')
but if your view has a model property set, then you just simply use DjangoModelPermissions, as documented here:
http://django-rest-framework.org/api-guide/permissions.html#djangomodelpermissions

Related

Django permissions when using Django Rest Framework + VueJs as a front

My question address to the usage of Django permission architecture even when the front is on Vue.js and data is requested/responded through Django REST framework.
I am a little confused about being able to use default permission libraries of Django when the app is combined with Rest Framework and VueJs:
from django.contrib.auth.decorators import login_required, permission_required
#permission_required('pobapp.can_add_instance')
#login_required
def addEmployeeInstance(request):
return render(request, 'pobapp/search.html')
If not, how can I restrict some data and pages for specific users? For example, if I only wanted to let authenticated users to view some specific pages?
If you want to restrict a logged user in pages, first, you should define a Role and a Resource model for User model.
Then, you can define a permission class for controlling this restriction. for example my permission class for this purpose is like this,
class IsAuthRolePermission(permissions.BasePermission):
def has_permission(self, request, view):
if request.user.is_authenticated:
try:
obj = Resource.objects.get(name=view.__class__.__name__,
roles__in=request.user.roles.all())
return True
except Resource.DoesNotExist:
return False
else:
return False

How to manage roles and permission in Django Rest framework mongoengine

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,)

How to make a Django view ONLY accessible to Unauthenticated users?

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

Django Rest Framework authentication - how to implement custom decorator

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,)

Having multiple decorators in Django and using only one

I am using django-tokenapi to allow for authentication of an Android project that is using Django as a backend. The project also has a web interface.
django-tokenapi uses the #token_required decorator to protect certain views. Django uses the #login_required decorator to protect certain views.
What I would like is to have only one view that is protected by #login_required OR #token_required so it can be used with either the webapp or Android app.
So, ideally, it would look like this:
#token_required
#login_required
def names_update(request):
....
....
However that does not work. Is there a better method of doing this? Or is the correct thing to have two views, one the webapp and one for Android, that are protected by the proper decorator, and then lead to the same method.
No there's no easy way, if possible at all, to write an generalized OR decorator according to you described.
However, you can write a new decorator that does exactly what you want:
from functools import wraps
from django.contrib.auth import authenticate, login
from django.views.decorators.csrf import csrf_exempt
def token_or_login_required(view_func):
"""
Decorator which ensures the user is either logged in or
has provided a correct user and token pair.
"""
#csrf_exempt
#wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
user = request.REQUEST.get('user')
if user and user.is_authenticated:
return view_func(request, *args, **kwargs)
token = request.REQUEST.get('token')
if user and token:
user = authenticate(pk=user, token=token)
if user:
login(request, user)
return view_func(request, *args, **kwargs)
return HttpResponseForbidden()
return _wrapped_view
this decorate combines both token_required and login_required decorators, and will allow access to the view either if the user is logged in, or the token is valid.
You could try assigning a new view variable to the old view:
#token_required
def names_update_token(request):
...
#login_required
names_update_login = names_update_token
This should have the effect of creating a second view named names_update_login which is just a pointer to the first so that the code remains the same.
EDIT:
Another thought, and one I have used, is to create a more "generic" view and call it from each of the other views:
def update_token(request):
...
#token_required
def names_update_token(request):
update_token(request)
#login_required
def names_update_login(request):
update_token(request)
This gets around the issue you mentioned while still maintaining only a single copy of the actual code that implements the view.
It is possible to use more than one decorator on a single view. But in this case I think you should seperate the view and apply the decorators seperately. Otherwise token_required decorator will try to authenticate using token which won't be available on a request made using browser.