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:
Terry Howe 2014-05-30 10:38:20 -06:00 committed by Terry Howe
parent b3736fd9df
commit b638488697
3 changed files with 91 additions and 12 deletions

View File

@ -16,6 +16,7 @@
"""Common identity code"""
from keystoneclient import exceptions as identity_exc
from keystoneclient.v3 import domains
from openstackclient.common import exceptions
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."
% name_type_or_id)
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})

View File

@ -24,6 +24,7 @@ from cliff import show
from openstackclient.common import parseractions
from openstackclient.common import utils
from openstackclient.identity import common
class CreateProject(show.ShowOne):
@ -73,10 +74,7 @@ class CreateProject(show.ShowOne):
identity_client = self.app.client_manager.identity
if parsed_args.domain:
domain = utils.find_resource(
identity_client.domains,
parsed_args.domain,
).id
domain = common.find_domain(identity_client, parsed_args.domain).id
else:
domain = None
@ -156,10 +154,8 @@ class ListProject(lister.Lister):
columns = ('ID', 'Name')
kwargs = {}
if parsed_args.domain:
kwargs['domain'] = utils.find_resource(
identity_client.domains,
parsed_args.domain,
).id
domain = common.find_domain(identity_client, parsed_args.domain)
kwargs['domain'] = domain.id
data = identity_client.projects.list(**kwargs)
return (columns,
(utils.get_item_properties(
@ -236,10 +232,8 @@ class SetProject(command.Command):
if parsed_args.name:
kwargs['name'] = parsed_args.name
if parsed_args.domain:
kwargs['domain'] = utils.find_resource(
identity_client.domains,
parsed_args.domain,
).id
domain = common.find_domain(identity_client, parsed_args.domain)
kwargs['domain'] = domain.id
if parsed_args.description:
kwargs['description'] = parsed_args.description
if parsed_args.enable:

View File

@ -14,6 +14,7 @@
#
import copy
import mock
from openstackclient.identity.v3 import project
from openstackclient.tests import fakes
@ -172,6 +173,45 @@ class TestProjectCreate(TestProject):
)
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):
arglist = [
'--enable',
@ -411,6 +451,30 @@ class TestProjectList(TestProject):
), )
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):