add tenant_id and make projectid optional
Add a new optional tenant_id keyword argument to the client classes cinderclient.client.HTTPClient and cinderclient.v1.client.Client to support authentication with tenant_id instead of projectid (which is acctually used as "tenantName" in the auth request body). Keystone can provide tokens without specifiying the tenant in any form, but a tenantName _or_ tenantId is required to generate the catalog (the keystone service code seems to prefer tenantName if both are specified). When using cinderclient programatically, it may be more convienent, depending on the context to authenticate with out specificying the tenant, or by tenant_id instead of tenant_name. Either way it's impractial to make the requirement in the client for projectid (tenantName) if the auth system has no such limitation. The new client signature is backwards compatible. There is no change in behavior for the shell client. Change-Id: I0c1bdbdabd9acc29133a48a798750323011f1f18
This commit is contained in:
@@ -39,7 +39,7 @@ class HTTPClient(httplib2.Http):
|
||||
USER_AGENT = 'python-cinderclient'
|
||||
|
||||
def __init__(self, user, password, projectid, auth_url, insecure=False,
|
||||
timeout=None, proxy_tenant_id=None,
|
||||
timeout=None, tenant_id=None, proxy_tenant_id=None,
|
||||
proxy_token=None, region_name=None,
|
||||
endpoint_type='publicURL', service_type=None,
|
||||
service_name=None, volume_service_name=None):
|
||||
@@ -47,6 +47,7 @@ class HTTPClient(httplib2.Http):
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.projectid = projectid
|
||||
self.tenant_id = tenant_id
|
||||
self.auth_url = auth_url.rstrip('/')
|
||||
self.version = 'v1'
|
||||
self.region_name = region_name
|
||||
@@ -282,6 +283,8 @@ class HTTPClient(httplib2.Http):
|
||||
|
||||
if self.projectid:
|
||||
body['auth']['tenantName'] = self.projectid
|
||||
elif self.tenant_id:
|
||||
body['auth']['tenantId'] = self.tenant_id
|
||||
|
||||
self._authenticate(url, body)
|
||||
|
||||
|
@@ -21,10 +21,9 @@ class Client(object):
|
||||
|
||||
"""
|
||||
|
||||
# FIXME(jesse): project_id isn't required to authenticate
|
||||
def __init__(self, username, api_key, project_id, auth_url,
|
||||
insecure=False, timeout=None, proxy_tenant_id=None,
|
||||
proxy_token=None, region_name=None,
|
||||
def __init__(self, username, api_key, project_id=None, auth_url='',
|
||||
insecure=False, timeout=None, tenant_id=None,
|
||||
proxy_tenant_id=None, proxy_token=None, region_name=None,
|
||||
endpoint_type='publicURL', extensions=None,
|
||||
service_type='compute', service_name=None,
|
||||
volume_service_name=None):
|
||||
@@ -51,6 +50,7 @@ class Client(object):
|
||||
auth_url,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
tenant_id=tenant_id,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
|
@@ -78,6 +78,76 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_authenticate_tenant_id(self):
|
||||
cs = client.Client("username", "password", auth_url="auth_url/v2.0",
|
||||
tenant_id='tenant_id', service_type='compute')
|
||||
resp = {
|
||||
"access": {
|
||||
"token": {
|
||||
"expires": "12345",
|
||||
"id": "FAKE_ID",
|
||||
"tenant": {
|
||||
"description": None,
|
||||
"enabled": True,
|
||||
"id": "tenant_id",
|
||||
"name": "demo"
|
||||
} # tenant associated with token
|
||||
},
|
||||
"serviceCatalog": [
|
||||
{
|
||||
"type": "compute",
|
||||
"endpoints": [
|
||||
{
|
||||
"region": "RegionOne",
|
||||
"adminURL": "http://localhost:8774/v1",
|
||||
"internalURL": "http://localhost:8774/v1",
|
||||
"publicURL": "http://localhost:8774/v1/",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
auth_response = httplib2.Response({
|
||||
"status": 200,
|
||||
"body": json.dumps(resp), })
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response,
|
||||
json.dumps(resp)))
|
||||
|
||||
@mock.patch.object(httplib2.Http, "request", mock_request)
|
||||
def test_auth_call():
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
'User-Agent': cs.client.USER_AGENT,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
body = {
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'username': cs.client.user,
|
||||
'password': cs.client.password,
|
||||
},
|
||||
'tenantId': cs.client.tenant_id,
|
||||
},
|
||||
}
|
||||
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
mock_request.assert_called_with(token_url, "POST",
|
||||
headers=headers,
|
||||
body=json.dumps(body))
|
||||
|
||||
endpoints = resp["access"]["serviceCatalog"][0]['endpoints']
|
||||
public_url = endpoints[0]["publicURL"].rstrip('/')
|
||||
self.assertEqual(cs.client.management_url, public_url)
|
||||
token_id = resp["access"]["token"]["id"]
|
||||
self.assertEqual(cs.client.auth_token, token_id)
|
||||
tenant_id = resp["access"]["token"]["tenant"]["id"]
|
||||
self.assertEqual(cs.client.tenant_id, tenant_id)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_authenticate_failure(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v2.0")
|
||||
|
Reference in New Issue
Block a user