Add LoginRequiredMiddleware to make (almost) every URL login required by default

This commit is contained in:
Neil Martinsen-Burrell 2023-06-01 16:03:31 -05:00
parent a54f804f4d
commit ae3ba84f1a
No known key found for this signature in database
GPG key ID: 6A3C818CC10D0184
8 changed files with 48 additions and 26 deletions

View file

@ -24,6 +24,7 @@ django-fsm = "*"
django-phonenumber-field = {extras = ["phonenumberslite"], version = "*"} django-phonenumber-field = {extras = ["phonenumberslite"], version = "*"}
boto3 = "*" boto3 = "*"
typing-extensions ='*' typing-extensions ='*'
django-login-required-middleware = "*"
[dev-packages] [dev-packages]
django-debug-toolbar = "*" django-debug-toolbar = "*"

45
src/Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "6a6f3722d88e1f0059e38f23c104708d7922d41b42be1f17287cd4d84dcf9b05" "sha256": "187278933085d8c5448015e2d28c23934c2098f4aadd82157379174629d8cf6b"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": {}, "requires": {},
@ -24,19 +24,19 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:2eb9e688aa86bf1fadcec0b6995b42ec9788e7cd5f1a9c8ac1b66a2506aa209f", "sha256:30f8ab1cf89d5864a80ba2d5eb5316dbd2a63c9469877e0cffb522630438aa85",
"sha256:5b7e9f2674fe8aa99e2d168744023a3f66da12d9c51e0624489dd0db7aafe30d" "sha256:77e8fa7c257f9ed8bfe0c3ffc2ccc47b1cfa27058f99415b6003699d1202e0c0"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.26.144" "version": "==1.26.145"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:c60b9158cbc7447411abdec77b87a71d86d9404064702e92d317dca6a1ec9a5b", "sha256:264a3f19ed280d80711b7e278be09acff7ed379a96432fdf179b4e6e3a687e6a",
"sha256:e2b970e68643cf4752cad4e45ba3319fc35707f1bff7f150f7ffcac1b1427b47" "sha256:65e2a2b1cc70583225f87d6d63736215f93c6234721967bdab872270ba7a1f45"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==1.29.144" "version": "==1.29.145"
}, },
"cachetools": { "cachetools": {
"hashes": [ "hashes": [
@ -306,6 +306,13 @@
"index": "pypi", "index": "pypi",
"version": "==2.8.1" "version": "==2.8.1"
}, },
"django-login-required-middleware": {
"hashes": [
"sha256:847ae9a69fd7a07618ed53192b3c06946af70a0caf6d0f4eb40a8f37593cd970"
],
"index": "pypi",
"version": "==0.9.0"
},
"django-phonenumber-field": { "django-phonenumber-field": {
"extras": [ "extras": [
"phonenumberslite" "phonenumberslite"
@ -793,11 +800,11 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:2eb9e688aa86bf1fadcec0b6995b42ec9788e7cd5f1a9c8ac1b66a2506aa209f", "sha256:30f8ab1cf89d5864a80ba2d5eb5316dbd2a63c9469877e0cffb522630438aa85",
"sha256:5b7e9f2674fe8aa99e2d168744023a3f66da12d9c51e0624489dd0db7aafe30d" "sha256:77e8fa7c257f9ed8bfe0c3ffc2ccc47b1cfa27058f99415b6003699d1202e0c0"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.26.144" "version": "==1.26.145"
}, },
"boto3-mocking": { "boto3-mocking": {
"hashes": [ "hashes": [
@ -809,27 +816,27 @@
}, },
"boto3-stubs": { "boto3-stubs": {
"hashes": [ "hashes": [
"sha256:1062612f63f154f47a4f5b7b40c2cb15debe5f44774587110da76fd292f528f0", "sha256:9413cb395c803d5b85e9ec7b16fba855a613ecd78b2e0011e2f6b62cf0b4fc1e",
"sha256:bc0cc5067f55b2da628db8a73119ecccd74b27cf424af83e56526cd90beaf9f8" "sha256:be2007f92138781288c7a22eba30b7d60742466fc28edd04637b31fabee854a5"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.26.144" "version": "==1.26.145"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:c60b9158cbc7447411abdec77b87a71d86d9404064702e92d317dca6a1ec9a5b", "sha256:264a3f19ed280d80711b7e278be09acff7ed379a96432fdf179b4e6e3a687e6a",
"sha256:e2b970e68643cf4752cad4e45ba3319fc35707f1bff7f150f7ffcac1b1427b47" "sha256:65e2a2b1cc70583225f87d6d63736215f93c6234721967bdab872270ba7a1f45"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==1.29.144" "version": "==1.29.145"
}, },
"botocore-stubs": { "botocore-stubs": {
"hashes": [ "hashes": [
"sha256:b9db32981b4deefb01784d9b196afeaca7df6f6f185d8ba7f96c02b1c3bc0d90", "sha256:80ffab72ad428d20cb1cf538ee55fcd94f7d81315b77d84fec99e218c3974e8b",
"sha256:d456543af79fbdd23df76a2d7a7525cd672b4bb5b057d7e060bc117d9af71694" "sha256:928c58a434dd83bef956e3b5bb1e96278fff5eee9f8b8ab08d916cef1e9a2014"
], ],
"markers": "python_version >= '3.7' and python_version < '4.0'", "markers": "python_version >= '3.7' and python_version < '4.0'",
"version": "==1.29.144" "version": "==1.29.145"
}, },
"click": { "click": {
"hashes": [ "hashes": [

View file

@ -104,6 +104,9 @@ class AvailableAPITest(TestCase):
def test_available_post(self): def test_available_post(self):
"""Cannot post to the /available/ API endpoint.""" """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(): with less_console_noise():
response = self.client.post(API_BASE_PATH + "nonsense") response = self.client.post(API_BASE_PATH + "nonsense")
self.assertEqual(response.status_code, 405) self.assertEqual(response.status_code, 405)

View file

@ -134,6 +134,8 @@ MIDDLEWARE = [
"django.middleware.csrf.CsrfViewMiddleware", "django.middleware.csrf.CsrfViewMiddleware",
# add `user` (the currently-logged-in user) to incoming HttpRequest objects # add `user` (the currently-logged-in user) to incoming HttpRequest objects
"django.contrib.auth.middleware.AuthenticationMiddleware", "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 # provide framework for displaying messages to the user, see documentation
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
# provide clickjacking protection via the X-Frame-Options header # provide clickjacking protection via the X-Frame-Options header
@ -461,6 +463,12 @@ AUTHENTICATION_BACKENDS = [
# the login_required() decorator, LoginRequiredMixin, or AccessMixin # the login_required() decorator, LoginRequiredMixin, or AccessMixin
LOGIN_URL = "/openid/login" 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 # where to go after logging out
LOGOUT_REDIRECT_URL = "home" LOGOUT_REDIRECT_URL = "home"

View file

@ -35,10 +35,9 @@ class TestViews(TestCase):
self.assertContains(response, "OK", status_code=200) self.assertContains(response, "OK", status_code=200)
def test_home_page(self): 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("/") response = self.client.get("/")
self.assertContains(response, "registrar", status_code=200) self.assertEqual(response.status_code, 302)
self.assertContains(response, "Sign in")
def test_whoami_page_no_user(self): def test_whoami_page_no_user(self):
"""Whoami page not accessible without a logged-in user.""" """Whoami page not accessible without a logged-in user."""

View file

@ -1,6 +1,5 @@
import logging import logging
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import Http404, HttpResponse, HttpResponseRedirect from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.urls import resolve, reverse from django.urls import resolve, reverse
@ -44,7 +43,7 @@ class Step(StrEnum):
REVIEW = "review" REVIEW = "review"
class ApplicationWizard(LoginRequiredMixin, TemplateView): class ApplicationWizard(TemplateView):
""" """
A common set of methods and configuration. A common set of methods and configuration.

View file

@ -1,6 +1,10 @@
from django.http import HttpResponse 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): def health(request):
return HttpResponse( return HttpResponse(
'<html lang="en"><head><title>OK - Get.gov</title></head><body>OK</body>' '<html lang="en"><head><title>OK - Get.gov</title></head><body>OK</body>'

View file

@ -1,7 +1,7 @@
-i https://pypi.python.org/simple -i https://pypi.python.org/simple
asgiref==3.7.2 ; python_version >= '3.7' asgiref==3.7.2 ; python_version >= '3.7'
boto3==1.26.144 boto3==1.26.145
botocore==1.29.144 ; python_version >= '3.7' botocore==1.29.145 ; python_version >= '3.7'
cachetools==5.3.1 cachetools==5.3.1
certifi==2023.5.7 ; python_version >= '3.6' certifi==2023.5.7 ; python_version >= '3.6'
cfenv==0.5.3 cfenv==0.5.3
@ -17,6 +17,7 @@ django-auditlog==2.3.0
django-cache-url==3.4.4 django-cache-url==3.4.4
django-csp==3.7 django-csp==3.7
django-fsm==2.8.1 django-fsm==2.8.1
django-login-required-middleware==0.9.0
django-phonenumber-field[phonenumberslite]==7.1.0 django-phonenumber-field[phonenumberslite]==7.1.0
django-widget-tweaks==1.4.12 django-widget-tweaks==1.4.12
environs[django]==9.5.0 environs[django]==9.5.0