diff --git a/app/__init__.py b/app/__init__.py index 46e1dd8c9..317e395d3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -158,6 +158,7 @@ def init_app(application): application.after_request(useful_headers_after_request) application.after_request(save_service_after_request) application.before_request(load_service_before_request) + application.before_request(request_helper.check_proxy_header_before_request) @application.context_processor def _attach_current_service(): diff --git a/app/cloudfoundry_config.py b/app/cloudfoundry_config.py index d04b3eb5d..6d55acf37 100644 --- a/app/cloudfoundry_config.py +++ b/app/cloudfoundry_config.py @@ -37,6 +37,8 @@ def extract_notify_config(notify_config): os.environ['ADMIN_BASE_URL'] = notify_config['credentials']['admin_base_url'] os.environ['SECRET_KEY'] = notify_config['credentials']['secret_key'] os.environ['DANGEROUS_SALT'] = notify_config['credentials']['dangerous_salt'] + os.environ['ROUTE_SECRET_KEY_1'] = notify_config['credentials']['route_secret_key_1'] + os.environ['ROUTE_SECRET_KEY_2'] = notify_config['credentials']['route_secret_key_2'] def extract_notify_aws_config(aws_config): diff --git a/app/config.py b/app/config.py index 71a1e21ad..eb2cdd5e2 100644 --- a/app/config.py +++ b/app/config.py @@ -98,6 +98,9 @@ class Config(object): ] LOGO_UPLOAD_BUCKET_NAME = 'public-logos-local' + ROUTE_SECRET_KEY_1 = os.environ.get('ROUTE_SECRET_KEY_1', '') + ROUTE_SECRET_KEY_2 = os.environ.get('ROUTE_SECRET_KEY_2', '') + CHECK_PROXY_HEADER = False class Development(Config): @@ -128,6 +131,7 @@ class Preview(Config): CSV_UPLOAD_BUCKET_NAME = 'preview-notifications-csv-upload' LOGO_UPLOAD_BUCKET_NAME = 'public-logos-preview' NOTIFY_ENVIRONMENT = 'preview' + CHECK_PROXY_HEADER = True class Staging(Config): @@ -138,6 +142,7 @@ class Staging(Config): CSV_UPLOAD_BUCKET_NAME = 'staging-notify-csv-upload' LOGO_UPLOAD_BUCKET_NAME = 'public-logos-staging' NOTIFY_ENVIRONMENT = 'staging' + CHECK_PROXY_HEADER = True class Live(Config): @@ -148,6 +153,7 @@ class Live(Config): CSV_UPLOAD_BUCKET_NAME = 'live-notifications-csv-upload' LOGO_UPLOAD_BUCKET_NAME = 'public-logos-production' NOTIFY_ENVIRONMENT = 'live' + CHECK_PROXY_HEADER = False class CloudFoundryConfig(Config): diff --git a/app/status/views/healthcheck.py b/app/status/views/healthcheck.py index 4d5515d57..2eedad519 100644 --- a/app/status/views/healthcheck.py +++ b/app/status/views/healthcheck.py @@ -1,4 +1,4 @@ -from flask import jsonify, request +from flask import jsonify, request, current_app from app import (version, status_api_client) from app.status import status from notifications_python_client.errors import HTTPError @@ -12,6 +12,7 @@ def show_status(): try: api_status = status_api_client.get_status() except HTTPError as e: + current_app.logger.exception("API failed to respond") return jsonify(status="error", message=str(e.message)), 500 return jsonify( status="ok", diff --git a/requirements.txt b/requirements.txt index 2f60866a5..a0ebd5053 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,4 +21,4 @@ notifications-python-client==4.6.0 awscli>=1.11,<1.12 awscli-cwlogs>=1.4,<1.5 -git+https://github.com/alphagov/notifications-utils.git@23.0.0#egg=notifications-utils==23.0.0 +git+https://github.com/alphagov/notifications-utils.git@23.0.1#egg=notifications-utils==23.0.1 diff --git a/tests/app/main/test_choose_time_form.py b/tests/app/main/test_choose_time_form.py index 77e9449f0..fbe1a5330 100644 --- a/tests/app/main/test_choose_time_form.py +++ b/tests/app/main/test_choose_time_form.py @@ -5,7 +5,7 @@ from freezegun import freeze_time @freeze_time("2016-01-01 11:09:00.061258") -def test_form_contains_next_24h(): +def test_form_contains_next_24h(app_): choices = ChooseTimeForm().scheduled_for.choices @@ -34,12 +34,12 @@ def test_form_contains_next_24h(): @freeze_time("2016-01-01 11:09:00.061258") -def test_form_defaults_to_now(client): +def test_form_defaults_to_now(app_): assert ChooseTimeForm().scheduled_for.data == '' @freeze_time("2016-01-01 11:09:00.061258") -def test_form_contains_next_three_days(): +def test_form_contains_next_three_days(app_): assert ChooseTimeForm().scheduled_for.categories == [ 'Later today', 'Tomorrow', 'Sunday', 'Monday' ] diff --git a/tests/app/main/test_request_header.py b/tests/app/main/test_request_header.py new file mode 100644 index 000000000..643d33c56 --- /dev/null +++ b/tests/app/main/test_request_header.py @@ -0,0 +1,26 @@ +import pytest + +from tests.conftest import set_config_values + + +@pytest.mark.parametrize('check_proxy_header,header_value,expected_code', [ + (True, 'key_1', 200), + (True, 'wrong_key', 403), + (False, 'wrong_key', 200), + (False, 'key_1', 200), +]) +def test_route_correct_secret_key(app_, check_proxy_header, header_value, expected_code): + with set_config_values(app_, { + 'ROUTE_SECRET_KEY_1': 'key_1', + 'ROUTE_SECRET_KEY_2': '', + 'CHECK_PROXY_HEADER': check_proxy_header, + }): + + with app_.test_client() as client: + response = client.get( + path='/_status?elb=True', + headers=[ + ('X-Custom-forwarder', header_value), + ] + ) + assert response.status_code == expected_code diff --git a/tests/app/test_cloudfoundry_config.py b/tests/app/test_cloudfoundry_config.py index 81852d7bf..22b8f93c0 100644 --- a/tests/app/test_cloudfoundry_config.py +++ b/tests/app/test_cloudfoundry_config.py @@ -16,6 +16,8 @@ def notify_config(): 'admin_client_secret': 'admin client secret', 'secret_key': 'secret key', 'dangerous_salt': 'dangerous salt', + 'route_secret_key_1': 'key 1', + 'route_secret_key_2': 'key 2', } } @@ -118,6 +120,8 @@ def test_notify_config(): assert os.environ['ADMIN_CLIENT_SECRET'] == 'admin client secret' assert os.environ['SECRET_KEY'] == 'secret key' assert os.environ['DANGEROUS_SALT'] == 'dangerous salt' + assert os.environ['ROUTE_SECRET_KEY_1'] == 'key 1' + assert os.environ['ROUTE_SECRET_KEY_2'] == 'key 2' @pytest.mark.usefixtures('os_environ', 'cloudfoundry_environ') diff --git a/tests/conftest.py b/tests/conftest.py index f0d871560..a7364b646 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -32,7 +32,7 @@ from notifications_utils.url_safe_token import generate_token import json -@pytest.fixture(scope='session') +@pytest.fixture def app_(request): app = Flask('app') create_app(app) @@ -2482,6 +2482,20 @@ def set_config(app, name, value): app.config[name] = old_val +@contextmanager +def set_config_values(app, dict): + old_values = {} + + for key in dict: + old_values[key] = app.config.get(key) + app.config[key] = dict[key] + + yield + + for key in dict: + app.config[key] = old_values[key] + + @pytest.fixture(scope='function') def valid_token(app_, fake_uuid): return generate_token(