diff --git a/doc/client-proxy.md b/doc/client-proxy.md index 59ba00f28..9a3e8b8b2 100644 --- a/doc/client-proxy.md +++ b/doc/client-proxy.md @@ -23,7 +23,7 @@ By default, Layman will not adjust URLs in its response to contain also URL path ## X-Forwarded-Prefix HTTP header -The value of the `X-Forwarded-Prefix` HTTP header will be used as prefix in some URL paths of Layman response. +The value of the `X-Forwarded-Prefix` HTTP header will be used as prefix in some URL paths of Layman response and is required to match regular expression `^(?:/[a-z0-9_-]+)*$`. For example, if you send request to `/layman-client-proxy/rest/publications` with HTTP header `X-Forwarded-Prefix=/layman-client-proxy` then response will change to diff --git a/src/layman/error_list.py b/src/layman/error_list.py index 3b045f2d8..b0eccf9bb 100644 --- a/src/layman/error_list.py +++ b/src/layman/error_list.py @@ -54,4 +54,5 @@ 51: (500, 'Error when generating thumbnail'), 52: (400, 'GeoServer HTTP or connection error'), 53: (500, 'Error when publishing on GeoServer. It happens for example for raster files with wrong explicit CRS.'), + 54: (400, 'Wrong header value'), } diff --git a/src/layman/util.py b/src/layman/util.py index 12cb8c60f..5412e6e32 100644 --- a/src/layman/util.py +++ b/src/layman/util.py @@ -548,4 +548,15 @@ def ensure_home_dir(): def get_x_forwarded_prefix(request_headers): - return request_headers.get('X-Forwarded-Prefix') + header_key = 'X-Forwarded-Prefix' + header_value = request_headers.get(header_key) + if header_value and not re.match(CLIENT_PROXY_PATTERN, header_value): + raise LaymanError(54, + {'header': header_key, + 'message': f'Optional header {header_key} is expected to be valid URL subpath starting with slash, or empty string.', + 'expected': f'Expected header matching regular expression {CLIENT_PROXY_PATTERN}', + 'found': header_value, + + } + ) + return header_value diff --git a/src/layman/util_test.py b/src/layman/util_test.py index 3ec02cce6..4691e17f9 100644 --- a/src/layman/util_test.py +++ b/src/layman/util_test.py @@ -1,6 +1,7 @@ import importlib import pytest +from test_tools import util as test_util from . import app, settings, LaymanError, util @@ -143,3 +144,23 @@ def test__url_for(endpoint, internal, params, expected_url): def test_get_x_forwarded_prefix(headers, exp_result): result = util.get_x_forwarded_prefix(headers) assert result == exp_result + + +@pytest.mark.parametrize('headers, exp_error', [ + pytest.param( + {'X-Forwarded-Prefix': 'layman-proxy'}, + { + 'http_code': 400, + 'code': 54, + 'data': { + 'header': 'X-Forwarded-Prefix', + 'message': 'Optional header X-Forwarded-Prefix is expected to be valid URL subpath starting with slash, or empty string.', + 'expected': 'Expected header matching regular expression ^(?:/[a-z0-9_-]+)*$', + 'found': 'layman-proxy', + }, + }, id='without_slash'), +]) +def test_get_x_forwarded_prefix_raises(headers, exp_error): + with pytest.raises(LaymanError) as exc_info: + util.get_x_forwarded_prefix(headers) + test_util.assert_error(exp_error, exc_info)