Add system role functionality

This commit adds the necessary bits to expose system role
assignments to openstackclient via python-keystoneclient.

bp system-scope

Depends-On: Iecbcbf020a15f2bec777334c648d4477f89f3b2c
Change-Id: I261e84700b51e8715eaebdc3f8f8bc46b68542c2
This commit is contained in:
Lance Bragstad 2017-11-30 22:42:55 +00:00
parent 47d0d0e0c0
commit 8bfa180430
7 changed files with 112 additions and 16 deletions

View File

@ -95,7 +95,7 @@ python-heatclient==1.10.0
python-ironic-inspector-client==1.5.0
python-ironicclient==2.3.0
python-karborclient==0.6.0
python-keystoneclient==3.8.0
python-keystoneclient==3.15.0
python-mimeparse==1.6.0
python-mistralclient==3.1.0
python-muranoclient==0.8.2

View File

@ -31,13 +31,18 @@ LOG = logging.getLogger(__name__)
def _add_identity_and_resource_options_to_parser(parser):
domain_or_project = parser.add_mutually_exclusive_group()
domain_or_project.add_argument(
system_or_domain_or_project = parser.add_mutually_exclusive_group()
system_or_domain_or_project.add_argument(
'--system',
metavar='<system>',
help=_('Include <system> (all)'),
)
system_or_domain_or_project.add_argument(
'--domain',
metavar='<domain>',
help=_('Include <domain> (name or ID)'),
)
domain_or_project.add_argument(
system_or_domain_or_project.add_argument(
'--project',
metavar='<project>',
help=_('Include <project> (name or ID)'),
@ -62,7 +67,14 @@ def _add_identity_and_resource_options_to_parser(parser):
def _process_identity_and_resource_options(parsed_args,
identity_client_manager):
kwargs = {}
if parsed_args.user and parsed_args.domain:
if parsed_args.user and parsed_args.system:
kwargs['user'] = common.find_user(
identity_client_manager,
parsed_args.user,
parsed_args.user_domain,
).id
kwargs['system'] = parsed_args.system
elif parsed_args.user and parsed_args.domain:
kwargs['user'] = common.find_user(
identity_client_manager,
parsed_args.user,
@ -83,6 +95,13 @@ def _process_identity_and_resource_options(parsed_args,
parsed_args.project,
parsed_args.project_domain,
).id
elif parsed_args.group and parsed_args.system:
kwargs['group'] = common.find_group(
identity_client_manager,
parsed_args.group,
parsed_args.group_domain,
).id
kwargs['system'] = parsed_args.system
elif parsed_args.group and parsed_args.domain:
kwargs['group'] = common.find_group(
identity_client_manager,
@ -109,8 +128,8 @@ def _process_identity_and_resource_options(parsed_args,
class AddRole(command.Command):
_description = _("Adds a role assignment to a user or group on a domain "
"or project")
_description = _("Adds a role assignment to a user or group on the "
"system, a domain, or a project")
def get_parser(self, prog_name):
parser = super(AddRole, self).get_parser(prog_name)
@ -381,7 +400,7 @@ class ListRole(command.Lister):
class RemoveRole(command.Command):
_description = _("Removes a role assignment from domain/project : "
_description = _("Removes a role assignment from system/domain/project : "
"user/group")
def get_parser(self, prog_name):

View File

@ -55,17 +55,22 @@ class ListRoleAssignment(command.Lister):
help=_('Group to filter (name or ID)'),
)
common.add_group_domain_option_to_parser(parser)
domain_or_project = parser.add_mutually_exclusive_group()
domain_or_project.add_argument(
system_or_domain_or_project = parser.add_mutually_exclusive_group()
system_or_domain_or_project.add_argument(
'--domain',
metavar='<domain>',
help=_('Domain to filter (name or ID)'),
)
domain_or_project.add_argument(
system_or_domain_or_project.add_argument(
'--project',
metavar='<project>',
help=_('Project to filter (name or ID)'),
)
system_or_domain_or_project.add_argument(
'--system',
metavar='<system>',
help=_('Filter based on system role assignments'),
)
common.add_project_domain_option_to_parser(parser)
common.add_inherited_option_to_parser(parser)
parser.add_argument(
@ -85,7 +90,8 @@ class ListRoleAssignment(command.Lister):
def _as_tuple(self, assignment):
return (assignment.role, assignment.user, assignment.group,
assignment.project, assignment.domain, assignment.inherited)
assignment.project, assignment.domain, assignment.system,
assignment.inherited)
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
@ -117,6 +123,10 @@ class ListRoleAssignment(command.Lister):
auth_ref.user_id
)
system = None
if parsed_args.system:
system = parsed_args.system
domain = None
if parsed_args.domain:
domain = common.find_domain(
@ -149,7 +159,9 @@ class ListRoleAssignment(command.Lister):
include_names = True if parsed_args.names else False
effective = True if parsed_args.effective else False
columns = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
columns = (
'Role', 'User', 'Group', 'Project', 'Domain', 'System', 'Inherited'
)
inherited_to = 'projects' if parsed_args.inherited else None
data = identity_client.role_assignments.list(
@ -157,6 +169,7 @@ class ListRoleAssignment(command.Lister):
user=user,
group=group,
project=project,
system=system,
role=role,
effective=effective,
os_inherit_extension_inherited_to=inherited_to,
@ -174,14 +187,24 @@ class ListRoleAssignment(command.Lister):
else:
setattr(assignment, 'project', scope['project']['id'])
assignment.domain = ''
assignment.system = ''
elif 'domain' in scope:
if include_names:
setattr(assignment, 'domain', scope['domain']['name'])
else:
setattr(assignment, 'domain', scope['domain']['id'])
assignment.project = ''
assignment.system = ''
elif 'system' in scope:
# NOTE(lbragstad): If, or when, keystone supports role
# assignments on subsets of a system, this will have to evolve
# to handle that case instead of hardcoding to the entire
# system.
setattr(assignment, 'system', 'all')
assignment.domain = ''
assignment.project = ''
else:
assignment.system = ''
assignment.domain = ''
assignment.project = ''

View File

@ -192,6 +192,12 @@ class IssueToken(command.ShowOne):
data['user_id'] = auth_ref.user_id
if auth_ref.domain_id:
data['domain_id'] = auth_ref.domain_id
if auth_ref.system_scoped:
# NOTE(lbragstad): This could change in the future when, or if,
# keystone supports the ability to scope to a subset of the entire
# deployment system. When that happens, this will have to relay
# scope information and IDs like we do for projects and domains.
data['system'] = 'all'
return zip(*sorted(six.iteritems(data)))

View File

@ -34,6 +34,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
'Group',
'Project',
'Domain',
'System',
'Inherited',
)
@ -95,6 +96,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
system=None,
group=None,
effective=False,
role=None,
@ -110,12 +112,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
'',
False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
identity_fakes.project_id,
'',
'',
False
),)
self.assertEqual(datalist, tuple(data))
@ -143,6 +147,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', identity_fakes.user_name),
('group', None),
('system', None),
('domain', None),
('project', None),
('role', None),
@ -159,6 +164,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
system=None,
user=self.users_mock.get(),
group=None,
project=None,
@ -174,12 +180,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
'',
identity_fakes.domain_id,
'',
False
), (identity_fakes.role_id,
identity_fakes.user_id,
'',
identity_fakes.project_id,
'',
'',
False
),)
self.assertEqual(datalist, tuple(data))
@ -207,6 +215,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', identity_fakes.group_name),
('system', None),
('domain', None),
('project', None),
('role', None),
@ -223,6 +232,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
system=None,
group=self.groups_mock.get(),
effective=False,
project=None,
@ -238,12 +248,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
identity_fakes.group_id,
'',
identity_fakes.domain_id,
'',
False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
identity_fakes.project_id,
'',
'',
False
),)
self.assertEqual(datalist, tuple(data))
@ -271,6 +283,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
('system', None),
('domain', identity_fakes.domain_name),
('project', None),
('role', None),
@ -287,6 +300,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=self.domains_mock.get(),
system=None,
group=None,
effective=False,
project=None,
@ -302,12 +316,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
'',
identity_fakes.domain_id,
'',
False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
'',
identity_fakes.domain_id,
'',
False
),)
self.assertEqual(datalist, tuple(data))
@ -335,6 +351,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
('system', None),
('domain', None),
('project', identity_fakes.project_name),
('role', None),
@ -351,6 +368,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
system=None,
group=None,
effective=False,
project=self.projects_mock.get(),
@ -366,12 +384,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
'',
False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
identity_fakes.project_id,
'',
'',
False
),)
self.assertEqual(datalist, tuple(data))
@ -398,6 +418,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
('system', None),
('domain', None),
('project', None),
('role', None),
@ -416,6 +437,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
system=None,
user=self.users_mock.get(),
group=None,
project=self.projects_mock.get(),
@ -431,6 +453,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
'',
False
),)
self.assertEqual(datalist, tuple(data))
@ -456,6 +479,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
('system', None),
('domain', None),
('project', None),
('role', None),
@ -472,6 +496,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
system=None,
group=None,
effective=True,
project=None,
@ -487,12 +512,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
'',
False
), (identity_fakes.role_id,
identity_fakes.user_id,
'',
'',
identity_fakes.domain_id,
'',
False
),)
self.assertEqual(tuple(data), datalist)
@ -520,6 +547,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
('system', None),
('domain', None),
('project', None),
('role', None),
@ -536,6 +564,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
system=None,
group=None,
effective=False,
project=None,
@ -551,12 +580,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
'',
True
), (identity_fakes.role_id,
identity_fakes.user_id,
'',
'',
identity_fakes.domain_id,
'',
True
),)
self.assertEqual(datalist, tuple(data))
@ -584,6 +615,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
('system', None),
('domain', None),
('project', None),
('role', None),
@ -602,6 +634,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
system=None,
group=None,
effective=False,
project=None,
@ -610,7 +643,9 @@ class TestRoleAssignmentList(TestRoleAssignment):
os_inherit_extension_inherited_to=None,
include_names=True)
collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
collist = (
'Role', 'User', 'Group', 'Project', 'Domain', 'System', 'Inherited'
)
self.assertEqual(columns, collist)
datalist1 = ((
@ -620,12 +655,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'@'.join([identity_fakes.project_name,
identity_fakes.domain_name]),
'',
'',
False
), (identity_fakes.role_name,
'@'.join([identity_fakes.user_name, identity_fakes.domain_name]),
'',
'',
identity_fakes.domain_name,
'',
False
),)
self.assertEqual(tuple(data), datalist1)
@ -648,6 +685,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
('system', None),
('domain', None),
('project', None),
('role', identity_fakes.ROLE_2['name']),
@ -664,6 +702,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
system=None,
user=None,
group=None,
project=None,
@ -679,6 +718,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
'',
identity_fakes.domain_id,
'',
False
),)
self.assertEqual(datalist, tuple(data))

View File

@ -0,0 +1,8 @@
---
features:
- |
Added support for system-scope. This includes support for the ability to
generate system-scoped tokens using ``system_scope: all`` in ``cloud.yaml``
or ``OS_SYSTEM_SCOPE=all`` in an environment variable. Support is also
included for managing role assignments on the system using ``--system``
when adding and removing roles.

View File

@ -12,6 +12,6 @@ osc-lib>=1.8.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0
python-glanceclient>=2.8.0 # Apache-2.0
python-keystoneclient>=3.8.0 # Apache-2.0
python-keystoneclient>=3.15.0 # Apache-2.0
python-novaclient>=9.1.0 # Apache-2.0
python-cinderclient>=3.3.0 # Apache-2.0