Add support for domain specific roles
A role entity can now be specified as domain specific. Closes-bug: #1606105 Change-Id: I564cf3da1d61f5bfcf85be591480d2f5c8d694a0
This commit is contained in:
parent
0b91368164
commit
5eb7e626b1
@ -14,6 +14,7 @@ List role assignments
|
||||
|
||||
os role assignment list
|
||||
[--role <role>]
|
||||
[--role-domain <role-domain>]
|
||||
[--user <user>]
|
||||
[--user-domain <user-domain>]
|
||||
[--group <group>]
|
||||
@ -31,6 +32,13 @@ List role assignments
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
.. option:: --role-domain <role-domain>
|
||||
|
||||
Domain the role belongs to (name or ID).
|
||||
This can be used in case collisions between role names exist.
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
.. option:: --user <user>
|
||||
|
||||
User to filter (name or ID)
|
||||
|
@ -15,6 +15,7 @@ Add role assignment to a user or group in a project or domain
|
||||
os role add
|
||||
--domain <domain> | --project <project> [--project-domain <project-domain>]
|
||||
--user <user> [--user-domain <user-domain>] | --group <group> [--group-domain <group-domain>]
|
||||
--role-domain <role-domain>
|
||||
--inherited
|
||||
<role>
|
||||
|
||||
@ -65,6 +66,13 @@ Add role assignment to a user or group in a project or domain
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
.. option:: --role-domain <role-domain>
|
||||
|
||||
Domain the role belongs to (name or ID).
|
||||
This must be specified when the name of a domain specific role is used.
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
.. describe:: <role>
|
||||
|
||||
Role to add to <project>:<user> (name or ID)
|
||||
@ -79,8 +87,15 @@ Create new role
|
||||
|
||||
os role create
|
||||
[--or-show]
|
||||
[--domain <domain>]
|
||||
<name>
|
||||
|
||||
.. option:: --domain <domain>
|
||||
|
||||
Domain the role belongs to (name or ID).
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
.. option:: --or-show
|
||||
|
||||
Return existing role
|
||||
@ -101,11 +116,18 @@ Delete role(s)
|
||||
|
||||
os role delete
|
||||
<role> [<role> ...]
|
||||
[--domain <domain>]
|
||||
|
||||
.. describe:: <role>
|
||||
|
||||
Role to delete (name or ID)
|
||||
|
||||
.. option:: --domain <domain>
|
||||
|
||||
Domain the role belongs to (name or ID).
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
role list
|
||||
---------
|
||||
|
||||
@ -123,7 +145,8 @@ List roles
|
||||
|
||||
Filter roles by <domain> (name or ID)
|
||||
|
||||
(Deprecated, please use ``role assignment list`` instead)
|
||||
(Deprecated if being used to list assignments in conjunction with the
|
||||
``--user <user>``, option, please use ``role assignment list`` instead)
|
||||
|
||||
.. option:: --project <project>
|
||||
|
||||
@ -189,6 +212,7 @@ Remove role assignment from domain/project : user/group
|
||||
os role remove
|
||||
--domain <domain> | --project <project> [--project-domain <project-domain>]
|
||||
--user <user> [--user-domain <user-domain>] | --group <group> [--group-domain <group-domain>]
|
||||
--role-domain <role-domain>
|
||||
--inherited
|
||||
<role>
|
||||
|
||||
@ -239,6 +263,13 @@ Remove role assignment from domain/project : user/group
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
.. option:: --role-domain <role-domain>
|
||||
|
||||
Domain the role belongs to (name or ID).
|
||||
This must be specified when the name of a domain specific role is used.
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
.. describe:: <role>
|
||||
|
||||
Role to remove (name or ID)
|
||||
@ -255,12 +286,19 @@ Set role properties
|
||||
|
||||
os role set
|
||||
[--name <name>]
|
||||
[--domain <domain>]
|
||||
<role>
|
||||
|
||||
.. option:: --name <name>
|
||||
|
||||
Set role name
|
||||
|
||||
.. option:: --domain <domain>
|
||||
|
||||
Domain the role belongs to (name or ID).
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
.. describe:: <role>
|
||||
|
||||
Role to modify (name or ID)
|
||||
@ -274,8 +312,15 @@ Display role details
|
||||
.. code:: bash
|
||||
|
||||
os role show
|
||||
[--domain <domain>]
|
||||
<role>
|
||||
|
||||
.. option:: --domain <domain>
|
||||
|
||||
Domain the role belongs to (name or ID).
|
||||
|
||||
.. versionadded:: 3
|
||||
|
||||
.. describe:: <role>
|
||||
|
||||
Role to display (name or ID)
|
||||
|
@ -201,6 +201,16 @@ def add_project_domain_option_to_parser(parser):
|
||||
)
|
||||
|
||||
|
||||
def add_role_domain_option_to_parser(parser):
|
||||
parser.add_argument(
|
||||
'--role-domain',
|
||||
metavar='<role-domain>',
|
||||
help=_('Domain the role belongs to (name or ID). '
|
||||
'This must be specified when the name of a domain specific '
|
||||
'role is used.'),
|
||||
)
|
||||
|
||||
|
||||
def add_inherited_option_to_parser(parser):
|
||||
parser.add_argument(
|
||||
'--inherited',
|
||||
|
@ -109,7 +109,7 @@ def _process_identity_and_resource_options(parsed_args,
|
||||
|
||||
|
||||
class AddRole(command.Command):
|
||||
"""Adds a role to a user or group on a domain or project"""
|
||||
"""Adds a role assignment to a user or group on a domain or project"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AddRole, self).get_parser(prog_name)
|
||||
@ -119,6 +119,7 @@ class AddRole(command.Command):
|
||||
help=_('Role to add to <user> (name or ID)'),
|
||||
)
|
||||
_add_identity_and_resource_options_to_parser(parser)
|
||||
common.add_role_domain_option_to_parser(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -127,9 +128,15 @@ class AddRole(command.Command):
|
||||
if (not parsed_args.user and not parsed_args.domain
|
||||
and not parsed_args.group and not parsed_args.project):
|
||||
return
|
||||
|
||||
domain_id = None
|
||||
if parsed_args.role_domain:
|
||||
domain_id = common.find_domain(identity_client,
|
||||
parsed_args.role_domain).id
|
||||
role = utils.find_resource(
|
||||
identity_client.roles,
|
||||
parsed_args.role,
|
||||
domain_id=domain_id
|
||||
)
|
||||
|
||||
kwargs = _process_identity_and_resource_options(
|
||||
@ -153,6 +160,11 @@ class CreateRole(command.ShowOne):
|
||||
metavar='<role-name>',
|
||||
help=_('New role name'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--domain',
|
||||
metavar='<domain>',
|
||||
help=_('Domain the role belongs to (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--or-show',
|
||||
action='store_true',
|
||||
@ -163,12 +175,20 @@ class CreateRole(command.ShowOne):
|
||||
def take_action(self, parsed_args):
|
||||
identity_client = self.app.client_manager.identity
|
||||
|
||||
domain_id = None
|
||||
if parsed_args.domain:
|
||||
domain_id = common.find_domain(identity_client,
|
||||
parsed_args.domain).id
|
||||
|
||||
try:
|
||||
role = identity_client.roles.create(name=parsed_args.name)
|
||||
role = identity_client.roles.create(
|
||||
name=parsed_args.name, domain=domain_id)
|
||||
|
||||
except ks_exc.Conflict:
|
||||
if parsed_args.or_show:
|
||||
role = utils.find_resource(identity_client.roles,
|
||||
parsed_args.name)
|
||||
parsed_args.name,
|
||||
domain_id=domain_id)
|
||||
LOG.info(_('Returning existing role %s'), role.name)
|
||||
else:
|
||||
raise
|
||||
@ -188,15 +208,26 @@ class DeleteRole(command.Command):
|
||||
nargs="+",
|
||||
help=_('Role(s) to delete (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--domain',
|
||||
metavar='<domain>',
|
||||
help=_('Domain the role belongs to (name or ID)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
identity_client = self.app.client_manager.identity
|
||||
|
||||
domain_id = None
|
||||
if parsed_args.domain:
|
||||
domain_id = common.find_domain(identity_client,
|
||||
parsed_args.domain).id
|
||||
|
||||
for role in parsed_args.roles:
|
||||
role_obj = utils.find_resource(
|
||||
identity_client.roles,
|
||||
role,
|
||||
domain_id=domain_id
|
||||
)
|
||||
identity_client.roles.delete(role_obj.id)
|
||||
|
||||
@ -206,6 +237,18 @@ class ListRole(command.Lister):
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListRole, self).get_parser(prog_name)
|
||||
|
||||
# TODO(henry-nash): The use of the List Role command to list
|
||||
# assignments (as well as roles) has been deprecated. In order
|
||||
# to support domain specific roles, we are overriding the domain
|
||||
# option to allow specification of the domain for the role. This does
|
||||
# not conflict with any existing commands, since for the deprecated
|
||||
# assignments listing you were never allowed to only specify a domain
|
||||
# (you also needed to specify a user).
|
||||
#
|
||||
# Once we have removed the deprecated options entirely, we must
|
||||
# replace the call to _add_identity_and_resource_options_to_parser()
|
||||
# below with just adding the domain option into the parser.
|
||||
_add_identity_and_resource_options_to_parser(parser)
|
||||
return parser
|
||||
|
||||
@ -239,8 +282,14 @@ class ListRole(command.Lister):
|
||||
|
||||
# no user or group specified, list all roles in the system
|
||||
if not parsed_args.user and not parsed_args.group:
|
||||
columns = ('ID', 'Name')
|
||||
data = identity_client.roles.list()
|
||||
if not parsed_args.domain:
|
||||
columns = ('ID', 'Name')
|
||||
data = identity_client.roles.list()
|
||||
else:
|
||||
columns = ('ID', 'Name', 'Domain')
|
||||
data = identity_client.roles.list(domain_id=domain.id)
|
||||
for role in data:
|
||||
role.domain = domain.name
|
||||
elif parsed_args.user and parsed_args.domain:
|
||||
columns = ('ID', 'Name', 'Domain', 'User')
|
||||
data = identity_client.roles.list(
|
||||
@ -322,7 +371,7 @@ class ListRole(command.Lister):
|
||||
|
||||
|
||||
class RemoveRole(command.Command):
|
||||
"""Remove role from domain/project : user/group"""
|
||||
"""Removes a role assignment from domain/project : user/group"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemoveRole, self).get_parser(prog_name)
|
||||
@ -332,6 +381,8 @@ class RemoveRole(command.Command):
|
||||
help=_('Role to remove (name or ID)'),
|
||||
)
|
||||
_add_identity_and_resource_options_to_parser(parser)
|
||||
common.add_role_domain_option_to_parser(parser)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -342,9 +393,15 @@ class RemoveRole(command.Command):
|
||||
sys.stderr.write(_("Incorrect set of arguments provided. "
|
||||
"See openstack --help for more details\n"))
|
||||
return
|
||||
|
||||
domain_id = None
|
||||
if parsed_args.role_domain:
|
||||
domain_id = common.find_domain(identity_client,
|
||||
parsed_args.role_domain).id
|
||||
role = utils.find_resource(
|
||||
identity_client.roles,
|
||||
parsed_args.role,
|
||||
domain_id=domain_id
|
||||
)
|
||||
|
||||
kwargs = _process_identity_and_resource_options(
|
||||
@ -367,6 +424,11 @@ class SetRole(command.Command):
|
||||
metavar='<role>',
|
||||
help=_('Role to modify (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--domain',
|
||||
metavar='<domain>',
|
||||
help=_('Domain the role belongs to (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
@ -377,10 +439,14 @@ class SetRole(command.Command):
|
||||
def take_action(self, parsed_args):
|
||||
identity_client = self.app.client_manager.identity
|
||||
|
||||
role = utils.find_resource(
|
||||
identity_client.roles,
|
||||
parsed_args.role,
|
||||
)
|
||||
domain_id = None
|
||||
if parsed_args.domain:
|
||||
domain_id = common.find_domain(identity_client,
|
||||
parsed_args.domain).id
|
||||
|
||||
role = utils.find_resource(identity_client.roles,
|
||||
parsed_args.role,
|
||||
domain_id=domain_id)
|
||||
|
||||
identity_client.roles.update(role.id, name=parsed_args.name)
|
||||
|
||||
@ -395,15 +461,24 @@ class ShowRole(command.ShowOne):
|
||||
metavar='<role>',
|
||||
help=_('Role to display (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--domain',
|
||||
metavar='<domain>',
|
||||
help=_('Domain the role belongs to (name or ID)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
identity_client = self.app.client_manager.identity
|
||||
|
||||
role = utils.find_resource(
|
||||
identity_client.roles,
|
||||
parsed_args.role,
|
||||
)
|
||||
domain_id = None
|
||||
if parsed_args.domain:
|
||||
domain_id = common.find_domain(identity_client,
|
||||
parsed_args.domain).id
|
||||
|
||||
role = utils.find_resource(identity_client.roles,
|
||||
parsed_args.role,
|
||||
domain_id=domain_id)
|
||||
|
||||
role._info.pop('links')
|
||||
return zip(*sorted(six.iteritems(role._info)))
|
||||
|
@ -36,6 +36,7 @@ class ListRoleAssignment(command.Lister):
|
||||
metavar='<role>',
|
||||
help=_('Role to filter (name or ID)'),
|
||||
)
|
||||
common.add_role_domain_option_to_parser(parser)
|
||||
parser.add_argument(
|
||||
'--names',
|
||||
action="store_true",
|
||||
@ -91,10 +92,15 @@ class ListRoleAssignment(command.Lister):
|
||||
auth_ref = self.app.client_manager.auth_ref
|
||||
|
||||
role = None
|
||||
role_domain_id = None
|
||||
if parsed_args.role_domain:
|
||||
role_domain_id = common.find_domain(identity_client,
|
||||
parsed_args.role_domain).id
|
||||
if parsed_args.role:
|
||||
role = utils.find_resource(
|
||||
identity_client.roles,
|
||||
parsed_args.role,
|
||||
domain_id=role_domain_id
|
||||
)
|
||||
|
||||
user = None
|
||||
@ -205,6 +211,12 @@ class ListRoleAssignment(command.Lister):
|
||||
|
||||
if hasattr(assignment, 'role'):
|
||||
if include_names:
|
||||
# TODO(henry-nash): If this is a domain specific role it
|
||||
# would be good show this as role@domain, although this
|
||||
# domain info is not yet included in the response from the
|
||||
# server. Although we could get it by re-reading the role
|
||||
# from the ID, let's wait until the server does the right
|
||||
# thing.
|
||||
setattr(assignment, 'role', assignment.role['name'])
|
||||
else:
|
||||
setattr(assignment, 'role', assignment.role['id'])
|
||||
|
@ -173,9 +173,17 @@ role_name = 'roller'
|
||||
ROLE = {
|
||||
'id': role_id,
|
||||
'name': role_name,
|
||||
'domain': None,
|
||||
'links': base_url + 'roles/' + role_id,
|
||||
}
|
||||
|
||||
ROLE_2 = {
|
||||
'id': 'r2',
|
||||
'name': 'Rolls Royce',
|
||||
'domain': domain_id,
|
||||
'links': base_url + 'roles/' + 'r2',
|
||||
}
|
||||
|
||||
service_id = 's-123'
|
||||
service_name = 'Texaco'
|
||||
service_type = 'gas'
|
||||
@ -358,6 +366,12 @@ ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID = {
|
||||
'role': {'id': role_id},
|
||||
}
|
||||
|
||||
ASSIGNMENT_WITH_DOMAIN_ROLE = {
|
||||
'scope': {'domain': {'id': domain_id}},
|
||||
'user': {'id': user_id},
|
||||
'role': {'id': ROLE_2['id']},
|
||||
}
|
||||
|
||||
ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INCLUDE_NAMES = {
|
||||
'scope': {
|
||||
'domain': {'id': domain_id,
|
||||
|
@ -230,6 +230,45 @@ class TestRoleAdd(TestRole):
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_role_add_domain_role_on_user_project(self):
|
||||
self.roles_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLE_2),
|
||||
loaded=True,
|
||||
)
|
||||
arglist = [
|
||||
'--user', identity_fakes.user_name,
|
||||
'--project', identity_fakes.project_name,
|
||||
'--role-domain', identity_fakes.domain_name,
|
||||
identity_fakes.ROLE_2['name'],
|
||||
]
|
||||
if self._is_inheritance_testcase():
|
||||
arglist.append('--inherited')
|
||||
verifylist = [
|
||||
('user', identity_fakes.user_name),
|
||||
('group', None),
|
||||
('domain', None),
|
||||
('project', identity_fakes.project_name),
|
||||
('role', identity_fakes.ROLE_2['name']),
|
||||
('inherited', self._is_inheritance_testcase()),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
# Set expected values
|
||||
kwargs = {
|
||||
'user': identity_fakes.user_id,
|
||||
'project': identity_fakes.project_id,
|
||||
'os_inherit_extension_inherited': self._is_inheritance_testcase(),
|
||||
}
|
||||
# RoleManager.grant(role, user=, group=, domain=, project=)
|
||||
self.roles_mock.grant.assert_called_with(
|
||||
identity_fakes.ROLE_2['id'],
|
||||
**kwargs
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestRoleAddInherited(TestRoleAdd, TestRoleInherited):
|
||||
pass
|
||||
@ -240,6 +279,12 @@ class TestRoleCreate(TestRole):
|
||||
def setUp(self):
|
||||
super(TestRoleCreate, self).setUp()
|
||||
|
||||
self.domains_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.DOMAIN),
|
||||
loaded=True,
|
||||
)
|
||||
|
||||
self.roles_mock.create.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLE),
|
||||
@ -265,22 +310,67 @@ class TestRoleCreate(TestRole):
|
||||
|
||||
# Set expected values
|
||||
kwargs = {
|
||||
'domain': None,
|
||||
'name': identity_fakes.role_name,
|
||||
}
|
||||
|
||||
# RoleManager.create(name=)
|
||||
# RoleManager.create(name=, domain=)
|
||||
self.roles_mock.create.assert_called_with(
|
||||
**kwargs
|
||||
)
|
||||
|
||||
collist = ('id', 'name')
|
||||
collist = ('domain', 'id', 'name')
|
||||
self.assertEqual(collist, columns)
|
||||
datalist = (
|
||||
None,
|
||||
identity_fakes.role_id,
|
||||
identity_fakes.role_name,
|
||||
)
|
||||
self.assertEqual(datalist, data)
|
||||
|
||||
def test_role_create_with_domain(self):
|
||||
|
||||
self.roles_mock.create.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLE_2),
|
||||
loaded=True,
|
||||
)
|
||||
|
||||
arglist = [
|
||||
'--domain', identity_fakes.domain_name,
|
||||
identity_fakes.ROLE_2['name'],
|
||||
]
|
||||
verifylist = [
|
||||
('domain', identity_fakes.domain_name),
|
||||
('name', identity_fakes.ROLE_2['name']),
|
||||
]
|
||||
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)
|
||||
|
||||
# Set expected values
|
||||
kwargs = {
|
||||
'domain': identity_fakes.domain_id,
|
||||
'name': identity_fakes.ROLE_2['name'],
|
||||
}
|
||||
|
||||
# RoleManager.create(name=, domain=)
|
||||
self.roles_mock.create.assert_called_with(
|
||||
**kwargs
|
||||
)
|
||||
|
||||
collist = ('domain', 'id', 'name')
|
||||
self.assertEqual(collist, columns)
|
||||
datalist = (
|
||||
identity_fakes.domain_id,
|
||||
identity_fakes.ROLE_2['id'],
|
||||
identity_fakes.ROLE_2['name'],
|
||||
)
|
||||
self.assertEqual(datalist, data)
|
||||
|
||||
|
||||
class TestRoleDelete(TestRole):
|
||||
|
||||
@ -313,6 +403,31 @@ class TestRoleDelete(TestRole):
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_role_delete_with_domain(self):
|
||||
self.roles_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLE_2),
|
||||
loaded=True,
|
||||
)
|
||||
self.roles_mock.delete.return_value = None
|
||||
|
||||
arglist = [
|
||||
'--domain', identity_fakes.domain_name,
|
||||
identity_fakes.ROLE_2['name'],
|
||||
]
|
||||
verifylist = [
|
||||
('roles', [identity_fakes.ROLE_2['name']]),
|
||||
('domain', identity_fakes.domain_name),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.roles_mock.delete.assert_called_with(
|
||||
identity_fakes.ROLE_2['id'],
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestRoleList(TestRole):
|
||||
|
||||
@ -583,6 +698,45 @@ class TestRoleList(TestRole):
|
||||
), )
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
def test_role_list_domain_role(self):
|
||||
self.roles_mock.list.return_value = [
|
||||
fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLE_2),
|
||||
loaded=True,
|
||||
),
|
||||
]
|
||||
arglist = [
|
||||
'--domain', identity_fakes.domain_name,
|
||||
]
|
||||
verifylist = [
|
||||
('domain', identity_fakes.domain_name),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
# In base command class Lister in cliff, abstract method take_action()
|
||||
# returns a tuple containing the column names and an iterable
|
||||
# containing the data to be listed.
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
# Set expected values
|
||||
kwargs = {
|
||||
'domain_id': identity_fakes.domain_id
|
||||
}
|
||||
# RoleManager.list(user=, group=, domain=, project=, **kwargs)
|
||||
self.roles_mock.list.assert_called_with(
|
||||
**kwargs
|
||||
)
|
||||
|
||||
collist = ('ID', 'Name', 'Domain')
|
||||
self.assertEqual(collist, columns)
|
||||
datalist = ((
|
||||
identity_fakes.ROLE_2['id'],
|
||||
identity_fakes.ROLE_2['name'],
|
||||
identity_fakes.domain_name,
|
||||
), )
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
|
||||
class TestRoleRemove(TestRole):
|
||||
|
||||
@ -756,6 +910,44 @@ class TestRoleRemove(TestRole):
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_role_remove_domain_role_on_group_domain(self):
|
||||
self.roles_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLE_2),
|
||||
loaded=True,
|
||||
)
|
||||
arglist = [
|
||||
'--group', identity_fakes.group_name,
|
||||
'--domain', identity_fakes.domain_name,
|
||||
identity_fakes.ROLE_2['name'],
|
||||
]
|
||||
if self._is_inheritance_testcase():
|
||||
arglist.append('--inherited')
|
||||
verifylist = [
|
||||
('user', None),
|
||||
('group', identity_fakes.group_name),
|
||||
('domain', identity_fakes.domain_name),
|
||||
('project', None),
|
||||
('role', identity_fakes.ROLE_2['name']),
|
||||
('inherited', self._is_inheritance_testcase()),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
# Set expected values
|
||||
kwargs = {
|
||||
'group': identity_fakes.group_id,
|
||||
'domain': identity_fakes.domain_id,
|
||||
'os_inherit_extension_inherited': self._is_inheritance_testcase(),
|
||||
}
|
||||
# RoleManager.revoke(role, user=, group=, domain=, project=)
|
||||
self.roles_mock.revoke.assert_called_with(
|
||||
identity_fakes.ROLE_2['id'],
|
||||
**kwargs
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestRoleSet(TestRole):
|
||||
|
||||
@ -796,6 +988,37 @@ class TestRoleSet(TestRole):
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_role_set_domain_role(self):
|
||||
self.roles_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLE_2),
|
||||
loaded=True,
|
||||
)
|
||||
arglist = [
|
||||
'--name', 'over',
|
||||
'--domain', identity_fakes.domain_name,
|
||||
identity_fakes.ROLE_2['name'],
|
||||
]
|
||||
verifylist = [
|
||||
('name', 'over'),
|
||||
('domain', identity_fakes.domain_name),
|
||||
('role', identity_fakes.ROLE_2['name']),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
# Set expected values
|
||||
kwargs = {
|
||||
'name': 'over',
|
||||
}
|
||||
# RoleManager.update(role, name=)
|
||||
self.roles_mock.update.assert_called_with(
|
||||
identity_fakes.ROLE_2['id'],
|
||||
**kwargs
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestRoleShow(TestRole):
|
||||
|
||||
@ -830,10 +1053,53 @@ class TestRoleShow(TestRole):
|
||||
identity_fakes.role_name,
|
||||
)
|
||||
|
||||
collist = ('id', 'name')
|
||||
collist = ('domain', 'id', 'name')
|
||||
self.assertEqual(collist, columns)
|
||||
datalist = (
|
||||
None,
|
||||
identity_fakes.role_id,
|
||||
identity_fakes.role_name,
|
||||
)
|
||||
self.assertEqual(datalist, data)
|
||||
|
||||
def test_role_show_domain_role(self):
|
||||
self.roles_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.ROLE_2),
|
||||
loaded=True,
|
||||
)
|
||||
arglist = [
|
||||
'--domain', identity_fakes.domain_name,
|
||||
identity_fakes.ROLE_2['name'],
|
||||
]
|
||||
verifylist = [
|
||||
('domain', identity_fakes.domain_name),
|
||||
('role', identity_fakes.ROLE_2['name']),
|
||||
]
|
||||
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.get(role). This is called from utils.find_resource().
|
||||
# In fact, the current implementation calls the get(role) first with
|
||||
# just the name, then with the name+domain_id. So technically we should
|
||||
# mock this out with a call list, with the first call returning None
|
||||
# and the second returning the object. However, if we did that we are
|
||||
# then just testing the current sequencing within the utils method, and
|
||||
# would become brittle to changes within that method. Hence we just
|
||||
# check for the first call which is always lookup by name.
|
||||
self.roles_mock.get.assert_called_with(
|
||||
identity_fakes.ROLE_2['name'],
|
||||
)
|
||||
|
||||
collist = ('domain', 'id', 'name')
|
||||
self.assertEqual(collist, columns)
|
||||
datalist = (
|
||||
identity_fakes.domain_id,
|
||||
identity_fakes.ROLE_2['id'],
|
||||
identity_fakes.ROLE_2['name'],
|
||||
)
|
||||
self.assertEqual(datalist, data)
|
||||
|
@ -628,3 +628,56 @@ class TestRoleAssignmentList(TestRoleAssignment):
|
||||
False
|
||||
),)
|
||||
self.assertEqual(tuple(data), datalist1)
|
||||
|
||||
def test_role_assignment_list_domain_role(self):
|
||||
|
||||
self.role_assignments_mock.list.return_value = [
|
||||
fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(
|
||||
identity_fakes.ASSIGNMENT_WITH_DOMAIN_ROLE),
|
||||
loaded=True,
|
||||
),
|
||||
]
|
||||
|
||||
arglist = [
|
||||
'--role', identity_fakes.ROLE_2['name'],
|
||||
'--role-domain', identity_fakes.domain_name
|
||||
]
|
||||
verifylist = [
|
||||
('user', None),
|
||||
('group', None),
|
||||
('domain', None),
|
||||
('project', None),
|
||||
('role', identity_fakes.ROLE_2['name']),
|
||||
('effective', False),
|
||||
('inherited', False),
|
||||
('names', False),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
# In base command class Lister in cliff, abstract method take_action()
|
||||
# returns a tuple containing the column names and an iterable
|
||||
# containing the data to be listed.
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.role_assignments_mock.list.assert_called_with(
|
||||
domain=None,
|
||||
user=None,
|
||||
group=None,
|
||||
project=None,
|
||||
role=self.roles_mock.get(),
|
||||
effective=False,
|
||||
os_inherit_extension_inherited_to=None,
|
||||
include_names=False)
|
||||
|
||||
self.assertEqual(self.columns, columns)
|
||||
datalist = ((
|
||||
identity_fakes.ROLE_2['id'],
|
||||
identity_fakes.user_id,
|
||||
'',
|
||||
'',
|
||||
identity_fakes.domain_id,
|
||||
False
|
||||
),)
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
4
releasenotes/notes/bug-1606105-ca06b230e22ab5c6.yaml
Normal file
4
releasenotes/notes/bug-1606105-ca06b230e22ab5c6.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Add support for domain specific roles in ``role`` and``role assignment`` commands.
|
||||
[Bug `1606105 <https://bugs.launchpad.net/python-openstackclient/+bug/1606105>`_]
|
Loading…
Reference in New Issue
Block a user