Skip to content

Commit b790925

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Use resource id when name given for identity show"
2 parents 377daeb + 337d013 commit b790925

9 files changed

Lines changed: 117 additions & 5 deletions

File tree

openstackclient/identity/common.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,39 @@ def find_service(identity_client, name_type_or_id):
4747
raise exceptions.CommandError(msg % name_type_or_id)
4848

4949

50+
def _get_token_resource(client, resource, parsed_name):
51+
"""Peek into the user's auth token to get resource IDs
52+
53+
Look into a user's token to try and find the ID of a domain, project or
54+
user, when given the name. Typically non-admin users will interact with
55+
the CLI using names. However, by default, keystone does not allow look up
56+
by name since it would involve listing all entities. Instead opt to use
57+
the correct ID (from the token) instead.
58+
:param client: An identity client
59+
:param resource: A resource to look at in the token, this may be `domain`,
60+
`project_domain`, `user_domain`, `project`, or `user`.
61+
:param parsed_name: This is input from parsed_args that the user is hoping
62+
to find in the token.
63+
64+
:returns: The ID of the resource from the token, or the original value from
65+
parsed_args if it does not match.
66+
"""
67+
68+
try:
69+
token = client.auth.client.get_token()
70+
token_data = client.tokens.get_token_data(token)
71+
token_dict = token_data['token']
72+
73+
# NOTE(stevemar): If domain is passed, just look at the project domain.
74+
if resource == 'domain':
75+
token_dict = token_dict['project']
76+
obj = token_dict[resource]
77+
return obj['id'] if obj['name'] == parsed_name else parsed_name
78+
# diaper defense in case parsing the token fails
79+
except Exception: # noqa
80+
return parsed_name
81+
82+
5083
def _get_domain_id_if_requested(identity_client, domain_name_or_id):
5184
if not domain_name_or_id:
5285
return None

openstackclient/identity/v3/domain.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import six
2525

2626
from openstackclient.i18n import _
27+
from openstackclient.identity import common
2728

2829

2930
LOG = logging.getLogger(__name__)
@@ -187,8 +188,12 @@ def get_parser(self, prog_name):
187188

188189
def take_action(self, parsed_args):
189190
identity_client = self.app.client_manager.identity
191+
192+
domain_str = common._get_token_resource(identity_client, 'domain',
193+
parsed_args.domain)
194+
190195
domain = utils.find_resource(identity_client.domains,
191-
parsed_args.domain)
196+
domain_str)
192197

193198
domain._info.pop('links')
194199
return zip(*sorted(six.iteritems(domain._info)))

openstackclient/identity/v3/project.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,18 +321,21 @@ def get_parser(self, prog_name):
321321
def take_action(self, parsed_args):
322322
identity_client = self.app.client_manager.identity
323323

324+
project_str = common._get_token_resource(identity_client, 'project',
325+
parsed_args.project)
326+
324327
if parsed_args.domain:
325328
domain = common.find_domain(identity_client, parsed_args.domain)
326329
project = utils.find_resource(
327330
identity_client.projects,
328-
parsed_args.project,
331+
project_str,
329332
domain_id=domain.id,
330333
parents_as_list=parsed_args.parents,
331334
subtree_as_list=parsed_args.children)
332335
else:
333336
project = utils.find_resource(
334337
identity_client.projects,
335-
parsed_args.project,
338+
project_str,
336339
parents_as_list=parsed_args.parents,
337340
subtree_as_list=parsed_args.children)
338341

openstackclient/identity/v3/user.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,14 +444,16 @@ def get_parser(self, prog_name):
444444
def take_action(self, parsed_args):
445445
identity_client = self.app.client_manager.identity
446446

447+
user_str = common._get_token_resource(identity_client, 'user',
448+
parsed_args.user)
447449
if parsed_args.domain:
448450
domain = common.find_domain(identity_client, parsed_args.domain)
449451
user = utils.find_resource(identity_client.users,
450-
parsed_args.user,
452+
user_str,
451453
domain_id=domain.id)
452454
else:
453455
user = utils.find_resource(identity_client.users,
454-
parsed_args.user)
456+
user_str)
455457

456458
user._info.pop('links')
457459
return zip(*sorted(six.iteritems(user._info)))

openstackclient/tests/identity/v3/fakes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,9 @@ def __init__(self, **kwargs):
502502
self.role_assignments.resource_class = fakes.FakeResource(None, {})
503503
self.auth_token = kwargs['token']
504504
self.management_url = kwargs['endpoint']
505+
self.auth = FakeAuth()
506+
self.auth.client = mock.Mock()
507+
self.auth.client.resource_class = fakes.FakeResource(None, {})
505508

506509

507510
class FakeFederationManager(object):

openstackclient/tests/identity/v3/test_domain.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,16 @@ def test_domain_show(self):
389389
('domain', identity_fakes.domain_id),
390390
]
391391
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
392+
self.app.client_manager.identity.tokens.get_token_data.return_value = \
393+
{'token':
394+
{'project':
395+
{'domain':
396+
{'id': 'd1',
397+
'name': 'd1'
398+
}
399+
}
400+
}
401+
}
392402

393403
# In base command class ShowOne in cliff, abstract method take_action()
394404
# returns a two-part tuple with a tuple of column names and a tuple of

openstackclient/tests/identity/v3/test_project.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ def setUp(self):
749749
self.cmd = project.ShowProject(self.app, None)
750750

751751
def test_project_show(self):
752+
752753
arglist = [
753754
identity_fakes.project_id,
754755
]
@@ -757,6 +758,16 @@ def test_project_show(self):
757758
]
758759
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
759760

761+
self.app.client_manager.identity.tokens.get_token_data.return_value = \
762+
{'token':
763+
{'project':
764+
{'domain': {},
765+
'name': parsed_args.project,
766+
'id': parsed_args.project
767+
}
768+
}
769+
}
770+
760771
# In base command class ShowOne in cliff, abstract method take_action()
761772
# returns a two-part tuple with a tuple of column names and a tuple of
762773
# data to be shown.
@@ -797,6 +808,15 @@ def test_project_show_parents(self):
797808
('children', False),
798809
]
799810
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
811+
self.app.client_manager.identity.tokens.get_token_data.return_value = \
812+
{'token':
813+
{'project':
814+
{'domain': {},
815+
'name': parsed_args.project,
816+
'id': parsed_args.project
817+
}
818+
}
819+
}
800820

801821
columns, data = self.cmd.take_action(parsed_args)
802822
self.projects_mock.get.assert_called_with(
@@ -845,6 +865,15 @@ def test_project_show_subtree(self):
845865
('children', True),
846866
]
847867
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
868+
self.app.client_manager.identity.tokens.get_token_data.return_value = \
869+
{'token':
870+
{'project':
871+
{'domain': {},
872+
'name': parsed_args.project,
873+
'id': parsed_args.project
874+
}
875+
}
876+
}
848877

849878
columns, data = self.cmd.take_action(parsed_args)
850879
self.projects_mock.get.assert_called_with(
@@ -895,6 +924,15 @@ def test_project_show_parents_and_children(self):
895924
('children', True),
896925
]
897926
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
927+
self.app.client_manager.identity.tokens.get_token_data.return_value = \
928+
{'token':
929+
{'project':
930+
{'domain': {},
931+
'name': parsed_args.project,
932+
'id': parsed_args.project
933+
}
934+
}
935+
}
898936

899937
columns, data = self.cmd.take_action(parsed_args)
900938
self.projects_mock.get.assert_called_with(

openstackclient/tests/identity/v3/test_user.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,17 @@ def setUp(self):
10941094

10951095
# Get the command object to test
10961096
self.cmd = user.ShowUser(self.app, None)
1097+
self.app.client_manager.identity.auth.client.get_user_id.\
1098+
return_value = 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa'
1099+
self.app.client_manager.identity.tokens.get_token_data.return_value = \
1100+
{'token':
1101+
{'user':
1102+
{'domain': {},
1103+
'id': 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa',
1104+
'name': 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa'
1105+
}
1106+
}
1107+
}
10971108

10981109
def test_user_show(self):
10991110
arglist = [
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- When performing ``domain show``, ``project show`` or ``user show``, peek
4+
into the user token to determine the ID or the resource (if supplied with
5+
only a name). This should make finding information about the user and
6+
their project easier for non-admin users.
7+
[Bug `1561599 <https://bugs.launchpad.net/bugs/1561599>`_]

0 commit comments

Comments
 (0)