Merge "Use resource id when name given for identity show"
This commit is contained in:
commit
b7909252a5
@ -47,6 +47,39 @@ def find_service(identity_client, name_type_or_id):
|
|||||||
raise exceptions.CommandError(msg % name_type_or_id)
|
raise exceptions.CommandError(msg % name_type_or_id)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_token_resource(client, resource, parsed_name):
|
||||||
|
"""Peek into the user's auth token to get resource IDs
|
||||||
|
|
||||||
|
Look into a user's token to try and find the ID of a domain, project or
|
||||||
|
user, when given the name. Typically non-admin users will interact with
|
||||||
|
the CLI using names. However, by default, keystone does not allow look up
|
||||||
|
by name since it would involve listing all entities. Instead opt to use
|
||||||
|
the correct ID (from the token) instead.
|
||||||
|
:param client: An identity client
|
||||||
|
:param resource: A resource to look at in the token, this may be `domain`,
|
||||||
|
`project_domain`, `user_domain`, `project`, or `user`.
|
||||||
|
:param parsed_name: This is input from parsed_args that the user is hoping
|
||||||
|
to find in the token.
|
||||||
|
|
||||||
|
:returns: The ID of the resource from the token, or the original value from
|
||||||
|
parsed_args if it does not match.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
token = client.auth.client.get_token()
|
||||||
|
token_data = client.tokens.get_token_data(token)
|
||||||
|
token_dict = token_data['token']
|
||||||
|
|
||||||
|
# NOTE(stevemar): If domain is passed, just look at the project domain.
|
||||||
|
if resource == 'domain':
|
||||||
|
token_dict = token_dict['project']
|
||||||
|
obj = token_dict[resource]
|
||||||
|
return obj['id'] if obj['name'] == parsed_name else parsed_name
|
||||||
|
# diaper defense in case parsing the token fails
|
||||||
|
except Exception: # noqa
|
||||||
|
return parsed_name
|
||||||
|
|
||||||
|
|
||||||
def _get_domain_id_if_requested(identity_client, domain_name_or_id):
|
def _get_domain_id_if_requested(identity_client, domain_name_or_id):
|
||||||
if not domain_name_or_id:
|
if not domain_name_or_id:
|
||||||
return None
|
return None
|
||||||
|
@ -24,6 +24,7 @@ from osc_lib import utils
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -187,8 +188,12 @@ class ShowDomain(command.ShowOne):
|
|||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
|
domain_str = common._get_token_resource(identity_client, 'domain',
|
||||||
|
parsed_args.domain)
|
||||||
|
|
||||||
domain = utils.find_resource(identity_client.domains,
|
domain = utils.find_resource(identity_client.domains,
|
||||||
parsed_args.domain)
|
domain_str)
|
||||||
|
|
||||||
domain._info.pop('links')
|
domain._info.pop('links')
|
||||||
return zip(*sorted(six.iteritems(domain._info)))
|
return zip(*sorted(six.iteritems(domain._info)))
|
||||||
|
@ -321,18 +321,21 @@ class ShowProject(command.ShowOne):
|
|||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
|
project_str = common._get_token_resource(identity_client, 'project',
|
||||||
|
parsed_args.project)
|
||||||
|
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain = common.find_domain(identity_client, parsed_args.domain)
|
domain = common.find_domain(identity_client, parsed_args.domain)
|
||||||
project = utils.find_resource(
|
project = utils.find_resource(
|
||||||
identity_client.projects,
|
identity_client.projects,
|
||||||
parsed_args.project,
|
project_str,
|
||||||
domain_id=domain.id,
|
domain_id=domain.id,
|
||||||
parents_as_list=parsed_args.parents,
|
parents_as_list=parsed_args.parents,
|
||||||
subtree_as_list=parsed_args.children)
|
subtree_as_list=parsed_args.children)
|
||||||
else:
|
else:
|
||||||
project = utils.find_resource(
|
project = utils.find_resource(
|
||||||
identity_client.projects,
|
identity_client.projects,
|
||||||
parsed_args.project,
|
project_str,
|
||||||
parents_as_list=parsed_args.parents,
|
parents_as_list=parsed_args.parents,
|
||||||
subtree_as_list=parsed_args.children)
|
subtree_as_list=parsed_args.children)
|
||||||
|
|
||||||
|
@ -444,14 +444,16 @@ class ShowUser(command.ShowOne):
|
|||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
|
user_str = common._get_token_resource(identity_client, 'user',
|
||||||
|
parsed_args.user)
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain = common.find_domain(identity_client, parsed_args.domain)
|
domain = common.find_domain(identity_client, parsed_args.domain)
|
||||||
user = utils.find_resource(identity_client.users,
|
user = utils.find_resource(identity_client.users,
|
||||||
parsed_args.user,
|
user_str,
|
||||||
domain_id=domain.id)
|
domain_id=domain.id)
|
||||||
else:
|
else:
|
||||||
user = utils.find_resource(identity_client.users,
|
user = utils.find_resource(identity_client.users,
|
||||||
parsed_args.user)
|
user_str)
|
||||||
|
|
||||||
user._info.pop('links')
|
user._info.pop('links')
|
||||||
return zip(*sorted(six.iteritems(user._info)))
|
return zip(*sorted(six.iteritems(user._info)))
|
||||||
|
@ -502,6 +502,9 @@ class FakeIdentityv3Client(object):
|
|||||||
self.role_assignments.resource_class = fakes.FakeResource(None, {})
|
self.role_assignments.resource_class = fakes.FakeResource(None, {})
|
||||||
self.auth_token = kwargs['token']
|
self.auth_token = kwargs['token']
|
||||||
self.management_url = kwargs['endpoint']
|
self.management_url = kwargs['endpoint']
|
||||||
|
self.auth = FakeAuth()
|
||||||
|
self.auth.client = mock.Mock()
|
||||||
|
self.auth.client.resource_class = fakes.FakeResource(None, {})
|
||||||
|
|
||||||
|
|
||||||
class FakeFederationManager(object):
|
class FakeFederationManager(object):
|
||||||
|
@ -389,6 +389,16 @@ class TestDomainShow(TestDomain):
|
|||||||
('domain', identity_fakes.domain_id),
|
('domain', identity_fakes.domain_id),
|
||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
self.app.client_manager.identity.tokens.get_token_data.return_value = \
|
||||||
|
{'token':
|
||||||
|
{'project':
|
||||||
|
{'domain':
|
||||||
|
{'id': 'd1',
|
||||||
|
'name': 'd1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# In base command class ShowOne in cliff, abstract method take_action()
|
# In base command class ShowOne in cliff, abstract method take_action()
|
||||||
# returns a two-part tuple with a tuple of column names and a tuple of
|
# returns a two-part tuple with a tuple of column names and a tuple of
|
||||||
|
@ -749,6 +749,7 @@ class TestProjectShow(TestProject):
|
|||||||
self.cmd = project.ShowProject(self.app, None)
|
self.cmd = project.ShowProject(self.app, None)
|
||||||
|
|
||||||
def test_project_show(self):
|
def test_project_show(self):
|
||||||
|
|
||||||
arglist = [
|
arglist = [
|
||||||
identity_fakes.project_id,
|
identity_fakes.project_id,
|
||||||
]
|
]
|
||||||
@ -757,6 +758,16 @@ class TestProjectShow(TestProject):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
self.app.client_manager.identity.tokens.get_token_data.return_value = \
|
||||||
|
{'token':
|
||||||
|
{'project':
|
||||||
|
{'domain': {},
|
||||||
|
'name': parsed_args.project,
|
||||||
|
'id': parsed_args.project
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# In base command class ShowOne in cliff, abstract method take_action()
|
# In base command class ShowOne in cliff, abstract method take_action()
|
||||||
# returns a two-part tuple with a tuple of column names and a tuple of
|
# returns a two-part tuple with a tuple of column names and a tuple of
|
||||||
# data to be shown.
|
# data to be shown.
|
||||||
@ -797,6 +808,15 @@ class TestProjectShow(TestProject):
|
|||||||
('children', False),
|
('children', False),
|
||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
self.app.client_manager.identity.tokens.get_token_data.return_value = \
|
||||||
|
{'token':
|
||||||
|
{'project':
|
||||||
|
{'domain': {},
|
||||||
|
'name': parsed_args.project,
|
||||||
|
'id': parsed_args.project
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.projects_mock.get.assert_called_with(
|
self.projects_mock.get.assert_called_with(
|
||||||
@ -845,6 +865,15 @@ class TestProjectShow(TestProject):
|
|||||||
('children', True),
|
('children', True),
|
||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
self.app.client_manager.identity.tokens.get_token_data.return_value = \
|
||||||
|
{'token':
|
||||||
|
{'project':
|
||||||
|
{'domain': {},
|
||||||
|
'name': parsed_args.project,
|
||||||
|
'id': parsed_args.project
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.projects_mock.get.assert_called_with(
|
self.projects_mock.get.assert_called_with(
|
||||||
@ -895,6 +924,15 @@ class TestProjectShow(TestProject):
|
|||||||
('children', True),
|
('children', True),
|
||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
self.app.client_manager.identity.tokens.get_token_data.return_value = \
|
||||||
|
{'token':
|
||||||
|
{'project':
|
||||||
|
{'domain': {},
|
||||||
|
'name': parsed_args.project,
|
||||||
|
'id': parsed_args.project
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.projects_mock.get.assert_called_with(
|
self.projects_mock.get.assert_called_with(
|
||||||
|
@ -1094,6 +1094,17 @@ class TestUserShow(TestUser):
|
|||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = user.ShowUser(self.app, None)
|
self.cmd = user.ShowUser(self.app, None)
|
||||||
|
self.app.client_manager.identity.auth.client.get_user_id.\
|
||||||
|
return_value = 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa'
|
||||||
|
self.app.client_manager.identity.tokens.get_token_data.return_value = \
|
||||||
|
{'token':
|
||||||
|
{'user':
|
||||||
|
{'domain': {},
|
||||||
|
'id': 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa',
|
||||||
|
'name': 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def test_user_show(self):
|
def test_user_show(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
|
7
releasenotes/notes/bug-1561599-d5f541f08ae6274a.yaml
Normal file
7
releasenotes/notes/bug-1561599-d5f541f08ae6274a.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- When performing ``domain show``, ``project show`` or ``user show``, peek
|
||||||
|
into the user token to determine the ID or the resource (if supplied with
|
||||||
|
only a name). This should make finding information about the user and
|
||||||
|
their project easier for non-admin users.
|
||||||
|
[Bug `1561599 <https://bugs.launchpad.net/bugs/1561599>`_]
|
Loading…
Reference in New Issue
Block a user