Django permission_required error message? - django

I've added this decorator to one of my views
#permission_required('codename')
When a user visits that page and doesn't have the required permissions, he is redirected the login page, but it doesn't really tell him why he's been redirected there. Worse yet, if he logs in, and still doesn't have the permissions, he's going to be completely clueless as to why he can't access that page!
Isn't there a way I can tap into the messages framework and post an error at the same time?

Not sure what version of Django you are using, but in Django 1.4 and higher you can use:
from django.contrib.auth.decorators import permission_required
#permission_required('app.permission',raise_exception=True)
def myView(request):
#your view code
This will raise a 403 exception and if you have a 403.html page at the base of your template folder it will server this out.
If you are using class based views:
from django.views.generic.base import View
from django.contrib.auth.decorators import permission_required
from django.utils.decorators import method_decorator
class MyView(View):
#method_decorator(permission_required('app.permission',raise_exception=True)
def get(self, request):
#your GET view
Hope this helps.

You can tap into the messages framework and provide an error message. See my answer to an identical question.

You could use login_url parameter in this decorator to redirect to some other page, rather than login page. Or you can simply write your own decorator based on the code from django:
def permission_required(perm, login_url=None):
"""
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.
"""
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
Simply change login_url to some redirect_to and it won't cause any confusion.

Use #permission_required_or_403('codename')
This will redirect the users to a 403 'permission denied' error page.

Related

How to restrict regular users from accessing admin page?

I would like to allow only admin users to access admin page and raise 404 error if user is not authenticated or is not a staff member. Is there a way to wrap admin view with another function to check if user is admin?
Edit:
Uhh, I should have pointed out first that by admin view I mean built-in django admin view. Any user can visit it and there they will see a login page for admins. I would like to override this behavior and raise 404 error, so that only admins are aware of this view. Also I have a login page for everyone, so there's no need for authorization in admin view
Try to wrap your dispatch method on admin view like this:
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import user_passes_test
#method_decorator(user_passes_test(lambda u: u.is_authenticated and u.is_staff))
def dispatch(self, request, *args, **kwargs): # noqa: D102
return super().dispatch(request, *args, **kwargs)
If you need this for multiple views, write mixin that overrides dispatch method.
so if you want easy solution just add to your main urls.py above admin urls:
path('admin/login/', view404), #asaing view that return 404
path('admin/', admin.site.urls),

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."

django logout and redirection

i am using django authentification to make a oneToone relation with the user table. I try to use decorator #login_required to the next page after the login. when i logout and click on the back button of firefox, i always go to the previous page even if #login_required is apply. but when i refresh it redirect me on the login page. Is it a cache problem? how can i solve it? I want to do it like how the administration works
def logout(request):
from django.contrib.auth import logout
from django.shortcuts import redirect
logout(request)
request.session.flush()
return redirect('/')
from django.contrib.auth.decorators import login_required
#login_required(login_url='/')
def administration(request):
do something
return something

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 can I disable Django's csrf protection only in certain cases?

I'm trying to write a site in Django where the API URLs are the same as user-facing URLs. But I'm having trouble with pages which use POST requests and CSRF protection. For example, if I have a page /foo/add I want to be able to send POST requests to it in two ways:
As an end user (authenticated using a session cookie) submitting a form. This requires CSRF protection.
As an API client (authenticated using a HTTP request header). This will fail if CSRF protection is enabled.
I have found various ways of disabling CSRF, such as #csrf_exempt, but these all disable it for the entire view. Is there any way of enabling/disabling it at a more fine-grained level? Or am I just going to have to implement by own CSRF protection from scratch?
Modify urls.py
If you manage your routes in urls.py, you can wrap your desired routes with csrf_exempt() to exclude them from the CSRF verification middleware.
for instance,
from django.views.decorators.csrf import csrf_exempt
urlpatterns = patterns(
# ...
# Will exclude `/api/v1/test` from CSRF
url(r'^api/v1/test', csrf_exempt(TestApiHandler.as_view()))
# ...
)
Alternatively, as a Decorator
Some may find the use of the #csrf_exempt decorator more suitable for their needs
for instance,
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
#csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
There is a section of Django's CSRF Protection documentation titled View needs protection for one path which describes a solution. The idea is to use #csrf_exempt on the whole view, but when the API client header is not present or invalid, then call a function
annotated with #csrf_protect.
If you are you using class base view (CBV) and want to use the csrf_exempt decorator you will need to use the method decorator.
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
#method_decorator(csrf_exempt, name='dispatch')
class MyView(View):
def post(self, request):
pass # my view code here
In my case, I am using JWT authentication plus csrf_token for some views. And for some reasons that I am unaware of, csrf_exempt does not work when I set it as a decorator or when I wrap the view name in the url patterns.
So here's what I ended up doing. I overrided the initialize_request available in the APIView class.
class ClasssName(views.APIView):
def initialize_request(self, request, *args, **kwargs):
setattr(request, 'csrf_processing_done', True)
return super().initialize_request(request, *args, **kwargs)