How to create multiple permission classes that would restrict user from accessing a template view ( that doesnt use any model).
For example: I have 4 user categories - ( admin, management, principal, teacher). I have a Admin dashboard template view which should be restricted to user type=Admin.
I would like to be able to write multiple permission classes, which I can then use in combination in any view.
Following code generates 403 error:
class AdministratorPermission(AccessMixin):
def has_permission(self):
return True
class GeneralPerm1(AccessMixin):
def has_permission(self):
return True
class DashboardView(PermissionRequiredMixin,
LoginRequiredMixin, TemplateView):
template_name = 'administrator/dashboard.html'
permission_required = (AdministratorPermission,GeneralPerm1)
Is there a way to do something like DRF permissions.
Thanks
Permission1Mixin.py:
from django.contrib.auth.mixins import AccessMixin
from django.core.exceptions import PermissionDenied
class IsAdministratorMixin(AccessMixin):
""" if user is not administrator, decline permission """
def dispatch(self, request, *args, **kwargs):
"""
if user is authenticated and administrator
we are good. otherwise permission denied
"""
if request.user.is_authenticated and \
request.user.category.category == \
UserCategoryEnum.ADMINISTRATOR.value:
return super().dispatch(request, *args, **kwargs)
raise PermissionDenied('Permission denied') # decline permission
view.py
class DashboardView(IsAdministratorMixin, TemplateView):
template_name = 'administrator/dashboard.html'
This way we can create multiple independent permission mixins and use them in combination.
Related
I am using Django Guardian to have object-level permission along with global permissions. Some of the users have a group with global permissions, and some have object-level permissions. With this, I seem to need to modify the PermissionRequiredMixin to check also for the object-level permission.
views.py
class MainPageView(PermissionRequiredMixin, TemplateView):
permission_required = "app.view_mainpage"
template_name = "web/mainpage.html"
This one works if the user has global permission but if the user is under a group with object-level permission, it won't. With guardian, to check for object-level permission, you also have to pass the object instance.
example:
self.request.user.has_perm('view_mainpage', obj)
And on PermissionRequiredMixin, the checking goes only like this, self.request.user.has_perms(perms)
So if the user has a group with permission of view_mainpage of a specific object, how can I check it too? By the way, all my permissions are of the same content_type. Is there any way I can execute this? Like I have to pass the object instance to PermissionRequiredMixin if the user is under an object-level group and None if the user is under a global group.
You can implement such mixin yourself with:
from django.core.exceptions import PermissionDenied
class ObjectPermissionRequiredMixin:
object_permission_required = None
object_permission_denied_message = None
def has_object_permission(self, obj):
return self.request.user.has_perm(self.object_permission_required, obj)
def get_object_permission_denied_message(self):
return self.object_permission_denied_message
def handle_no_object_permission(self):
raise PermissionDenied(self.get_object_permission_denied_message())
def get_object(self, *args, **kwargs):
obj = super().get_object(*args, **kwargs)
if not self.has_object_permission(obj)
return self.handle_no_object_permission()
return obj
Then you can mix this mixin into a view, for example:
class MyUpdateView(ObjectPermissionRequiredMixin, UpdateView):
model = MyModel
object_permission_required = 'app.update_my_model'
object_permission_denied_message = 'You can not edit this object'
This will work for all Views that usee the SingleObjectMixin [Django-doc] such as a DetailView, UpdateView, and the DeleteView.
I am just getting started with Django Rest framework and want to create a feature such that it allows superuser to create a message in admin.py and allow it only to be seen by a certain group(s) ie. "HR","Managers","Interns" etc.
in other words, only a user belonging to "HR" group will be allowed to get data from view assigned to "HR" group by admin. I would like to have only one view that appropriately gives permission.
Something like
#views.py
class message_view(APIView):
def get(request):
user = request.user
group = get_user_group(user) #fetches user's respective group
try:
#if message assigned to 'group' then return API response
except:
#otherwise 401 Error
I need some guidance with this.
I propose you to define a permission in your app (<your-app>/permissions.py) as below:
class HasGroupMemberPermission(permissions.BasePermission):
message = 'Your custom message...'
methods_list = ['GET', ]
def has_permission(self, request, view):
if request.method not in methods_list:
return True
group_name ='put_your_desired_group_name_here'
if request.user.groups.filter(name__exact=group_name).exists():
return False
return True
Then, import the above permission in your views module (<your-app>/views.py) as well as the serializer of the Message model (let's assume MessageSerializer).
from rest_framework.generics import RetrieveAPIView
from .permissions import HasGroupMemberPermission
from .serializers import MessageSerializer
class MessageView(RetrieveAPIView):
# use indicative authentication class
authentication_classes = (BasicAuthentication, )
permission_classes = (HasGroupMemberPermission, )
serializer_class = MessageSerializer
lookup_url_kwarg = 'message_id'
def get_object(self):
""" Do your query in the db """
qs = Message.objects.get(pk=self.kwargs['message_id'])
return qs
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
401 will be returned if user is not authenticated. 403 will be returned if the logged in user is not member of the desired group. 200 will be returned if logged in user is member of the group and the message_id exists.
I'm having a problem when checking custom attributes of a user to allow access to the view.
Lets suppose I just want to allow access to the users that have activated in their userprofile the self_store option.
When i use function views is very easy i put this in the beginning of the view and works fine
if not request.user.is_authenticated():
return redirect('auth_login')
if not request.user.userprofile.self_store:
return do_something_here
Well like i said is very easy to use and do different things for different options activated or not in my userprofile
But i cant find the way to do this in class based views, if i want to allow access to the users that have activated the self_store option in the usersprofile how can i do for example in this updateview
class ClientUpdateView(UpdateView):
model = Client
template_name = "clients/update.html"
pk_url_kwarg = 'client_id'
fields = [
'first_name',
'last_name',
'email',
'phone',
'phone_2',
'address',
]
I can use logginrequired mixin perfect but i want to check like i said custom users properties that i use in the userprofile.
The check that i need to make is something like the PermissionRequired Mixin but for the attributes in the userprofile.
In a CBV you can access request as a self.request. So you can easily check your profile attribute as a self.request.user.profile.attribute. There is also a dispatch(request, *args, **kwargs) method in a CBV (see description)
The view part of the view – the method that accepts a request argument plus arguments, and returns a HTTP response.
The default implementation will inspect the HTTP method and attempt to delegate to a method that matches the HTTP method; a GET will be delegated to get(), a POST to post(), and so on.
In my opinion, you can validate your user permissions in a dispatch method. For example you can either validate user by profile attributes:
def dispatch(self, request, *args, **kwargs):
"""Return 403 if flag is not set in a user profile. """
if not request.user.profile.has_flag:
return HttpResponseForbidden()
return super().dispatch(request, *args, **kwargs)
or by user permissions:
def dispatch(self, request, *args, **kwargs):
"""Return 403 if user does not have specific permission. """
if not request.user.has_perm('client_permission.client_edit'):
return HttpResponseForbidden()
return super().dispatch(request, *args, **kwargs)
If you want to validate not a request user but a client itself you may check client permissions while getting object:
from django.core.exceptions import PermissionDenied
def get_object(self, *args, **kwargs):
client = super().get_object(*args, **kwargs)
if not client.profile.has_flag:
# The PermissionDenied exception is raised
# when a user does not have permission
# to perform the action requested.
raise PermissionDenied
return client
You can read more about dispatch method here: https://docs.djangoproject.com/en/1.11/ref/class-based-views/base/#django.views.generic.base.View.dispatch
I want also to note that if you have a task to check any permissions using Django Rest Framework you may use permission_classes (See http://www.django-rest-framework.org/api-guide/permissions/). All that you should do is to define your own permission class:
from rest_framework.permissions import BasePermission
class UserHasFlag(BasePermission):
"""Check that user has a flag set in a profile. """
def has_permission(self, request, view):
return request.user.profile.has_flag
Your view will be looking like this:
from rest_framework.permissions import IsAuthenticated
class ClientUpdateView(UpdateView):
permission_classes = (IsAuthenticated, UserHasFlag)
model = Client
template_name = "clients/update.html"
pk_url_kwarg = 'client_id'
fields = [
'first_name',
'last_name',
'email',
'phone',
'phone_2',
'address',
]
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,)