Merge "Implied Roles"
This commit is contained in:
commit
aa4cdf1dc8
57
doc/source/cli/command-objects/implied_role.rst
Normal file
57
doc/source/cli/command-objects/implied_role.rst
Normal file
@ -0,0 +1,57 @@
|
||||
============
|
||||
implied role
|
||||
============
|
||||
|
||||
Identity v3
|
||||
|
||||
|
||||
implied role create
|
||||
-------------------
|
||||
|
||||
Creates an association between prior and implied roles
|
||||
|
||||
.. program:: implied role create
|
||||
.. code:: bash
|
||||
|
||||
openstack implied role create
|
||||
<role>
|
||||
--implied-role <role>
|
||||
|
||||
.. option:: <role>
|
||||
|
||||
Prior role <role> (name or ID) implies another role
|
||||
|
||||
.. option:: --implied-role <role>
|
||||
|
||||
<role> (name or ID) implied by another role
|
||||
|
||||
|
||||
implied role delete
|
||||
-------------------
|
||||
|
||||
Deletes an association between prior and implied roles
|
||||
|
||||
.. program:: implied role delete
|
||||
.. code:: bash
|
||||
|
||||
openstack implied role delete
|
||||
<role>
|
||||
--implied-role <role>
|
||||
|
||||
.. option:: <role>
|
||||
|
||||
Prior role <role> (name or ID) implies another role
|
||||
|
||||
.. option:: --implied-role <role>
|
||||
|
||||
<role> (name or ID) implied by another role
|
||||
|
||||
implied role list
|
||||
-----------------
|
||||
|
||||
List implied roles
|
||||
|
||||
.. program:: implied role list
|
||||
.. code:: bash
|
||||
|
||||
openstack implied role list
|
129
openstackclient/identity/v3/implied_role.py
Normal file
129
openstackclient/identity/v3/implied_role.py
Normal file
@ -0,0 +1,129 @@
|
||||
# Copyright 2012-2013 OpenStack Foundation
|
||||
#
|
||||
# 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 Implied Role action implementations"""
|
||||
|
||||
import logging
|
||||
|
||||
from osc_lib.command import command
|
||||
import six
|
||||
|
||||
from openstackclient.i18n import _
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_role_ids(identity_client, parsed_args):
|
||||
"""Return prior and implied role id(s)
|
||||
|
||||
If prior and implied role id(s) are retrievable from identity
|
||||
client, return tuple containing them.
|
||||
"""
|
||||
role_id = None
|
||||
implied_role_id = None
|
||||
|
||||
roles = identity_client.roles.list()
|
||||
|
||||
for role in roles:
|
||||
role_id_or_name = (role.name, role.id)
|
||||
|
||||
if parsed_args.role in role_id_or_name:
|
||||
role_id = role.id
|
||||
elif parsed_args.implied_role in role_id_or_name:
|
||||
implied_role_id = role.id
|
||||
|
||||
return (role_id, implied_role_id)
|
||||
|
||||
|
||||
class CreateImpliedRole(command.ShowOne):
|
||||
|
||||
_description = _("Creates an association between prior and implied roles")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateImpliedRole, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'role',
|
||||
metavar='<role>',
|
||||
help=_('Role (name or ID) that implies another role'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--implied-role',
|
||||
metavar='<role>',
|
||||
help='<role> (name or ID) implied by another role',
|
||||
required=True,
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
identity_client = self.app.client_manager.identity
|
||||
(prior_role_id, implied_role_id) = _get_role_ids(
|
||||
identity_client, parsed_args)
|
||||
response = identity_client.roles.create_implied(
|
||||
prior_role_id, implied_role_id)
|
||||
response._info.pop('links', None)
|
||||
return zip(*sorted([(k, v['id'])
|
||||
for k, v in six.iteritems(response._info)]))
|
||||
|
||||
|
||||
class DeleteImpliedRole(command.Command):
|
||||
|
||||
_description = _("Deletes an association between prior and implied roles")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteImpliedRole, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'role',
|
||||
metavar='<role>',
|
||||
help=_('Role (name or ID) that implies another role'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--implied-role',
|
||||
metavar='<role>',
|
||||
help='<role> (name or ID) implied by another role',
|
||||
required=True,
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
identity_client = self.app.client_manager.identity
|
||||
(prior_role_id, implied_role_id) = _get_role_ids(
|
||||
identity_client, parsed_args)
|
||||
identity_client.roles.delete_implied(
|
||||
prior_role_id, implied_role_id)
|
||||
|
||||
|
||||
class ListImpliedRole(command.Lister):
|
||||
|
||||
_description = _("List implied roles")
|
||||
_COLUMNS = ['Prior Role ID', 'Prior Role Name',
|
||||
'Implied Role ID', 'Implied Role Name']
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListImpliedRole, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
def _list_implied(response):
|
||||
for rule in response:
|
||||
for implies in rule.implies:
|
||||
yield (rule.prior_role['id'],
|
||||
rule.prior_role['name'],
|
||||
implies['id'],
|
||||
implies['name'])
|
||||
|
||||
identity_client = self.app.client_manager.identity
|
||||
response = identity_client.roles.list_inference_roles()
|
||||
return (self._COLUMNS, _list_implied(response))
|
@ -184,6 +184,8 @@ ROLE_2 = {
|
||||
'links': base_url + 'roles/' + 'r2',
|
||||
}
|
||||
|
||||
ROLES = [ROLE, ROLE_2]
|
||||
|
||||
service_id = 's-123'
|
||||
service_name = 'Texaco'
|
||||
service_type = 'gas'
|
||||
@ -968,3 +970,25 @@ class FakeRoleAssignment(object):
|
||||
info=copy.deepcopy(role_assignment_info), loaded=True)
|
||||
|
||||
return role_assignment
|
||||
|
||||
|
||||
class FakeImpliedRoleResponse(object):
|
||||
"""Fake one or more role assignment."""
|
||||
def __init__(self, prior_role, implied_roles):
|
||||
self.prior_role = prior_role
|
||||
self.implies = [role for role in implied_roles]
|
||||
|
||||
@staticmethod
|
||||
def create_list():
|
||||
"""Create a fake implied role list response.
|
||||
|
||||
:return:
|
||||
A list of FakeImpliedRoleResponse objects
|
||||
"""
|
||||
|
||||
# set default attributes.
|
||||
implied_roles = [
|
||||
FakeImpliedRoleResponse(ROLES[0], [ROLES[1]])
|
||||
]
|
||||
|
||||
return implied_roles
|
||||
|
181
openstackclient/tests/unit/identity/v3/test_implied_role.py
Normal file
181
openstackclient/tests/unit/identity/v3/test_implied_role.py
Normal file
@ -0,0 +1,181 @@
|
||||
# Copyright 2013 Nebula Inc.
|
||||
#
|
||||
# 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 implied_role
|
||||
from openstackclient.tests.unit import fakes
|
||||
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
|
||||
|
||||
|
||||
class TestRole(identity_fakes.TestIdentityv3):
|
||||
|
||||
def setUp(self):
|
||||
super(TestRole, 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 UserManager 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()
|
||||
|
||||
def _is_inheritance_testcase(self):
|
||||
return False
|
||||
|
||||
|
||||
class TestImpliedRoleCreate(TestRole):
|
||||
|
||||
def setUp(self):
|
||||
super(TestImpliedRoleCreate, self).setUp()
|
||||
|
||||
self.roles_mock.list.return_value = [
|
||||
fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLES[0]),
|
||||
loaded=True,
|
||||
),
|
||||
fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLES[1]),
|
||||
loaded=True,
|
||||
),
|
||||
]
|
||||
|
||||
self.roles_mock.create_implied.return_value = fakes.FakeResource(
|
||||
None,
|
||||
{'prior_role': copy.deepcopy(identity_fakes.ROLES[0]),
|
||||
'implied': copy.deepcopy(identity_fakes.ROLES[1]), },
|
||||
loaded=True,
|
||||
)
|
||||
|
||||
self.cmd = implied_role.CreateImpliedRole(self.app, None)
|
||||
|
||||
def test_implied_role_create(self):
|
||||
|
||||
arglist = [
|
||||
identity_fakes.ROLES[0]['id'],
|
||||
'--implied-role', identity_fakes.ROLES[1]['id'],
|
||||
]
|
||||
verifylist = [
|
||||
('role', identity_fakes.ROLES[0]['id']),
|
||||
('implied_role', identity_fakes.ROLES[1]['id']),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
# In base command class ShowOne in cliff, abstract method take_action()
|
||||
# returns a two-part tuple with a tuple of column names and a tuple of
|
||||
# data to be shown.
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
# RoleManager.create_implied(prior, implied)
|
||||
self.roles_mock.create_implied.assert_called_with(
|
||||
identity_fakes.ROLES[0]['id'],
|
||||
identity_fakes.ROLES[1]['id']
|
||||
)
|
||||
|
||||
collist = ('implied', 'prior_role')
|
||||
self.assertEqual(collist, columns)
|
||||
datalist = (
|
||||
identity_fakes.ROLES[1]['id'],
|
||||
identity_fakes.ROLES[0]['id']
|
||||
)
|
||||
self.assertEqual(datalist, data)
|
||||
|
||||
|
||||
class TestImpliedRoleDelete(TestRole):
|
||||
|
||||
def setUp(self):
|
||||
super(TestImpliedRoleDelete, self).setUp()
|
||||
|
||||
self.roles_mock.list.return_value = [
|
||||
fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLES[0]),
|
||||
loaded=True,
|
||||
),
|
||||
fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLES[1]),
|
||||
loaded=True,
|
||||
),
|
||||
]
|
||||
|
||||
self.roles_mock.delete_implied.return_value = fakes.FakeResource(
|
||||
None,
|
||||
{'prior-role': copy.deepcopy(identity_fakes.ROLES[0]),
|
||||
'implied': copy.deepcopy(identity_fakes.ROLES[1]), },
|
||||
loaded=True,
|
||||
)
|
||||
|
||||
self.cmd = implied_role.DeleteImpliedRole(self.app, None)
|
||||
|
||||
def test_implied_role_delete(self):
|
||||
arglist = [
|
||||
identity_fakes.ROLES[0]['id'],
|
||||
'--implied-role', identity_fakes.ROLES[1]['id'],
|
||||
]
|
||||
verifylist = [
|
||||
('role', identity_fakes.ROLES[0]['id']),
|
||||
('implied_role', identity_fakes.ROLES[1]['id']),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.roles_mock.delete_implied.assert_called_with(
|
||||
identity_fakes.ROLES[0]['id'],
|
||||
identity_fakes.ROLES[1]['id']
|
||||
)
|
||||
|
||||
|
||||
class TestImpliedRoleList(TestRole):
|
||||
|
||||
def setUp(self):
|
||||
super(TestImpliedRoleList, self).setUp()
|
||||
|
||||
self.roles_mock.list_inference_roles.return_value = (
|
||||
identity_fakes.FakeImpliedRoleResponse.create_list())
|
||||
|
||||
self.cmd = implied_role.ListImpliedRole(self.app, None)
|
||||
|
||||
def test_implied_role_list(self):
|
||||
arglist = []
|
||||
verifylist = []
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.roles_mock.list_inference_roles.assert_called_with()
|
||||
|
||||
collist = ['Prior Role ID', 'Prior Role Name',
|
||||
'Implied Role ID', 'Implied Role Name']
|
||||
self.assertEqual(collist, columns)
|
||||
datalist = [
|
||||
(identity_fakes.ROLES[0]['id'], identity_fakes.ROLES[0]['name'],
|
||||
identity_fakes.ROLES[1]['id'], identity_fakes.ROLES[1]['name'])
|
||||
]
|
||||
x = [d for d in data]
|
||||
self.assertEqual(datalist, x)
|
16
releasenotes/notes/add-implied-role-0cdafb131fbd7453.yaml
Normal file
16
releasenotes/notes/add-implied-role-0cdafb131fbd7453.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support for creating, deleting, and listing implied roles has been added.
|
||||
This allows users to create an inference rule between two roles. The
|
||||
first, called the prior role is the role explicitly assigned to an
|
||||
individual. The second, called the implied role, is one that the user
|
||||
is assgined implicitly. Additionally, these rules can be chained, such
|
||||
that an implied role from the first inference rule can be the implied role
|
||||
in the second. Thus one explicitly assigned role can lead to multiple
|
||||
implied roles.
|
||||
``implied role create <role> --implied-role <implied-role>`` creates an
|
||||
association between prior and implied roles.
|
||||
``implied role delete <role> --implied-role <implied-role>`` removes an
|
||||
association between prior and implied roles.
|
||||
``implied role list`` Lists all implied roles that currently exist.
|
@ -247,6 +247,10 @@ openstack.identity.v3 =
|
||||
identity_provider_set = openstackclient.identity.v3.identity_provider:SetIdentityProvider
|
||||
identity_provider_show = openstackclient.identity.v3.identity_provider:ShowIdentityProvider
|
||||
|
||||
implied_role_create = openstackclient.identity.v3.implied_role:CreateImpliedRole
|
||||
implied_role_delete = openstackclient.identity.v3.implied_role:DeleteImpliedRole
|
||||
implied_role_list = openstackclient.identity.v3.implied_role:ListImpliedRole
|
||||
|
||||
mapping_create = openstackclient.identity.v3.mapping:CreateMapping
|
||||
mapping_delete = openstackclient.identity.v3.mapping:DeleteMapping
|
||||
mapping_list = openstackclient.identity.v3.mapping:ListMapping
|
||||
|
Loading…
x
Reference in New Issue
Block a user