2

One of my company's project is in Django and I was assigned to a task to 'add authentication to the top-level router' so we don't forget to add an authentication code to every view, such as:

if not request.user.is_authenticated:
    return HttpResponseRedirect('/admin/login/?next=/admin/some-page/')

After doing a bit of research I found out that this can be accomplished by writing my own middleware. I was not familiar at all with the concept until doing my homework. So now I am trying to write a LoginRequiredMiddleware class that intercepts every request and sends the user to the login page if not authenticated and, after authentication, to the original page the user was trying to access.

This is the code I have so far. middleware.py

from django.conf import settings
from django.http import HttpResponseRedirect
from django.utils.deprecation import MiddlewareMixin
from django.utils.http import is_safe_url
import re


EXEMPT_URLS = [re.compile(settings.LOGIN_REDIRECT_URL.lstrip('/'))] # '/admin'


class LoginRequiredMiddleware(MiddlewareMixin):

    def process_request(self, request):
        assert hasattr(request, 'user'), "The Login Required Middleware"
        if not request.user.is_authenticated:
            path = request.path_info.lstrip('/')
            
            if not any(m.match(path) for m in EXEMPT_URLS):
                redirect_to = settings.LOGIN_REDIRECT_URL

                if len(path) > 0 and is_safe_url(
                      url=request.path_info, allowed_hosts=request.get_host()):
                    redirect_to = f"{settings.LOGIN_REDIRECT_URL}/login?next={request.path_info}"

                return HttpResponseRedirect(redirect_to)

I have already registered the middleware in the MIDDLEWARE list in settings.py and included both SessionMiddleware and AuthenticationMiddleware but I have not managed to get it to work. I can access a page that requires authentication in incognito mode, for example, without logging in.

I would like some tips on what am I doing wrong or which better path I should be following.

everspader
  • 1,272
  • 14
  • 44
  • 1. Did you add this to your middleware in settings.py? 2. Can you debug what happens when you hit ```if not any(m.match(path) for m in EXEMPT_URLS):``` ? – Rob L Feb 01 '21 at 16:35
  • As I mentioned, I have added all the required middleware in settings.py and double-checked just to be sure. In fact, that `if not any...` is the condition that is never being validated. Any page that I try to access is of the form `admin/some-page/...` and my `LOGIN_REDIRECT_URL = '/admin'` – everspader Feb 01 '21 at 16:38
  • When you say it's "never being validated," does that mean that the condition is never checked, or that it's returning false? What, for example, is the value of ```not request.user.is_authenticated ``` ? – Rob L Feb 01 '21 at 17:52

1 Answers1

1
import re

from django.conf import settings
from django.shortcuts import redirect
from django.contrib.auth import logout
from django.utils.deprecation import MiddlewareMixin


EXEMPT_URLS = [re.compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
    EXEMPT_URLS += [re.compile(url) for url in settings.LOGIN_EXEMPT_URLS]
# Other login exempt urls can be added in settings with 'LOGIN_EXEMPT_URLS'


class LoginRequiredMiddleware(MiddlewareMixin):
    def __init__(self, get_response):
        self.get_response = get_response

    def process_request(self, request):
        assert hasattr(request, 'user')

        path = request.path_info.lstrip('/')

        for endpoint in ["api/", "settings", "mfa", "reset"]:
            if path.startswith(endpoint):
                return self.get_response(request)

        url_is_exempt = any(url.match(path) for url in EXEMPT_URLS)

        if path == settings.LOGOUT_URL.lstrip('/'):
            logout(request)

        if request.user.is_authenticated and url_is_exempt:
            # If the user is authenticated and the URL is in the exempt list
            # redirect to the login page
            return redirect(settings.LOGIN_REDIRECT_URL)

        elif request.user.is_authenticated or url_is_exempt:
            # Do nothing if the user is authenticated and the URL is not in the
            # exempt list
            return None

        else:
            # Trying to access any page as a non authenticated user
            return redirect(f"{settings.LOGIN_URL}?next=/{path}")
everspader
  • 1,272
  • 14
  • 44
  • django `MiddlewareMixin` is deprecated as your import clrealy wanrs you... Why whould you create a new middleware with a deprecated API? – ניר Jan 08 '23 at 13:25