From a4b64a2dcfcbf10d19d51813ba78dde3a0fea668 Mon Sep 17 00:00:00 2001 From: Mike Olund Date: Thu, 23 Mar 2017 07:55:42 -0700 Subject: [PATCH] Added basic auth to keep the public out of dev and test --- .../core/middleware/basicauth_middleware.py | 44 +++++++++++++++++++ edivorce/apps/core/templates/401.html | 30 +++++++++++++ edivorce/settings/base.py | 3 ++ edivorce/settings/openshift.py | 5 ++- .../edivorce-environment-template.yaml | 21 ++++++++- 5 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 edivorce/apps/core/middleware/basicauth_middleware.py create mode 100644 edivorce/apps/core/templates/401.html diff --git a/edivorce/apps/core/middleware/basicauth_middleware.py b/edivorce/apps/core/middleware/basicauth_middleware.py new file mode 100644 index 00000000..eefc0c2d --- /dev/null +++ b/edivorce/apps/core/middleware/basicauth_middleware.py @@ -0,0 +1,44 @@ +import base64 +from django.http import HttpResponse +from django.conf import settings +from django.template.loader import render_to_string + + +class BasicAuthMiddleware(object): + """ + Simple Basic Authentication module to password protect test environments + based on : https://djangosnippets.org/snippets/2468/ + This could have also been implemented via the NGINX-PROXY, but a Django + implementation allows environment variables to be used to store + username + password + """ + def process_request(self, request): + + # allow all OpenShift health checks + if request.path == settings.FORCE_SCRIPT_NAME + 'health': + return None + + # check if the middleware is enabled in settings + if not settings.BASICAUTH_ENABLED: + return None + + if not 'HTTP_AUTHORIZATION' in request.META: + return self.__not_authorized() + else: + authentication = request.META['HTTP_AUTHORIZATION'] + (authmeth, auth) = authentication.split(' ', 1) + if 'basic' != authmeth.lower(): + return self.__not_authorized() + auth = base64.b64decode(auth.strip()).decode('utf-8') + username, password = auth.split(':', 1) + if username == settings.BASICAUTH_USERNAME and password == settings.BASICAUTH_PASSWORD: + return None + + return self.__not_authorized() + + def __not_authorized(self): + auth_template = render_to_string('401.html') + response = HttpResponse(auth_template, content_type="text/html") + response['WWW-Authenticate'] = 'Basic realm="Development"' + response.status_code = 401 + return response diff --git a/edivorce/apps/core/templates/401.html b/edivorce/apps/core/templates/401.html new file mode 100644 index 00000000..52af6ef6 --- /dev/null +++ b/edivorce/apps/core/templates/401.html @@ -0,0 +1,30 @@ + + + + Authentication Required + + + +

This probably isn't the subdirectory you're looking for

+

+ This is our test server. It has been password protected to make sure nobody accidentally + uses it. Our test server is susceptible to untested bugs and periodic outages, and as a + bonus it will most likely lose all your saved data within a few days. +

+

+ You were probably looking for + https://justice.gov.bc.ca/divorce. +

+ + \ No newline at end of file diff --git a/edivorce/settings/base.py b/edivorce/settings/base.py index 9d845755..86413ec7 100644 --- a/edivorce/settings/base.py +++ b/edivorce/settings/base.py @@ -49,6 +49,7 @@ INSTALLED_APPS = ( ) MIDDLEWARE_CLASSES = ( + 'edivorce.apps.core.middleware.basicauth_middleware.BasicAuthMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -117,3 +118,5 @@ FORCE_SCRIPT_NAME = '/' FIXTURE_DIRS = ( os.path.join(PROJECT_ROOT, 'edivorce', 'fixtures'), ) + +BASICAUTH_ENABLED = False diff --git a/edivorce/settings/openshift.py b/edivorce/settings/openshift.py index 93cd6343..7cce0f56 100644 --- a/edivorce/settings/openshift.py +++ b/edivorce/settings/openshift.py @@ -73,5 +73,8 @@ WEASYPRINT_CSS_LOOPBACK += PROXY_URL_PREFIX PROXY_BASE_URL = 'https://justice.gov.bc.ca' LOGOUT_URL = 'https://logon.gov.bc.ca/clp-cgi/logoff.cgi?returl=%s%s&retnow=1' % (PROXY_BASE_URL, PROXY_URL_PREFIX) - +# Basic Authentication to prevent anyone from accidentally stumbling across publicly accessible dev/test environments +BASICAUTH_ENABLED = os.getenv('BASICAUTH_ENABLED', '').lower() == 'true' +BASICAUTH_USERNAME = os.getenv('BASICAUTH_USERNAME', '') +BASICAUTH_PASSWORD = os.getenv('BASICAUTH_PASSWORD', '') diff --git a/openshift/templates/edivorce-environment-template.yaml b/openshift/templates/edivorce-environment-template.yaml index 93efb6cd..a0420bdd 100644 --- a/openshift/templates/edivorce-environment-template.yaml +++ b/openshift/templates/edivorce-environment-template.yaml @@ -88,6 +88,12 @@ objects: value: "${ENVIRONMENT_TYPE}" - name: PROXY_NETWORK value: "${PROXY_NETWORK}" + - name: BASICAUTH_ENABLED + value: "${BASICAUTH_ENABLED}" + - name: BASICAUTH_USERNAME + value: "${BASICAUTH_USERNAME}" + - name: BASICAUTH_PASSWORD + value: "${BASICAUTH_PASSWORD}" resources: limits: memory: "${MEMORY_LIMIT}" @@ -217,7 +223,7 @@ objects: description: Weasyprint microservice using aquavitae/weasyprint spec: strategy: - type: Recreate + type: Rolling triggers: - type: ImageChange imageChangeParams: @@ -228,6 +234,7 @@ objects: kind: ImageStreamTag namespace: aquavitae name: 'weasyprint:latest' + - type: ConfigChange replicas: 1 selector: name: weasyprint @@ -333,3 +340,15 @@ parameters: displayName: Network of upstream proxy value: 0.0.0.0/0 required: true +- name: BASICAUTH_ENABLED + displayName: Enable basic auth (recommended for Dev and Test environments) + value: "False" + required: true +- name: BASICAUTH_USERNAME + displayName: Username for basic auth + value: divorce + required: true +- name: BASICAUTH_PASSWORD + displayName: Password for basic auth + generate: expression + from: "[a-zA-Z0-9]{16}"