32
33
_logger = logging.getLogger(__name__)
35
class HTTPClient(httplib2.Http):
37
keyring_available = True
42
if (hasattr(sys.stderr, 'isatty') and sys.stderr.isatty()):
43
print >> sys.stderr, 'Failed to load keyring modules.'
45
_logger.warning('Failed to load keyring modules.')
46
keyring_available = False
49
class HTTPClient(object):
37
51
USER_AGENT = 'python-keystoneclient'
39
57
def __init__(self, username=None, tenant_id=None, tenant_name=None,
40
58
password=None, auth_url=None, region_name=None, timeout=None,
41
59
endpoint=None, token=None, cacert=None, key=None,
42
60
cert=None, insecure=False, original_ip=None, debug=False,
44
super(HTTPClient, self).__init__(timeout=timeout, ca_certs=cacert)
47
self.add_certificate(key=key, cert=cert, domain='')
49
self.add_certificate(key=cert, cert=cert, domain='')
61
auth_ref=None, use_keyring=False, force_new_token=False,
50
63
self.version = 'v2.0'
51
64
# set baseline defaults
52
65
self.username = None
94
113
ch = logging.StreamHandler()
95
114
_logger.setLevel(logging.DEBUG)
96
115
_logger.addHandler(ch)
98
def authenticate(self):
99
""" Authenticate against the Identity API.
116
self.requests_config['verbose'] = sys.stderr
119
self.use_keyring = use_keyring and keyring_available
120
self.force_new_token = force_new_token
121
self.stale_duration = stale_duration or access.STALE_TOKEN_DURATION
122
self.stale_duration = int(self.stale_duration)
124
def authenticate(self, username=None, password=None, tenant_name=None,
125
tenant_id=None, auth_url=None, token=None):
126
""" Authenticate user.
128
Uses the data provided at instantiation to authenticate against
129
the Keystone server. This may use either a username and password
130
or token for authentication. If a tenant name or id was provided
131
then the resulting authenticated client will be scoped to that
132
tenant and contain a service catalog of available endpoints.
134
With the v2.0 API, if a tenant name or ID is not provided, the
135
authenication token returned will be 'unscoped' and limited in
136
capabilities until a fully-scoped token is acquired.
138
If successful, sets the self.auth_ref and self.auth_token with
139
the returned token. If not already set, will also set
140
self.management_url from the details provided in the token.
142
:returns: ``True`` if authentication was successful.
143
:raises: AuthorizationFailure if unable to authenticate or validate
144
the existing authorization token
145
:raises: ValueError if insufficient parameters are used.
147
If keyring is used, token is retrieved from keyring instead.
148
Authentication will only be necessary if any of the following
151
* keyring is not used
152
* if token is not found in keyring
153
* if token retrieved from keyring is expired or about to
154
expired (as determined by stale_duration)
155
* if force_new_token is true
158
auth_url = auth_url or self.auth_url
159
username = username or self.username
160
password = password or self.password
161
tenant_name = tenant_name or self.tenant_name
162
tenant_id = tenant_id or self.tenant_id
163
token = token or self.auth_token
165
(keyring_key, auth_ref) = self.get_auth_ref_from_keyring(auth_url,
170
new_token_needed = False
171
if auth_ref is None or self.force_new_token:
172
new_token_needed = True
173
raw_token = self.get_raw_token_from_identity_service(auth_url,
179
self.auth_ref = access.AccessInfo(**raw_token)
181
self.auth_ref = auth_ref
184
self.store_auth_ref_into_keyring(keyring_key)
187
def _build_keyring_key(self, auth_url, username, tenant_name,
189
""" Create a unique key for keyring.
191
Used to store and retrieve auth_ref from keyring.
194
keys = [auth_url, username, tenant_name, tenant_id, token]
195
for index, key in enumerate(keys):
198
keyring_key = '/'.join(keys)
201
def get_auth_ref_from_keyring(self, auth_url, username, tenant_name,
203
""" Retrieve auth_ref from keyring.
205
If auth_ref is found in keyring, (keyring_key, auth_ref) is returned.
206
Otherwise, (keyring_key, None) is returned.
208
:returns: (keyring_key, auth_ref) or (keyring_key, None)
214
keyring_key = self._build_keyring_key(auth_url, username,
215
tenant_name, tenant_id,
218
auth_ref = keyring.get_password("keystoneclient_auth",
221
auth_ref = pickle.loads(auth_ref)
222
if auth_ref.will_expire_soon(self.stale_duration):
223
# token has expired, don't use it
225
except Exception as e:
227
_logger.warning('Unable to retrieve token from keyring %s' % (
229
return (keyring_key, auth_ref)
231
def store_auth_ref_into_keyring(self, keyring_key):
232
""" Store auth_ref into keyring.
237
keyring.set_password("keystoneclient_auth",
239
pickle.dumps(self.auth_ref))
240
except Exception as e:
241
_logger.warning("Failed to store token into keyring %s" % (e))
243
def process_token(self):
244
""" Extract and process information from the new auth_ref.
247
raise NotImplementedError
249
def get_raw_token_from_identity_service(self, auth_url, username=None,
250
password=None, tenant_name=None,
251
tenant_id=None, token=None):
252
""" Authenticate against the Identity API and get a token.
101
254
Not implemented here because auth protocols should be API
102
255
version-specific.
156
316
self.original_ip, self.USER_AGENT)
157
317
if 'body' in kwargs:
158
318
request_kwargs['headers']['Content-Type'] = 'application/json'
159
request_kwargs['body'] = self.serialize(kwargs['body'])
319
request_kwargs['data'] = self.serialize(kwargs['body'])
320
del request_kwargs['body']
322
request_kwargs['cert'] = self.cert
161
324
self.http_log_req((url, method,), request_kwargs)
162
resp, body = super(HTTPClient, self).request(url,
165
self.http_log_resp(resp, body)
167
if resp.status in (400, 401, 403, 404, 408, 409, 413, 500, 501):
168
_logger.debug("Request returned failure status: %s", resp.status)
169
raise exceptions.from_response(resp, body)
170
elif resp.status in (301, 302, 305):
325
resp = requests.request(
328
verify=self.verify_cert,
329
config=self.requests_config,
332
self.http_log_resp(resp)
334
if resp.status_code in (400, 401, 403, 404, 408, 409, 413, 500, 501):
336
"Request returned failure status: %s",
338
raise exceptions.from_response(resp, resp.text)
339
elif resp.status_code in (301, 302, 305):
171
340
# Redirected. Reissue the request to the new location.
172
return self.request(resp['location'], method, **kwargs)
341
return self.request(resp.headers['location'], method, **kwargs)
176
body = json.loads(body)
345
body = json.loads(resp.text)
177
346
except ValueError:
178
_logger.debug("Could not decode JSON from body: %s" % body)
348
_logger.debug("Could not decode JSON from body: %s"
180
351
_logger.debug("No body was returned.")
187
358
concatenating self.management_url and url and passing in method and
188
359
any associated kwargs. """
190
if self.management_url is None:
361
is_management = kwargs.pop('management', True)
363
if is_management and self.management_url is None:
191
364
raise exceptions.AuthorizationFailure(
192
365
'Current authorization does not have a known management url')
367
url_to_use = self.auth_url
369
url_to_use = self.management_url
193
371
kwargs.setdefault('headers', {})
194
372
if self.auth_token:
195
373
kwargs['headers']['X-Auth-Token'] = self.auth_token
197
resp, body = self.request(self.management_url + url, method,
375
resp, body = self.request(url_to_use + url, method,
199
377
return resp, body