prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Add unittests for auth stack
  Add full coverage for https://github.com/inventree/InvenTree/pull/2976
  And the full auth stack in the middleware
  Add unittests for auth stack
  Add full coverage for https://github.com/inventree/InvenTree/pull/2976
  And the full auth stack in the middleware
  
  </issue>
  <code>
  [start of InvenTree/InvenTree/middleware.py]
  1 # -*- coding: utf-8 -*-
  2 
  3 from django.conf import settings
  4 from django.contrib.auth.middleware import PersistentRemoteUserMiddleware
  5 from django.http import HttpResponse
  6 from django.shortcuts import HttpResponseRedirect
  7 from django.shortcuts import redirect
  8 from django.urls import reverse_lazy, Resolver404
  9 from django.urls import include, re_path
  10 
  11 import logging
  12 
  13 from rest_framework.authtoken.models import Token
  14 from allauth_2fa.middleware import BaseRequire2FAMiddleware, AllauthTwoFactorMiddleware
  15 
  16 from InvenTree.urls import frontendpatterns
  17 from common.models import InvenTreeSetting
  18 
  19 
  20 logger = logging.getLogger("inventree")
  21 
  22 
  23 class AuthRequiredMiddleware(object):
  24     def __init__(self, get_response):
  25         self.get_response = get_response
  26 
  27     def __call__(self, request):
  28         # Code to be executed for each request before
  29         # the view (and later middleware) are called.
  30 
  31         assert hasattr(request, 'user')
  32 
  33         # API requests are handled by the DRF library
  34         if request.path_info.startswith('/api/'):
  35             return self.get_response(request)
  36 
  37         if not request.user.is_authenticated:
  38             """
  39             Normally, a web-based session would use csrftoken based authentication.
  40             However when running an external application (e.g. the InvenTree app or Python library),
  41             we must validate the user token manually.
  42             """
  43 
  44             authorized = False
  45 
  46             # Allow static files to be accessed without auth
  47             # Important for e.g. login page
  48             if request.path_info.startswith('/static/'):
  49                 authorized = True
  50 
  51             # Unauthorized users can access the login page
  52             elif request.path_info.startswith('/accounts/'):
  53                 authorized = True
  54 
  55             elif 'Authorization' in request.headers.keys() or 'authorization' in request.headers.keys():
  56                 auth = request.headers.get('Authorization', request.headers.get('authorization')).strip()
  57 
  58                 if auth.lower().startswith('token') and len(auth.split()) == 2:
  59                     token_key = auth.split()[1]
  60 
  61                     # Does the provided token match a valid user?
  62                     try:
  63                         token = Token.objects.get(key=token_key)
  64 
  65                         # Provide the user information to the request
  66                         request.user = token.user
  67                         authorized = True
  68 
  69                     except Token.DoesNotExist:
  70                         logger.warning(f"Access denied for unknown token {token_key}")
  71 
  72             # No authorization was found for the request
  73             if not authorized:
  74                 # A logout request will redirect the user to the login screen
  75                 if request.path_info == reverse_lazy('account_logout'):
  76                     return HttpResponseRedirect(reverse_lazy('account_login'))
  77 
  78                 path = request.path_info
  79 
  80                 # List of URL endpoints we *do not* want to redirect to
  81                 urls = [
  82                     reverse_lazy('account_login'),
  83                     reverse_lazy('account_logout'),
  84                     reverse_lazy('admin:login'),
  85                     reverse_lazy('admin:logout'),
  86                 ]
  87 
  88                 # Do not redirect requests to any of these paths
  89                 paths_ignore = [
  90                     '/api/',
  91                     '/js/',
  92                     '/media/',
  93                     '/static/',
  94                 ]
  95 
  96                 if path not in urls and not any([path.startswith(p) for p in paths_ignore]):
  97                     # Save the 'next' parameter to pass through to the login view
  98 
  99                     return redirect('{}?next={}'.format(reverse_lazy('account_login'), request.path))
  100 
  101                 else:
  102                     # Return a 401 (Unauthorized) response code for this request
  103                     return HttpResponse('Unauthorized', status=401)
  104 
  105         response = self.get_response(request)
  106 
  107         return response
  108 
  109 
  110 url_matcher = re_path('', include(frontendpatterns))
  111 
  112 
  113 class Check2FAMiddleware(BaseRequire2FAMiddleware):
  114     """check if user is required to have MFA enabled"""
  115     def require_2fa(self, request):
  116         # Superusers are require to have 2FA.
  117         try:
  118             if url_matcher.resolve(request.path[1:]):
  119                 return InvenTreeSetting.get_setting('LOGIN_ENFORCE_MFA')
  120         except Resolver404:
  121             pass
  122         return False
  123 
  124 
  125 class CustomAllauthTwoFactorMiddleware(AllauthTwoFactorMiddleware):
  126     """This function ensures only frontend code triggers the MFA auth cycle"""
  127     def process_request(self, request):
  128         try:
  129             if not url_matcher.resolve(request.path[1:]):
  130                 super().process_request(request)
  131         except Resolver404:
  132             pass
  133 
  134 
  135 class InvenTreeRemoteUserMiddleware(PersistentRemoteUserMiddleware):
  136     """
  137     Middleware to check if HTTP-header based auth is enabled and to set it up
  138     """
  139     header = settings.REMOTE_LOGIN_HEADER
  140 
  141     def process_request(self, request):
  142         if not settings.REMOTE_LOGIN:
  143             return
  144 
  145         return super().process_request(request)
  146
  [end of InvenTree/InvenTree/middleware.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/InvenTree/InvenTree/middleware.py b/InvenTree/InvenTree/middleware.py
  --- a/InvenTree/InvenTree/middleware.py
  +++ b/InvenTree/InvenTree/middleware.py
  @@ -3,7 +3,6 @@
   from django.conf import settings
   from django.contrib.auth.middleware import PersistentRemoteUserMiddleware
   from django.http import HttpResponse
  -from django.shortcuts import HttpResponseRedirect
   from django.shortcuts import redirect
   from django.urls import reverse_lazy, Resolver404
   from django.urls import include, re_path
  @@ -71,10 +70,6 @@
   
               # No authorization was found for the request
               if not authorized:
  -                # A logout request will redirect the user to the login screen
  -                if request.path_info == reverse_lazy('account_logout'):
  -                    return HttpResponseRedirect(reverse_lazy('account_login'))
  -
                   path = request.path_info
   
                   # List of URL endpoints we *do not* want to redirect to
  
verification_info: {"golden_diff": "diff --git a/InvenTree/InvenTree/middleware.py b/InvenTree/InvenTree/middleware.py\n--- a/InvenTree/InvenTree/middleware.py\n+++ b/InvenTree/InvenTree/middleware.py\n@@ -3,7 +3,6 @@\n from django.conf import settings\n from django.contrib.auth.middleware import PersistentRemoteUserMiddleware\n from django.http import HttpResponse\n-from django.shortcuts import HttpResponseRedirect\n from django.shortcuts import redirect\n from django.urls import reverse_lazy, Resolver404\n from django.urls import include, re_path\n@@ -71,10 +70,6 @@\n \n             # No authorization was found for the request\n             if not authorized:\n-                # A logout request will redirect the user to the login screen\n-                if request.path_info == reverse_lazy('account_logout'):\n-                    return HttpResponseRedirect(reverse_lazy('account_login'))\n-\n                 path = request.path_info\n \n                 # List of URL endpoints we *do not* want to redirect to\n", "issue": "Add unittests for auth stack\nAdd full coverage for https://github.com/inventree/InvenTree/pull/2976\r\nAnd the full auth stack in the middleware\nAdd unittests for auth stack\nAdd full coverage for https://github.com/inventree/InvenTree/pull/2976\r\nAnd the full auth stack in the middleware\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\nfrom django.conf import settings\nfrom django.contrib.auth.middleware import PersistentRemoteUserMiddleware\nfrom django.http import HttpResponse\nfrom django.shortcuts import HttpResponseRedirect\nfrom django.shortcuts import redirect\nfrom django.urls import reverse_lazy, Resolver404\nfrom django.urls import include, re_path\n\nimport logging\n\nfrom rest_framework.authtoken.models import Token\nfrom allauth_2fa.middleware import BaseRequire2FAMiddleware, AllauthTwoFactorMiddleware\n\nfrom InvenTree.urls import frontendpatterns\nfrom common.models import InvenTreeSetting\n\n\nlogger = logging.getLogger(\"inventree\")\n\n\nclass AuthRequiredMiddleware(object):\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        # Code to be executed for each request before\n        # the view (and later middleware) are called.\n\n        assert hasattr(request, 'user')\n\n        # API requests are handled by the DRF library\n        if request.path_info.startswith('/api/'):\n            return self.get_response(request)\n\n        if not request.user.is_authenticated:\n            \"\"\"\n            Normally, a web-based session would use csrftoken based authentication.\n            However when running an external application (e.g. the InvenTree app or Python library),\n            we must validate the user token manually.\n            \"\"\"\n\n            authorized = False\n\n            # Allow static files to be accessed without auth\n            # Important for e.g. login page\n            if request.path_info.startswith('/static/'):\n                authorized = True\n\n            # Unauthorized users can access the login page\n            elif request.path_info.startswith('/accounts/'):\n                authorized = True\n\n            elif 'Authorization' in request.headers.keys() or 'authorization' in request.headers.keys():\n                auth = request.headers.get('Authorization', request.headers.get('authorization')).strip()\n\n                if auth.lower().startswith('token') and len(auth.split()) == 2:\n                    token_key = auth.split()[1]\n\n                    # Does the provided token match a valid user?\n                    try:\n                        token = Token.objects.get(key=token_key)\n\n                        # Provide the user information to the request\n                        request.user = token.user\n                        authorized = True\n\n                    except Token.DoesNotExist:\n                        logger.warning(f\"Access denied for unknown token {token_key}\")\n\n            # No authorization was found for the request\n            if not authorized:\n                # A logout request will redirect the user to the login screen\n                if request.path_info == reverse_lazy('account_logout'):\n                    return HttpResponseRedirect(reverse_lazy('account_login'))\n\n                path = request.path_info\n\n                # List of URL endpoints we *do not* want to redirect to\n                urls = [\n                    reverse_lazy('account_login'),\n                    reverse_lazy('account_logout'),\n                    reverse_lazy('admin:login'),\n                    reverse_lazy('admin:logout'),\n                ]\n\n                # Do not redirect requests to any of these paths\n                paths_ignore = [\n                    '/api/',\n                    '/js/',\n                    '/media/',\n                    '/static/',\n                ]\n\n                if path not in urls and not any([path.startswith(p) for p in paths_ignore]):\n                    # Save the 'next' parameter to pass through to the login view\n\n                    return redirect('{}?next={}'.format(reverse_lazy('account_login'), request.path))\n\n                else:\n                    # Return a 401 (Unauthorized) response code for this request\n                    return HttpResponse('Unauthorized', status=401)\n\n        response = self.get_response(request)\n\n        return response\n\n\nurl_matcher = re_path('', include(frontendpatterns))\n\n\nclass Check2FAMiddleware(BaseRequire2FAMiddleware):\n    \"\"\"check if user is required to have MFA enabled\"\"\"\n    def require_2fa(self, request):\n        # Superusers are require to have 2FA.\n        try:\n            if url_matcher.resolve(request.path[1:]):\n                return InvenTreeSetting.get_setting('LOGIN_ENFORCE_MFA')\n        except Resolver404:\n            pass\n        return False\n\n\nclass CustomAllauthTwoFactorMiddleware(AllauthTwoFactorMiddleware):\n    \"\"\"This function ensures only frontend code triggers the MFA auth cycle\"\"\"\n    def process_request(self, request):\n        try:\n            if not url_matcher.resolve(request.path[1:]):\n                super().process_request(request)\n        except Resolver404:\n            pass\n\n\nclass InvenTreeRemoteUserMiddleware(PersistentRemoteUserMiddleware):\n    \"\"\"\n    Middleware to check if HTTP-header based auth is enabled and to set it up\n    \"\"\"\n    header = settings.REMOTE_LOGIN_HEADER\n\n    def process_request(self, request):\n        if not settings.REMOTE_LOGIN:\n            return\n\n        return super().process_request(request)\n", "path": "InvenTree/InvenTree/middleware.py"}]}