Make standalone heat work with keystone v3

- Fix authpassword middleware
- Pass user_domain/project_domain all through. user_domain
is required whenever authenticating a user with 'username'
and creating the 'password' auth_plugin.

Change-Id: I692639a16be42b755628695d89731aa23eb9221b
Closes-Bug: #1699418
This commit is contained in:
rabi 2017-06-20 14:21:32 +05:30 committed by Rabi Mishra
parent a5ac2c3b1d
commit 923e2e2316
7 changed files with 67 additions and 84 deletions

View File

@ -72,37 +72,31 @@ class KeystonePasswordAuthProtocol(object):
def _build_user_headers(self, token_info):
"""Build headers that represent authenticated user from auth token."""
if token_info.get('version') == 'v3':
keystone_token_info = {'token': token_info}
tenant_id = token_info['project']['id']
tenant_name = token_info['project']['name']
user_id = token_info['user']['id']
user_name = token_info['user']['name']
roles = ','.join(
[role['name'] for role in token_info['roles']])
service_catalog = None
auth_token = token_info['auth_token']
if token_info.version == 'v3':
project_id = token_info.project_id
project_name = token_info.project_name
else:
keystone_token_info = token_info
tenant_id = token_info['token']['tenant']['id']
tenant_name = token_info['token']['tenant']['name']
user_id = token_info['user']['id']
user_name = token_info['user']['name']
roles = ','.join(
[role['name'] for role in token_info['user']['roles']])
service_catalog = token_info['serviceCatalog']
auth_token = token_info['token']['id']
project_id = token_info.tenant_id
project_name = token_info.tenant_name
user_id = token_info.user_id
user_name = token_info.username
roles = ','.join(
[role for role in token_info.role_names])
service_catalog = token_info.service_catalog
auth_token = token_info.auth_token
user_domain_id = token_info.user_domain_id
headers = {
'keystone.token_info': keystone_token_info,
'HTTP_X_IDENTITY_STATUS': 'Confirmed',
'HTTP_X_PROJECT_ID': tenant_id,
'HTTP_X_PROJECT_NAME': tenant_name,
'HTTP_X_PROJECT_ID': project_id,
'HTTP_X_PROJECT_NAME': project_name,
'HTTP_X_USER_ID': user_id,
'HTTP_X_USER_NAME': user_name,
'HTTP_X_ROLES': roles,
'HTTP_X_SERVICE_CATALOG': service_catalog,
'HTTP_X_AUTH_TOKEN': auth_token,
'HTTP_X_USER_DOMAIN_ID': user_domain_id,
}
return headers

View File

@ -178,8 +178,8 @@ class RequestContext(context.RequestContext):
'show_deleted': self.show_deleted,
'region_name': self.region_name,
'user_identity': user_idt,
'user_domain_id': self.user_domain,
'project_domain_id': self.project_domain}
'user_domain': self.user_domain,
'project_domain': self.project_domain}
@classmethod
def from_dict(cls, values):
@ -253,6 +253,13 @@ class RequestContext(context.RequestContext):
return access_plugin.AccessInfoPlugin(
auth_ref=access_info, auth_url=self.keystone_v3_endpoint)
if self.password:
return generic.Password(username=self.username,
password=self.password,
project_id=self.tenant_id,
user_domain_id=self.user_domain,
auth_url=self.keystone_v3_endpoint)
if self.auth_token:
# FIXME(jamielennox): This is broken but consistent. If you
# only have a token but don't load a service catalog then
@ -261,13 +268,6 @@ class RequestContext(context.RequestContext):
return token_endpoint.Token(endpoint=self.keystone_v3_endpoint,
token=self.auth_token)
if self.password:
return generic.Password(username=self.username,
password=self.password,
project_id=self.tenant_id,
user_domain_id=self.user_domain,
auth_url=self.keystone_v3_endpoint)
LOG.error("Keystone API connection failed, no password "
"trust or auth_token!")
raise exception.AuthorizationFailure()
@ -352,6 +352,8 @@ class ContextMiddleware(wsgi.Middleware):
username = None
password = None
aws_creds = None
user_domain = None
project_domain = None
if headers.get('X-Auth-User') is not None:
username = headers.get('X-Auth-User')
@ -359,6 +361,12 @@ class ContextMiddleware(wsgi.Middleware):
elif headers.get('X-Auth-EC2-Creds') is not None:
aws_creds = headers.get('X-Auth-EC2-Creds')
if headers.get('X-User-Domain-Id') is not None:
user_domain = headers.get('X-User-Domain-Id')
if headers.get('X-Project-Domain-Id') is not None:
project_domain = headers.get('X-Project-Domain-Id')
project_name = headers.get('X-Project-Name')
region_name = headers.get('X-Region-Name')
auth_url = headers.get('X-Auth-Url')
@ -375,6 +383,8 @@ class ContextMiddleware(wsgi.Middleware):
password=password,
auth_url=auth_url,
request_id=req_id,
user_domain=user_domain,
project_domain=project_domain,
auth_token_info=token_info,
region_name=region_name,
auth_plugin=auth_plugin,

View File

@ -471,6 +471,7 @@ class KeystoneClientTest(common.HeatTestCase):
ctx = utils.dummy_context()
ctx.auth_token = None
ctx.password = 'password'
ctx.trust_id = None
ctx.user_domain = 'adomain123'
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)

View File

@ -933,6 +933,7 @@ class SqlAlchemyTest(common.HeatTestCase):
self.m.VerifyAll()
def test_user_creds_password(self):
self.ctx.password = 'password'
self.ctx.trust_id = None
self.ctx.region_name = 'RegionOne'
db_creds = db_api.user_creds_create(self.ctx)

View File

@ -40,54 +40,35 @@ EXPECTED_ENV_RESPONSE = {
'HTTP_X_AUTH_TOKEN': 'lalalalalala',
}
TOKEN_V2_RESPONSE = {
'token': {
'id': 'lalalalalala',
'expires': '2020-01-01T00:00:10.000123Z',
'tenant': {
'id': 'tenant_id1',
'name': 'tenant_name1',
},
},
'user': {
'id': 'user_id1',
'name': 'user_name1',
'roles': [
{'name': 'role1'},
{'name': 'role2'},
],
},
'serviceCatalog': {}
}
TOKEN_V3_RESPONSE = {
'version': 'v3',
'project': {
'id': 'tenant_id1',
'name': 'tenant_name1',
},
'token': {
'id': 'lalalalalala',
'expires': '2020-01-01T00:00:10.000123Z',
'tenant': {
'id': 'tenant_id1',
'name': 'tenant_name1',
},
'methods': ['password'],
},
'user': {
'id': 'user_id1',
'name': 'user_name1',
},
'roles': [
{'name': 'role1'},
{'name': 'role2'},
],
'auth_token': 'lalalalalala'
'project_id': 'tenant_id1',
'project_name': 'tenant_name1',
'user_id': 'user_id1',
'username': 'user_name1',
'service_catalog': None,
'role_names': ['role1', 'role2'],
'auth_token': 'lalalalalala',
'user_domain_id': 'domain1'
}
TOKEN_V2_RESPONSE = {
'version': 'v2',
'tenant_id': 'tenant_id1',
'tenant_name': 'tenant_name1',
'user_id': 'user_id1',
'service_catalog': None,
'username': 'user_name1',
'role_names': ['role1', 'role2'],
'auth_token': 'lalalalalala',
'user_domain_id': 'domain1'
}
class FakeAccessInfo(object):
def __init__(self, **args):
self.__dict__.update(args)
class FakeApp(object):
"""This represents a WSGI app protected by our auth middleware."""
@ -131,9 +112,8 @@ class KeystonePasswordAuthProtocolTest(common.HeatTestCase):
username='user_name1').AndReturn(mock_auth)
m = mock_auth.get_access(mox.IsA(ks_session.Session))
m.AndReturn(TOKEN_V2_RESPONSE)
m.AndReturn(FakeAccessInfo(**TOKEN_V2_RESPONSE))
self.app.expected_env['keystone.token_info'] = TOKEN_V2_RESPONSE
self.m.ReplayAll()
req = webob.Request.blank('/tenant_id1/')
req.headers['X_AUTH_USER'] = 'user_name1'
@ -154,11 +134,8 @@ class KeystonePasswordAuthProtocolTest(common.HeatTestCase):
username='user_name1').AndReturn(mock_auth)
m = mock_auth.get_access(mox.IsA(ks_session.Session))
m.AndReturn(TOKEN_V3_RESPONSE)
m.AndReturn(FakeAccessInfo(**TOKEN_V3_RESPONSE))
self.app.expected_env['keystone.token_info'] = {
'token': TOKEN_V3_RESPONSE
}
self.m.ReplayAll()
req = webob.Request.blank('/tenant_id1/')
req.headers['X_AUTH_USER'] = 'user_name1'

View File

@ -50,8 +50,8 @@ class TestRequestContext(common.HeatTestCase):
'aws_creds': 'blah',
'region_name': 'RegionOne',
'user_identity': 'fooUser 456tenant',
'user_domain_id': None,
'project_domain_id': None}
'user_domain': None,
'project_domain': None}
super(TestRequestContext, self).setUp()

View File

@ -72,7 +72,7 @@ def reset_dummy_db():
def dummy_context(user='test_username', tenant_id='test_tenant_id',
password='password', roles=None, user_id=None,
password='', roles=None, user_id=None,
trust_id=None, region_name=None, is_admin=False):
roles = roles or []
return context.RequestContext.from_dict({