Allow tenant ID for authentication

Under the Keystone v3 API Tenant names are not necessarily uniques
across Domains for a User, so the client should also allow
authentication by tenant_id

Fixes bug 1196486

Change-Id: I3f385a19c1d3d66f5539f901796bbaa22d315762
This commit is contained in:
Phil Day 2013-07-01 12:01:00 +01:00
parent 4e3be8d342
commit 037497da52
5 changed files with 65 additions and 9 deletions

@ -94,7 +94,7 @@ class HTTPClient(httplib2.Http):
USER_AGENT = 'python-neutronclient'
def __init__(self, username=None, tenant_name=None,
def __init__(self, username=None, tenant_name=None, tenant_id=None,
password=None, auth_url=None,
token=None, region_name=None, timeout=None,
endpoint_url=None, insecure=False,
@ -103,6 +103,7 @@ class HTTPClient(httplib2.Http):
super(HTTPClient, self).__init__(timeout=timeout)
self.username = username
self.tenant_name = tenant_name
self.tenant_id = tenant_id
self.password = password
self.auth_url = auth_url.rstrip('/') if auth_url else None
self.endpoint_type = endpoint_type
@ -183,10 +184,16 @@ class HTTPClient(httplib2.Http):
def authenticate(self):
if self.auth_strategy != 'keystone':
raise exceptions.Unauthorized(message='unknown auth strategy')
body = {'auth': {'passwordCredentials':
{'username': self.username,
'password': self.password, },
'tenantName': self.tenant_name, }, }
if self.tenant_id:
body = {'auth': {'passwordCredentials':
{'username': self.username,
'password': self.password, },
'tenantId': self.tenant_id, }, }
else:
body = {'auth': {'passwordCredentials':
{'username': self.username,
'password': self.password, },
'tenantName': self.tenant_name, }, }
token_url = self.auth_url + "/tokens"

@ -76,6 +76,7 @@ class ClientManager(object):
if not self._url:
httpclient = client.HTTPClient(username=self._username,
tenant_name=self._tenant_name,
tenant_id=self._tenant_id,
password=self._password,
region_name=self._region_name,
auth_url=self._auth_url,

@ -295,6 +295,11 @@ class NeutronShell(app.App):
'--os_tenant_name',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-tenant-id', metavar='<auth-tenant-id>',
default=env('OS_TENANT_ID'),
help='Authentication tenant name (Env: OS_TENANT_ID)')
parser.add_argument(
'--os-username', metavar='<auth-username>',
default=utils.env('OS_USERNAME'),
@ -482,10 +487,12 @@ class NeutronShell(app.App):
"You must provide a password via"
" either --os-password or env[OS_PASSWORD]")
if not (self.options.os_tenant_name):
if (not self.options.os_tenant_name
and not self.options.os_tenant_id):
raise exc.CommandError(
"You must provide a tenant_name via"
" either --os-tenant-name or via env[OS_TENANT_NAME]")
"You must provide a tenant_name or tenant_id via"
" --os-tenant-name, env[OS_TENANT_NAME]"
" --os-tenant-id, or via env[OS_TENANT_ID]")
if not self.options.os_auth_url:
raise exc.CommandError(
@ -502,6 +509,7 @@ class NeutronShell(app.App):
url=self.options.os_url,
auth_url=self.options.os_auth_url,
tenant_name=self.options.os_tenant_name,
tenant_id=self.options.os_tenant_id,
username=self.options.os_username,
password=self.options.os_password,
region_name=self.options.os_region_name,

@ -118,6 +118,7 @@ class Client(object):
:param string password: Password for authentication. (optional)
:param string token: Token for authentication. (optional)
:param string tenant_name: Tenant name. (optional)
:param string tenant_id: Tenant id. (optional)
:param string auth_url: Keystone service endpoint for authorization.
:param string endpoint_type: Network service endpoint type to pull from the
keystone catalog (e.g. 'publicURL',

@ -29,6 +29,7 @@ from neutronclient.common import exceptions
USERNAME = 'testuser'
TENANT_NAME = 'testtenant'
TENANT_ID = 'testtenantid'
PASSWORD = 'password'
AUTH_URL = 'authurl'
ENDPOINT_URL = 'localurl'
@ -67,6 +68,9 @@ ENDPOINTS_RESULT = {
class CLITestAuthKeystone(testtools.TestCase):
# Auth Body expected when using tenant name
auth_type = 'tenantName'
def setUp(self):
"""Prepare the test environment."""
super(CLITestAuthKeystone, self).setUp()
@ -87,7 +91,7 @@ class CLITestAuthKeystone(testtools.TestCase):
self.client.request(
AUTH_URL + '/tokens', 'POST',
body=mox.IsA(str), headers=mox.IsA(dict)
body=mox.StrContains(self.auth_type), headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
@ -318,3 +322,38 @@ class CLITestAuthKeystone(testtools.TestCase):
self.assertRaises(exceptions.EndpointTypeNotFound,
self.client._extract_service_catalog,
resources)
class CLITestAuthKeystoneWithId(CLITestAuthKeystone):
# Auth Body expected when using tenant Id
auth_type = 'tenantId'
def setUp(self):
"""Prepare the test environment."""
super(CLITestAuthKeystone, self).setUp()
self.mox = mox.Mox()
self.client = client.HTTPClient(username=USERNAME,
tenant_id=TENANT_ID,
password=PASSWORD,
auth_url=AUTH_URL,
region_name=REGION)
self.addCleanup(self.mox.VerifyAll)
self.addCleanup(self.mox.UnsetStubs)
class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone):
# Auth Body expected when using tenant Id
auth_type = 'tenantId'
def setUp(self):
"""Prepare the test environment."""
super(CLITestAuthKeystone, self).setUp()
self.mox = mox.Mox()
self.client = client.HTTPClient(username=USERNAME,
tenant_id=TENANT_ID,
tenant_name=TENANT_NAME,
password=PASSWORD,
auth_url=AUTH_URL,
region_name=REGION)