Skip to content

Commit 4c107e6

Browse files
committed
Role operations should not require list object permission
When using Keystone's policy.v3cloudsample.json policy file, a project admin is supposed to be able to manage role assignments. Unfortunately, a project admin isn't allowed to perform these operations using python-openstackclient, as we attempt to perform list operations for any of the object types specified (users, groups, projects). This is done in an attempt to lookup the id of the object by name, but we perform this list operation even when the user specifies everything by id. This causes 403 errors. This patch still attempts to look up the object id by name, but we catch the 403 and assume that the user specified an id if the list operation is not allowed. This is similar to what we do with the --domain option for other commands. Closes-bug: #1445528 Change-Id: Id95a8520e935c1092d5a22ecd8ea01f572334ac8
1 parent b72f2fb commit 4c107e6

3 files changed

Lines changed: 108 additions & 49 deletions

File tree

‎openstackclient/identity/common.py‎

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
from keystoneclient import exceptions as identity_exc
1919
from keystoneclient.v3 import domains
20+
from keystoneclient.v3 import groups
21+
from keystoneclient.v3 import projects
22+
from keystoneclient.v3 import users
2023
from openstackclient.common import exceptions
2124
from openstackclient.common import utils
2225

@@ -56,4 +59,58 @@ def find_domain(identity_client, name_or_id):
5659
return dom
5760
except identity_exc.Forbidden:
5861
pass
59-
return domains.Domain(None, {'id': name_or_id})
62+
return domains.Domain(None, {'id': name_or_id, 'name': name_or_id})
63+
64+
65+
def find_group(identity_client, name_or_id):
66+
"""Find a group.
67+
68+
If the user does not have permissions to to perform a list groups call,
69+
e.g., if the user is a project admin, assume that the group given is the
70+
id rather than the name. This method is used by the role add command to
71+
allow a role to be assigned to a group by a project admin who does not
72+
have permission to list groups.
73+
"""
74+
try:
75+
group = utils.find_resource(identity_client.groups, name_or_id)
76+
if group is not None:
77+
return group
78+
except identity_exc.Forbidden:
79+
pass
80+
return groups.Group(None, {'id': name_or_id, 'name': name_or_id})
81+
82+
83+
def find_project(identity_client, name_or_id):
84+
"""Find a project.
85+
86+
If the user does not have permissions to to perform a list projects
87+
call, e.g., if the user is a project admin, assume that the project
88+
given is the id rather than the name. This method is used by the role
89+
add command to allow a role to be assigned to a user by a project admin
90+
who does not have permission to list projects.
91+
"""
92+
try:
93+
project = utils.find_resource(identity_client.projects, name_or_id)
94+
if project is not None:
95+
return project
96+
except identity_exc.Forbidden:
97+
pass
98+
return projects.Project(None, {'id': name_or_id, 'name': name_or_id})
99+
100+
101+
def find_user(identity_client, name_or_id):
102+
"""Find a user.
103+
104+
If the user does not have permissions to to perform a list users call,
105+
e.g., if the user is a project admin, assume that the user given is the
106+
id rather than the name. This method is used by the role add command to
107+
allow a role to be assigned to a user by a project admin who does not
108+
have permission to list users.
109+
"""
110+
try:
111+
user = utils.find_resource(identity_client.users, name_or_id)
112+
if user is not None:
113+
return user
114+
except identity_exc.Forbidden:
115+
pass
116+
return users.User(None, {'id': name_or_id, 'name': name_or_id})

‎openstackclient/identity/v3/role.py‎

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
from openstackclient.common import utils
2828
from openstackclient.i18n import _ # noqa
29+
from openstackclient.identity import common
2930

3031

3132
class AddRole(command.Command):
@@ -78,12 +79,12 @@ def take_action(self, parsed_args):
7879
)
7980

8081
if parsed_args.user and parsed_args.domain:
81-
user = utils.find_resource(
82-
identity_client.users,
82+
user = common.find_user(
83+
identity_client,
8384
parsed_args.user,
8485
)
85-
domain = utils.find_resource(
86-
identity_client.domains,
86+
domain = common.find_domain(
87+
identity_client,
8788
parsed_args.domain,
8889
)
8990
identity_client.roles.grant(
@@ -92,12 +93,12 @@ def take_action(self, parsed_args):
9293
domain=domain.id,
9394
)
9495
elif parsed_args.user and parsed_args.project:
95-
user = utils.find_resource(
96-
identity_client.users,
96+
user = common.find_user(
97+
identity_client,
9798
parsed_args.user,
9899
)
99-
project = utils.find_resource(
100-
identity_client.projects,
100+
project = common.find_project(
101+
identity_client,
101102
parsed_args.project,
102103
)
103104
identity_client.roles.grant(
@@ -106,12 +107,12 @@ def take_action(self, parsed_args):
106107
project=project.id,
107108
)
108109
elif parsed_args.group and parsed_args.domain:
109-
group = utils.find_resource(
110-
identity_client.groups,
110+
group = common.find_group(
111+
identity_client,
111112
parsed_args.group,
112113
)
113-
domain = utils.find_resource(
114-
identity_client.domains,
114+
domain = common.find_domain(
115+
identity_client,
115116
parsed_args.domain,
116117
)
117118
identity_client.roles.grant(
@@ -120,12 +121,12 @@ def take_action(self, parsed_args):
120121
domain=domain.id,
121122
)
122123
elif parsed_args.group and parsed_args.project:
123-
group = utils.find_resource(
124-
identity_client.groups,
124+
group = common.find_group(
125+
identity_client,
125126
parsed_args.group,
126127
)
127-
project = utils.find_resource(
128-
identity_client.projects,
128+
project = common.find_project(
129+
identity_client,
129130
parsed_args.project,
130131
)
131132
identity_client.roles.grant(
@@ -240,24 +241,24 @@ def take_action(self, parsed_args):
240241
identity_client = self.app.client_manager.identity
241242

242243
if parsed_args.user:
243-
user = utils.find_resource(
244-
identity_client.users,
244+
user = common.find_user(
245+
identity_client,
245246
parsed_args.user,
246247
)
247248
elif parsed_args.group:
248-
group = utils.find_resource(
249-
identity_client.groups,
249+
group = common.find_group(
250+
identity_client,
250251
parsed_args.group,
251252
)
252253

253254
if parsed_args.domain:
254-
domain = utils.find_resource(
255-
identity_client.domains,
255+
domain = common.find_domain(
256+
identity_client,
256257
parsed_args.domain,
257258
)
258259
elif parsed_args.project:
259-
project = utils.find_resource(
260-
identity_client.projects,
260+
project = common.find_project(
261+
identity_client,
261262
parsed_args.project,
262263
)
263264

@@ -370,12 +371,12 @@ def take_action(self, parsed_args):
370371
)
371372

372373
if parsed_args.user and parsed_args.domain:
373-
user = utils.find_resource(
374-
identity_client.users,
374+
user = common.find_user(
375+
identity_client,
375376
parsed_args.user,
376377
)
377-
domain = utils.find_resource(
378-
identity_client.domains,
378+
domain = common.find_domain(
379+
identity_client,
379380
parsed_args.domain,
380381
)
381382
identity_client.roles.revoke(
@@ -384,12 +385,12 @@ def take_action(self, parsed_args):
384385
domain=domain.id,
385386
)
386387
elif parsed_args.user and parsed_args.project:
387-
user = utils.find_resource(
388-
identity_client.users,
388+
user = common.find_user(
389+
identity_client,
389390
parsed_args.user,
390391
)
391-
project = utils.find_resource(
392-
identity_client.projects,
392+
project = common.find_project(
393+
identity_client,
393394
parsed_args.project,
394395
)
395396
identity_client.roles.revoke(
@@ -398,12 +399,12 @@ def take_action(self, parsed_args):
398399
project=project.id,
399400
)
400401
elif parsed_args.group and parsed_args.domain:
401-
group = utils.find_resource(
402-
identity_client.groups,
402+
group = common.find_group(
403+
identity_client,
403404
parsed_args.group,
404405
)
405-
domain = utils.find_resource(
406-
identity_client.domains,
406+
domain = common.find_domain(
407+
identity_client,
407408
parsed_args.domain,
408409
)
409410
identity_client.roles.revoke(
@@ -412,12 +413,12 @@ def take_action(self, parsed_args):
412413
domain=domain.id,
413414
)
414415
elif parsed_args.group and parsed_args.project:
415-
group = utils.find_resource(
416-
identity_client.groups,
416+
group = common.find_group(
417+
identity_client,
417418
parsed_args.group,
418419
)
419-
project = utils.find_resource(
420-
identity_client.projects,
420+
project = common.find_project(
421+
identity_client,
421422
parsed_args.project,
422423
)
423424
identity_client.roles.revoke(

‎openstackclient/identity/v3/role_assignment.py‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from cliff import lister
1919

2020
from openstackclient.common import utils
21+
from openstackclient.identity import common
2122

2223

2324
class ListRoleAssignment(lister.Lister):
@@ -80,29 +81,29 @@ def take_action(self, parsed_args):
8081

8182
user = None
8283
if parsed_args.user:
83-
user = utils.find_resource(
84-
identity_client.users,
84+
user = common.find_user(
85+
identity_client,
8586
parsed_args.user,
8687
)
8788

8889
domain = None
8990
if parsed_args.domain:
90-
domain = utils.find_resource(
91-
identity_client.domains,
91+
domain = common.find_domain(
92+
identity_client,
9293
parsed_args.domain,
9394
)
9495

9596
project = None
9697
if parsed_args.project:
97-
project = utils.find_resource(
98-
identity_client.projects,
98+
project = common.find_project(
99+
identity_client,
99100
parsed_args.project,
100101
)
101102

102103
group = None
103104
if parsed_args.group:
104-
group = utils.find_resource(
105-
identity_client.groups,
105+
group = common.find_group(
106+
identity_client,
106107
parsed_args.group,
107108
)
108109

0 commit comments

Comments
 (0)