Django is_staff permission decorator - django

I am trying to limit access to pages using 2 user levels. Superuser and admin.
Super user is a regular Django user with 'is_superuser' assigned.
Admin user is also a regular user with only the 'is_staff' permission assigned.
The problem is that when i use this decorator for an admin user, it doesn't pass the test:
#permission_required('is_staff')
def my_view(....)
#permission_required('is_staff') returns false for anonymous users. (correct)
#permission_required('is_superuser') only returns true for superusers (correct)
#permission_required('is_staff') returns FALSE for users with the 'is_staff' perm assigned. (wrong).
Any thoughts?

is_staff isn't a permission so instead of permission_required you could use:
#user_passes_test(lambda u: u.is_staff)
or
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required

for Class Based Views you can add permission_required('is_staff') to the urls.py:
from django.contrib.auth.decorators import permission_required
url(r'^your-url$', permission_required('is_staff')(YourView.as_view()), name='my-view'),

For class-based views, the UserPassesTestMixin is convenient, e.g.
class ImportFilePostView(LoginRequiredMixin, UserPassesTestMixin):
def test_func(self):
return self.request.user.is_staff
...

A variant of #arie 's answer without lambda which might be a tad faster (but I didn't check):
import operator
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(operator.attrgetter('is_staff'))
def my_view(....)
Having said that, I think the better approach is to create a Permission for your view and use it with the permission_required decorator.

Related

Restrict unauthorised access in Django

I have various applications in a Django project, but I only want users who are logged in to be able to access those pages. How can I restrict access to every pages except the login page which is my main page. For instance, mywebsite.com/home/user should be only available to user and if someone types in that it should redirect them to mywebsite.com
Currently I have two apps, main and Home, I am using ClassBased views on my Home app how can I restrict access to all my pages except login page and show a message as well?
I want to create a template that users can see other user profile details but not change or edit them. How can I do those above steps
Thanks in advance!
According to Docs you can decorate the class based views with #login_required
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
#method_decorator(login_required, name='dispatch')
class ClassBasedView(View):
...
...
Since you are using class based view, you need to add method decorator, else you can use #logine_required directly.
And the other part in the question is again a separate one from this.
You can try This, In very simple way
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
return HttpResponse()
using #login_required means user have to login to access that view
Or If you you Want to use class then try this
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
class RestrictedView(LoginRequiredMixin, TemplateView):
template_name = 'foo/restricted.html'
raise_exception = True
permission_denied_message = "You are not allowed here."

Registering Different types of Users in Django

I wont be needing to write codes much,I just need an idea.I am trying to make users in two different categories.That is a User can either have a Profile or Shop both not both.
class Profile(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True,editable=True)
class Shop(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True,editable=True)
I want all users to either have one of both,They will register with the same form without being able to switch from one to another once they have registered for it.As at now what I'm doing is If i dont want a shop-owned User to have the previlege of a profile i do
def post_product(request):
try:
if bool(request.user.profile)==False:
.....
except:
pass
But I have to do this for all views that require previledges. I want something site-wide that i can put in settings or put at the view like #login_required .Please how can I go about this.Thanks
As per answer given by Prabhakar, you should use django auth groups.
But, if you want to write your custom permission class, you could also do it.
1) Assuming file name -> self_defined_permission.py
from rest_framework import permissions
from django.contrib.auth.models import Group
class ShopPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
'''
check user group. Assuming you keep users having profile in group 1 and shop in group 2
'''
shop_gp = Group.objects.get(id=2)
if shop_gp in request.user.groups.all():
return True
else:
return False
'''
similarly you could write permission class for profile owners
'''
2) In your class based view
from self_defined_permission import ShopPermissions
class SomeView(generics.ListCreateAPIView):
...
permission_classes = (ShopPermissions, )
3) In case you are using function based view.
from rest_framework.decorators import permission_classes
#permission_classes((ShopPermissions, ))
def some_view(request):
....
Try the user_passes_test decorator:
from django.contrib.auth.decorators import user_passes_test
def is_shop(user):
return Shop.objects.filter(user=user).exists()
def is_normal_user(user):
return Profile.objects.filter(user=user).exists()
#user_passes_test(is_shop)
def only_for_shops(request):
return render(request, 'edit_price_list.html')

Django Authentication process

I noticed that when I logged in as administrator, I don't need to reauthenticate when I access non-admin area.
However, when I login as simple user and access admin zone, Django, of course, checks whether I am admin.
Where this check occurs? I want to restrict access of moderators to non-admin part of site, so need to check that.
Thank you.
Use the #user_passes_test decorator:
from django.contrib.auth.decorators import user_passes_test
def not_staff_user(user):
return not user.is_staff
#user_passes_test(not_staff_user)
def my_view(request):
...
If you want to restrict ALL pages except /admin/ then middleware is a good option:
from django.conf import settings
from django.shortcuts import redirect
class NonStaffMiddleware(object):
def process_request(self, request):
if request.user.is_staff and not \
(request.path.startswith('/admin/') or
request.path.startswith(settings.LOGIN_URL) or
request.path.startswith(settings.LOGOUT_URL)):
return redirect(settings.LOGIN_URL)
You have the user.is_staff, user.is_superuser and user.is_authenticated functions to check this wether in your views or templates. If you need to do this in your templates, the user is in the {{ request.user }} and also in your views with request.user.

How to limit a view to superuser only?

view.py
#login_required
#permission_required('is_superuser')
def score_reset(request):
pass
url.py
url(r'^score-reset/$', score_reset, name='score-reset'),
I have the following code and to my surprise I still hit the function, despite being logged in with a non superuser. I was expecting to get a permission denied.
What am I missing?
is_superuser isn't a permission, it's an attribute on the user model. Django already has another decorator you can make use of called user_passes_test to perform this check:
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(lambda u: u.is_superuser)
def score_reset(self,...):
...
allowing only super user login
Django is_staff permission decorator
Above answers seems to be for very early versions of django.
They are bit complicated than for the more later version
for django 1.11 here is a bit similar but simpler strategy.
views.py
from django.contrib.auth.decorators import login_required
#login_required
def some_view(request):
if request.user.is_superuser:
//allow access only to superuser
return render(request, 'app/template1.html', args)
else:
//allow access only to user
return render(request, 'app/template2.html', args)
Make use of Django's UserPassesTestMixin
Create a custom mixin SuperuserRequiredMixin
#mixins.py
from django.contrib.auth.mixins import UserPassesTestMixin
class SuperuserRequiredMixin(UserPassesTestMixin):
def test_func(self):
return self.request.user.is_superuser
Usage
class SomeSuperUserOnlyView(SuperuserRequiredMixin, ListView):
form_class = ExamForm
template_name = 'exam/newexam.html'
#user_passes_test is not an elegant solution if you want to perform this check on many views.
You can easily write your own decorathor having for example #staff_member_require.
Here you can see one of the possible solutions.
You can use the user passes test decorator to restrict access any way you want. Here is a restriction based on user email example:
from django.contrib.auth.decorators import user_passes_test
def email_check(user):
x = False
if user.email == 'anyemailhere':
x = True
return x
# Create your views here.
#user_passes_test(email_check)
def dash_index(request):
...
More here https://docs.djangoproject.com/en/2.1/topics/auth/default/#the-permission-required-decorator
SuperuserRequiredMixin
Another permission-based mixin. This is specifically for requiring a user to be a superuser. Comes in handy for tools that only privileged users should have access to.
First install: pip install django-braces
views.py
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
class SomeSuperuserView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
template_name = "path/to/template.html"

allowing only super user login

I have written a django page that requires only super users to login. So I have added
foo_view = staff_member_required(foo_view)
but it doesn't cut, now I can control only allowing staff marked users to login but this doesn't cut. I have tried something like
def foo_view(request):
if not request.user.is_superuser:
#render some "not allowed page"
#else render the page
but it doesn't seem to help as giving me errors.
Try:
from django.contrib.auth.decorators import user_passes_test
def foo_view(request):
# ...
foo_view = user_passes_test(lambda u: u.is_superuser)(foo_view)
Or (with Python >= 2.4):
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(lambda u: u.is_superuser)
def foo_view(request):
# ...
The code you suggested works perfect for me, I used it in many projects since Django 1.4:
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
#login_required
def foo_view(request):
if not request.user.is_superuser:
return HttpResponse('The user is not superuser')
# Do whatever you need to do
This code works perfect for me since Django 1.4 to 1.7 at least.
Above answers seems to be for very early versions of django. They are bit complicated than for the more later version
for django 1.11 here is a bit similar but simpler strategy.
click here