From 58f80e4c7544c0ee064c1629b3f128a628fc71d8 Mon Sep 17 00:00:00 2001 From: henriquetruta Date: Wed, 28 May 2014 11:09:46 -0300 Subject: [PATCH] Add role assignments list support to identity v3 The assignments manager and its test class were created. Some fake stubs were also added on the fakes.py module. The "openstack role assignment list" command was created. Change-Id: Iae94f4fee608ea3e09ff38961ad22edc38efb89c Implements: blueprint roles-assignment-list Closes-Bug: 1246310 --- .../identity/v3/role_assignment.py | 156 +++++++ openstackclient/tests/identity/v3/fakes.py | 27 ++ .../tests/identity/v3/test_role_assignment.py | 388 ++++++++++++++++++ setup.cfg | 1 + 4 files changed, 572 insertions(+) create mode 100644 openstackclient/identity/v3/role_assignment.py create mode 100644 openstackclient/tests/identity/v3/test_role_assignment.py diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py new file mode 100644 index 000000000..5cc97e8d0 --- /dev/null +++ b/openstackclient/identity/v3/role_assignment.py @@ -0,0 +1,156 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""Identity v3 Assignment action implementations """ + +import logging + +from cliff import lister + +from openstackclient.common import utils + + +class ListRoleAssignment(lister.Lister): + """Lists role assignments according to the given filters""" + + log = logging.getLogger(__name__ + '.ListRoleAssignment') + + def get_parser(self, prog_name): + parser = super(ListRoleAssignment, self).get_parser(prog_name) + parser.add_argument( + '--effective', + action="store_true", + default=False, + help='Returns only effective role assignments', + ) + parser.add_argument( + '--role', + metavar='', + help='Name or ID of role to filter', + ) + user_or_group = parser.add_mutually_exclusive_group() + user_or_group.add_argument( + '--user', + metavar='', + help='Name or ID of user to filter', + ) + user_or_group.add_argument( + '--group', + metavar='', + help='Name or ID of group to filter', + ) + domain_or_project = parser.add_mutually_exclusive_group() + domain_or_project.add_argument( + '--domain', + metavar='', + help='Name or ID of domain to filter', + ) + domain_or_project.add_argument( + '--project', + metavar='', + help='Name or ID of project to filter', + ) + + return parser + + def _as_tuple(self, assignment): + return (assignment.role, assignment.user, assignment.group, + assignment.project, assignment.domain) + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + identity_client = self.app.client_manager.identity + + role = None + if parsed_args.role: + role = utils.find_resource( + identity_client.roles, + parsed_args.role, + ) + + user = None + if parsed_args.user: + user = utils.find_resource( + identity_client.users, + parsed_args.user, + ) + + domain = None + if parsed_args.domain: + domain = utils.find_resource( + identity_client.domains, + parsed_args.domain, + ) + + project = None + if parsed_args.project: + project = utils.find_resource( + identity_client.projects, + parsed_args.project, + ) + + group = None + if parsed_args.group: + group = utils.find_resource( + identity_client.groups, + parsed_args.group, + ) + + effective = True if parsed_args.effective else False + self.log.debug('take_action(%s)' % parsed_args) + columns = ('Role', 'User', 'Group', 'Project', 'Domain') + data = identity_client.role_assignments.list( + domain=domain, + user=user, + group=group, + project=project, + role=role, + effective=effective) + + data_parsed = [] + for assignment in data: + # Removing the extra "scope" layer in the assignment json + scope = assignment.scope + if 'project' in scope: + setattr(assignment, 'project', scope['project']['id']) + assignment.domain = '' + elif 'domain' in scope: + setattr(assignment, 'domain', scope['domain']['id']) + assignment.project = '' + + else: + assignment.domain = '' + assignment.project = '' + + del assignment.scope + + if hasattr(assignment, 'user'): + setattr(assignment, 'user', assignment.user['id']) + assignment.group = '' + elif hasattr(assignment, 'group'): + setattr(assignment, 'group', assignment.group['id']) + assignment.user = '' + else: + assignment.user = '' + assignment.group = '' + + if hasattr(assignment, 'role'): + setattr(assignment, 'role', assignment.role['id']) + else: + assignment.role = '' + + # Creating a tuple from data object fields + # (including the blank ones) + data_parsed.append(self._as_tuple(assignment)) + + return columns, tuple(data_parsed) diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py index ffa89a5f4..86ef2f0c7 100644 --- a/openstackclient/tests/identity/v3/fakes.py +++ b/openstackclient/tests/identity/v3/fakes.py @@ -114,6 +114,31 @@ IDENTITY_PROVIDER = { 'description': idp_description } +#Assignments +ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID = { + 'scope': {'project': {'id': project_id}}, + 'user': {'id': user_id}, + 'role': {'id': role_id}, +} + +ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID = { + 'scope': {'project': {'id': project_id}}, + 'group': {'id': group_id}, + 'role': {'id': role_id}, +} + +ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID = { + 'scope': {'domain': {'id': domain_id}}, + 'user': {'id': user_id}, + 'role': {'id': role_id}, +} + +ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID = { + 'scope': {'domain': {'id': domain_id}}, + 'group': {'id': group_id}, + 'role': {'id': role_id}, +} + class FakeIdentityv3Client(object): def __init__(self, **kwargs): @@ -130,6 +155,8 @@ class FakeIdentityv3Client(object): self.service_catalog = mock.Mock() self.users = mock.Mock() self.users.resource_class = fakes.FakeResource(None, {}) + self.role_assignments = mock.Mock() + self.role_assignments.resource_class = fakes.FakeResource(None, {}) self.auth_token = kwargs['token'] self.management_url = kwargs['endpoint'] diff --git a/openstackclient/tests/identity/v3/test_role_assignment.py b/openstackclient/tests/identity/v3/test_role_assignment.py new file mode 100644 index 000000000..6497ca8ed --- /dev/null +++ b/openstackclient/tests/identity/v3/test_role_assignment.py @@ -0,0 +1,388 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import copy + +from openstackclient.identity.v3 import role_assignment +from openstackclient.tests import fakes +from openstackclient.tests.identity.v3 import fakes as identity_fakes + + +class TestRoleAssignment(identity_fakes.TestIdentityv3): + + def setUp(self): + super(TestRoleAssignment, self).setUp() + + +class TestRoleAssignmentList(TestRoleAssignment): + + def setUp(self): + super(TestRoleAssignment, self).setUp() + + # Get a shortcut to the UserManager Mock + self.users_mock = self.app.client_manager.identity.users + self.users_mock.reset_mock() + + # Get a shortcut to the GroupManager Mock + self.groups_mock = self.app.client_manager.identity.groups + self.groups_mock.reset_mock() + + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock.reset_mock() + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.reset_mock() + + # Get a shortcut to the RoleManager Mock + self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock.reset_mock() + + self.role_assignments_mock = self.app.client_manager.identity.\ + role_assignments + self.role_assignments_mock.reset_mock() + + # Get the command object to test + self.cmd = role_assignment.ListRoleAssignment(self.app, None) + + def test_role_assignment_list_no_filters(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID), + loaded=True, + ), + ] + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=None, + effective=False, + role=None, + user=None, + project=None) + + collist = ('Role', 'User', 'Group', 'Project', 'Domain') + self.assertEqual(columns, collist) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '' + ), (identity_fakes.role_id, + '', + identity_fakes.group_id, + identity_fakes.project_id, + '' + ),) + self.assertEqual(tuple(data), datalist) + + def test_role_assignment_list_user(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), + loaded=True, + ), + ] + + arglist = [ + '--user', identity_fakes.user_name + ] + verifylist = [ + ('user', identity_fakes.user_name), + ('group', None), + ('domain', None), + ('project', None), + ('role', None), + ('effective', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + user=self.users_mock.get(), + group=None, + project=None, + role=None, + effective=False) + + collist = ('Role', 'User', 'Group', 'Project', 'Domain') + self.assertEqual(columns, collist) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + '', + identity_fakes.domain_id + ), (identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '' + ),) + self.assertEqual(tuple(data), datalist) + + def test_role_assignment_list_group(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID), + loaded=True, + ), + ] + + arglist = [ + '--group', identity_fakes.group_name + ] + verifylist = [ + ('user', None), + ('group', identity_fakes.group_name), + ('domain', None), + ('project', None), + ('role', None), + ('effective', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=self.groups_mock.get(), + effective=False, + project=None, + role=None, + user=None) + + collist = ('Role', 'User', 'Group', 'Project', 'Domain') + self.assertEqual(columns, collist) + datalist = (( + identity_fakes.role_id, + '', + identity_fakes.group_id, + '', + identity_fakes.domain_id + ), (identity_fakes.role_id, + '', + identity_fakes.group_id, + identity_fakes.project_id, + '' + ),) + self.assertEqual(tuple(data), datalist) + + def test_role_assignment_list_domain(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID), + loaded=True, + ), + ] + + arglist = [ + '--domain', identity_fakes.domain_name + ] + verifylist = [ + ('user', None), + ('group', None), + ('domain', identity_fakes.domain_name), + ('project', None), + ('role', None), + ('effective', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=self.domains_mock.get(), + group=None, + effective=False, + project=None, + role=None, + user=None) + + collist = ('Role', 'User', 'Group', 'Project', 'Domain') + self.assertEqual(columns, collist) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + '', + identity_fakes.domain_id + ), (identity_fakes.role_id, + '', + identity_fakes.group_id, + '', + identity_fakes.domain_id + ),) + self.assertEqual(tuple(data), datalist) + + def test_role_assignment_list_project(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID), + loaded=True, + ), + ] + + arglist = [ + '--project', identity_fakes.project_name + ] + verifylist = [ + ('user', None), + ('group', None), + ('domain', None), + ('project', identity_fakes.project_name), + ('role', None), + ('effective', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=None, + effective=False, + project=self.projects_mock.get(), + role=None, + user=None) + + collist = ('Role', 'User', 'Group', 'Project', 'Domain') + self.assertEqual(columns, collist) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '' + ), (identity_fakes.role_id, + '', + identity_fakes.group_id, + identity_fakes.project_id, + '' + ),) + self.assertEqual(tuple(data), datalist) + + def test_role_assignment_list_effective(self): + + self.role_assignments_mock.list.return_value = [ + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), + loaded=True, + ), + fakes.FakeResource( + None, + copy.deepcopy( + identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID), + loaded=True, + ), + ] + + arglist = ['--effective'] + verifylist = [ + ('user', None), + ('group', None), + ('domain', None), + ('project', None), + ('role', None), + ('effective', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.role_assignments_mock.list.assert_called_with( + domain=None, + group=None, + effective=True, + project=None, + role=None, + user=None) + + collist = ('Role', 'User', 'Group', 'Project', 'Domain') + self.assertEqual(columns, collist) + datalist = (( + identity_fakes.role_id, + identity_fakes.user_id, + '', + identity_fakes.project_id, + '' + ), (identity_fakes.role_id, + identity_fakes.user_id, + '', + '', + identity_fakes.domain_id, + ),) + self.assertEqual(tuple(data), datalist) diff --git a/setup.cfg b/setup.cfg index 5a4fa743f..3ef54551b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -228,6 +228,7 @@ openstack.identity.v3 = role_remove = openstackclient.identity.v3.role:RemoveRole role_show = openstackclient.identity.v3.role:ShowRole role_set = openstackclient.identity.v3.role:SetRole + role_assignment_list = openstackclient.identity.v3.role_assignment:ListRoleAssignment service_create = openstackclient.identity.v3.service:CreateService service_delete = openstackclient.identity.v3.service:DeleteService