summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2015-02-05 02:20:37 +0000
committerGerrit Code Review <review@openstack.org>2015-02-05 02:20:37 +0000
commit500fb35e69ed3b66879e1a316f8a628a07a62b10 (patch)
tree88adb3995d653f3e2037045aead132e4d23e0d31
parentd4909becac293e4fcb32ccb6e31333aab7b6a6cd (diff)
parent340a692de2661e8bf5a710c325dcf0866c2cbfed (diff)
downloadkeystone-500fb35e69ed3b66879e1a316f8a628a07a62b10.tar.gz
Merge "Handle SSL termination proxies for version list"2015.1.0b2
-rw-r--r--keystone/common/config.py8
-rw-r--r--keystone/common/wsgi.py10
-rw-r--r--keystone/tests/test_versions.py49
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)