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),
Related
My django-admin page is located at
http://127.0.0.1:8000/admin/
Suppose there are 3 users in my website.
Superuser
Staff
End user
If anyone of these three users tries to access this link http://127.0.0.1:8000/admin/, 1st and 2nd can access it.
And End User is not able to access it. Till this, it is fine.
What I want is to do is to show Error 404 to End User. He/She should never know that this link is for admin page. Moreover, if anyone is not logged in, they should also see same 404 Page.
I have referred this link but it takes me to another default page.
PFA for the same
PS: I am using Signin With Google, so it should not redirect me there.
I am trying this since 3 continous days, so any help is highly appreciated. Thanks in advance.
Django-Version: 3. 0. 5
You first need to make a custom decorator that would give a 404 if the user is not a staff:
from django.http import Http404
from functools import wraps
def staff_required(func):
#wraps(func)
def wrapper(request, *args, **kwargs):
if request.user.is_staff:
return func(request, *args, **kwargs)
raise Http404()
return wrapper
Next we will use this decorator as described in your linked question,:
from django.contrib import admin
admin.site.login = staff_required(admin.site.login)
urlpatterns = [
path('admin/', admin.site.urls),
]
Edit: Above method was a bit hacky it would show the login pages url to the user even if it gives a 404 error. It would be better to make a custom admin site class and use it. The admin site has a method admin_view which decorates the admin views which we shall override. We shall do this in the file admin.py in the projects main app (let's say the project is named myproject)
from functools import update_wrapper
from django.contrib import admin
from django.http import (
Http404, HttpResponseRedirect,
)
from django.urls import reverse
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
class MyAdminSite(admin.AdminSite):
def admin_view(self, view, cacheable=False):
def inner(request, *args, **kwargs):
if not self.has_permission(request):
if request.path == reverse('admin:logout', current_app=self.name):
index_path = reverse('admin:index', current_app=self.name)
return HttpResponseRedirect(index_path)
raise Http404()
return view(request, *args, **kwargs)
if not cacheable:
inner = never_cache(inner)
# We add csrf_protect here so this function can be used as a utility
# function for any view, without having to repeat 'csrf_protect'.
if not getattr(view, 'csrf_exempt', False):
inner = csrf_protect(inner)
return update_wrapper(inner, view)
Now we need to replace the default admin site with our custom admin site. To do this we will follow Overriding the default admin site [Django docs]:
In the projects main app's apps.py file (assuming project to be named myproject):
from django.contrib.admin.apps import AdminConfig
class MyAdminConfig(AdminConfig):
default_site = 'myproject.admin.MyAdminSite'
Now in settings.py we will replace 'django.contrib.admin' with our own config class:
INSTALLED_APPS = [
...
'myproject.apps.MyAdminConfig', # replaces 'django.contrib.admin'
...
]
A simpler way is to create your own middleware that whenever the path starts with /admin/ and the user is logged in but doesn't have is_staff set to True (both staff and superusers have it), then raise the 404.
from django.http import Http404
class NoDjangoAdminForEndUserMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.path.startswith("/admin/"):
if request.user.is_authenticated and not request.user.is_staff:
raise Http404()
response = self.get_response(request)
return response
Then, to activate it, add it to the MIDDLEWARE list in your Django settings.
You'll still see the URL with http://127.0.0.1:8000/admin/, despite seeing a 404 page. If you want to redirect the user to a different page, instead of raising the Http404(), then return an HttpResponseRedirect().
When a visitor is logged out and visits my homepage, I want them to see a registration form. When the user logs in, they will be redirected back to the homepage, but it will show a listview-like view.
Is the best way to achieve this to subclass both ListView and FormView and override each method necessary to get the behavior I want? Or is there a better way? Checking if the user is authenticated in each method doesn't seem like the Django way to do this. Hoping there's a smart design pattern approach for doing this that I don't know about.
class HomepageView(ListView, FormView):
def get_template_names(self):
if self.request.user.is_authenticated:
return ['homepage/loggedin_index.html']
else:
return ['homepage/loggedout_index.html']
def get_context_data(self, **kwargs):
if self.request.user.is_authenticated:
...
else:
...
def ... and so on
My must-have requirement is that the URL for both logged-in and logged-out users must resolve to the root URL since it's a homepage.
from django.urls import path
from .views import HomepageView
app_name = 'homepage'
urlpatterns = [
path('', HomepageView.as_view(), name='index'),
]
There is also a CreateListView that do what you want. And then you can change its form to the one you want.
I made an admin panel in django. Which consists of login registration and dashboard for admin but i am facing a problem that:
If suppose a user is logged in to the system, and then the user goes to the url and go to register page using the url for ex. localhost/register when he is already logged in and no need of going to register page to register another user account. I don,t want this to happen.
How can I disallow the user to go to the register page when he is already logged in. How can we resolve this issue? Please help
You can try like this:
def register(request):
if request.user.is_authenticated:
return redirect('/') # or reverse('home_url_name')
else:
....
Update from comments:
You need to override LoginView for adding this functionality in login as well, for example:
from django.contrib.auth import views as auth_views
class CustomLoginView(auth_views.LoginView):
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('/')
return super(CustomLogin, self).get(request, *args, **kwargs)
# URL
path('accounts/login/', CustomLoginView.as_view()),
I am trying to use Django with allauth trying to create a login page.
I am able to login and redirect the user to LOGIN_REDIRECT_URL (= "/users/{id}/mypage") page successfully. I have a separate view for /users/{id}/mypage called the UserHomePageView as defined in views.py:
from django.views.generic import TemplateView
class UserHomePageView(TemplateView):
template_name = "user_homepage.html"
I want a security measure to prevent anyone (basically non-loggedin-users) to navigating to "/users/{id}/mypage" (e.g /users/5/mypage). e.i If unauthenticated user tries to navigate to /users/5/mypage, he/she should get redirected to signin page/homepage.
Basically, how do i present a unauthenticated user to view pages that are meant for authenticated users (I don't want to use template tags users.authenitcated, but wish to override the TemplateView)
Another solution which is generally used is to decorate the dispatch method with the login_required decorator:
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
class UserHomePageView(TemplateView):
template_name = "user_homepage.html"
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(UserHomePageView, self).dispatch(*args, **kwargs)
This has the advantage that it works with all methods (get, post, put, etc.), and that using the login_required decorator is more DRY than explicitly checking request.user.is_authenticated and redirecting.
I found the answer...
By overriding get() and using self.request.user.is_authenticated()
class UserHomePageView(TemplateView):
template_name = "user_homepage.html"
redirect_field_name = "next"
def get(self, request, *args, **kwargs):
'''
Overriding TemplateView.get() in order to
prevent unauthorized user to view UserHomePageView
'''
# Redirect to Homepage if user is not signed in
if not self.request.user.is_authenticated():
return redirect(self.get_redirect_url())
# Process normally if User is signed in
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
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.