Search code examples
djangodjango-admin

How to restrict non staff users from accessing django-admin


My django-admin page is located at

http://127.0.0.1:8000/admin/

Suppose there are 3 users in my website.

  1. Superuser
  2. Staff
  3. 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


enter image description here


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


Solution

  • 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'
        ...
    ]