summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2017-08-23 16:42:26 +0000
committerGerrit Code Review <review@openstack.org>2017-08-23 16:42:26 +0000
commitaa4cdf1dc8050cc91bdd3a871de3edf4ff67033d (patch)
tree244c409d830d49a0dca0754ba4be9542b64d132c
parentd33ab499ed510a6dfb34a44b0a33945e7f8b0d1f (diff)
parent8cd3e258c5029a8efedab40019d6cfd3eac379f5 (diff)
downloadpython-openstackclient-aa4cdf1dc8050cc91bdd3a871de3edf4ff67033d.tar.gz
Merge "Implied Roles"
-rw-r--r--doc/source/cli/command-objects/implied_role.rst57
-rw-r--r--openstackclient/identity/v3/implied_role.py129
-rw-r--r--openstackclient/tests/unit/identity/v3/fakes.py24
-rw-r--r--openstackclient/tests/unit/identity/v3/test_implied_role.py181
-rw-r--r--releasenotes/notes/add-implied-role-0cdafb131fbd7453.yaml16
-rw-r--r--setup.cfg4
6 files changed, 411 insertions, 0 deletions
diff --git a/doc/source/cli/command-objects/implied_role.rst b/doc/source/cli/command-objects/implied_role.rst
new file mode 100644
index 00000000..e43c9ea3
--- /dev/null
+++ b/doc/source/cli/command-objects/implied_role.rst
@@ -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
diff --git a/openstackclient/identity/v3/implied_role.py b/openstackclient/identity/v3/implied_role.py
new file mode 100644
index 00000000..c7623389
--- /dev/null
+++ b/openstackclient/identity/v3/implied_role.py
@@ -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))
diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py
index 997bcf63..7de25152 100644
--- a/openstackclient/tests/unit/identity/v3/fakes.py
+++ b/openstackclient/tests/unit/identity/v3/fakes.py
@@ -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
diff --git a/openstackclient/tests/unit/identity/v3/test_implied_role.py b/openstackclient/tests/unit/identity/v3/test_implied_role.py
new file mode 100644
index 00000000..08273f73
--- /dev/null
+++ b/openstackclient/tests/unit/identity/v3/test_implied_role.py
@@ -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)
diff --git a/releasenotes/notes/add-implied-role-0cdafb131fbd7453.yaml b/releasenotes/notes/add-implied-role-0cdafb131fbd7453.yaml
new file mode 100644
index 00000000..8d5d76ea
--- /dev/null
+++ b/releasenotes/notes/add-implied-role-0cdafb131fbd7453.yaml
@@ -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.
diff --git a/setup.cfg b/setup.cfg
index ec91988f..b4b6827d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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