OpenStack Orchestration (Heat)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
heat/heat/tests/openstack/keystone/test_role_assignments.py

666 lines
22 KiB

#
# 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.clients.os.keystone import fake_keystoneclient as fake_ks
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
self.parse_assgmnts = self.test_role_assignment.parse_list_assignments
self.test_role_assignment.parse_list_assignments = mock.MagicMock()
self.test_role_assignment.parse_list_assignments.return_value = [
{'role': 'role_1',
'domain': 'domain_1',
'project': None},
{'role': 'role_1',
'project': 'project_1',
'domain': None}]
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):
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):
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_update_roles_no_change(self):
prop_diff = {}
self.test_role_assignment.update_assignment(
group_id='group_1',
prop_diff=prop_diff)
self.assertEqual(0, self.roles.grant.call_count)
self.assertEqual(0, self.roles.revoke.call_count)
self.test_role_assignment.update_assignment(
user_id='user_1',
prop_diff=prop_diff)
self.assertEqual(0, self.roles.grant.call_count)
self.assertEqual(0, self.roles.revoke.call_count)
def test_role_assignment_delete_user(self):
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.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_role_assignment_delete_removed(self):
self.test_role_assignment.parse_list_assignments.return_value = [
{'role': 'role_1',
'domain': 'domain_1',
'project': None}]
self.assertIsNone(self.test_role_assignment.delete_assignment(
user_id='user_1'))
expected = [
({'role': 'role_1', 'user': 'user_1', 'domain': 'domain_1'},)
]
self.assertItemsEqual(expected, self.roles.revoke.call_args_list)
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)
def test_empty_parse_list_assignments(self):
self.test_role_assignment.parse_list_assignments = self.parse_assgmnts
self.assertEqual([],
self.test_role_assignment.parse_list_assignments())
def test_user_parse_list_assignments(self):
self._test_parse_list_assignments('user')
def test_group_parse_list_assignments(self):
self._test_parse_list_assignments('group')
def _test_parse_list_assignments(self, entity=None):
self.test_role_assignment.parse_list_assignments = self.parse_assgmnts
dict_obj = mock.MagicMock()
dict_obj.to_dict.side_effect = [{'scope': {
'project': {'id': 'fc0fe982401643368ff2eb11d9ca70f1'}},
'role': {'id': '3b8b253648f44256a457a5073b78021d'},
entity: {'id': '4147558a763046cfb68fb870d58ef4cf'}},
{'role': {'id': '3b8b253648f44258021d6a457a5073b7'},
entity: {'id': '4147558a763046cfb68fb870d58ef4cf'}}]
self.keystoneclient.role_assignments.list.return_value = [dict_obj,
dict_obj]
kwargs = {'%s_id' % entity: '4147558a763046cfb68fb870d58ef4cf'}
list_assignments = self.test_role_assignment.parse_list_assignments(
**kwargs)
expected = [
{'role': '3b8b253648f44256a457a5073b78021d',
'project': 'fc0fe982401643368ff2eb11d9ca70f1',
'domain': None},
{'role': '3b8b253648f44258021d6a457a5073b7',
'project': None,
'domain': None},
]
self.assertEqual(expected, list_assignments)
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.Mock()
self.patchobject(resource.Resource, 'client',
return_value=fake_ks.FakeKeystoneClient(
client=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
self.test_role_assignment.parse_list_assignments = mock.MagicMock()
self.test_role_assignment.parse_list_assignments.return_value = [
{'role': 'role_1',
'domain': 'domain_1',
'project': None},
{'role': 'role_1',
'project': 'project_1',
'domain': None}]
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):
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.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')
def test_user_role_assignment_delete_user_not_found(self):
self.keystone_client_plugin.get_user_id.side_effect = [
exception.EntityNotFound]
self.assertIsNone(self.test_role_assignment.handle_delete())
self.roles.revoke.assert_not_called()
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.Mock()
self.patchobject(resource.Resource, 'client',
return_value=fake_ks.FakeKeystoneClient(
client=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
self.test_role_assignment.parse_list_assignments = mock.MagicMock()
self.test_role_assignment.parse_list_assignments.return_value = [
{'role': 'role_1',
'domain': 'domain_1',
'project': None},
{'role': 'role_1',
'project': 'project_1',
'domain': None}]
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):
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.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')
def test_group_role_assignment_delete_group_not_found(self):
self.keystone_client_plugin.get_group_id.side_effect = [
exception.EntityNotFound]
self.assertIsNone(self.test_role_assignment.handle_delete())
self.roles.revoke.assert_not_called()