Skip to content

Commit 921b95f

Browse files
authored
Fix: healthcheck user agents (#1285)
2 parents 2b95c78 + 1d423d0 commit 921b95f

File tree

5 files changed

+59
-1
lines changed

5 files changed

+59
-1
lines changed

benefits/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
__version__ = "2023.01.1"
1+
__version__ = "2023.02.1"
22

33
VERSION = __version__

benefits/core/middleware.py

+12
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,18 @@ def __call__(self, request):
109109
return self.get_response(request)
110110

111111

112+
class HealthcheckUserAgents(MiddlewareMixin):
113+
"""Middleware to return healthcheck for user agents specified in HEALTHCHECK_USER_AGENTS."""
114+
115+
def process_request(self, request):
116+
if hasattr(request, "META"):
117+
user_agent = request.META.get("HTTP_USER_AGENT", "")
118+
if user_agent in settings.HEALTHCHECK_USER_AGENTS:
119+
return HttpResponse("Healthy", content_type="text/plain")
120+
121+
return self.get_response(request)
122+
123+
112124
class VerifierSessionRequired(MiddlewareMixin):
113125
"""Middleware raises an exception for sessions lacking an eligibility verifier configuration."""
114126

benefits/settings.py

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def _filter_empty(ls):
4949
"django.contrib.messages.middleware.MessageMiddleware",
5050
"django.middleware.locale.LocaleMiddleware",
5151
"benefits.core.middleware.Healthcheck",
52+
"benefits.core.middleware.HealthcheckUserAgents",
5253
"django.middleware.common.CommonMiddleware",
5354
"django.middleware.csrf.CsrfViewMiddleware",
5455
"django.middleware.clickjacking.XFrameOptionsMiddleware",
@@ -67,6 +68,7 @@ def _filter_empty(ls):
6768
if DEBUG:
6869
MIDDLEWARE.append("benefits.core.middleware.DebugSession")
6970

71+
HEALTHCHECK_USER_AGENTS = _filter_empty(os.environ.get("HEALTHCHECK_USER_AGENTS", "").split(","))
7072

7173
# Azure Insights
7274
# https://docs.microsoft.com/en-us/azure/azure-monitor/app/opencensus-python-request#tracking-django-applications

docs/configuration/environment-variables.md

+8
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@ Django's primary secret, keep this safe!
145145

146146
Comma-separated list of hosts which are trusted origins for unsafe requests (e.g. POST)
147147

148+
### `HEALTHCHECK_USER_AGENTS`
149+
150+
!!! warning "Deployment configuration"
151+
152+
You must change this setting when deploying the app to a non-localhost domain
153+
154+
Comma-separated list of User-Agent strings which, when present as an HTTP header, should only receive healthcheck responses. Used by our `HealthcheckUserAgent` middleware.
155+
148156
## Cypress tests
149157

150158
!!! tldr "Cypress docs"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from django.test import Client
2+
from django.urls import reverse
3+
4+
import pytest
5+
6+
from benefits.core import session
7+
from benefits.core.models import TransitAgency
8+
from benefits.core.views import ROUTE_INDEX, TEMPLATE_PAGE
9+
10+
11+
@pytest.mark.django_db
12+
@pytest.mark.parametrize("user_agent", ["AlwaysOn", "Edge Health Probe"])
13+
def test_healthcheck_user_agent(mocker, user_agent):
14+
mocker.patch.object(session.settings, "HEALTHCHECK_USER_AGENTS", [user_agent])
15+
client = Client(HTTP_USER_AGENT=user_agent)
16+
17+
response = client.get(reverse(ROUTE_INDEX))
18+
19+
assert response.status_code == 200
20+
assert response.content.decode("UTF-8") == "Healthy"
21+
22+
23+
@pytest.mark.django_db
24+
def test_non_healthcheck_user_agent(mocker, model_TransitAgency, client):
25+
# create another Transit Agency by cloning the original to ensure there are multiple
26+
# https://stackoverflow.com/a/48149675/453168
27+
new_agency = TransitAgency.objects.get(pk=model_TransitAgency.id)
28+
new_agency.pk = None
29+
new_agency.save()
30+
31+
mocker.patch.object(session.settings, "HEALTHCHECK_USER_AGENTS", ["AlwaysOn"])
32+
33+
response = client.get(reverse(ROUTE_INDEX))
34+
35+
assert response.status_code == 200
36+
assert response.template_name == TEMPLATE_PAGE

0 commit comments

Comments
 (0)