Skip to content

Commit 7028374

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Domain administrator cannot do project operations"
2 parents e8f0587 + b638488 commit 7028374

3 files changed

Lines changed: 91 additions & 12 deletions

File tree

‎openstackclient/identity/common.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"""Common identity code"""
1717

1818
from keystoneclient import exceptions as identity_exc
19+
from keystoneclient.v3 import domains
1920
from openstackclient.common import exceptions
2021
from openstackclient.common import utils
2122

@@ -36,3 +37,23 @@ def find_service(identity_client, name_type_or_id):
3637
msg = ("No service with a type, name or ID of '%s' exists."
3738
% name_type_or_id)
3839
raise exceptions.CommandError(msg)
40+
41+
42+
def find_domain(identity_client, name_or_id):
43+
"""Find a domain.
44+
45+
If the user does not have permssions to access the v3 domain API,
46+
assume that domain given is the id rather than the name. This
47+
method is used by the project list command, so errors access the
48+
domain will be ignored and if the user has access to the project
49+
API, everything will work fine.
50+
51+
Closes bugs #1317478 and #1317485.
52+
"""
53+
try:
54+
dom = utils.find_resource(identity_client.domains, name_or_id)
55+
if dom is not None:
56+
return dom
57+
except identity_exc.Forbidden:
58+
pass
59+
return domains.Domain(None, {'id': name_or_id})

‎openstackclient/identity/v3/project.py‎

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
from openstackclient.common import parseractions
2626
from openstackclient.common import utils
27+
from openstackclient.identity import common
2728

2829

2930
class CreateProject(show.ShowOne):
@@ -73,10 +74,7 @@ def take_action(self, parsed_args):
7374
identity_client = self.app.client_manager.identity
7475

7576
if parsed_args.domain:
76-
domain = utils.find_resource(
77-
identity_client.domains,
78-
parsed_args.domain,
79-
).id
77+
domain = common.find_domain(identity_client, parsed_args.domain).id
8078
else:
8179
domain = None
8280

@@ -156,10 +154,8 @@ def take_action(self, parsed_args):
156154
columns = ('ID', 'Name')
157155
kwargs = {}
158156
if parsed_args.domain:
159-
kwargs['domain'] = utils.find_resource(
160-
identity_client.domains,
161-
parsed_args.domain,
162-
).id
157+
domain = common.find_domain(identity_client, parsed_args.domain)
158+
kwargs['domain'] = domain.id
163159
data = identity_client.projects.list(**kwargs)
164160
return (columns,
165161
(utils.get_item_properties(
@@ -236,10 +232,8 @@ def take_action(self, parsed_args):
236232
if parsed_args.name:
237233
kwargs['name'] = parsed_args.name
238234
if parsed_args.domain:
239-
kwargs['domain'] = utils.find_resource(
240-
identity_client.domains,
241-
parsed_args.domain,
242-
).id
235+
domain = common.find_domain(identity_client, parsed_args.domain)
236+
kwargs['domain'] = domain.id
243237
if parsed_args.description:
244238
kwargs['description'] = parsed_args.description
245239
if parsed_args.enable:

‎openstackclient/tests/identity/v3/test_project.py‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#
1515

1616
import copy
17+
import mock
1718

1819
from openstackclient.identity.v3 import project
1920
from openstackclient.tests import fakes
@@ -172,6 +173,45 @@ def test_project_create_domain(self):
172173
)
173174
self.assertEqual(data, datalist)
174175

176+
def test_project_create_domain_no_perms(self):
177+
arglist = [
178+
'--domain', identity_fakes.domain_id,
179+
identity_fakes.project_name,
180+
]
181+
verifylist = [
182+
('domain', identity_fakes.domain_id),
183+
('enable', False),
184+
('disable', False),
185+
('name', identity_fakes.project_name),
186+
]
187+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
188+
mocker = mock.Mock()
189+
mocker.return_value = None
190+
191+
with mock.patch("openstackclient.common.utils.find_resource", mocker):
192+
columns, data = self.cmd.take_action(parsed_args)
193+
194+
# Set expected values
195+
kwargs = {
196+
'name': identity_fakes.project_name,
197+
'domain': identity_fakes.domain_id,
198+
'description': None,
199+
'enabled': True,
200+
}
201+
self.projects_mock.create.assert_called_with(
202+
**kwargs
203+
)
204+
collist = ('description', 'domain_id', 'enabled', 'id', 'name')
205+
self.assertEqual(columns, collist)
206+
datalist = (
207+
identity_fakes.project_description,
208+
identity_fakes.domain_id,
209+
True,
210+
identity_fakes.project_id,
211+
identity_fakes.project_name,
212+
)
213+
self.assertEqual(data, datalist)
214+
175215
def test_project_create_enable(self):
176216
arglist = [
177217
'--enable',
@@ -411,6 +451,30 @@ def test_project_list_domain(self):
411451
), )
412452
self.assertEqual(tuple(data), datalist)
413453

454+
def test_project_list_domain_no_perms(self):
455+
arglist = [
456+
'--domain', identity_fakes.domain_id,
457+
]
458+
verifylist = [
459+
('domain', identity_fakes.domain_id),
460+
]
461+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
462+
mocker = mock.Mock()
463+
mocker.return_value = None
464+
465+
with mock.patch("openstackclient.common.utils.find_resource", mocker):
466+
columns, data = self.cmd.take_action(parsed_args)
467+
468+
self.projects_mock.list.assert_called_with(
469+
domain=identity_fakes.domain_id)
470+
collist = ('ID', 'Name')
471+
self.assertEqual(columns, collist)
472+
datalist = ((
473+
identity_fakes.project_id,
474+
identity_fakes.project_name,
475+
), )
476+
self.assertEqual(tuple(data), datalist)
477+
414478

415479
class TestProjectSet(TestProject):
416480

0 commit comments

Comments
 (0)