Преглед изворни кода

Add cache-control headers to restrict webpage caching (#3296)

Co-authored-by: Athithyaa Selvam <aselvam@cloudera.com>
Athithyaa Selvam пре 2 година
родитељ
комит
f2425d6a37

+ 4 - 0
desktop/conf.dist/hue.ini

@@ -99,6 +99,10 @@ http_500_debug_mode=false
 ## limit_request_fields=100
 ## limit_request_fields=100
 ## limit_request_line=4094
 ## limit_request_line=4094
 
 
+# Flag to disable webpage caching. Enabling this flag will reduce the performance of the application but it ensures that
+# the client is always receiving the latest version of the resource.
+## custom_cache_control=true
+
 # Filename of SSL Certificate
 # Filename of SSL Certificate
 ## ssl_certificate=
 ## ssl_certificate=
 
 

+ 4 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -104,6 +104,10 @@
   ## limit_request_fields=100
   ## limit_request_fields=100
   ## limit_request_line=4094
   ## limit_request_line=4094
 
 
+  # Flag to disable webpage caching. Enabling this flag will reduce the performance of the application but it ensures
+  # that the client is always receiving the latest version of the resource.
+  ## custom_cache_control=true
+
   # Filename of SSL Certificate
   # Filename of SSL Certificate
   ## ssl_certificate=
   ## ssl_certificate=
 
 

+ 7 - 0
desktop/core/src/desktop/conf.py

@@ -1671,6 +1671,13 @@ SEND_DBUG_MESSAGES = Config(
   default=False
   default=False
 )
 )
 
 
+CUSTOM_CACHE_CONTROL = Config(
+  key="custom_cache_control",
+  help=_("Flag to disable webpage caching."),
+  type=coerce_bool,
+  default=False
+)
+
 DATABASE_LOGGING = Config(
 DATABASE_LOGGING = Config(
   key="database_logging",
   key="database_logging",
   help=_("Enable or disable database debug mode."),
   help=_("Enable or disable database debug mode."),

+ 18 - 1
desktop/core/src/desktop/middleware.py

@@ -53,7 +53,8 @@ import desktop.views
 from desktop import appmanager, metrics
 from desktop import appmanager, metrics
 from desktop.auth.backend import is_admin, find_or_create_user, ensure_has_a_group, rewrite_user
 from desktop.auth.backend import is_admin, find_or_create_user, ensure_has_a_group, rewrite_user
 from desktop.conf import AUTH, HTTP_ALLOWED_METHODS, ENABLE_PROMETHEUS, KNOX, DJANGO_DEBUG_MODE, AUDIT_EVENT_LOG_DIR, \
 from desktop.conf import AUTH, HTTP_ALLOWED_METHODS, ENABLE_PROMETHEUS, KNOX, DJANGO_DEBUG_MODE, AUDIT_EVENT_LOG_DIR, \
-    METRICS, SERVER_USER, REDIRECT_WHITELIST, SECURE_CONTENT_SECURITY_POLICY, has_connectors, is_gunicorn_report_enabled
+    METRICS, SERVER_USER, REDIRECT_WHITELIST, SECURE_CONTENT_SECURITY_POLICY, has_connectors, is_gunicorn_report_enabled, \
+    CUSTOM_CACHE_CONTROL
 from desktop.context_processors import get_app_name
 from desktop.context_processors import get_app_name
 from desktop.lib import apputil, i18n, fsmanager
 from desktop.lib import apputil, i18n, fsmanager
 from desktop.lib.django_util import JsonResponse, render, render_json
 from desktop.lib.django_util import JsonResponse, render, render_json
@@ -953,3 +954,19 @@ class MultipleProxyMiddleware:
       request.META['HTTP_X_FORWARDED_FOR'] = request.META['REMOTE_ADDR']
       request.META['HTTP_X_FORWARDED_FOR'] = request.META['REMOTE_ADDR']
 
 
     return self.get_response(request)
     return self.get_response(request)
+
+
+class CacheControlMiddleware(MiddlewareMixin):
+  def __init__(self, get_response):
+    self.get_response = get_response
+    self.custom_cache_control = CUSTOM_CACHE_CONTROL.get()
+    if not self.custom_cache_control:
+      LOG.info('Unloading CacheControlMiddleware')
+      raise exceptions.MiddlewareNotUsed
+
+  def process_response(self, request, response):
+    if self.custom_cache_control:
+      response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
+      response['Pragma'] = 'no-cache'
+      response['Expires'] = '0'
+    return response

+ 34 - 2
desktop/core/src/desktop/middleware_test.py

@@ -22,12 +22,15 @@ import tempfile
 
 
 from django.conf import settings
 from django.conf import settings
 from django.test.client import Client
 from django.test.client import Client
-from nose.tools import assert_equal, assert_false, assert_true
+from nose.tools import assert_equal, assert_false, assert_true, assert_not_in
 from nose.plugins.skip import SkipTest
 from nose.plugins.skip import SkipTest
+from django.http import HttpResponse
+from django.core import exceptions
 
 
 import desktop.conf
 import desktop.conf
 
 
-from desktop.conf import AUDIT_EVENT_LOG_DIR
+from desktop.middleware import CacheControlMiddleware
+from desktop.conf import AUDIT_EVENT_LOG_DIR, CUSTOM_CACHE_CONTROL
 from desktop.lib.django_test_util import make_logged_in_client
 from desktop.lib.django_test_util import make_logged_in_client
 from desktop.lib.test_utils import add_permission
 from desktop.lib.test_utils import add_permission
 
 
@@ -201,3 +204,32 @@ def test_spnego_middleware():
       finish()
       finish()
     settings.AUTHENTICATION_BACKENDS = orig_backends
     settings.AUTHENTICATION_BACKENDS = orig_backends
 
 
+def test_cache_control_middleware():
+  c = Client()
+  request = c.get("/")
+
+  def dummy_get_response(request):
+    return HttpResponse()
+
+  reset = CUSTOM_CACHE_CONTROL.set_for_testing(True)
+  try:
+    middleware = CacheControlMiddleware(dummy_get_response)
+    response = middleware(request)
+    assert_equal(response['Cache-Control'], 'no-cache, no-store, must-revalidate')
+    assert_equal(response['Pragma'], 'no-cache')
+    assert_equal(response['Expires'], '0')
+  finally:
+    reset()
+
+  reset = CUSTOM_CACHE_CONTROL.set_for_testing(False)
+  try:
+    middleware = CacheControlMiddleware(dummy_get_response)
+    response = middleware(request)
+    assert_not_in('Cache-Control', response)
+    assert_not_in('Pragma', response)
+    assert_not_in('Expires', response)
+  except exceptions.MiddlewareNotUsed:
+    response = dummy_get_response(request)
+  finally:
+    reset()
+

+ 1 - 1
desktop/core/src/desktop/settings.py

@@ -164,7 +164,7 @@ MIDDLEWARE = [
     'desktop.middleware.ExceptionMiddleware',
     'desktop.middleware.ExceptionMiddleware',
     'desktop.middleware.ClusterMiddleware',
     'desktop.middleware.ClusterMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
-
+    'desktop.middleware.CacheControlMiddleware',
     'django.middleware.http.ConditionalGetMiddleware',
     'django.middleware.http.ConditionalGetMiddleware',
     #'axes.middleware.FailedLoginMiddleware',
     #'axes.middleware.FailedLoginMiddleware',
     'desktop.middleware.MimeTypeJSFileFixStreamingMiddleware',
     'desktop.middleware.MimeTypeJSFileFixStreamingMiddleware',