diff --git a/django_adminlte_2/middleware.py b/django_adminlte_2/middleware.py index 06e18c9d83eb617b0d4caaaeb01df9ef9875f414..502b6443ef565920666d04280c89a280ff9de562 100644 --- a/django_adminlte_2/middleware.py +++ b/django_adminlte_2/middleware.py @@ -76,9 +76,15 @@ class AuthMiddleware: # Get the path and current url name and if either listed in exempt whitelist, return true path = request.path - current_url_name = resolve(path).url_name - - if current_url_name in LOGIN_EXEMPT_WHITELIST or path in LOGIN_EXEMPT_WHITELIST: + resolver = resolve(path) + app_name = resolver.app_name + current_url_name = resolver.url_name + fully_qualified_url_name = f"{app_name}:{current_url_name}" + if ( + current_url_name in LOGIN_EXEMPT_WHITELIST + or fully_qualified_url_name in LOGIN_EXEMPT_WHITELIST + or path in LOGIN_EXEMPT_WHITELIST + ): return True # Failed all tests, return False @@ -129,19 +135,21 @@ class AuthMiddleware: one_of_permissions = getattr(view.func, 'one_of_permissions', []) login_required = getattr(view.func, 'login_required', False) - # If there are permissions, or login_required - if exempt or permissions or one_of_permissions or login_required: - return True - - # Permissions or Login Required not set, add messages, warnings, and return False - warning_message = ( - f"The view '{view.func.__qualname__}' does not have the" - " permission_required, one_of_permission, or login_required" - " attribute set and the option ADMINLTE2_USE_VIEW_STRICT_POLICY is" - " set to True. This means that this view is inaccessible until" - " either permissions are set on the view or the url_name for the" - " view is added to the ADMINLTE2_STRICT_POLICY_WHITELIST setting." - ) - warnings.warn(warning_message) - messages.debug(request, warning_message) + # If there are permissions, or login_required + if exempt or permissions or one_of_permissions or login_required: + return True + + # Permissions or Login Required not set, add messages, warnings, and return False + warning_message = ( + f"The view '{view.func.__qualname__}' does not have the" + " permission_required, one_of_permission, or login_required" + " attribute set and the option ADMINLTE2_USE_STRICT_POLICY is" + " set to True. This means that this view is inaccessible until" + " either permissions are set on the view or the url_name for the" + " view is added to the ADMINLTE2_STRICT_POLICY_WHITELIST setting." + ) + warnings.warn(warning_message) + messages.debug(request, warning_message) + + # Failed somewhere along the way, return false. return False diff --git a/tests/test_middleware.py b/tests/test_middleware.py new file mode 100644 index 0000000000000000000000000000000000000000..5d2d125064c957301030a95ffed3e1abfd870a54 --- /dev/null +++ b/tests/test_middleware.py @@ -0,0 +1,296 @@ +""" +Tests for Middleware +""" + +import warnings +from django.contrib.auth import get_user_model +from django.contrib.auth.models import AnonymousUser +from django.contrib.auth.models import Permission +from django.test import TestCase +from django.urls import reverse +from unittest.mock import patch + +from django_adminlte_2.constants import LOGIN_EXEMPT_WHITELIST, STRICT_POLICY_WHITELIST + +UserModel = get_user_model() # pylint: disable=invalid-name + +UPDATED_LOGIN_EXEMPT_WHITELIST = LOGIN_EXEMPT_WHITELIST + ['django_adminlte_2:demo-css'] +UPDATED_STRICT_POLICY_WHITELIST = STRICT_POLICY_WHITELIST + ['django_adminlte_2:demo-css'] + +class MiddlewareTestCase(TestCase): + """ + Test Middleware + """ + + # |------------------------------------------------------------------------- + # | Setup + # |------------------------------------------------------------------------- + def setUp(self): + self.test_anonymous_user = AnonymousUser() + + self.test_user_w_perms = UserModel() + self.test_user_w_perms.username = "test_user_w_perms" + self.test_user_w_perms.set_password('password') + self.test_user_w_perms.save() + + all_permissions = Permission.objects.all() + for permission in all_permissions: + self.test_user_w_perms.user_permissions.add(permission) + + # |------------------------------------------------------------------------- + # | Test middleware works as intended + # |------------------------------------------------------------------------- + + # Test format is as follows: + # def test_middleware_{result}_{user}_{login}_{strict}_{login_WL}_{strict_WL} + + # Additional details + # {login} means that the LOGIN_REQUIRED middleware is active. + # {strict} means that the STRICT_POLICY middleware is active. + # {login_WL} means that the node's route is listed in the LOGIN_EXEMPT_WHITELIST - omitted means it isn't. + # {strict_WL} means that the node's route is listed in the STRICT_POLICY_WHITELIST - omitted means it isn't. + + # ************************************************************************** + # Anonymous User + # ************************************************************************** + + def test_middleware_allows_when_user_anonymous_login_off_strict_off_login_wl_off_strict_wl_off(self): + """test_middleware_allows_when_user_anonymous_login_off_strict_off_login_wl_off_strict_wl_off""" + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + def test_middleware_blocks_when_user_anonymous_login_on_strict_off_login_wl_off_strict_wl_off(self): + """test_middleware_blocks_when_user_anonymous_login_on_strict_off_login_wl_off_strict_wl_off""" + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Login") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.LOGIN_EXEMPT_WHITELIST', UPDATED_LOGIN_EXEMPT_WHITELIST) + def test_middleware_allows_when_user_anonymous_login_on_strict_off_login_wl_on_strict_wl_off(self): + """test_middleware_allows_when_user_anonymous_login_on_strict_off_login_wl_on_strict_wl_off""" + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + def test_middleware_blocks_when_user_anonymous_login_off_strict_on_login_wl_off_strict_wl_off(self): + """test_middleware_blocks_when_user_anonymous_login_off_strict_on_login_wl_off_strict_wl_off""" + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Home") + + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY_WHITELIST', UPDATED_STRICT_POLICY_WHITELIST) + def test_middleware_allows_when_user_anonymous_login_off_strict_on_login_wl_off_strict_wl_on(self): + """test_middleware_allows_when_user_anonymous_login_off_strict_on_login_wl_off_strict_wl_on""" + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + def test_middleware_blocks_when_user_anonymous_login_on_strict_on_login_wl_off_strict_wl_off(self): + """test_middleware_blocks_when_user_anonymous_login_on_strict_on_login_wl_off_strict_wl_off""" + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Login") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + @patch('django_adminlte_2.middleware.LOGIN_EXEMPT_WHITELIST', UPDATED_LOGIN_EXEMPT_WHITELIST) + def test_middleware_blocks_when_user_anonymous_login_on_strict_on_login_wl_on_strict_wl_off(self): + """test_middleware_blocks_when_user_anonymous_login_on_strict_on_login_wl_on_strict_wl_off""" + # NOTE: This test goes to demo-css, fails the strict policy, then goes to home. + # Home is a new request that fails the login required being on and thus redirect to login page. + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Login") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY_WHITELIST', UPDATED_STRICT_POLICY_WHITELIST) + def test_middleware_blocks_when_user_anonymous_login_on_strict_on_login_wl_off_strict_wl_on(self): + """test_middleware_allows_when_user_anonymous_login_on_strict_on_login_wl_off_strict_wl_on""" + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Login") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + @patch('django_adminlte_2.middleware.LOGIN_EXEMPT_WHITELIST', UPDATED_LOGIN_EXEMPT_WHITELIST) + @patch('django_adminlte_2.middleware.STRICT_POLICY_WHITELIST', UPDATED_STRICT_POLICY_WHITELIST) + def test_middleware_allows_when_user_anonymous_login_on_strict_on_login_wl_on_strict_wl_on(self): + """test_middleware_allows_when_user_anonymous_login_on_strict_on_login_wl_on_strict_wl_on""" + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + + # ************************************************************************** + # Logged In User - All Perms + # ************************************************************************** + + def test_middleware_allows_when_user_logged_in_login_off_strict_off_login_wl_off_strict_wl_off(self): + """test_middleware_allows_when_user_logged_in_login_off_strict_off_login_wl_off_strict_wl_off""" + self.client.force_login(self.test_user_w_perms) + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + def test_middleware_allows_when_user_logged_in_login_on_strict_off_login_wl_off_strict_wl_off(self): + """test_middleware_blocks_when_user_logged_in_login_on_strict_off_login_wl_off_strict_wl_off""" + self.client.force_login(self.test_user_w_perms) + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.LOGIN_EXEMPT_WHITELIST', UPDATED_LOGIN_EXEMPT_WHITELIST) + def test_middleware_allows_when_user_logged_in_login_on_strict_off_login_wl_on_strict_wl_off(self): + """test_middleware_allows_when_user_logged_in_login_on_strict_off_login_wl_on_strict_wl_off""" + self.client.force_login(self.test_user_w_perms) + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + def test_middleware_blocks_when_user_logged_in_login_off_strict_on_login_wl_off_strict_wl_off(self): + """test_middleware_blocks_when_user_logged_in_login_off_strict_on_login_wl_off_strict_wl_off""" + self.client.force_login(self.test_user_w_perms) + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Home") + + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY_WHITELIST', UPDATED_STRICT_POLICY_WHITELIST) + def test_middleware_allows_when_user_logged_in_login_off_strict_on_login_wl_off_strict_wl_on(self): + """test_middleware_allows_when_user_logged_in_login_off_strict_on_login_wl_off_strict_wl_on""" + self.client.force_login(self.test_user_w_perms) + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + def test_middleware_blocks_when_user_logged_in_login_on_strict_on_login_wl_off_strict_wl_off(self): + """test_middleware_blocks_when_user_logged_in_login_on_strict_on_login_wl_off_strict_wl_off""" + self.client.force_login(self.test_user_w_perms) + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Home") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + @patch('django_adminlte_2.middleware.LOGIN_EXEMPT_WHITELIST', UPDATED_LOGIN_EXEMPT_WHITELIST) + def test_middleware_blocks_when_user_logged_in_login_on_strict_on_login_wl_on_strict_wl_off(self): + """test_middleware_blocks_when_user_logged_in_login_on_strict_on_login_wl_on_strict_wl_off""" + with warnings.catch_warnings(record=True) as w: + self.client.force_login(self.test_user_w_perms) + warning_message = ( + "The view 'demo_css' does not have the" + " permission_required, one_of_permission, or login_required" + " attribute set and the option ADMINLTE2_USE_STRICT_POLICY is" + " set to True. This means that this view is inaccessible until" + " either permissions are set on the view or the url_name for the" + " view is added to the ADMINLTE2_STRICT_POLICY_WHITELIST setting." + ) + + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Home") + self.assertEqual(len(w), 1) + self.assertIn(warning_message, str(w[-1].message)) + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY_WHITELIST', UPDATED_STRICT_POLICY_WHITELIST) + def test_middleware_allows_when_user_logged_in_login_on_strict_on_login_wl_off_strict_wl_on(self): + """test_middleware_allows_when_user_logged_in_login_on_strict_on_login_wl_off_strict_wl_on""" + self.client.force_login(self.test_user_w_perms) + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + @patch('django_adminlte_2.middleware.LOGIN_REQUIRED', True) + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + @patch('django_adminlte_2.middleware.LOGIN_EXEMPT_WHITELIST', UPDATED_LOGIN_EXEMPT_WHITELIST) + @patch('django_adminlte_2.middleware.STRICT_POLICY_WHITELIST', UPDATED_STRICT_POLICY_WHITELIST) + def test_middleware_allows_when_user_logged_in_login_on_strict_on_login_wl_on_strict_wl_on(self): + """test_middleware_allows_when_user_logged_in_login_on_strict_on_login_wl_on_strict_wl_on""" + self.client.force_login(self.test_user_w_perms) + response = self.client.get( + reverse('django_adminlte_2:demo-css'), + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Demo CSS") + + # ************************************************************************** + # Logged In User - All Perms - Visiting 404 + # ************************************************************************** + + @patch('django_adminlte_2.middleware.STRICT_POLICY', True) + def test_middleware_blocks_when_user_logged_in_login_off_strict_on_login_wl_off_strict_wl_off_route_unknown(self): + """test_middleware_blocks_when_user_logged_in_login_off_strict_on_login_wl_off_strict_wl_off_route_unknown""" + self.client.force_login(self.test_user_w_perms) + response = self.client.get( + 'unknown/route/', + follow=True + ) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Home")