From 9886a1b52553231599cd0a8800ad8330adf6040c Mon Sep 17 00:00:00 2001
From: Emmanuel Cazenave <ecazenave@entrouvert.com>
Date: Wed, 8 Jun 2022 16:12:13 +0200
Subject: [PATCH 2/2] middleware: add a maintenance middleware (#65447)

---
 tests/test_maintenance.py               | 51 +++++++++++++++++++++++++
 wcs/middleware.py                       | 30 +++++++++++++++
 wcs/settings.py                         |  1 +
 wcs/templates/wcs/maintenance_page.html |  8 ++++
 4 files changed, 90 insertions(+)
 create mode 100644 tests/test_maintenance.py
 create mode 100644 wcs/templates/wcs/maintenance_page.html

diff --git a/tests/test_maintenance.py b/tests/test_maintenance.py
new file mode 100644
index 000000000..4c8cbdc06
--- /dev/null
+++ b/tests/test_maintenance.py
@@ -0,0 +1,51 @@
+import os
+
+from .utilities import create_temporary_pub, get_app
+
+
+def test_maintenance_page(settings):
+    pub = create_temporary_pub()
+    app = get_app(pub)
+    resp = app.get('/')
+    assert resp.status_code == 200
+
+    site_options_path = os.path.join(pub.app_dir, 'site-options.cfg')
+    with open(site_options_path, 'w') as fd:
+        fd.write(
+            '''\
+            [variables]
+            maintenance_page = True
+            '''
+        )
+    resp = app.get('/', status=503)
+    assert 'This site is currently unavailable.' in resp.text
+
+    with open(site_options_path, 'w') as fd:
+        fd.write(
+            '''\
+            [variables]
+            maintenance_page = True
+            maintenance_page_message = foo bar
+            '''
+        )
+    resp = app.get('/', status=503)
+    assert 'This site is currently unavailable.' in resp.text
+    assert 'foo bar' in resp.text
+
+    settings.MAINTENANCE_PASS_THROUGH_IPS = ['127.0.0.1']
+    resp = app.get('/')
+    assert resp.status_code == 200
+
+    settings.MAINTENANCE_PASS_THROUGH_IPS = []
+    resp = app.get('/', status=503)
+
+    with open(site_options_path, 'w') as fd:
+        fd.write(
+            '''\
+            [variables]
+            maintenance_page = True
+            maintenance_pass_through_header = X-Entrouvert
+            '''
+        )
+    resp = app.get('/', headers={'X-Entrouvert': 'yes'})
+    assert resp.status_code == 200
diff --git a/wcs/middleware.py b/wcs/middleware.py
index bd69004a3..408145fd0 100644
--- a/wcs/middleware.py
+++ b/wcs/middleware.py
@@ -19,7 +19,9 @@ import threading
 import time
 import urllib.parse
 
+from django.conf import settings
 from django.http import HttpResponseBadRequest, HttpResponseRedirect
+from django.template.response import TemplateResponse
 from django.utils.deprecation import MiddlewareMixin
 from quixote import get_publisher
 from quixote.errors import RequestError
@@ -114,3 +116,31 @@ class AfterJobsMiddleware(MiddlewareMixin):
             else:
                 http_response.process_after_jobs()
         return response
+
+
+def pass_through(request, pub):
+    remote_addr = request.META.get('REMOTE_ADDR')
+    if remote_addr:
+        pass_through_ips = getattr(settings, 'MAINTENANCE_PASS_THROUGH_IPS', [])
+        if remote_addr in pass_through_ips:
+            return True
+    pass_through_header = pub.get_site_option('maintenance_pass_through_header', 'variables')
+    if pass_through_header and pass_through_header in request.headers:
+        return True
+    return False
+
+
+class MaintenanceMiddleware(MiddlewareMixin):
+    def process_request(self, request):
+        pub = get_publisher()
+        maintenance_mode = pub.get_site_option('maintenance_page', 'variables')
+        if maintenance_mode and not pass_through(request, pub):
+            maintenance_message = pub.get_site_option('maintenance_page_message', 'variables')
+            context = {'maintenance_message': maintenance_message or ''}
+            return TemplateResponse(
+                request,
+                ['hobo/maintenance/maintenance_page.html', 'wcs/maintenance_page.html'],
+                context=context,
+                status=503,
+            ).render()
+        return self.get_response(request)
diff --git a/wcs/settings.py b/wcs/settings.py
index 073312b6e..a6bcc813e 100644
--- a/wcs/settings.py
+++ b/wcs/settings.py
@@ -115,6 +115,7 @@ MIDDLEWARE = (
     'wcs.middleware.PublisherInitialisationMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
     'wcs.compat.PublishErrorMiddleware',
+    'wcs.middleware.MaintenanceMiddleware',
     'wcs.middleware.AfterJobsMiddleware',
 )
 
diff --git a/wcs/templates/wcs/maintenance_page.html b/wcs/templates/wcs/maintenance_page.html
new file mode 100644
index 000000000..d5289aa8c
--- /dev/null
+++ b/wcs/templates/wcs/maintenance_page.html
@@ -0,0 +1,8 @@
+{% load i18n %}
+
+<html>
+  <body>
+    <h1>{% trans "This site is currently unavailable." %}</h1>
+    <p>{{ maintenance_message|default:"" }}</p>
+  </body>
+</html>
-- 
2.30.2

