From 07fc63dd0d86dda048d95105bb01b0e9f515d3f0 Mon Sep 17 00:00:00 2001
From: David Barnes <barnesdavidj@gmail.com>
Date: Sun, 15 Jan 2023 16:34:20 -0500
Subject: [PATCH] Fix bug where password change routes were not exempt from the
 strict policy. Add WebSocket setting that can be used like the media route
 setting to define the pattern that should be exempt from strict mode.

---
 adminlte2_pdq/constants.py  |  5 +++++
 adminlte2_pdq/middleware.py | 10 ++++++++++
 tests/test_middleware.py    | 12 ++++++++++++
 3 files changed, 27 insertions(+)

diff --git a/adminlte2_pdq/constants.py b/adminlte2_pdq/constants.py
index 97f69ca..0ce52a2 100644
--- a/adminlte2_pdq/constants.py
+++ b/adminlte2_pdq/constants.py
@@ -10,9 +10,12 @@ PWD_RESET_CONFIRM_ROUTE = getattr(settings, 'PWD_RESET_CONFIRM_ROUTE', 'password
 PWD_RESET_COMPLETE_ROUTE = getattr(settings, 'PWD_RESET_COMPLETE_ROUTE', 'password_reset_complete')
 REGISTER_ROUTE = getattr(settings, 'REGISTER_ROUTE', 'adminlte2_pdq:register')
 MEDIA_ROUTE = getattr(settings, 'MEDIA_URL', '/media/')
+WEBSOCKET_ROUTE = getattr(settings, 'WEBSOCKET_URL', '/ws/')
 
 # Known routes that should never have a permission check done.
 HOME_ROUTE = getattr(settings, 'ADMINLTE2_HOME_ROUTE', 'adminlte2_pdq:home')
+PWD_CHANGE = getattr(settings, 'PWD_CHANGE', 'password_change')
+PWD_CHANGE_DONE = getattr(settings, 'PWD_CHANGE_DONE', 'password_change_done')
 
 # List of known routes that should never require being logged in.
 LOGIN_EXEMPT_WHITELIST = [
@@ -27,6 +30,8 @@ LOGIN_EXEMPT_WHITELIST = [
 # List of known routes that should never require permissions to access.
 STRICT_POLICY_WHITELIST = [
     HOME_ROUTE,
+    PWD_CHANGE,
+    PWD_CHANGE_DONE,
 ] + LOGIN_EXEMPT_WHITELIST
 
 # Add any user defined list of exempt urls to the constant.
diff --git a/adminlte2_pdq/middleware.py b/adminlte2_pdq/middleware.py
index 7655aa3..69eeb7b 100644
--- a/adminlte2_pdq/middleware.py
+++ b/adminlte2_pdq/middleware.py
@@ -17,6 +17,7 @@ from .constants import (
     LOGIN_URL,
     HOME_ROUTE,
     MEDIA_ROUTE,
+    WEBSOCKET_ROUTE,
 )
 
 class AuthMiddleware:
@@ -88,6 +89,7 @@ class AuthMiddleware:
             or path in LOGIN_EXEMPT_WHITELIST
             or self.login_required_hook(request)
             or self.verify_media_route(path)
+            or self.verify_websocket_route(path)
         )
 
 
@@ -131,6 +133,7 @@ class AuthMiddleware:
                 or path in STRICT_POLICY_WHITELIST
                 or app_name == 'admin'
                 or self.verify_media_route(path)
+                or self.verify_websocket_route(path)
                 or self.verify_redirect_route(view_class)
             ):
                 exempt = True
@@ -178,6 +181,13 @@ class AuthMiddleware:
             return_val = path.startswith(MEDIA_ROUTE)
         return return_val
 
+    def verify_websocket_route(self, path):
+        """Verify that the path of the request is not a WEBSOCKET URL"""
+        return_val = False
+        if WEBSOCKET_ROUTE and WEBSOCKET_ROUTE != '/':
+            return_val = path.startswith(WEBSOCKET_ROUTE)
+        return return_val
+
     def verify_redirect_route(self, view_class):
         """Verify that the view class is a RedirectView"""
         return view_class and view_class == RedirectView
diff --git a/tests/test_middleware.py b/tests/test_middleware.py
index cad6a4a..7554dbf 100644
--- a/tests/test_middleware.py
+++ b/tests/test_middleware.py
@@ -340,6 +340,18 @@ class MiddlewareTestCase(TestCase):
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, "<h1>Demo CSS</h1>")
 
+    @patch('adminlte2_pdq.middleware.LOGIN_REQUIRED', True)
+    @patch('adminlte2_pdq.middleware.STRICT_POLICY', True)
+    @patch('adminlte2_pdq.middleware.WEBSOCKET_ROUTE', '/demo-css/')  # Pretend the demo-css route is a websocket file.
+    def test_middleware_allows_when_websocket_url_defined_login_on_strict_on_login_wl_on_strict_wl_on(self):
+        """test_middleware_allows_when_websocket_url_defined_login_on_strict_on_login_wl_on_strict_wl_on"""
+        response = self.client.get(
+            reverse('adminlte2_pdq:demo-css'),
+            follow=True
+        )
+        self.assertEqual(response.status_code, 200)
+        self.assertContains(response, "<h1>Demo CSS</h1>")
+
     # **************************************************************************
     # Logged In User - All Perms - Staff Status - Can see Admin page.
     # **************************************************************************
-- 
GitLab