diff --git a/src/Pipfile b/src/Pipfile index 433f41632..3d92c36ab 100644 --- a/src/Pipfile +++ b/src/Pipfile @@ -24,6 +24,7 @@ django-fsm = "*" django-phonenumber-field = {extras = ["phonenumberslite"], version = "*"} boto3 = "*" typing-extensions ='*' +django-login-required-middleware = "*" [dev-packages] django-debug-toolbar = "*" diff --git a/src/Pipfile.lock b/src/Pipfile.lock index e485a9b1a..cba61498c 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "6a6f3722d88e1f0059e38f23c104708d7922d41b42be1f17287cd4d84dcf9b05" + "sha256": "187278933085d8c5448015e2d28c23934c2098f4aadd82157379174629d8cf6b" }, "pipfile-spec": 6, "requires": {}, @@ -24,19 +24,19 @@ }, "boto3": { "hashes": [ - "sha256:2eb9e688aa86bf1fadcec0b6995b42ec9788e7cd5f1a9c8ac1b66a2506aa209f", - "sha256:5b7e9f2674fe8aa99e2d168744023a3f66da12d9c51e0624489dd0db7aafe30d" + "sha256:30f8ab1cf89d5864a80ba2d5eb5316dbd2a63c9469877e0cffb522630438aa85", + "sha256:77e8fa7c257f9ed8bfe0c3ffc2ccc47b1cfa27058f99415b6003699d1202e0c0" ], "index": "pypi", - "version": "==1.26.144" + "version": "==1.26.145" }, "botocore": { "hashes": [ - "sha256:c60b9158cbc7447411abdec77b87a71d86d9404064702e92d317dca6a1ec9a5b", - "sha256:e2b970e68643cf4752cad4e45ba3319fc35707f1bff7f150f7ffcac1b1427b47" + "sha256:264a3f19ed280d80711b7e278be09acff7ed379a96432fdf179b4e6e3a687e6a", + "sha256:65e2a2b1cc70583225f87d6d63736215f93c6234721967bdab872270ba7a1f45" ], "markers": "python_version >= '3.7'", - "version": "==1.29.144" + "version": "==1.29.145" }, "cachetools": { "hashes": [ @@ -306,6 +306,13 @@ "index": "pypi", "version": "==2.8.1" }, + "django-login-required-middleware": { + "hashes": [ + "sha256:847ae9a69fd7a07618ed53192b3c06946af70a0caf6d0f4eb40a8f37593cd970" + ], + "index": "pypi", + "version": "==0.9.0" + }, "django-phonenumber-field": { "extras": [ "phonenumberslite" @@ -793,11 +800,11 @@ }, "boto3": { "hashes": [ - "sha256:2eb9e688aa86bf1fadcec0b6995b42ec9788e7cd5f1a9c8ac1b66a2506aa209f", - "sha256:5b7e9f2674fe8aa99e2d168744023a3f66da12d9c51e0624489dd0db7aafe30d" + "sha256:30f8ab1cf89d5864a80ba2d5eb5316dbd2a63c9469877e0cffb522630438aa85", + "sha256:77e8fa7c257f9ed8bfe0c3ffc2ccc47b1cfa27058f99415b6003699d1202e0c0" ], "index": "pypi", - "version": "==1.26.144" + "version": "==1.26.145" }, "boto3-mocking": { "hashes": [ @@ -809,27 +816,27 @@ }, "boto3-stubs": { "hashes": [ - "sha256:1062612f63f154f47a4f5b7b40c2cb15debe5f44774587110da76fd292f528f0", - "sha256:bc0cc5067f55b2da628db8a73119ecccd74b27cf424af83e56526cd90beaf9f8" + "sha256:9413cb395c803d5b85e9ec7b16fba855a613ecd78b2e0011e2f6b62cf0b4fc1e", + "sha256:be2007f92138781288c7a22eba30b7d60742466fc28edd04637b31fabee854a5" ], "index": "pypi", - "version": "==1.26.144" + "version": "==1.26.145" }, "botocore": { "hashes": [ - "sha256:c60b9158cbc7447411abdec77b87a71d86d9404064702e92d317dca6a1ec9a5b", - "sha256:e2b970e68643cf4752cad4e45ba3319fc35707f1bff7f150f7ffcac1b1427b47" + "sha256:264a3f19ed280d80711b7e278be09acff7ed379a96432fdf179b4e6e3a687e6a", + "sha256:65e2a2b1cc70583225f87d6d63736215f93c6234721967bdab872270ba7a1f45" ], "markers": "python_version >= '3.7'", - "version": "==1.29.144" + "version": "==1.29.145" }, "botocore-stubs": { "hashes": [ - "sha256:b9db32981b4deefb01784d9b196afeaca7df6f6f185d8ba7f96c02b1c3bc0d90", - "sha256:d456543af79fbdd23df76a2d7a7525cd672b4bb5b057d7e060bc117d9af71694" + "sha256:80ffab72ad428d20cb1cf538ee55fcd94f7d81315b77d84fec99e218c3974e8b", + "sha256:928c58a434dd83bef956e3b5bb1e96278fff5eee9f8b8ab08d916cef1e9a2014" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==1.29.144" + "version": "==1.29.145" }, "click": { "hashes": [ diff --git a/src/api/tests/test_available.py b/src/api/tests/test_available.py index 061b6274c..39ddba071 100644 --- a/src/api/tests/test_available.py +++ b/src/api/tests/test_available.py @@ -104,6 +104,9 @@ class AvailableAPITest(TestCase): def test_available_post(self): """Cannot post to the /available/ API endpoint.""" + # have to log in to test the correct thing now that we require login + # for all URLs by default + self.client.force_login(self.user) with less_console_noise(): response = self.client.post(API_BASE_PATH + "nonsense") self.assertEqual(response.status_code, 405) diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 54eb35d9f..5020fe382 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -134,6 +134,8 @@ MIDDLEWARE = [ "django.middleware.csrf.CsrfViewMiddleware", # add `user` (the currently-logged-in user) to incoming HttpRequest objects "django.contrib.auth.middleware.AuthenticationMiddleware", + # Require login for every single request by default + "login_required.middleware.LoginRequiredMiddleware", # provide framework for displaying messages to the user, see documentation "django.contrib.messages.middleware.MessageMiddleware", # provide clickjacking protection via the X-Frame-Options header @@ -461,6 +463,12 @@ AUTHENTICATION_BACKENDS = [ # the login_required() decorator, LoginRequiredMixin, or AccessMixin LOGIN_URL = "/openid/login" +# We don't want the OIDC app to be login-required because then it can't handle +# the initial login requests without erroring. +LOGIN_REQUIRED_IGNORE_PATHS = [ + r"/openid/(.+)$", +] + # where to go after logging out LOGOUT_REDIRECT_URL = "home" diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index bf1cef2f2..500912952 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -35,10 +35,9 @@ class TestViews(TestCase): self.assertContains(response, "OK", status_code=200) def test_home_page(self): - """Home page should be available without a login.""" + """Home page should NOT be available without a login.""" response = self.client.get("/") - self.assertContains(response, "registrar", status_code=200) - self.assertContains(response, "Sign in") + self.assertEqual(response.status_code, 302) def test_whoami_page_no_user(self): """Whoami page not accessible without a logged-in user.""" diff --git a/src/registrar/views/application.py b/src/registrar/views/application.py index b8454bf78..d4368e868 100644 --- a/src/registrar/views/application.py +++ b/src/registrar/views/application.py @@ -1,6 +1,5 @@ import logging -from django.contrib.auth.mixins import LoginRequiredMixin from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import redirect, render from django.urls import resolve, reverse @@ -44,7 +43,7 @@ class Step(StrEnum): REVIEW = "review" -class ApplicationWizard(LoginRequiredMixin, TemplateView): +class ApplicationWizard(TemplateView): """ A common set of methods and configuration. diff --git a/src/registrar/views/health.py b/src/registrar/views/health.py index 53179382a..40f7330a0 100644 --- a/src/registrar/views/health.py +++ b/src/registrar/views/health.py @@ -1,6 +1,10 @@ from django.http import HttpResponse +from login_required import login_not_required +# the health check endpoint needs to be globally available so that the +# PaaS orchestrator can make sure the app has come up properly +@login_not_required def health(request): return HttpResponse( 'OK - Get.govOK' diff --git a/src/requirements.txt b/src/requirements.txt index 9d17f87f7..077cfed2a 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,7 +1,7 @@ -i https://pypi.python.org/simple asgiref==3.7.2 ; python_version >= '3.7' -boto3==1.26.144 -botocore==1.29.144 ; python_version >= '3.7' +boto3==1.26.145 +botocore==1.29.145 ; python_version >= '3.7' cachetools==5.3.1 certifi==2023.5.7 ; python_version >= '3.6' cfenv==0.5.3 @@ -17,6 +17,7 @@ django-auditlog==2.3.0 django-cache-url==3.4.4 django-csp==3.7 django-fsm==2.8.1 +django-login-required-middleware==0.9.0 django-phonenumber-field[phonenumberslite]==7.1.0 django-widget-tweaks==1.4.12 environs[django]==9.5.0