# # 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 import mock from heat.common import exception from heat.engine import properties from heat.engine import resource from heat.engine.resources.openstack.keystone import role_assignments from heat.engine import stack from heat.engine import template from heat.tests import common from heat.tests import generic_resource from heat.tests import utils RESOURCE_TYPE = 'OS::Keystone::DummyRoleAssignment' keystone_role_assignment_template = { 'heat_template_version': '2015-10-15', 'resources': { 'test_role_assignment': { 'type': RESOURCE_TYPE, 'properties': { 'roles': [ { 'role': 'role_1', 'project': 'project_1', }, { 'role': 'role_1', 'domain': 'domain_1' } ] } } } } MixinClass = role_assignments.KeystoneRoleAssignmentMixin class DummyRoleAssignment(generic_resource.GenericResource, MixinClass): properties_schema = {} properties_schema.update(MixinClass.mixin_properties_schema) def validate(self): super(DummyRoleAssignment, self).validate() self.validate_assignment_properties() class KeystoneRoleAssignmentMixinTest(common.HeatTestCase): def setUp(self): super(KeystoneRoleAssignmentMixinTest, self).setUp() self.ctx = utils.dummy_context() # For unit testing purpose. Register resource provider explicitly. resource._register_class(RESOURCE_TYPE, DummyRoleAssignment) self.stack = stack.Stack( self.ctx, 'test_stack_keystone', template.Template(keystone_role_assignment_template) ) self.test_role_assignment = self.stack['test_role_assignment'] # Mock client self.keystoneclient = mock.MagicMock() self.test_role_assignment.client = mock.MagicMock() self.test_role_assignment.client.return_value = self.keystoneclient self.roles = self.keystoneclient.roles # Mock client plugin def _side_effect(value): return value self.keystone_client_plugin = mock.MagicMock() (self.keystone_client_plugin.get_domain_id. side_effect) = _side_effect (self.keystone_client_plugin.get_role_id. side_effect) = _side_effect (self.keystone_client_plugin.get_project_id. side_effect) = _side_effect self.test_role_assignment.client_plugin = mock.MagicMock() (self.test_role_assignment.client_plugin. return_value) = self.keystone_client_plugin def test_resource_mapping(self): mapping = role_assignments.resource_mapping() self.assertEqual(2, len(mapping)) self.assertEqual(role_assignments.KeystoneUserRoleAssignment, mapping['OS::Keystone::UserRoleAssignment']) self.assertEqual(role_assignments.KeystoneGroupRoleAssignment, mapping['OS::Keystone::GroupRoleAssignment']) def test_properties_title(self): property_title_map = {MixinClass.ROLES: 'roles'} for actual_title, expected_title in property_title_map.items(): self.assertEqual( expected_title, actual_title, 'KeystoneRoleAssignmentMixin PROPERTIES(%s) title modified.' % actual_title) def test_property_roles_validate_schema(self): schema = MixinClass.mixin_properties_schema[MixinClass.ROLES] self.assertEqual( True, schema.update_allowed, 'update_allowed for property %s is modified' % MixinClass.ROLES) self.assertEqual(properties.Schema.LIST, schema.type, 'type for property %s is modified' % MixinClass.ROLES) self.assertEqual('List of role assignments.', schema.description, 'description for property %s is modified' % MixinClass.ROLES) def test_role_assignment_create_user(self): expected = [ { 'role': 'role_1', 'project': 'project_1', 'domain': None }, { 'role': 'role_1', 'project': None, 'domain': 'domain_1' } ] # validate the properties self.assertEqual( expected, self.test_role_assignment.properties.get(MixinClass.ROLES)) self.test_role_assignment.create_assignment(user_id='user_1') # validate role assignment creation # role-user-domain self.roles.grant.assert_any_call( role='role_1', user='user_1', domain='domain_1') # role-user-project self.roles.grant.assert_any_call( role='role_1', user='user_1', project='project_1') def test_role_assignment_create_group(self): expected = [ { 'role': 'role_1', 'project': 'project_1', 'domain': None }, { 'role': 'role_1', 'project': None, 'domain': 'domain_1' } ] # validate the properties self.assertEqual( expected, self.test_role_assignment.properties.get(MixinClass.ROLES)) self.test_role_assignment.create_assignment(group_id='group_1') # validate role assignment creation # role-group-domain self.roles.grant.assert_any_call( role='role_1', group='group_1', domain='domain_1') # role-group-project self.roles.grant.assert_any_call( role='role_1', group='group_1', project='project_1') def test_role_assignment_update_user(self): self.test_role_assignment._stored_properties_data = { 'roles': [ { 'role': 'role_1', 'project': 'project_1' }, { 'role': 'role_1', 'domain': 'domain_1' } ] } prop_diff = { MixinClass.ROLES: [ { 'role': 'role_2', 'project': 'project_1' }, { 'role': 'role_2', 'domain': 'domain_1' } ] } self.test_role_assignment.update_assignment( user_id='user_1', prop_diff=prop_diff) # Add role2-project1-domain1 # role-user-domain self.roles.grant.assert_any_call( role='role_2', user='user_1', domain='domain_1') # role-user-project self.roles.grant.assert_any_call( role='role_2', user='user_1', project='project_1') # Remove role1-project1-domain1 # role-user-domain self.roles.revoke.assert_any_call( role='role_1', user='user_1', domain='domain_1') # role-user-project self.roles.revoke.assert_any_call( role='role_1', user='user_1', project='project_1') def test_role_assignment_update_group(self): self.test_role_assignment._stored_properties_data = { 'roles': [ { 'role': 'role_1', 'project': 'project_1' }, { 'role': 'role_1', 'domain': 'domain_1' } ] } prop_diff = { MixinClass.ROLES: [ { 'role': 'role_2', 'project': 'project_1' }, { 'role': 'role_2', 'domain': 'domain_1' } ] } self.test_role_assignment.update_assignment( group_id='group_1', prop_diff=prop_diff) # Add role2-project1-domain1 # role-group-domain self.roles.grant.assert_any_call( role='role_2', group='group_1', domain='domain_1') # role-group-project self.roles.grant.assert_any_call( role='role_2', group='group_1', project='project_1') # Remove role1-project1-domain1 # role-group-domain self.roles.revoke.assert_any_call( role='role_1', group='group_1', domain='domain_1') # role-group-project self.roles.revoke.assert_any_call( role='role_1', group='group_1', project='project_1') def test_role_assignment_delete_user(self): self.test_role_assignment._stored_properties_data = { 'roles': [ { 'role': 'role_1', 'project': 'project_1' }, { 'role': 'role_1', 'domain': 'domain_1' } ] } self.assertIsNone(self.test_role_assignment.delete_assignment( user_id='user_1')) # Remove role1-project1-domain1 # role-user-domain self.roles.revoke.assert_any_call( role='role_1', user='user_1', domain='domain_1') # role-user-project self.roles.revoke.assert_any_call( role='role_1', user='user_1', project='project_1') def test_role_assignment_delete_group(self): self.test_role_assignment._stored_properties_data = { 'roles': [ { 'role': 'role_1', 'project': 'project_1' }, { 'role': 'role_1', 'domain': 'domain_1' } ] } self.assertIsNone(self.test_role_assignment.delete_assignment( group_id='group_1' )) # Remove role1-project1-domain1 # role-group-domain self.roles.revoke.assert_any_call( role='role_1', group='group_1', domain='domain_1') # role-group-project self.roles.revoke.assert_any_call( role='role_1', group='group_1', project='project_1') def test_validate_1(self): self.test_role_assignment.properties = mock.MagicMock() # both project and domain are none self.test_role_assignment.properties.get.return_value = [ dict(role='role1')] self.assertRaises(exception.StackValidationFailed, self.test_role_assignment.validate) def test_validate_2(self): self.test_role_assignment.properties = mock.MagicMock() # both project and domain are not none self.test_role_assignment.properties.get.return_value = [ dict(role='role1', project='project1', domain='domain1') ] self.assertRaises(exception.ResourcePropertyConflict, self.test_role_assignment.validate) class KeystoneUserRoleAssignmentTest(common.HeatTestCase): role_assignment_template = copy.deepcopy(keystone_role_assignment_template) role = role_assignment_template['resources']['test_role_assignment'] role['properties']['user'] = 'user_1' role['type'] = 'OS::Keystone::UserRoleAssignment' def setUp(self): super(KeystoneUserRoleAssignmentTest, self).setUp() self.ctx = utils.dummy_context() self.stack = stack.Stack( self.ctx, 'test_stack_keystone_user_role_add', template.Template(self.role_assignment_template) ) self.test_role_assignment = self.stack['test_role_assignment'] # Mock client self.keystoneclient = mock.MagicMock() self.test_role_assignment.client = mock.MagicMock() self.test_role_assignment.client.return_value = self.keystoneclient self.roles = self.keystoneclient.roles # Mock client plugin def _side_effect(value): return value self.keystone_client_plugin = mock.MagicMock() self.keystone_client_plugin.get_user_id.side_effect = _side_effect self.keystone_client_plugin.get_domain_id.side_effect = _side_effect self.keystone_client_plugin.get_role_id.side_effect = _side_effect self.keystone_client_plugin.get_project_id.side_effect = _side_effect self.test_role_assignment.client_plugin = mock.MagicMock() (self.test_role_assignment.client_plugin. return_value) = self.keystone_client_plugin def test_user_role_assignment_handle_create(self): self.test_role_assignment.handle_create() # role-user-domain created self.roles.grant.assert_any_call( role='role_1', user='user_1', domain='domain_1') # role-user-project created self.roles.grant.assert_any_call( role='role_1', user='user_1', project='project_1') def test_user_role_assignment_handle_update(self): self.test_role_assignment._stored_properties_data = { 'roles': [ { 'role': 'role_1', 'project': 'project_1' }, { 'role': 'role_1', 'domain': 'domain_1' } ] } prop_diff = { MixinClass.ROLES: [ { 'role': 'role_2', 'project': 'project_1' }, { 'role': 'role_2', 'domain': 'domain_1' } ] } self.test_role_assignment.handle_update(json_snippet=None, tmpl_diff=None, prop_diff=prop_diff) # Add role2-project1-domain1 # role-user-domain self.roles.grant.assert_any_call( role='role_2', user='user_1', domain='domain_1') # role-user-project self.roles.grant.assert_any_call( role='role_2', user='user_1', project='project_1') # Remove role1-project1-domain1 # role-user-domain self.roles.revoke.assert_any_call( role='role_1', user='user_1', domain='domain_1') # role-user-project self.roles.revoke.assert_any_call( role='role_1', user='user_1', project='project_1') def test_user_role_assignment_handle_delete(self): self.test_role_assignment._stored_properties_data = { 'roles': [ { 'role': 'role_1', 'project': 'project_1' }, { 'role': 'role_1', 'domain': 'domain_1' } ] } self.assertIsNone(self.test_role_assignment.handle_delete()) # Remove role1-project1-domain1 # role-user-domain self.roles.revoke.assert_any_call( role='role_1', user='user_1', domain='domain_1') # role-user-project self.roles.revoke.assert_any_call( role='role_1', user='user_1', project='project_1') class KeystoneGroupRoleAssignmentTest(common.HeatTestCase): role_assignment_template = copy.deepcopy(keystone_role_assignment_template) role = role_assignment_template['resources']['test_role_assignment'] role['properties']['group'] = 'group_1' role['type'] = 'OS::Keystone::GroupRoleAssignment' def setUp(self): super(KeystoneGroupRoleAssignmentTest, self).setUp() self.ctx = utils.dummy_context() self.stack = stack.Stack( self.ctx, 'test_stack_keystone_group_role_add', template.Template(self.role_assignment_template) ) self.test_role_assignment = self.stack['test_role_assignment'] # Mock client self.keystoneclient = mock.MagicMock() self.test_role_assignment.client = mock.MagicMock() self.test_role_assignment.client.return_value = self.keystoneclient self.roles = self.keystoneclient.roles # Mock client plugin def _side_effect(value): return value self.keystone_client_plugin = mock.MagicMock() self.keystone_client_plugin.get_group_id.side_effect = _side_effect self.keystone_client_plugin.get_domain_id.side_effect = _side_effect self.keystone_client_plugin.get_role_id.side_effect = _side_effect self.keystone_client_plugin.get_project_id.side_effect = _side_effect self.test_role_assignment.client_plugin = mock.MagicMock() (self.test_role_assignment.client_plugin. return_value) = self.keystone_client_plugin def test_group_role_assignment_handle_create(self): self.test_role_assignment.handle_create() # role-group-domain created self.roles.grant.assert_any_call( role='role_1', group='group_1', domain='domain_1') # role-group-project created self.roles.grant.assert_any_call( role='role_1', group='group_1', project='project_1') def test_group_role_assignment_handle_update(self): self.test_role_assignment._stored_properties_data = { 'roles': [ { 'role': 'role_1', 'project': 'project_1' }, { 'role': 'role_1', 'domain': 'domain_1' } ] } prop_diff = { MixinClass.ROLES: [ { 'role': 'role_2', 'project': 'project_1' }, { 'role': 'role_2', 'domain': 'domain_1' } ] } self.test_role_assignment.handle_update(json_snippet=None, tmpl_diff=None, prop_diff=prop_diff) # Add role2-project1-domain1 # role-group-domain self.roles.grant.assert_any_call( role='role_2', group='group_1', domain='domain_1') # role-group-project self.roles.grant.assert_any_call( role='role_2', group='group_1', project='project_1') # Remove role1-project1-domain1 # role-group-domain self.roles.revoke.assert_any_call( role='role_1', group='group_1', domain='domain_1') # role-group-project self.roles.revoke.assert_any_call( role='role_1', group='group_1', project='project_1') def test_group_role_assignment_handle_delete(self): self.test_role_assignment._stored_properties_data = { 'roles': [ { 'role': 'role_1', 'project': 'project_1' }, { 'role': 'role_1', 'domain': 'domain_1' } ] } self.assertIsNone(self.test_role_assignment.handle_delete()) # Remove role1-project1-domain1 # role-group-domain self.roles.revoke.assert_any_call( role='role_1', group='group_1', domain='domain_1') # role-group-project self.roles.revoke.assert_any_call( role='role_1', group='group_1', project='project_1')