Domain administrator cannot do project operations
Domain administrator cannot do project operations because the require access to the domain API (which they don't have). When attempting to find a domain for project operations, ignore errors because the API returns nothing without indicating there is a problem. The domain administrators will have to use a domain id, but they will still be able to do project operations. If the user does not have permission to read the domain table, they cannot use domain names. Change-Id: Ieed5d420022a407c8296a0bb3569d9469c89d752 Closes-Bug: #1317478 Closes-Bug: #1317485
This commit is contained in:
parent
b3736fd9df
commit
b638488697
@ -16,6 +16,7 @@
|
|||||||
"""Common identity code"""
|
"""Common identity code"""
|
||||||
|
|
||||||
from keystoneclient import exceptions as identity_exc
|
from keystoneclient import exceptions as identity_exc
|
||||||
|
from keystoneclient.v3 import domains
|
||||||
from openstackclient.common import exceptions
|
from openstackclient.common import exceptions
|
||||||
from openstackclient.common import utils
|
from openstackclient.common import utils
|
||||||
|
|
||||||
@ -36,3 +37,23 @@ def find_service(identity_client, name_type_or_id):
|
|||||||
msg = ("No service with a type, name or ID of '%s' exists."
|
msg = ("No service with a type, name or ID of '%s' exists."
|
||||||
% name_type_or_id)
|
% name_type_or_id)
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def find_domain(identity_client, name_or_id):
|
||||||
|
"""Find a domain.
|
||||||
|
|
||||||
|
If the user does not have permssions to access the v3 domain API,
|
||||||
|
assume that domain given is the id rather than the name. This
|
||||||
|
method is used by the project list command, so errors access the
|
||||||
|
domain will be ignored and if the user has access to the project
|
||||||
|
API, everything will work fine.
|
||||||
|
|
||||||
|
Closes bugs #1317478 and #1317485.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
dom = utils.find_resource(identity_client.domains, name_or_id)
|
||||||
|
if dom is not None:
|
||||||
|
return dom
|
||||||
|
except identity_exc.Forbidden:
|
||||||
|
pass
|
||||||
|
return domains.Domain(None, {'id': name_or_id})
|
||||||
|
@ -24,6 +24,7 @@ from cliff import show
|
|||||||
|
|
||||||
from openstackclient.common import parseractions
|
from openstackclient.common import parseractions
|
||||||
from openstackclient.common import utils
|
from openstackclient.common import utils
|
||||||
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
||||||
class CreateProject(show.ShowOne):
|
class CreateProject(show.ShowOne):
|
||||||
@ -73,10 +74,7 @@ class CreateProject(show.ShowOne):
|
|||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain = utils.find_resource(
|
domain = common.find_domain(identity_client, parsed_args.domain).id
|
||||||
identity_client.domains,
|
|
||||||
parsed_args.domain,
|
|
||||||
).id
|
|
||||||
else:
|
else:
|
||||||
domain = None
|
domain = None
|
||||||
|
|
||||||
@ -156,10 +154,8 @@ class ListProject(lister.Lister):
|
|||||||
columns = ('ID', 'Name')
|
columns = ('ID', 'Name')
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
kwargs['domain'] = utils.find_resource(
|
domain = common.find_domain(identity_client, parsed_args.domain)
|
||||||
identity_client.domains,
|
kwargs['domain'] = domain.id
|
||||||
parsed_args.domain,
|
|
||||||
).id
|
|
||||||
data = identity_client.projects.list(**kwargs)
|
data = identity_client.projects.list(**kwargs)
|
||||||
return (columns,
|
return (columns,
|
||||||
(utils.get_item_properties(
|
(utils.get_item_properties(
|
||||||
@ -236,10 +232,8 @@ class SetProject(command.Command):
|
|||||||
if parsed_args.name:
|
if parsed_args.name:
|
||||||
kwargs['name'] = parsed_args.name
|
kwargs['name'] = parsed_args.name
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
kwargs['domain'] = utils.find_resource(
|
domain = common.find_domain(identity_client, parsed_args.domain)
|
||||||
identity_client.domains,
|
kwargs['domain'] = domain.id
|
||||||
parsed_args.domain,
|
|
||||||
).id
|
|
||||||
if parsed_args.description:
|
if parsed_args.description:
|
||||||
kwargs['description'] = parsed_args.description
|
kwargs['description'] = parsed_args.description
|
||||||
if parsed_args.enable:
|
if parsed_args.enable:
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import mock
|
||||||
|
|
||||||
from openstackclient.identity.v3 import project
|
from openstackclient.identity.v3 import project
|
||||||
from openstackclient.tests import fakes
|
from openstackclient.tests import fakes
|
||||||
@ -172,6 +173,45 @@ class TestProjectCreate(TestProject):
|
|||||||
)
|
)
|
||||||
self.assertEqual(data, datalist)
|
self.assertEqual(data, datalist)
|
||||||
|
|
||||||
|
def test_project_create_domain_no_perms(self):
|
||||||
|
arglist = [
|
||||||
|
'--domain', identity_fakes.domain_id,
|
||||||
|
identity_fakes.project_name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('domain', identity_fakes.domain_id),
|
||||||
|
('enable', False),
|
||||||
|
('disable', False),
|
||||||
|
('name', identity_fakes.project_name),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
mocker = mock.Mock()
|
||||||
|
mocker.return_value = None
|
||||||
|
|
||||||
|
with mock.patch("openstackclient.common.utils.find_resource", mocker):
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
# Set expected values
|
||||||
|
kwargs = {
|
||||||
|
'name': identity_fakes.project_name,
|
||||||
|
'domain': identity_fakes.domain_id,
|
||||||
|
'description': None,
|
||||||
|
'enabled': True,
|
||||||
|
}
|
||||||
|
self.projects_mock.create.assert_called_with(
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
collist = ('description', 'domain_id', 'enabled', 'id', 'name')
|
||||||
|
self.assertEqual(columns, collist)
|
||||||
|
datalist = (
|
||||||
|
identity_fakes.project_description,
|
||||||
|
identity_fakes.domain_id,
|
||||||
|
True,
|
||||||
|
identity_fakes.project_id,
|
||||||
|
identity_fakes.project_name,
|
||||||
|
)
|
||||||
|
self.assertEqual(data, datalist)
|
||||||
|
|
||||||
def test_project_create_enable(self):
|
def test_project_create_enable(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--enable',
|
'--enable',
|
||||||
@ -411,6 +451,30 @@ class TestProjectList(TestProject):
|
|||||||
), )
|
), )
|
||||||
self.assertEqual(tuple(data), datalist)
|
self.assertEqual(tuple(data), datalist)
|
||||||
|
|
||||||
|
def test_project_list_domain_no_perms(self):
|
||||||
|
arglist = [
|
||||||
|
'--domain', identity_fakes.domain_id,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('domain', identity_fakes.domain_id),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
mocker = mock.Mock()
|
||||||
|
mocker.return_value = None
|
||||||
|
|
||||||
|
with mock.patch("openstackclient.common.utils.find_resource", mocker):
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
self.projects_mock.list.assert_called_with(
|
||||||
|
domain=identity_fakes.domain_id)
|
||||||
|
collist = ('ID', 'Name')
|
||||||
|
self.assertEqual(columns, collist)
|
||||||
|
datalist = ((
|
||||||
|
identity_fakes.project_id,
|
||||||
|
identity_fakes.project_name,
|
||||||
|
), )
|
||||||
|
self.assertEqual(tuple(data), datalist)
|
||||||
|
|
||||||
|
|
||||||
class TestProjectSet(TestProject):
|
class TestProjectSet(TestProject):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user