From 5de32b640732c2811d0aad2ccc541bba15233129 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 18 Mar 2025 18:54:00 -0400 Subject: [PATCH 1/2] Middleware router logs --- src/registrar/config/settings.py | 2 + src/registrar/registrar_middleware.py | 28 +++++++++++ .../tests/test_middleware_logging.py | 49 +++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 src/registrar/tests/test_middleware_logging.py diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index acd319ee5..92c2b2d61 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -203,6 +203,8 @@ MIDDLEWARE = [ "registrar.registrar_middleware.CheckPortfolioMiddleware", # Restrict access using Opt-Out approach "registrar.registrar_middleware.RestrictAccessMiddleware", + # Our own router logs that included user info to speed up log tracing time on stable + "registrar.registrar_middleware.RequestLoggingMiddleware", ] # application object used by Django's built-in servers (e.g. `runserver`) diff --git a/src/registrar/registrar_middleware.py b/src/registrar/registrar_middleware.py index 8c9c79876..36be3cd39 100644 --- a/src/registrar/registrar_middleware.py +++ b/src/registrar/registrar_middleware.py @@ -222,3 +222,31 @@ class RestrictAccessMiddleware: raise PermissionDenied # Deny access if the view lacks explicit permission handling return self.get_response(request) + + +class RequestLoggingMiddleware: + """ + Middleware to log user email, remote address, and request path. + """ + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + response = self.get_response(request) + + # Only log in production (stable) + if getattr(settings, "IS_PRODUCTION", False): + # Get user email (if authenticated), else "Anonymous" + user_email = request.user.email if request.user.is_authenticated else "Anonymous" + + # Get remote IP address + remote_ip = request.META.get("REMOTE_ADDR", "Unknown IP") + + # Get request path + request_path = request.path + + # Log user information + logger.info(f"Router log | User: {user_email} | IP: {remote_ip} | Path: {request_path}") + + return response diff --git a/src/registrar/tests/test_middleware_logging.py b/src/registrar/tests/test_middleware_logging.py new file mode 100644 index 000000000..deb19a4f0 --- /dev/null +++ b/src/registrar/tests/test_middleware_logging.py @@ -0,0 +1,49 @@ +from django.test import TestCase, RequestFactory, override_settings +from unittest.mock import patch, MagicMock +from django.conf import settings +from django.contrib.auth.models import AnonymousUser, User + +from registrar.registrar_middleware import RequestLoggingMiddleware + + +class RequestLoggingMiddlewareTest(TestCase): + """Test 'our' middleware logging.""" + + def setUp(self): + self.factory = RequestFactory() + self.get_response_mock = MagicMock() + self.middleware = RequestLoggingMiddleware(self.get_response_mock) + + @override_settings(IS_PRODUCTION=True) # Scopes change to this test only + @patch("logging.Logger.info") + def test_logging_enabled_in_production(self, mock_logger): + """Test that logging occurs when IS_PRODUCTION is True""" + request = self.factory.get("/test-path", **{"REMOTE_ADDR": "Unknown IP"}) # Override IP + request.user = User(username="testuser", email="testuser@example.com") + + self.middleware(request) # Call middleware + + mock_logger.assert_called_once_with( + "Router log | User: testuser@example.com | IP: Unknown IP | Path: /test-path" + ) + + @patch("logging.Logger.info") + def test_logging_disabled_in_non_production(self, mock_logger): + """Test that logging does not occur when IS_PRODUCTION is False""" + request = self.factory.get("/test-path") + request.user = User(username="testuser", email="testuser@example.com") + + self.middleware(request) # Call middleware + + mock_logger.assert_not_called() # Ensure no logs are generated + + @override_settings(IS_PRODUCTION=True) # Scopes change to this test only + @patch("logging.Logger.info") + def test_logging_anonymous_user(self, mock_logger): + """Test logging for an anonymous user""" + request = self.factory.get("/anonymous-path", **{"REMOTE_ADDR": "Unknown IP"}) # Override IP + request.user = AnonymousUser() # Simulate an anonymous user + + self.middleware(request) # Call middleware + + mock_logger.assert_called_once_with("Router log | User: Anonymous | IP: Unknown IP | Path: /anonymous-path") From ae405c9652a36720324ca4e5d91660e0890fe857 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 18 Mar 2025 19:03:34 -0400 Subject: [PATCH 2/2] lint --- src/registrar/tests/test_middleware_logging.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/registrar/tests/test_middleware_logging.py b/src/registrar/tests/test_middleware_logging.py index deb19a4f0..d84c25af5 100644 --- a/src/registrar/tests/test_middleware_logging.py +++ b/src/registrar/tests/test_middleware_logging.py @@ -1,6 +1,5 @@ from django.test import TestCase, RequestFactory, override_settings from unittest.mock import patch, MagicMock -from django.conf import settings from django.contrib.auth.models import AnonymousUser, User from registrar.registrar_middleware import RequestLoggingMiddleware