How to limit a view to superuser only? - django

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"

Related

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

Accessing user in Class-based generic views

I'm trying to check if user.is_authenticated() or if user.has_perm() but it seems impossible extending django Class-based genering views. The only method I found where the request appears is get().
class MyDetailView(DetailView):
def get(self, request, **kwargs):
import pdb
pdb.set_trace()
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
there I found that request.user is instance of AnonymusClass no matter if I'm logged in or not.
(Pdb) request.user.__class__
<class 'django.contrib.auth.models.AnonymousUser'>
so checking for authentification or perms will always fail:
(Pdb) self.request.user.is_authenticated()
False
I've tried overriding other methods such as get_object(), get_context_data() adn others. I each of them there is self.request attribute available, but user is still Anonymus.
So my question is: How on Earth am I supposed to check if user is logged in using Class-based views!?
Does it mean I have to (go back and) use function-based views?
I'm using Python 2.7.1+ and Django version 1.4 pre-alpha SVN-16627
In response to EVIAAC post:
Using login_required or permissions_required decorators is not an option. I need to check for permissions/logon after I retrieve object: if object has boolean field registration_required set to True only reqistered users will be able to see the page, others will get redirected to logon page (example behavior borrowed from django.contrib.flatpages).
Works properly in 1.3:
class TestView(DetailView):
def get(self, request, **kwargs):
import ipdb; ipdb.set_trace()
ipdb> request.user
<User: zk>
ipdb> request.user.is_authenticated()
True
Possibly a bug?
Try using the decorators from django.contrib.auth.decorators. In your urls.py, you can do something like:
from django.contrib.auth.decorators import login_required
...
url(r'^something/?$', login_required(MyDetailView.as_view()))
...
For checking permissions, you can use the premissions_required decorator. For more info, check out the docs: https://docs.djangoproject.com/en/dev/topics/auth/#the-login-required-decorator
I use mixins for class based views. In this case, you can do it like this:
Django Class-Based Generic Views and Authentication

Django is_staff permission decorator

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.

What is the opposite of #login_required decorator for Django views?

If I want to make sure that a view is listed as having public access, is there a decorator equivalent to #public_access which would be the opposite of #login_required and make it clear that the view should be publicly accessible always?
One use case I have in mind is to automatically add "#csrf_exempt" to all public views in addition to making it clear in the code that the view should be publicly accessible.
Unfortunately, there's currently no built-in support for this in Django, leaving you at risk of exposing sensitive info when #login_required is accidentally forgotten.
Here's a solution from one of my projects:
middleware/security.py:
def public(function):
"""
Decorator for public views that do not require authentication
"""
orig_func = function
while isinstance(orig_func, partial): # if partial - use original function for authorization
orig_func = orig_func.func
orig_func.is_public_view = True
return function
def is_public(function):
try: # cache is found
return function.is_public_view
except AttributeError: # cache is not found
result = function.__module__.startswith('django.') and not function.__module__.startswith('django.views.generic') # Avoid modifying admin and other built-in views
try: # try to recreate cache
function.is_public_view = result
except AttributeError:
pass
return result
class NonpublicMiddleware(object):
def process_view_check_logged(self, request, view_func, view_args, view_kwargs):
return
def process_view(self, request, view_func, view_args, view_kwargs):
while isinstance(view_func, partial): # if partial - use original function for authorization
view_func = view_func.func
request.public = is_public(view_func)
if not is_public(view_func):
if request.user.is_authenticated(): # only extended checks are needed
return self.process_view_check_logged(request, view_func, view_args, view_kwargs)
return self.redirect_to_login(request.get_full_path()) # => login page
def redirect_to_login(self, original_target, login_url=settings.LOGIN_URL):
return HttpResponseRedirect("%s?%s=%s" % (login_url, REDIRECT_FIELD_NAME, urlquote(original_target)))
settings.py:
MIDDLEWARE_CLASSES = (
#...
'middleware.security.NonpublicProfilefullMiddleware',
#...
)
and, finally, view code:
from <projname>.middleware import publi
#public
def some_view(request):
#...
# Login required is added automatically
def some_private_view(request):
#...
Also, you may want to look at "Automatically decorating all views of a django project" blog post
As a previous poster mentioned, login not required is the default.
However, sometimes it's useful to block certain views from logged in users -- for instance, it makes no sense for a logged-in user to be able to use the site's signup page. In that case, you could do something like this, based off the existing login_required decorator
from django.contrib.auth.decorators import user_passes_test
from django.conf import settings
LOGGED_IN_HOME = settings.LOGGED_IN_HOME
def login_forbidden(function=None, redirect_field_name=None, redirect_to=LOGGED_IN_HOME):
"""
Decorator for views that checks that the user is NOT logged in, redirecting
to the homepage if necessary.
"""
actual_decorator = user_passes_test(
lambda u: not u.is_authenticated(),
login_url=redirect_to,
redirect_field_name=redirect_field_name
)
if function:
return actual_decorator(function)
return actual_decorator
A bit late, but another simple way to tackle this issue would be to rely on another decorator and add a lambda function:
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(lambda u: u.is_anonymous)
You can use user_passes_test and add a requirement, anonymous_required. This would work like the opposite to login_required and you can use on your register and login page - it is a bit irritating for users to still see the login page, after the are logged in.
This is how you would do it:
from django.contrib.auth.decorators import user_passes_test
#Anonymous required
def anonymous_required(function=None, redirect_url=None):
if not redirect_url:
redirect_url = settings.LOGIN_REDIRECT_URL
actual_decorator = user_passes_test(
lambda u: u.is_anonymous,
login_url=redirect_url
)
if function:
return actual_decorator(function)
return actual_decorator
#Application of the Decorator
#anonymous_required
def register(request):
#Your code goes here
"Login not required" is the default. If you want to annotate that a view should never be login-restricted then you should do so in the docstring.
I use django-decorator-include to use the login_required decorator to secure an entire app, rather than one view at a time. My app's main urls.py looks like this:
path('my_secret_app/', decorator_include(login_required, ('my_secret_app.urls', 'my_secret_app'))),
This works great, except for when one of my apps has one view which needs to be public.
To get around this, I wrote my own login_required and login_not_required. My login_required is based on django's django.contrib.auth.decorators.login_required, but is slightly modified to actually care when a view is marked as not requiring login.
My project is called mysite.
My app is called my_secret_app.
My public view within my_secret_app is called MyPublicView.
My entire solution looks like this:
mysite/lib.py
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import user_passes_test
# A copy of django.contrib.auth.decorators.login_required that looks for login_not_required attr
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
actual_decorator = user_passes_test(
lambda u: u.is_authenticated,
login_url=login_url,
redirect_field_name=redirect_field_name
)
if function:
login_req = getattr(function, "login_required", True)
if login_req:
return actual_decorator(function)
else:
return function
else:
return actual_decorator
# Decorator to mark a view as not requiring login to access
def login_not_required(f):
f.login_required = False
return f
mysite/urls.py
from .lib import login_required
path('my_secret_app/', decorator_include(login_required, ('my_secret_app.urls', 'my_secret_app'))),
my_secret_app/views.py:
from django.utils.decorators import method_decorator
from mysite.lib import login_not_required
class MyPublicView(View):
#method_decorator(login_not_required)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request):
...
def post(self, request, *args, **kwargs):
...
You should be able to do the same thing no matter if you're subclassing View, ListView, CreateView, UpdateView, TemplateView, or any of the other ones. It should also work if you're using a function as your view, though I haven't tried it:
#login_not_required
def MyPublicView(request):
...
#permission_classes([permissions.AllowAny])

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