include keystonev2 role assignments

This will require that user is set with project possibly being None,
otherwise we would have to query over all combinations of users and
projects to find all the assignments, which I would say is unreasonable.

project is not required by keystoneclient, but based on what I was told
by someone in #openstack-keystone, it is highly discouraged to create
roles and users and assignments without the project being involved, so
we won't be allowing it.

Change-Id: Id5b7b9fb44a9dbecb7488eb8f0ef30773efed6d2
This commit is contained in:
Daniel Wallace 2016-01-21 13:26:00 -06:00
parent e39ad09df4
commit b2deeefc8b
5 changed files with 118 additions and 7 deletions

View File

@ -0,0 +1,3 @@
---
features:
- Implement list_role_assignments for keystone v2, using roles_for_user.

View File

@ -687,6 +687,11 @@ class RoleAssignmentList(task_manager.Task):
return client.keystone_client.role_assignments.list(**self.args)
class RolesForUser(task_manager.Task):
def main(self, client):
return client.keystone_client.roles.roles_for_user(**self.args)
class StackList(task_manager.Task):
def main(self, client):
return client.heat_client.stacks.list()

View File

@ -1286,6 +1286,31 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
"""
return _utils._get_entity(self.search_roles, name_or_id, filters)
def _keystone_v2_role_assignments(self, user, project=None,
role=None, **kwargs):
with _utils.shade_exceptions("Failed to list role assignments"):
roles = self.manager.submitTask(
_tasks.RolesForUser(user=user, tenant=project)
)
ret = []
for tmprole in roles:
if role is not None and role != tmprole.id:
continue
ret.append({
'role': {
'id': tmprole.id
},
'scope': {
'project': {
'id': project,
}
},
'user': {
'id': user,
}
})
return ret
def list_role_assignments(self, filters=None):
"""List Keystone role assignments
@ -1304,6 +1329,9 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
'user' and 'group' are mutually exclusive, as are 'domain' and
'project'.
NOTE: For keystone v2, only user, project, and role are used.
Project and user are both required in filters.
:returns: a list of dicts containing the role assignment description.
Contains the following attributes::
@ -1317,10 +1345,17 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
if not filters:
filters = {}
with _utils.shade_exceptions("Failed to list role assignments"):
assignments = self.manager.submitTask(
_tasks.RoleAssignmentList(**filters)
)
if self.cloud_config.get_api_version('identity').startswith('2'):
if filters.get('project') is None or filters.get('user') is None:
raise OpenStackCloudException(
"Must provide project and user for keystone v2"
)
assignments = self._keystone_v2_role_assignments(**filters)
else:
with _utils.shade_exceptions("Failed to list role assignments"):
assignments = self.manager.submitTask(
_tasks.RoleAssignmentList(**filters)
)
return _utils.normalize_role_assignments(assignments)
def create_flavor(self, name, ram, vcpus, disk, flavorid="auto",

View File

@ -89,3 +89,11 @@ class TestIdentity(base.TestCase):
assignments = self.cloud.list_role_assignments()
self.assertIsInstance(assignments, list)
self.assertTrue(len(assignments) > 0)
def test_list_role_assignments_v2(self):
user = self.cloud.get_user('demo')
project = self.cloud.get_project('demo')
assignments = self.cloud.list_role_assignments(
filters={'user': user.id, 'project': project.id})
self.assertIsInstance(assignments, list)
self.assertTrue(len(assignments) > 0)

View File

@ -14,6 +14,7 @@
import mock
import testtools
import os_client_config as occ
import shade
from shade import meta
from shade import _utils
@ -82,8 +83,10 @@ class TestIdentityRoles(base.TestCase):
self.assertTrue(self.cloud.delete_role('1234'))
self.assertTrue(mock_keystone.roles.delete.called)
@mock.patch.object(occ.cloud_config.CloudConfig, 'get_api_version')
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
def test_list_role_assignments(self, mock_keystone):
def test_list_role_assignments(self, mock_keystone, mock_api_version):
mock_api_version.return_value = '3'
mock_keystone.role_assignments.list.return_value = RAW_ROLE_ASSIGNMENTS
ret = self.cloud.list_role_assignments()
mock_keystone.role_assignments.list.assert_called_once_with()
@ -92,17 +95,74 @@ class TestIdentityRoles(base.TestCase):
)
self.assertEqual(normalized_assignments, ret)
@mock.patch.object(occ.cloud_config.CloudConfig, 'get_api_version')
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
def test_list_role_assignments_filters(self, mock_keystone):
def test_list_role_assignments_filters(self, mock_keystone,
mock_api_version):
mock_api_version.return_value = '3'
params = dict(user='123', domain='456', effective=True)
self.cloud.list_role_assignments(filters=params)
mock_keystone.role_assignments.list.assert_called_once_with(**params)
@mock.patch.object(occ.cloud_config.CloudConfig, 'get_api_version')
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
def test_list_role_assignments_exception(self, mock_keystone):
def test_list_role_assignments_exception(self, mock_keystone,
mock_api_version):
mock_api_version.return_value = '3'
mock_keystone.role_assignments.list.side_effect = Exception()
with testtools.ExpectedException(
shade.OpenStackCloudException,
"Failed to list role assignments"
):
self.cloud.list_role_assignments()
@mock.patch.object(occ.cloud_config.CloudConfig, 'get_api_version')
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
def test_list_role_assignments_keystone_v2(self, mock_keystone,
mock_api_version):
fake_role = fakes.FakeRole(id='1234', name='fake_role')
mock_api_version.return_value = '2.0'
mock_keystone.roles.roles_for_user.return_value = [fake_role]
ret = self.cloud.list_role_assignments(filters={'user': '2222',
'project': '3333'})
self.assertEqual(ret, [{'id': fake_role.id,
'project': '3333',
'user': '2222'}])
@mock.patch.object(occ.cloud_config.CloudConfig, 'get_api_version')
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
def test_list_role_assignments_keystone_v2_with_role(self, mock_keystone,
mock_api_version):
fake_role1 = fakes.FakeRole(id='1234', name='fake_role')
fake_role2 = fakes.FakeRole(id='4321', name='fake_role')
mock_api_version.return_value = '2.0'
mock_keystone.roles.roles_for_user.return_value = [fake_role1,
fake_role2]
ret = self.cloud.list_role_assignments(filters={'role': fake_role1.id,
'user': '2222',
'project': '3333'})
self.assertEqual(ret, [{'id': fake_role1.id,
'project': '3333',
'user': '2222'}])
@mock.patch.object(occ.cloud_config.CloudConfig, 'get_api_version')
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
def test_list_role_assignments_exception_v2(self, mock_keystone,
mock_api_version):
mock_api_version.return_value = '2.0'
with testtools.ExpectedException(
shade.OpenStackCloudException,
"Must provide project and user for keystone v2"
):
self.cloud.list_role_assignments()
@mock.patch.object(occ.cloud_config.CloudConfig, 'get_api_version')
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
def test_list_role_assignments_exception_v2_no_project(self, mock_keystone,
mock_api_version):
mock_api_version.return_value = '2.0'
with testtools.ExpectedException(
shade.OpenStackCloudException,
"Must provide project and user for keystone v2"
):
self.cloud.list_role_assignments(filters={'user': '12345'})