Glance api to pass identity headers to registry v1

This patch introduces the send_identity_headers
config option that allows glance-api to pass auth identity
headers when making calls to the registry v1.

docImpact
Fixes bug 1199990

Change-Id: Ie5f07ed6dfeaa8428de4f79c4d40d182328e6ab4
This commit is contained in:
Alex Meade 2013-07-10 18:45:40 +00:00
parent c13692f27d
commit 3fa3891595
5 changed files with 101 additions and 1 deletions

View File

@ -90,6 +90,13 @@ workers = 1
# The default value is false.
#show_image_direct_url = False
# Send headers containing user and tenant information when making requests to
# the v1 glance registry. This allows the registry to function as if a user is
# authenticated without the need to authenticate a user itself using the
# auth_token middleware.
# The default value is false.
#send_identity_headers = False
# ================= Syslog Options ============================
# Send logs to syslog (/dev/log) instead of to file specified

View File

@ -6,6 +6,12 @@ pipeline = unauthenticated-context registryapp
[pipeline:glance-registry-keystone]
pipeline = authtoken context registryapp
# Use this pipeline for authZ only. This means that the registry will treat a
# user as authenticated without making requests to keystone to reauthenticate
# the user.
[pipeline:glance-registry-trusted-auth]
pipeline = context registryapp
[app:registryapp]
paste.app_factory = glance.registry.api.v1:API.factory

View File

@ -19,6 +19,7 @@
Registry's Client API
"""
import json
import os
from oslo.config import cfg
@ -55,6 +56,16 @@ registry_client_ctx_opts = [
cfg.BoolOpt('use_user_token', default=True,
help=_('Whether to pass through the user token when '
'making requests to the registry.')),
cfg.BoolOpt('send_identity_headers', default=False,
help=_('Whether to pass through headers containing user '
'and tenant information when making requests to '
'the registry. This allows the registry to use the '
'context middleware without the keystoneclients\' '
'auth_token middleware, removing calls to the keystone '
'auth service. It is recommended that when using this '
'option, secure communication between glance api and '
'glance registry is ensured by means other than '
'auth_token middleware.')),
cfg.StrOpt('admin_user', secret=True,
help=_('The administrators user name.')),
cfg.StrOpt('admin_password', secret=True,
@ -141,6 +152,16 @@ def get_registry_client(cxt):
kwargs['auth_tok'] = cxt.auth_tok
if _CLIENT_CREDS:
kwargs['creds'] = _CLIENT_CREDS
if CONF.send_identity_headers:
identity_headers = {
'X-User-Id': cxt.user,
'X-Tenant-Id': cxt.tenant,
'X-Roles': ','.join(cxt.roles),
'X-Identity-Status': 'Confirmed',
'X-Service-Catalog': json.dumps(cxt.service_catalog),
}
kwargs['identity_headers'] = identity_headers
return client.RegistryClient(_CLIENT_HOST, _CLIENT_PORT,
_METADATA_ENCRYPTION_KEY, **kwargs)

View File

@ -37,7 +37,7 @@ class RegistryClient(BaseClient):
DEFAULT_PORT = 9191
def __init__(self, host=None, port=None, metadata_encryption_key=None,
**kwargs):
identity_headers=None, **kwargs):
"""
:param metadata_encryption_key: Key used to encrypt 'location' metadata
"""
@ -45,6 +45,7 @@ class RegistryClient(BaseClient):
# NOTE (dprince): by default base client overwrites host and port
# settings when using keystone. configure_via_auth=False disables
# this behaviour to ensure we still send requests to the Registry API
self.identity_headers = identity_headers
BaseClient.__init__(self, host, port, configure_via_auth=False,
**kwargs)
@ -85,6 +86,8 @@ class RegistryClient(BaseClient):
def do_request(self, method, action, **kwargs):
try:
kwargs['headers'] = kwargs.get('headers', {})
kwargs['headers'].update(self.identity_headers or {})
res = super(RegistryClient, self).do_request(method,
action,
**kwargs)

View File

@ -1215,6 +1215,7 @@ class TestRegistryV1ClientApi(base.IsolatedUnitTest):
"""Establish a clean test environment"""
super(TestRegistryV1ClientApi, self).setUp()
self.mox = mox.Mox()
self.context = context.RequestContext()
reload(rapi)
def tearDown(self):
@ -1222,6 +1223,23 @@ class TestRegistryV1ClientApi(base.IsolatedUnitTest):
super(TestRegistryV1ClientApi, self).tearDown()
self.mox.UnsetStubs()
def test_get_registry_client(self):
actual_client = rapi.get_registry_client(self.context)
self.assertEqual(actual_client.identity_headers, None)
def test_get_registry_client_with_identity_headers(self):
self.config(send_identity_headers=True)
expected_identity_headers = {
'X-User-Id': self.context.user,
'X-Tenant-Id': self.context.tenant,
'X-Roles': ','.join(self.context.roles),
'X-Identity-Status': 'Confirmed',
'X-Service-Catalog': 'null',
}
actual_client = rapi.get_registry_client(self.context)
self.assertEqual(actual_client.identity_headers,
expected_identity_headers)
def test_configure_registry_client_not_using_use_user_token(self):
self.config(use_user_token=False)
self.mox.StubOutWithMock(rapi, 'configure_registry_admin_creds')
@ -1273,3 +1291,48 @@ class TestRegistryV1ClientApi(base.IsolatedUnitTest):
self.assertEquals(rapi._CLIENT_CREDS, None)
rapi.configure_registry_admin_creds()
self.assertEquals(rapi._CLIENT_CREDS, expected)
class FakeResponse():
status = 202
def getheader(*args, **kwargs):
return None
class TestRegistryV1ClientRequests(base.IsolatedUnitTest):
def setUp(self):
super(TestRegistryV1ClientRequests, self).setUp()
self.mox = mox.Mox()
def tearDown(self):
super(TestRegistryV1ClientRequests, self).tearDown()
self.mox.UnsetStubs()
def test_do_request_with_identity_headers(self):
identity_headers = {'foo': 'bar'}
self.client = rclient.RegistryClient("0.0.0.0",
identity_headers=identity_headers)
self.mox.StubOutWithMock(test_client.BaseClient, 'do_request')
test_client.BaseClient.do_request("GET", "/images",
headers=identity_headers).AndReturn(
FakeResponse())
self.mox.ReplayAll()
self.client.do_request("GET", "/images")
self.mox.VerifyAll()
def test_do_request(self):
self.client = rclient.RegistryClient("0.0.0.0")
self.mox.StubOutWithMock(test_client.BaseClient, 'do_request')
test_client.BaseClient.do_request("GET", "/images",
headers={}).AndReturn(FakeResponse())
self.mox.ReplayAll()
self.client.do_request("GET", "/images")
self.mox.VerifyAll()