Merge "Revert project-specific APIs for servers"
This commit is contained in:
@@ -56,6 +56,7 @@ PROJECT_MEMBER = 'rule:project_member_api'
|
||||
PROJECT_READER = 'rule:project_reader_api'
|
||||
PROJECT_MEMBER_OR_SYSTEM_ADMIN = 'rule:system_admin_or_owner'
|
||||
PROJECT_READER_OR_SYSTEM_READER = 'rule:system_or_project_reader'
|
||||
ADMIN = 'rule:context_is_admin'
|
||||
|
||||
# NOTE(gmann): Below is the mapping of new roles and scope_types
|
||||
# with legacy roles::
|
||||
@@ -88,7 +89,8 @@ rules = [
|
||||
policy.RuleDefault(
|
||||
"context_is_admin",
|
||||
"role:admin",
|
||||
"Decides what is required for the 'is_admin:True' check to succeed."),
|
||||
"Decides what is required for the 'is_admin:True' check to succeed.",
|
||||
deprecated_rule=DEPRECATED_ADMIN_POLICY),
|
||||
policy.RuleDefault(
|
||||
"admin_or_owner",
|
||||
"is_admin:True or project_id:%(project_id)s",
|
||||
@@ -126,7 +128,8 @@ rules = [
|
||||
policy.RuleDefault(
|
||||
"project_reader_api",
|
||||
"role:reader and project_id:%(project_id)s",
|
||||
"Default rule for Project level read only APIs."),
|
||||
"Default rule for Project level read only APIs.",
|
||||
deprecated_rule=DEPRECATED_ADMIN_OR_OWNER_POLICY),
|
||||
policy.RuleDefault(
|
||||
name="system_admin_or_owner",
|
||||
check_str="rule:system_admin_api or rule:project_member_api",
|
||||
|
||||
@@ -24,7 +24,7 @@ BASE_POLICY_NAME = 'os_compute_api:os-extended-server-attributes'
|
||||
extended_server_attributes_policies = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=BASE_POLICY_NAME,
|
||||
check_str=base.SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_ADMIN,
|
||||
description="""Return extended attributes for server.
|
||||
|
||||
This rule will control the visibility for a set of servers attributes:
|
||||
@@ -66,7 +66,7 @@ is therefore deprecated and will be removed in a future release.
|
||||
'path': '/servers/{server_id}/action (rebuild)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']
|
||||
scope_types=['project']
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ flavor_extra_specs_policies = [
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'create',
|
||||
check_str=base.SYSTEM_ADMIN,
|
||||
check_str=base.ADMIN,
|
||||
description="Create extra specs for a flavor",
|
||||
operations=[
|
||||
{
|
||||
@@ -49,7 +49,7 @@ flavor_extra_specs_policies = [
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'update',
|
||||
check_str=base.SYSTEM_ADMIN,
|
||||
check_str=base.ADMIN,
|
||||
description="Update an extra spec for a flavor",
|
||||
operations=[
|
||||
{
|
||||
@@ -62,7 +62,7 @@ flavor_extra_specs_policies = [
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'delete',
|
||||
check_str=base.SYSTEM_ADMIN,
|
||||
check_str=base.ADMIN,
|
||||
description="Delete an extra spec for a flavor",
|
||||
operations=[
|
||||
{
|
||||
@@ -75,7 +75,7 @@ flavor_extra_specs_policies = [
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'index',
|
||||
check_str=base.PROJECT_READER_OR_SYSTEM_READER,
|
||||
check_str=base.PROJECT_READER,
|
||||
description="List extra specs for a flavor. Starting with "
|
||||
"microversion 2.47, the flavor used for a server is also returned "
|
||||
"in the response when showing server details, updating a server or "
|
||||
|
||||
+48
-68
@@ -25,7 +25,7 @@ CROSS_CELL_RESIZE = 'compute:servers:resize:cross_cell'
|
||||
rules = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'index',
|
||||
check_str=base.PROJECT_READER_OR_SYSTEM_READER,
|
||||
check_str=base.PROJECT_READER,
|
||||
description="List all servers",
|
||||
operations=[
|
||||
{
|
||||
@@ -33,10 +33,10 @@ rules = [
|
||||
'path': '/servers'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'detail',
|
||||
check_str=base.PROJECT_READER_OR_SYSTEM_READER,
|
||||
check_str=base.PROJECT_READER,
|
||||
description="List all servers with detailed information",
|
||||
operations=[
|
||||
{
|
||||
@@ -44,10 +44,10 @@ rules = [
|
||||
'path': '/servers/detail'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'index:get_all_tenants',
|
||||
check_str=base.SYSTEM_READER,
|
||||
check_str=base.PROJECT_ADMIN,
|
||||
description="List all servers for all projects",
|
||||
operations=[
|
||||
{
|
||||
@@ -55,10 +55,11 @@ rules = [
|
||||
'path': '/servers'
|
||||
}
|
||||
],
|
||||
scope_types=['system']),
|
||||
scope_types=['project']),
|
||||
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'detail:get_all_tenants',
|
||||
check_str=base.SYSTEM_READER,
|
||||
check_str=base.PROJECT_ADMIN,
|
||||
description="List all servers with detailed information for "
|
||||
" all projects",
|
||||
operations=[
|
||||
@@ -67,10 +68,10 @@ rules = [
|
||||
'path': '/servers/detail'
|
||||
}
|
||||
],
|
||||
scope_types=['system']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'allow_all_filters',
|
||||
check_str=base.SYSTEM_READER,
|
||||
check_str=base.PROJECT_ADMIN,
|
||||
description="Allow all filters when listing servers",
|
||||
operations=[
|
||||
{
|
||||
@@ -82,10 +83,10 @@ rules = [
|
||||
'path': '/servers/detail'
|
||||
}
|
||||
],
|
||||
scope_types=['system']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'show',
|
||||
check_str=base.PROJECT_READER_OR_SYSTEM_READER,
|
||||
check_str=base.PROJECT_READER,
|
||||
description="Show a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -93,12 +94,12 @@ rules = [
|
||||
'path': '/servers/{server_id}'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
# the details in host_status are pretty sensitive, only admins
|
||||
# should do that by default.
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'show:host_status',
|
||||
check_str=base.SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_ADMIN,
|
||||
description="""
|
||||
Show a server with additional host status information.
|
||||
|
||||
@@ -129,10 +130,10 @@ API responses which are also controlled by this policy rule, like the
|
||||
'path': '/servers/{server_id}/action (rebuild)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'show:host_status:unknown-only',
|
||||
check_str=base.SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_ADMIN,
|
||||
description="""
|
||||
Show a server with additional host status information, only if host status is
|
||||
UNKNOWN.
|
||||
@@ -162,7 +163,7 @@ allow everyone.
|
||||
'path': '/servers/{server_id}/action (rebuild)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project'],),
|
||||
scope_types=['project'],),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'create',
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
@@ -176,16 +177,6 @@ allow everyone.
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'create:forced_host',
|
||||
# TODO(gmann): We need to make it SYSTEM_ADMIN.
|
||||
# PROJECT_ADMIN is added for now because create server
|
||||
# policy is project scoped and there is no way to
|
||||
# pass the project_id in request body for system scoped
|
||||
# roles so that create server for other project with force host.
|
||||
# To achieve that, we need to update the create server API to
|
||||
# accept the project_id for whom the server needs to be created
|
||||
# and then change the scope of this policy to system-only
|
||||
# Because that is API change it needs to be done with new
|
||||
# microversion.
|
||||
check_str=base.PROJECT_ADMIN,
|
||||
description="""
|
||||
Create a server on the specified host and/or node.
|
||||
@@ -200,20 +191,9 @@ host and/or node by bypassing the scheduler filters unlike the
|
||||
'path': '/servers'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=REQUESTED_DESTINATION,
|
||||
# TODO(gmann): We need to make it SYSTEM_ADMIN.
|
||||
# PROJECT_ADMIN is added for now because create server
|
||||
# policy is project scoped and there is no way to
|
||||
# pass the project_id in request body for system scoped
|
||||
# roles so that create server for other project with requested
|
||||
# destination.
|
||||
# To achieve that, we need to update the create server API to
|
||||
# accept the project_id for whom the server needs to be created
|
||||
# and then change the scope of this policy to system-only
|
||||
# Because that is API change it needs to be done with new
|
||||
# microversion.
|
||||
check_str=base.PROJECT_ADMIN,
|
||||
description="""
|
||||
Create a server on the requested compute service host and/or
|
||||
@@ -229,7 +209,7 @@ validated by the scheduler filters unlike the
|
||||
'path': '/servers'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'create:attach_volume',
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
@@ -288,7 +268,7 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=NETWORK_ATTACH_EXTERNAL,
|
||||
check_str=base.PROJECT_ADMIN,
|
||||
@@ -305,10 +285,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/os-interface'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'delete',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Delete a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -316,10 +296,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'update',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Update a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -327,10 +307,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'confirm_resize',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Confirm a server resize",
|
||||
operations=[
|
||||
{
|
||||
@@ -338,10 +318,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (confirmResize)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'revert_resize',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Revert a server resize",
|
||||
operations=[
|
||||
{
|
||||
@@ -349,10 +329,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (revertResize)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'reboot',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Reboot a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -360,10 +340,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (reboot)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'resize',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Resize a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -371,7 +351,7 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (resize)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=CROSS_CELL_RESIZE,
|
||||
check_str=base.RULE_NOBODY,
|
||||
@@ -386,10 +366,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (resize)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'rebuild',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Rebuild a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -397,10 +377,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (rebuild)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'rebuild:trusted_certs',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Rebuild a server with trusted image certificate IDs",
|
||||
operations=[
|
||||
{
|
||||
@@ -408,10 +388,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (rebuild)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'create_image',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Create an image from a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -419,10 +399,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (createImage)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'create_image:allow_volume_backed',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Create an image from a volume backed server",
|
||||
operations=[
|
||||
{
|
||||
@@ -430,10 +410,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (createImage)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'start',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Start a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -441,10 +421,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (os-start)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'stop',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Stop a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -452,10 +432,10 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (os-stop)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=SERVERS % 'trigger_crash_dump',
|
||||
check_str=base.PROJECT_MEMBER_OR_SYSTEM_ADMIN,
|
||||
check_str=base.PROJECT_MEMBER,
|
||||
description="Trigger crash dump in a server",
|
||||
operations=[
|
||||
{
|
||||
@@ -463,7 +443,7 @@ https://bugs.launchpad.net/nova/+bug/1739646 for details.
|
||||
'path': '/servers/{server_id}/action (trigger_crash_dump)'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['project']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -95,14 +95,28 @@ class BasePolicyTest(test.TestCase):
|
||||
project_id=self.project_id_other,
|
||||
roles=['reader'])
|
||||
|
||||
self.all_contexts = [
|
||||
self.all_contexts = set([
|
||||
self.legacy_admin_context, self.system_admin_context,
|
||||
self.system_member_context, self.system_reader_context,
|
||||
self.system_foo_context,
|
||||
self.project_admin_context, self.project_member_context,
|
||||
self.project_reader_context, self.other_project_member_context,
|
||||
self.project_foo_context, self.other_project_reader_context
|
||||
]
|
||||
])
|
||||
|
||||
# All the project contexts for easy access.
|
||||
self.all_project_contexts = set([
|
||||
self.legacy_admin_context,
|
||||
self.project_admin_context, self.project_member_context,
|
||||
self.project_reader_context, self.project_foo_context,
|
||||
self.other_project_member_context,
|
||||
self.other_project_reader_context,
|
||||
])
|
||||
# All the system contexts for easy access.
|
||||
self.all_system_contexts = set([
|
||||
self.system_admin_context, self.system_foo_context,
|
||||
self.system_member_context, self.system_reader_context,
|
||||
])
|
||||
|
||||
if self.without_deprecated_rules:
|
||||
# To simulate the new world, remove deprecations by overriding
|
||||
@@ -118,10 +132,58 @@ class BasePolicyTest(test.TestCase):
|
||||
"role:reader and system_scope:all",
|
||||
"project_member_api":
|
||||
"role:member and project_id:%(project_id)s",
|
||||
"project_reader_api":
|
||||
"role:reader and project_id:%(project_id)s",
|
||||
})
|
||||
self.policy.set_rules(self.rules_without_deprecation,
|
||||
overwrite=False)
|
||||
|
||||
def reduce_set(self, name, new_set):
|
||||
"""Reduce a named set of contexts in a subclass.
|
||||
|
||||
This removes things from a set in a child test class by taking
|
||||
a new set, but asserts that no *new* contexts are added over
|
||||
what is defined in the parent.
|
||||
|
||||
:param name: The name of a set of contexts on self
|
||||
(i.e. 'project' for self.project_contexts
|
||||
:param new_set: The new set of contexts that should be used in
|
||||
the above set. The new_set is asserted to be a
|
||||
perfect subset of the existing set
|
||||
"""
|
||||
current = getattr(self, '%s_contexts' % name)
|
||||
|
||||
errors = ','.join(x.user_id for x in new_set - current)
|
||||
self.assertEqual('', errors,
|
||||
'Attempt to reduce set would add %s' % errors)
|
||||
|
||||
LOG.info('%s.%s_contexts: removing %s',
|
||||
self.__class__.__name__,
|
||||
name,
|
||||
','.join(x.user_id for x in current - new_set))
|
||||
setattr(self, '%s_contexts' % name, new_set)
|
||||
|
||||
def common_policy_auth(self, authorized_contexts,
|
||||
rule_name,
|
||||
func, req, *arg, **kwarg):
|
||||
"""Check a policy rule against a set of authorized contexts.
|
||||
|
||||
This is exactly like common_policy_check, except that it
|
||||
assumes any contexts not in the authorized set are in the
|
||||
unauthorized set.
|
||||
"""
|
||||
# The unauthorized users are any not in the authorized set.
|
||||
unauth = list(set(self.all_contexts) - set(authorized_contexts))
|
||||
# In case a set was passed in, convert to list for stable ordering.
|
||||
authorized_contexts = list(authorized_contexts)
|
||||
# Log both sets in the order we will test them to aid debugging of
|
||||
# fatal=False responses.
|
||||
LOG.info('Authorized users: %s', list(
|
||||
x.user_id for x in authorized_contexts))
|
||||
LOG.info('Unauthorized users: %s', list(x.user_id for x in unauth))
|
||||
return self.common_policy_check(authorized_contexts, unauth,
|
||||
rule_name, func, req, *arg, **kwarg)
|
||||
|
||||
def common_policy_check(self, authorized_contexts,
|
||||
unauthorized_contexts, rule_name,
|
||||
func, req, *arg, **kwarg):
|
||||
|
||||
@@ -77,99 +77,74 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest):
|
||||
self.stub_out('nova.api.openstack.common.get_flavor',
|
||||
get_flavor_extra_specs)
|
||||
|
||||
# Check that all are able to get flavor extra specs.
|
||||
self.all_authorized_contexts = [
|
||||
self.legacy_admin_context, self.system_admin_context,
|
||||
self.project_admin_context, self.project_member_context,
|
||||
self.project_reader_context, self.project_foo_context,
|
||||
self.system_member_context, self.system_reader_context,
|
||||
self.system_foo_context,
|
||||
self.other_project_member_context,
|
||||
self.other_project_reader_context
|
||||
]
|
||||
self.all_unauthorized_contexts = []
|
||||
# Check that all system scoped are able to get flavor extra specs.
|
||||
self.all_system_authorized_contexts = [
|
||||
self.legacy_admin_context, self.system_admin_context,
|
||||
self.project_admin_context, self.project_member_context,
|
||||
self.project_reader_context, self.project_foo_context,
|
||||
self.system_member_context, self.system_reader_context,
|
||||
self.system_foo_context,
|
||||
self.other_project_member_context,
|
||||
self.other_project_reader_context
|
||||
]
|
||||
self.all_system_unauthorized_contexts = []
|
||||
# In the base/legacy case, all project and system contexts are
|
||||
# authorized in the "anyone" case.
|
||||
self.all_authorized_contexts = (self.all_project_contexts |
|
||||
self.all_system_contexts)
|
||||
|
||||
# Check that admin is able to create, update and delete flavor
|
||||
# extra specs.
|
||||
self.admin_authorized_contexts = [
|
||||
self.legacy_admin_context, self.system_admin_context,
|
||||
self.project_admin_context]
|
||||
# Check that non-admin is not able to create, update and
|
||||
# delete flavor extra specs.
|
||||
self.admin_unauthorized_contexts = [
|
||||
self.system_member_context, self.system_reader_context,
|
||||
self.system_foo_context, self.project_member_context,
|
||||
self.project_reader_context, self.project_foo_context,
|
||||
self.other_project_member_context,
|
||||
self.other_project_reader_context
|
||||
]
|
||||
# In the base/legacy case, all project and system contexts are
|
||||
# authorized in the case of things that distinguish between
|
||||
# scopes, since scope checking is disabled.
|
||||
self.all_system_authorized_contexts = (self.all_project_contexts |
|
||||
self.all_system_contexts)
|
||||
self.all_project_authorized_contexts = (self.all_project_contexts |
|
||||
self.all_system_contexts)
|
||||
|
||||
# In the base/legacy case, any admin is an admin.
|
||||
self.admin_authorized_contexts = set([self.project_admin_context,
|
||||
self.system_admin_context,
|
||||
self.legacy_admin_context])
|
||||
|
||||
@mock.patch('nova.objects.Flavor.save')
|
||||
def test_create_flavor_extra_specs_policy(self, mock_save):
|
||||
body = {'extra_specs': {'hw:numa_nodes': '1'}}
|
||||
rule_name = policies.POLICY_ROOT % 'create'
|
||||
self.common_policy_check(self.admin_authorized_contexts,
|
||||
self.admin_unauthorized_contexts,
|
||||
rule_name,
|
||||
self.controller.create,
|
||||
self.req, '1234',
|
||||
body=body)
|
||||
self.common_policy_auth(self.admin_authorized_contexts,
|
||||
rule_name,
|
||||
self.controller.create,
|
||||
self.req, '1234',
|
||||
body=body)
|
||||
|
||||
@mock.patch('nova.objects.Flavor._flavor_extra_specs_del')
|
||||
@mock.patch('nova.objects.Flavor.save')
|
||||
def test_delete_flavor_extra_specs_policy(self, mock_save, mock_delete):
|
||||
rule_name = policies.POLICY_ROOT % 'delete'
|
||||
self.common_policy_check(self.admin_authorized_contexts,
|
||||
self.admin_unauthorized_contexts,
|
||||
rule_name,
|
||||
self.controller.delete,
|
||||
self.req, '1234', 'hw:cpu_policy')
|
||||
self.common_policy_auth(self.admin_authorized_contexts,
|
||||
rule_name,
|
||||
self.controller.delete,
|
||||
self.req, '1234', 'hw:cpu_policy')
|
||||
|
||||
@mock.patch('nova.objects.Flavor.save')
|
||||
def test_update_flavor_extra_specs_policy(self, mock_save):
|
||||
body = {'hw:cpu_policy': 'shared'}
|
||||
rule_name = policies.POLICY_ROOT % 'update'
|
||||
self.common_policy_check(self.admin_authorized_contexts,
|
||||
self.admin_unauthorized_contexts,
|
||||
rule_name,
|
||||
self.controller.update,
|
||||
self.req, '1234', 'hw:cpu_policy',
|
||||
body=body)
|
||||
self.common_policy_auth(self.admin_authorized_contexts,
|
||||
rule_name,
|
||||
self.controller.update,
|
||||
self.req, '1234', 'hw:cpu_policy',
|
||||
body=body)
|
||||
|
||||
def test_show_flavor_extra_specs_policy(self):
|
||||
rule_name = policies.POLICY_ROOT % 'show'
|
||||
self.common_policy_check(self.all_authorized_contexts,
|
||||
self.all_unauthorized_contexts,
|
||||
rule_name,
|
||||
self.controller.show,
|
||||
self.req, '1234',
|
||||
'hw:cpu_policy')
|
||||
self.common_policy_auth(self.all_authorized_contexts,
|
||||
rule_name,
|
||||
self.controller.show,
|
||||
self.req, '1234',
|
||||
'hw:cpu_policy')
|
||||
|
||||
def test_index_flavor_extra_specs_policy(self):
|
||||
rule_name = policies.POLICY_ROOT % 'index'
|
||||
self.common_policy_check(self.all_authorized_contexts,
|
||||
self.all_unauthorized_contexts,
|
||||
rule_name,
|
||||
self.controller.index,
|
||||
self.req, '1234')
|
||||
self.common_policy_auth(self.all_authorized_contexts,
|
||||
rule_name,
|
||||
self.controller.index,
|
||||
self.req, '1234')
|
||||
|
||||
def test_flavor_detail_with_extra_specs_policy(self):
|
||||
fakes.stub_out_flavor_get_all(self)
|
||||
rule_name = policies.POLICY_ROOT % 'index'
|
||||
req = fakes.HTTPRequest.blank('', version='2.61')
|
||||
authorize_res, unauthorize_res = self.common_policy_check(
|
||||
self.all_authorized_contexts, self.all_unauthorized_contexts,
|
||||
authorize_res, unauthorize_res = self.common_policy_auth(
|
||||
self.all_authorized_contexts,
|
||||
rule_name, self.flavor_ctrl.detail, req,
|
||||
fatal=False)
|
||||
for resp in authorize_res:
|
||||
@@ -181,8 +156,8 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest):
|
||||
fakes.stub_out_flavor_get_by_flavor_id(self)
|
||||
rule_name = policies.POLICY_ROOT % 'index'
|
||||
req = fakes.HTTPRequest.blank('', version='2.61')
|
||||
authorize_res, unauthorize_res = self.common_policy_check(
|
||||
self.all_authorized_contexts, self.all_unauthorized_contexts,
|
||||
authorize_res, unauthorize_res = self.common_policy_auth(
|
||||
self.all_authorized_contexts,
|
||||
rule_name, self.flavor_ctrl.show, req, '1',
|
||||
fatal=False)
|
||||
for resp in authorize_res:
|
||||
@@ -221,9 +196,8 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest):
|
||||
"disk": 1,
|
||||
}
|
||||
}
|
||||
authorize_res, unauthorize_res = self.common_policy_check(
|
||||
authorize_res, unauthorize_res = self.common_policy_auth(
|
||||
self.all_system_authorized_contexts,
|
||||
self.all_system_unauthorized_contexts,
|
||||
rule_name, self.fm_ctrl._create, req, body=body,
|
||||
fatal=False)
|
||||
for resp in authorize_res:
|
||||
@@ -242,9 +216,8 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest):
|
||||
self.policy.set_rules({rule: "@"}, overwrite=False)
|
||||
req = fakes.HTTPRequest.blank('', version='2.61')
|
||||
|
||||
authorize_res, unauthorize_res = self.common_policy_check(
|
||||
authorize_res, unauthorize_res = self.common_policy_auth(
|
||||
self.all_system_authorized_contexts,
|
||||
self.all_system_unauthorized_contexts,
|
||||
rule_name, self.fm_ctrl._update, req, '1',
|
||||
body={'flavor': {'description': None}},
|
||||
fatal=False)
|
||||
@@ -261,8 +234,8 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest):
|
||||
self.policy.set_rules({rule: "@"}, overwrite=False)
|
||||
req = fakes.HTTPRequest.blank('', version='2.47')
|
||||
rule_name = policies.POLICY_ROOT % 'index'
|
||||
authorize_res, unauthorize_res = self.common_policy_check(
|
||||
self.all_authorized_contexts, self.all_unauthorized_contexts,
|
||||
authorize_res, unauthorize_res = self.common_policy_auth(
|
||||
self.all_project_authorized_contexts,
|
||||
rule_name, self.server_ctrl.detail, req,
|
||||
fatal=False)
|
||||
for resp in authorize_res:
|
||||
@@ -280,9 +253,8 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest):
|
||||
self.policy.set_rules({rule: "@"}, overwrite=False)
|
||||
req = fakes.HTTPRequest.blank('', version='2.47')
|
||||
rule_name = policies.POLICY_ROOT % 'index'
|
||||
authorize_res, unauthorize_res = self.common_policy_check(
|
||||
self.all_authorized_contexts,
|
||||
self.all_unauthorized_contexts,
|
||||
authorize_res, unauthorize_res = self.common_policy_auth(
|
||||
self.all_project_authorized_contexts,
|
||||
rule_name, self.server_ctrl.show, req, 'fake',
|
||||
fatal=False)
|
||||
for resp in authorize_res:
|
||||
@@ -302,9 +274,8 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest):
|
||||
self.policy.set_rules({rule: "@"}, overwrite=False)
|
||||
req = fakes.HTTPRequest.blank('', version='2.47')
|
||||
rule_name = policies.POLICY_ROOT % 'index'
|
||||
authorize_res, unauthorize_res = self.common_policy_check(
|
||||
self.all_authorized_contexts,
|
||||
self.all_unauthorized_contexts,
|
||||
authorize_res, unauthorize_res = self.common_policy_auth(
|
||||
self.all_project_authorized_contexts,
|
||||
rule_name, self.server_ctrl._action_rebuild,
|
||||
req, self.instance.uuid,
|
||||
body={'rebuild': {"imageRef": uuids.fake_id}},
|
||||
@@ -323,9 +294,8 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest):
|
||||
self.policy.set_rules({rule: "@"}, overwrite=False)
|
||||
req = fakes.HTTPRequest.blank('', version='2.47')
|
||||
rule_name = policies.POLICY_ROOT % 'index'
|
||||
authorize_res, unauthorize_res = self.common_policy_check(
|
||||
self.all_authorized_contexts,
|
||||
self.all_unauthorized_contexts,
|
||||
authorize_res, unauthorize_res = self.common_policy_auth(
|
||||
self.all_project_authorized_contexts,
|
||||
rule_name, self.server_ctrl.update,
|
||||
req, self.instance.uuid,
|
||||
body={'server': {'name': 'test'}},
|
||||
@@ -350,32 +320,44 @@ class FlavorExtraSpecsScopeTypePolicyTest(FlavorExtraSpecsPolicyTest):
|
||||
super(FlavorExtraSpecsScopeTypePolicyTest, self).setUp()
|
||||
self.flags(enforce_scope=True, group="oslo_policy")
|
||||
|
||||
# Check that all system scoped are able to get flavor extra specs.
|
||||
self.all_system_authorized_contexts = [
|
||||
self.system_admin_context, self.system_member_context,
|
||||
self.system_reader_context, self.system_foo_context
|
||||
]
|
||||
self.all_system_unauthorized_contexts = [
|
||||
self.legacy_admin_context,
|
||||
self.project_admin_context, self.project_member_context,
|
||||
self.project_reader_context, self.project_foo_context,
|
||||
self.other_project_member_context,
|
||||
self.other_project_reader_context
|
||||
]
|
||||
# Check that system admin is able to create, update and delete flavor
|
||||
# extra specs.
|
||||
self.admin_authorized_contexts = [
|
||||
self.system_admin_context]
|
||||
# Check that non-system admin is not able to create, update and
|
||||
# delete flavor extra specs.
|
||||
self.admin_unauthorized_contexts = [
|
||||
self.legacy_admin_context, self.project_admin_context,
|
||||
self.system_member_context, self.system_reader_context,
|
||||
self.system_foo_context, self.project_member_context,
|
||||
self.project_reader_context, self.project_foo_context,
|
||||
self.other_project_member_context,
|
||||
self.other_project_reader_context
|
||||
]
|
||||
# Only system users are authorized for system APIs
|
||||
self.all_system_authorized_contexts = self.all_system_contexts
|
||||
|
||||
# Only system_admin can do system admin things
|
||||
self.admin_authorized_contexts = [self.system_admin_context]
|
||||
|
||||
# Scope checking is in effect, so break apart project/system
|
||||
# authorization. Note that even for the server tests above, we
|
||||
# are technically authorizing against a server-embedded flavor
|
||||
# (which has no project affiliation like the actual flavor it
|
||||
# came from) and thus the other_project_* contexts are
|
||||
# technically valid here. In reality, failure for
|
||||
# other_project_* to get the server itself would prevent those
|
||||
# projects from seeing the flavor extra_specs for it.
|
||||
self.all_project_authorized_contexts = self.all_project_contexts
|
||||
self.all_system_authorized_contexts = self.all_system_contexts
|
||||
|
||||
|
||||
class FlavorExtraSpecsNoLegacyNoScopeTest(FlavorExtraSpecsPolicyTest):
|
||||
"""Test Flavor Extra Specs API policies with deprecated rules
|
||||
disabled, but scope checking still disabled.
|
||||
"""
|
||||
without_deprecated_rules = True
|
||||
|
||||
def setUp(self):
|
||||
super(FlavorExtraSpecsNoLegacyNoScopeTest, self).setUp()
|
||||
|
||||
# Disabling legacy rules means that random roles no longer
|
||||
# have power, but without scope checking there is no
|
||||
# difference between project and system
|
||||
everything_but_foo = (
|
||||
self.all_project_contexts | self.all_system_contexts) - set([
|
||||
self.system_foo_context,
|
||||
self.project_foo_context,
|
||||
])
|
||||
self.reduce_set('all_project_authorized', everything_but_foo)
|
||||
self.reduce_set('all_system_authorized', everything_but_foo)
|
||||
self.reduce_set('all_authorized', everything_but_foo)
|
||||
|
||||
|
||||
class FlavorExtraSpecsNoLegacyPolicyTest(FlavorExtraSpecsScopeTypePolicyTest):
|
||||
@@ -387,28 +369,19 @@ class FlavorExtraSpecsNoLegacyPolicyTest(FlavorExtraSpecsScopeTypePolicyTest):
|
||||
|
||||
def setUp(self):
|
||||
super(FlavorExtraSpecsNoLegacyPolicyTest, self).setUp()
|
||||
# Check that system or project reader are able to get flavor
|
||||
# extra specs.
|
||||
self.all_authorized_contexts = [
|
||||
self.legacy_admin_context, self.system_admin_context,
|
||||
self.project_admin_context, self.project_member_context,
|
||||
self.project_reader_context, self.system_member_context,
|
||||
self.system_reader_context, self.other_project_member_context,
|
||||
self.other_project_reader_context
|
||||
]
|
||||
self.all_unauthorized_contexts = [
|
||||
self.project_foo_context, self.system_foo_context
|
||||
]
|
||||
# Check that all system scoped reader are able to get flavor
|
||||
# extra specs.
|
||||
self.all_system_authorized_contexts = [
|
||||
self.system_admin_context, self.system_member_context,
|
||||
self.system_reader_context
|
||||
]
|
||||
self.all_system_unauthorized_contexts = [
|
||||
self.legacy_admin_context, self.system_foo_context,
|
||||
self.project_admin_context, self.project_member_context,
|
||||
self.project_reader_context, self.project_foo_context,
|
||||
self.other_project_member_context,
|
||||
self.other_project_reader_context
|
||||
]
|
||||
# Non-legacy rules do not imply random roles have any
|
||||
# access. Same note as above, regarding other_project_*
|
||||
# contexts. With scope checking enabled, project and system
|
||||
# contexts stay separate.
|
||||
self.reduce_set(
|
||||
'all_project_authorized',
|
||||
self.all_project_contexts - set([self.project_foo_context]))
|
||||
self.reduce_set(
|
||||
'all_system_authorized',
|
||||
self.all_system_contexts - set([self.system_foo_context]))
|
||||
everything_but_foo = (
|
||||
self.all_project_contexts | self.all_system_contexts) - set([
|
||||
self.system_foo_context,
|
||||
self.project_foo_context,
|
||||
])
|
||||
self.reduce_set('all_authorized', everything_but_foo)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -306,7 +306,7 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
||||
self.non_admin_context = context.RequestContext('fake', 'fake',
|
||||
roles=['member'])
|
||||
self.admin_context = context.RequestContext('fake', 'fake', True,
|
||||
roles=['member'])
|
||||
roles=['admin', 'member'])
|
||||
self.target = {}
|
||||
self.fake_policy = jsonutils.loads(fake_policy.policy_data)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user