Merge "Add support for querying role assignments."
This commit is contained in:
commit
2486cae218
@ -677,6 +677,11 @@ class RoleDelete(task_manager.Task):
|
||||
return client.keystone_client.roles.delete(**self.args)
|
||||
|
||||
|
||||
class RoleAssignmentList(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.role_assignments.list(**self.args)
|
||||
|
||||
|
||||
class StackList(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.heat_client.stacks.list()
|
||||
|
@ -363,6 +363,56 @@ def normalize_groups(domains):
|
||||
return meta.obj_list_to_dict(ret)
|
||||
|
||||
|
||||
def normalize_role_assignments(assignments):
|
||||
"""Put role_assignments into a form that works with search/get interface.
|
||||
|
||||
Role assignments have the structure::
|
||||
|
||||
[
|
||||
{
|
||||
"role": {
|
||||
"id": "--role-id--"
|
||||
},
|
||||
"scope": {
|
||||
"domain": {
|
||||
"id": "--domain-id--"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"id": "--user-id--"
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
Which is hard to work with in the rest of our interface. Map this to be::
|
||||
|
||||
[
|
||||
{
|
||||
"id": "--role-id--",
|
||||
"domain": "--domain-id--",
|
||||
"user": "--user-id--",
|
||||
}
|
||||
]
|
||||
|
||||
Scope can be "domain" or "project" and "user" can also be "group".
|
||||
|
||||
:param list assignments: A list of dictionaries of role assignments.
|
||||
|
||||
:returns: A list of flattened/normalized role assignment dicts.
|
||||
"""
|
||||
new_assignments = []
|
||||
for assignment in assignments:
|
||||
new_val = {'id': assignment['role']['id']}
|
||||
for scope in ('project', 'domain'):
|
||||
if scope in assignment['scope']:
|
||||
new_val[scope] = assignment['scope'][scope]['id']
|
||||
for assignee in ('user', 'group'):
|
||||
if assignee in assignment:
|
||||
new_val[assignee] = assignment[assignee]['id']
|
||||
new_assignments.append(new_val)
|
||||
return new_assignments
|
||||
|
||||
|
||||
def valid_kwargs(*valid_args):
|
||||
# This decorator checks if argument passed as **kwargs to a function are
|
||||
# present in valid_args.
|
||||
|
@ -1286,6 +1286,43 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
"""
|
||||
return _utils._get_entity(self.search_roles, name_or_id, filters)
|
||||
|
||||
def list_role_assignments(self, filters=None):
|
||||
"""List Keystone role assignments
|
||||
|
||||
:param dict filters: Dict of filter conditions. Acceptable keys are::
|
||||
|
||||
- 'user' (string) - User ID to be used as query filter.
|
||||
- 'group' (string) - Group ID to be used as query filter.
|
||||
- 'project' (string) - Project ID to be used as query filter.
|
||||
- 'domain' (string) - Domain ID to be used as query filter.
|
||||
- 'role' (string) - Role ID to be used as query filter.
|
||||
- 'os_inherit_extension_inherited_to' (string) - Return inherited
|
||||
role assignments for either 'projects' or 'domains'
|
||||
- 'effective' (boolean) - Return effective role assignments.
|
||||
- 'include_subtree' (boolean) - Include subtree
|
||||
|
||||
'user' and 'group' are mutually exclusive, as are 'domain' and
|
||||
'project'.
|
||||
|
||||
:returns: a list of dicts containing the role assignment description.
|
||||
Contains the following attributes::
|
||||
|
||||
- id: <role id>
|
||||
- user|group: <user or group id>
|
||||
- project|domain: <project or domain id>
|
||||
|
||||
:raises: ``OpenStackCloudException``: if something goes wrong during
|
||||
the openstack API call.
|
||||
"""
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
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",
|
||||
ephemeral=0, swap=0, rxtx_factor=1.0, is_public=True):
|
||||
"""Create a new flavor.
|
||||
|
@ -79,3 +79,13 @@ class TestIdentity(base.TestCase):
|
||||
role = self.cloud.create_role(role_name)
|
||||
self.assertIsNotNone(role)
|
||||
self.assertTrue(self.cloud.delete_role(role_name))
|
||||
|
||||
# TODO(Shrews): Once we can support assigning roles within shade, we
|
||||
# need to make this test a little more specific, and add more for testing
|
||||
# filtering functionality.
|
||||
def test_list_role_assignments(self):
|
||||
if self.cloud.cloud_config.get_api_version('identity') in ('2', '2.0'):
|
||||
self.skipTest("Identity service does not support role assignments")
|
||||
assignments = self.cloud.list_role_assignments()
|
||||
self.assertIsInstance(assignments, list)
|
||||
self.assertTrue(len(assignments) > 0)
|
||||
|
@ -12,13 +12,31 @@
|
||||
# limitations under the License.
|
||||
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
import shade
|
||||
from shade import meta
|
||||
from shade import _utils
|
||||
from shade.tests.unit import base
|
||||
from shade.tests import fakes
|
||||
|
||||
|
||||
RAW_ROLE_ASSIGNMENTS = [
|
||||
{
|
||||
"links": {"assignment": "http://example"},
|
||||
"role": {"id": "123456"},
|
||||
"scope": {"domain": {"id": "161718"}},
|
||||
"user": {"id": "313233"}
|
||||
},
|
||||
{
|
||||
"links": {"assignment": "http://example"},
|
||||
"group": {"id": "101112"},
|
||||
"role": {"id": "123456"},
|
||||
"scope": {"project": {"id": "456789"}}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class TestIdentityRoles(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -63,3 +81,28 @@ class TestIdentityRoles(base.TestCase):
|
||||
mock_get.return_value = meta.obj_to_dict(role_obj)
|
||||
self.assertTrue(self.cloud.delete_role('1234'))
|
||||
self.assertTrue(mock_keystone.roles.delete.called)
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
|
||||
def test_list_role_assignments(self, mock_keystone):
|
||||
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()
|
||||
normalized_assignments = _utils.normalize_role_assignments(
|
||||
RAW_ROLE_ASSIGNMENTS
|
||||
)
|
||||
self.assertEqual(normalized_assignments, ret)
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
|
||||
def test_list_role_assignments_filters(self, mock_keystone):
|
||||
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(shade.OpenStackCloud, 'keystone_client')
|
||||
def test_list_role_assignments_exception(self, mock_keystone):
|
||||
mock_keystone.role_assignments.list.side_effect = Exception()
|
||||
with testtools.ExpectedException(
|
||||
shade.OpenStackCloudException,
|
||||
"Failed to list role assignments"
|
||||
):
|
||||
self.cloud.list_role_assignments()
|
||||
|
Loading…
Reference in New Issue
Block a user