Allow user ID for authentication

In Keystone V3 user names are no longer necessarily unique
accross domains.

A user can still authenticate a user in the non default
domain via the V2 API providng they use IDs instead of names.

Tenant_ID is already supported, this change adds support
for user ID

Change-Id: I36ba75f3e67c8cdb959e31923d5e557414ab6f9b
This commit is contained in:
Phil Day 2014-03-06 12:37:12 +00:00
parent d0b6550d66
commit dd8bde71ff
5 changed files with 43 additions and 13 deletions

View File

@ -64,8 +64,9 @@ class HTTPClient(object):
os_cache=False, no_cache=True,
http_log_debug=False, auth_system='keystone',
auth_plugin=None, auth_token=None,
cacert=None, tenant_id=None):
cacert=None, tenant_id=None, user_id=None):
self.user = user
self.user_id = user_id
self.password = password
self.projectid = projectid
self.tenant_id = tenant_id
@ -456,6 +457,10 @@ class HTTPClient(object):
if self.auth_token:
body = {"auth": {
"token": {"id": self.auth_token}}}
elif self.user_id:
body = {"auth": {
"passwordCredentials": {"userId": self.user_id,
"password": self._get_password()}}}
else:
body = {"auth": {
"passwordCredentials": {"username": self.user,

View File

@ -285,6 +285,11 @@ class OpenStackComputeShell(object):
parser.add_argument('--os_username',
help=argparse.SUPPRESS)
parser.add_argument('--os-user-id',
metavar='<auth-user-id>',
default=utils.env('OS_USER_ID'),
help=_('Defaults to env[OS_USER_ID].'))
parser.add_argument('--os-password',
metavar='<auth-password>',
default=utils.env('OS_PASSWORD', 'NOVA_PASSWORD'),
@ -550,6 +555,7 @@ class OpenStackComputeShell(object):
return 0
os_username = args.os_username
os_user_id = args.os_user_id
os_password = None # Fetched and set later as needed
os_tenant_name = args.os_tenant_name
os_tenant_id = args.os_tenant_id
@ -606,9 +612,10 @@ class OpenStackComputeShell(object):
auth_plugin.parse_opts(args)
if not auth_plugin or not auth_plugin.opts:
if not os_username:
if not os_username and not os_user_id:
raise exc.CommandError(_("You must provide a username "
"via either --os-username or env[OS_USERNAME]"))
"or user id via --os-username, --os-user-id, "
"env[OS_USERNAME] or env[OS_USER_ID]"))
if not os_tenant_name and not os_tenant_id:
raise exc.CommandError(_("You must provide a tenant name "
@ -639,8 +646,9 @@ class OpenStackComputeShell(object):
raise exc.CommandError(_("You must provide an auth url "
"via either --os-auth-url or env[OS_AUTH_URL]"))
self.cs = client.Client(options.os_compute_api_version, os_username,
os_password, os_tenant_name, tenant_id=os_tenant_id,
self.cs = client.Client(options.os_compute_api_version,
os_username, os_password, os_tenant_name,
tenant_id=os_tenant_id, user_id=os_user_id,
auth_url=os_auth_url, insecure=insecure,
region_name=os_region_name, endpoint_type=endpoint_type,
extensions=self.extensions, service_type=service_type,

View File

@ -32,7 +32,7 @@ FAKE_ENV = {'OS_USERNAME': 'username',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': 'http://no.where'}
FAKE_ENV2 = {'OS_USERNAME': 'username',
FAKE_ENV2 = {'OS_USER_ID': 'user_id',
'OS_PASSWORD': 'password',
'OS_TENANT_ID': 'tenant_id',
'OS_AUTH_URL': 'http://no.where'}
@ -133,25 +133,38 @@ class ShellTest(utils.TestCase):
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def test_no_username(self):
required = ('You must provide a username'
' via either --os-username or env[OS_USERNAME]',)
required = ('You must provide a username or user id'
' via --os-username, --os-user-id,'
' env[OS_USERNAME] or env[OS_USER_ID]')
self.make_env(exclude='OS_USERNAME')
try:
self.shell('list')
except exceptions.CommandError as message:
self.assertEqual(required, message.args)
self.assertEqual(required, message.args[0])
else:
self.fail('CommandError not raised')
def test_no_user_id(self):
required = ('You must provide a username or user id'
' via --os-username, --os-user-id,'
' env[OS_USERNAME] or env[OS_USER_ID]')
self.make_env(exclude='OS_USER_ID', fake_env=FAKE_ENV2)
try:
self.shell('list')
except exceptions.CommandError as message:
self.assertEqual(required, message.args[0])
else:
self.fail('CommandError not raised')
def test_no_tenant_name(self):
required = ('You must provide a tenant name or tenant id'
' via --os-tenant-name, --os-tenant-id,'
' env[OS_TENANT_NAME] or env[OS_TENANT_ID]',)
' env[OS_TENANT_NAME] or env[OS_TENANT_ID]')
self.make_env(exclude='OS_TENANT_NAME')
try:
self.shell('list')
except exceptions.CommandError as message:
self.assertEqual(required, message.args)
self.assertEqual(required, message.args[0])
else:
self.fail('CommandError not raised')

View File

@ -73,12 +73,13 @@ class Client(object):
bypass_url=None, os_cache=False, no_cache=True,
http_log_debug=False, auth_system='keystone',
auth_plugin=None, auth_token=None,
cacert=None, tenant_id=None):
cacert=None, tenant_id=None, user_id=None):
# FIXME(comstud): Rename the api_key argument above when we
# know it's not being used as keyword argument
password = api_key
self.projectid = project_id
self.tenant_id = tenant_id
self.user_id = user_id
self.flavors = flavors.FlavorManager(self)
self.flavor_access = flavor_access.FlavorAccessManager(self)
self.images = images.ImageManager(self)
@ -126,6 +127,7 @@ class Client(object):
self.client = client.HTTPClient(username,
password,
user_id=user_id,
projectid=project_id,
tenant_id=tenant_id,
auth_url=auth_url,

View File

@ -59,9 +59,10 @@ class Client(object):
bypass_url=None, os_cache=False, no_cache=True,
http_log_debug=False, auth_system='keystone',
auth_plugin=None, auth_token=None,
cacert=None, tenant_id=None):
cacert=None, tenant_id=None, user_id=None):
self.projectid = project_id
self.tenant_id = tenant_id
self.user_id = user_id
self.os_cache = os_cache or not no_cache
#TODO(bnemec): Add back in v3 extensions
self.agents = agents.AgentsManager(self)
@ -91,6 +92,7 @@ class Client(object):
self.client = client.HTTPClient(username,
password,
user_id=user_id,
projectid=project_id,
tenant_id=tenant_id,
auth_url=auth_url,