diff options
author | Jenkins <jenkins@review.openstack.org> | 2015-02-05 02:20:37 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2015-02-05 02:20:37 +0000 |
commit | 500fb35e69ed3b66879e1a316f8a628a07a62b10 (patch) | |
tree | 88adb3995d653f3e2037045aead132e4d23e0d31 | |
parent | d4909becac293e4fcb32ccb6e31333aab7b6a6cd (diff) | |
parent | 340a692de2661e8bf5a710c325dcf0866c2cbfed (diff) | |
download | keystone-500fb35e69ed3b66879e1a316f8a628a07a62b10.tar.gz |
Merge "Handle SSL termination proxies for version list"2015.1.0b2
-rw-r--r-- | keystone/common/config.py | 8 | ||||
-rw-r--r-- | keystone/common/wsgi.py | 10 | ||||
-rw-r--r-- | keystone/tests/test_versions.py | 49 |
3 files changed, 66 insertions, 1 deletions
diff --git a/keystone/common/config.py b/keystone/common/config.py index 121f32f43..60e0ef12e 100644 --- a/keystone/common/config.py +++ b/keystone/common/config.py @@ -139,7 +139,13 @@ FILE_OPTIONS = { 'exceeds the maximum length, the operation will fail ' 'with an HTTP 403 Forbidden error. If set to false, ' 'passwords are automatically truncated to the ' - 'maximum length.')], + 'maximum length.'), + cfg.StrOpt('secure_proxy_ssl_header', + help='The HTTP header used to determine the scheme for the ' + 'original request, even if it was removed by an SSL ' + 'terminating proxy. Typical value is ' + '"HTTP_X_FORWARDED_PROTO".'), + ], 'identity': [ cfg.StrOpt('default_domain_id', default='default', help='This references the domain to use for all ' diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py index a9db94325..4311abdae 100644 --- a/keystone/common/wsgi.py +++ b/keystone/common/wsgi.py @@ -196,6 +196,16 @@ class Application(BaseApplication): context['query_string'] = dict(six.iteritems(req.params)) context['headers'] = dict(six.iteritems(req.headers)) context['path'] = req.environ['PATH_INFO'] + scheme = (None if not CONF.secure_proxy_ssl_header + else req.environ.get(CONF.secure_proxy_ssl_header)) + if scheme: + # NOTE(andrey-mp): "wsgi.url_scheme" contains the protocol used + # before the proxy removed it ('https' usually). So if + # the webob.Request instance is modified in order to use this + # scheme instead of the one defined by API, the call to + # webob.Request.relative_url() will return a URL with the correct + # scheme. + req.environ['wsgi.url_scheme'] = scheme context['host_url'] = req.host_url params = req.environ.get(PARAMS_ENV, {}) # authentication and authorization attributes are set as environment diff --git a/keystone/tests/test_versions.py b/keystone/tests/test_versions.py index d26819ab1..76b007c43 100644 --- a/keystone/tests/test_versions.py +++ b/keystone/tests/test_versions.py @@ -741,3 +741,52 @@ class VersionInheritEnabledTestCase(tests.TestCase): self.assertThat(jsonutils.loads(resp.body), tt_matchers.Equals(exp_json_home_data)) + + +class VersionBehindSslTestCase(tests.TestCase): + def setUp(self): + super(VersionBehindSslTestCase, self).setUp() + self.load_backends() + self.public_app = self.loadapp('keystone', 'main') + + def config_overrides(self): + super(VersionBehindSslTestCase, self).config_overrides() + self.config_fixture.config( + secure_proxy_ssl_header='HTTP_X_FORWARDED_PROTO') + + def _paste_in_port(self, response, port): + for link in response['links']: + if link['rel'] == 'self': + link['href'] = port + + def _get_expected(self, host): + expected = VERSIONS_RESPONSE + for version in expected['versions']['values']: + if version['id'] == 'v3.0': + self._paste_in_port(version, host + 'v3/') + elif version['id'] == 'v2.0': + self._paste_in_port(version, host + 'v2.0/') + return expected + + def test_versions_without_headers(self): + client = self.client(self.public_app) + host_name = 'host-%d' % random.randint(10, 30) + host_port = random.randint(10000, 30000) + host = 'http://%s:%s/' % (host_name, host_port) + resp = client.get(host) + self.assertEqual(300, resp.status_int) + data = jsonutils.loads(resp.body) + expected = self._get_expected(host) + self.assertEqual(expected, data) + + def test_versions_with_header(self): + client = self.client(self.public_app) + host_name = 'host-%d' % random.randint(10, 30) + host_port = random.randint(10000, 30000) + resp = client.get('http://%s:%s/' % (host_name, host_port), + headers={'X-Forwarded-Proto': 'https'}) + self.assertEqual(300, resp.status_int) + data = jsonutils.loads(resp.body) + expected = self._get_expected('https://%s:%s/' % (host_name, + host_port)) + self.assertEqual(expected, data) |