38
38
directly instantiate instances of this class inside resources themselves
40
40
def __init__(self, context):
41
self.context = context
42
41
# We have to maintain two clients authenticated with keystone:
43
42
# - ec2 interface is v2.0 only
44
43
# - trusts is v3 only
45
# - passing a v2 auth_token to the v3 client won't work until lp bug
47
# - passing a v3 token to the v2 client works but we have to either
48
# md5sum it or use the nocatalog option to auth/tokens (not yet
49
# supported by keystoneclient), or we hit the v2 8192byte size limit
44
# If a trust_id is specified in the context, we immediately
45
# authenticate so we can populate the context with a trust token
46
# otherwise, we delay client authentication until needed to avoid
47
# unnecessary calls to keystone.
49
# Note that when you obtain a token using a trust, it cannot be
50
# used to reauthenticate and get another token, so we have to
51
# get a new trust-token even if context.auth_token is set.
50
53
# - context.auth_url is expected to contain the v2.0 keystone endpoint
51
if cfg.CONF.deferred_auth_method == 'trusts':
54
self.context = context
55
self._client_v2 = None
56
self._client_v3 = None
58
if self.context.trust_id:
59
# Create a connection to the v2 API, with the trust_id, this
60
# populates self.context.auth_token with a trust-scoped token
61
self._client_v2 = self._v2_client_init()
65
if not self._client_v3:
52
66
# Create connection to v3 API
53
self.client_v3 = self._v3_client_init()
55
# Set context auth_token to md5sum of v3 token
56
auth_token = self.client_v3.auth_ref.get('auth_token')
57
self.context.auth_token = self._md5_token(auth_token)
59
# Create the connection to the v2 API, reusing the md5-ified token
60
self.client_v2 = self._v2_client_init()
62
# Create the connection to the v2 API, using the context creds
63
self.client_v2 = self._v2_client_init()
66
def _md5_token(self, auth_token):
67
# Get the md5sum of the v3 token, which we can pass instead of the
68
# actual token to avoid v2 8192byte size limit on the v2 token API
70
m_enc.update(auth_token)
71
return m_enc.hexdigest()
67
self._client_v3 = self._v3_client_init()
68
return self._client_v3
72
if not self._client_v2:
73
self._client_v2 = self._v2_client_init()
74
return self._client_v2
73
76
def _v2_client_init(self):
75
78
'auth_url': self.context.auth_url
77
# Note check for auth_token first so we use existing token if
78
# available from v3 auth
79
if self.context.auth_token is not None:
81
# Note try trust_id first, as we can't reuse auth_token in that case
82
if self.context.trust_id is not None:
83
# We got a trust_id, so we use the admin credentials
84
# to authenticate, then re-scope the token to the
85
# trust impersonating the trustor user.
86
# Note that this currently requires the trustor tenant_id
87
# to be passed to the authenticate(), unlike the v3 call
88
kwargs.update(self._service_admin_creds(api_version=2))
89
auth_kwargs['trust_id'] = self.context.trust_id
90
auth_kwargs['tenant_id'] = self.context.tenant_id
91
elif self.context.auth_token is not None:
80
92
kwargs['tenant_name'] = self.context.tenant
81
93
kwargs['token'] = self.context.auth_token
82
94
elif self.context.password is not None:
90
102
raise exception.AuthorizationFailure()
91
103
client_v2 = kc.Client(**kwargs)
92
if not client_v2.authenticate():
93
logger.error("Keystone v2 API authentication failed")
94
raise exception.AuthorizationFailure()
105
client_v2.authenticate(**auth_kwargs)
106
# If we are authenticating with a trust auth_kwargs are set, so set
107
# the context auth_token with the re-scoped trust token
110
if not client_v2.auth_ref.trust_scoped:
111
logger.error("v2 trust token re-scoping failed!")
112
raise exception.AuthorizationFailure()
113
# All OK so update the context with the token
114
self.context.auth_token = client_v2.auth_ref.auth_token
115
self.context.auth_url = kwargs.get('auth_url')
140
162
raise exception.AuthorizationFailure()
142
client_v3 = kc_v3.Client(**kwargs)
143
if not client_v3.authenticate():
144
logger.error("Keystone v3 API authentication failed")
145
raise exception.AuthorizationFailure()
164
client = kc_v3.Client(**kwargs)
165
# Have to explicitly authenticate() or client.auth_ref is None
166
client.authenticate()
148
170
def create_trust_context(self):
150
172
If cfg.CONF.deferred_auth_method is trusts, we create a
151
173
trust using the trustor identity in the current context, with the
152
trustee as the heat service user
154
If deferred_auth_method != trusts, we do nothing
156
If the current context already contains a trust_id, we do nothing
174
trustee as the heat service user and return a context containing
177
If deferred_auth_method != trusts, or the current context already
178
contains a trust_id, we do nothing and return the current context
158
if cfg.CONF.deferred_auth_method != 'trusts':
161
180
if self.context.trust_id:
164
183
# We need the service admin user ID (not name), as the trustor user
165
184
# can't lookup the ID in keystoneclient unless they're admin
167
186
# then getting the user ID from the auth_ref
168
187
admin_creds = self._service_admin_creds()
169
188
admin_client = kc.Client(**admin_creds)
170
if not admin_client.authenticate():
171
logger.error("Keystone v2 API admin authentication failed")
172
raise exception.AuthorizationFailure()
174
trustee_user_id = admin_client.auth_ref['user']['id']
175
trustor_user_id = self.client_v3.auth_ref['user']['id']
176
trustor_project_id = self.client_v3.auth_ref['project']['id']
189
trustee_user_id = admin_client.auth_ref.user_id
190
trustor_user_id = self.client_v3.auth_ref.user_id
191
trustor_project_id = self.client_v3.auth_ref.project_id
177
192
roles = cfg.CONF.trusts_delegated_roles
178
193
trust = self.client_v3.trusts.create(trustor_user=trustor_user_id,
179
194
trustee_user=trustee_user_id,
180
195
project=trustor_project_id,
181
196
impersonation=True,
182
197
role_names=roles)
183
self.context.trust_id = trust.id
184
self.context.trustor_user_id = trustor_user_id
186
def delete_trust_context(self):
188
If a trust_id exists in the context, we delete it
191
if not self.context.trust_id:
194
self.client_v3.trusts.delete(self.context.trust_id)
196
self.context.trust_id = None
197
self.context.trustor_user_id = None
199
trust_context = context.RequestContext.from_dict(
200
self.context.to_dict())
201
trust_context.trust_id = trust.id
202
trust_context.trustor_user_id = trustor_user_id
205
def delete_trust(self, trust_id):
207
Delete the specified trust.
209
self.client_v3.trusts.delete(trust_id)
199
211
def create_stack_user(self, username, password=''):