Merge "Add new default roles in os-instance-actions policies"

This commit is contained in:
Zuul 2020-03-21 04:58:26 +00:00 committed by Gerrit Code Review
commit 177dadc193
8 changed files with 209 additions and 77 deletions

View File

@ -84,7 +84,7 @@ class InstanceActionsController(wsgi.Controller):
"""Returns the list of actions recorded for a given instance."""
context = req.environ["nova.context"]
instance = self._get_instance(req, context, server_id)
context.can(ia_policies.BASE_POLICY_NAME,
context.can(ia_policies.BASE_POLICY_NAME % 'list',
target={'project_id': instance.project_id})
actions_raw = self.action_api.actions_get(context, instance)
actions = [self._format_action(action, ACTION_KEYS)
@ -101,7 +101,7 @@ class InstanceActionsController(wsgi.Controller):
"""Returns the list of actions recorded for a given instance."""
context = req.environ["nova.context"]
instance = self._get_instance(req, context, server_id)
context.can(ia_policies.BASE_POLICY_NAME,
context.can(ia_policies.BASE_POLICY_NAME % 'list',
target={'project_id': instance.project_id})
search_opts = {}
search_opts.update(req.GET)
@ -140,7 +140,7 @@ class InstanceActionsController(wsgi.Controller):
"""Return data about the given instance action."""
context = req.environ['nova.context']
instance = self._get_instance(req, context, server_id)
context.can(ia_policies.BASE_POLICY_NAME,
context.can(ia_policies.BASE_POLICY_NAME % 'show',
target={'project_id': instance.project_id})
action = self.action_api.action_get_by_request_id(context, instance,
id)
@ -161,7 +161,7 @@ class InstanceActionsController(wsgi.Controller):
show_events = False
show_traceback = False
show_host = False
if context.can(ia_policies.POLICY_ROOT % 'events',
if context.can(ia_policies.BASE_POLICY_NAME % 'events',
target={'project_id': instance.project_id},
fatal=False):
# For all microversions, the user can see all event details

View File

@ -18,23 +18,31 @@ from oslo_policy import policy
from nova.policies import base
BASE_POLICY_NAME = 'os_compute_api:os-instance-actions'
POLICY_ROOT = 'os_compute_api:os-instance-actions:%s'
ROOT_POLICY = 'os_compute_api:os-instance-actions'
BASE_POLICY_NAME = 'os_compute_api:os-instance-actions:%s'
DEPRECATED_INSTANCE_ACTION_POLICY = policy.DeprecatedRule(
ROOT_POLICY,
base.RULE_ADMIN_OR_OWNER,
)
DEPRECATED_REASON = """
Nova API policies are introducing new default roles with scope_type
capabilities. Old policies are deprecated and silently going to be ignored
in nova 23.0.0 release.
"""
instance_actions_policies = [
policy.DocumentedRuleDefault(
name=POLICY_ROOT % 'events',
check_str=base.RULE_ADMIN_API,
name=BASE_POLICY_NAME % 'events',
check_str=base.SYSTEM_READER,
description="""Add events details in action details for a server.
This check is performed only after the check
os_compute_api:os-instance-actions passes. Beginning with
Microversion 2.51, events details are always included; traceback
information is provided per event if policy enforcement passes.
Beginning with Microversion 2.62, each event includes a hashed
host identifier and, if policy enforcement passes, the name of
the host.""",
os_compute_api:os-instance-actions:show passes. Beginning with Microversion
2.51, events details are always included; traceback information is provided
per event if policy enforcement passes. Beginning with Microversion 2.62,
each event includes a hashed host identifier and, if policy enforcement
passes, the name of the host.""",
operations=[
{
'method': 'GET',
@ -43,20 +51,33 @@ the host.""",
],
scope_types=['system']),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME,
check_str=base.RULE_ADMIN_OR_OWNER,
description="""List actions and show action details for a server.""",
name=BASE_POLICY_NAME % 'list',
check_str=base.PROJECT_READER_OR_SYSTEM_READER,
description="""List actions for a server.""",
operations=[
{
'method': 'GET',
'path': '/servers/{server_id}/os-instance-actions'
},
}
],
scope_types=['system', 'project'],
deprecated_rule=DEPRECATED_INSTANCE_ACTION_POLICY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since='21.0.0'),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'show',
check_str=base.PROJECT_READER_OR_SYSTEM_READER,
description="""Show action details for a server.""",
operations=[
{
'method': 'GET',
'path': '/servers/{server_id}/os-instance-actions/{request_id}'
}
],
scope_types=['system', 'project']),
scope_types=['system', 'project'],
deprecated_rule=DEPRECATED_INSTANCE_ACTION_POLICY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since='21.0.0'),
]

View File

@ -15,6 +15,7 @@
from nova.tests.functional.api_sample_tests import test_servers
from nova.tests.functional import api_samples_test_base
from nova.tests.unit import policy_fixture
class ServerActionsSampleJsonTest(test_servers.ServersSampleBase):
@ -34,6 +35,7 @@ class ServerActionsSampleJsonTest(test_servers.ServersSampleBase):
actions = api_samples_test_base.objectify(response_data)
self.action_stop = actions['instanceActions'][0]
self._wait_for_state_change({'id': self.uuid}, 'SHUTOFF')
self.policy = self.useFixture(policy_fixture.RealPolicyFixture())
def _get_subs(self):
return {

View File

@ -121,7 +121,7 @@ class InstanceActionsTestV21(test.NoDBTestCase):
def _set_policy_rules(self):
rules = {'compute:get': '',
'os_compute_api:os-instance-actions': '',
'os_compute_api:os-instance-actions:show': '',
'os_compute_api:os-instance-actions:events': 'is_admin:True'}
policy.set_rules(oslo_policy.Rules.from_dict(rules))

View File

@ -117,13 +117,16 @@ class TestPolicyCheck(test.NoDBTestCase):
r.name for r in ia_policies.list_rules()]
passing_rules = self.cmd._filter_rules(
context, 'os-instance-actions', target)
context, 'os-instance-actions:list', target)
passing_rules += self.cmd._filter_rules(
context, 'os-instance-actions:show', target)
passing_rules += self.cmd._filter_rules(
context, 'os-instance-actions:events', target)
self.assertEqual(set(expected_rules), set(passing_rules))
def test_filter_rules_non_admin(self):
context = nova_context.RequestContext()
rule_conditions = [base_policies.RULE_ANY,
base_policies.RULE_ADMIN_OR_OWNER]
rule_conditions = [base_policies.PROJECT_READER_OR_SYSTEM_READER]
expected_rules = [r.name for r in ia_policies.list_rules() if
r.check_str in rule_conditions]
self._check_filter_rules(context, expected_rules=expected_rules)
@ -150,8 +153,7 @@ class TestPolicyCheck(test.NoDBTestCase):
db_context = nova_context.RequestContext(user_id='fake-user',
project_id='fake-project')
instance = fake_instance.fake_instance_obj(db_context)
rule_conditions = [base_policies.RULE_ANY,
base_policies.RULE_ADMIN_OR_OWNER]
rule_conditions = [base_policies.PROJECT_READER_OR_SYSTEM_READER]
expected_rules = [r.name for r in ia_policies.list_rules() if
r.check_str in rule_conditions]
self._check_filter_rules(db_context, instance, expected_rules)

View File

@ -49,7 +49,9 @@ policy_data = """
"os_compute_api:os-flavor-manage:delete": "",
"os_compute_api:os-floating-ip-pools": "",
"os_compute_api:os-floating-ips": "",
"os_compute_api:os-instance-actions": "",
"os_compute_api:os-instance-actions:list": "",
"os_compute_api:os-instance-actions:show": "",
"os_compute_api:os-instance-actions:events": "",
"os_compute_api:os-instance-usage-audit-log": "",
"os_compute_api:os-lock-server:lock": "",

View File

@ -20,12 +20,16 @@ from oslo_utils import timeutils
from nova.api.openstack.compute import instance_actions as instance_actions_v21
from nova.compute import vm_states
from nova import exception
from nova.policies import base as base_policy
from nova.policies import instance_actions as ia_policies
from nova import policy
from nova.tests.unit.api.openstack.compute import test_instance_actions
from nova.tests.unit.api.openstack import fakes
from nova.tests.unit import fake_instance
from nova.tests.unit import fake_server_actions
from nova.tests.unit.policies import base
from nova.tests.unit import policy_fixture
FAKE_UUID = fake_server_actions.FAKE_UUID
FAKE_REQUEST_ID = fake_server_actions.FAKE_REQUEST_ID1
@ -57,68 +61,61 @@ class InstanceActionsPolicyTest(base.BasePolicyTest):
task_state=None, launched_at=timeutils.utcnow())
self.mock_get.return_value = self.instance
# Check that admin or owner is able to list/show
# instance actions.
self.admin_or_owner_authorized_contexts = [
# Check that system reader are able to show the instance
# actions events.
self.system_reader_authorized_contexts = [
self.system_admin_context, self.system_member_context,
self.system_reader_context, self.legacy_admin_context,
self.project_admin_context]
# Check that non-system-reader are not able to show the instance
# actions events.
self.system_reader_unauthorized_contexts = [
self.system_foo_context, self.other_project_member_context,
self.project_foo_context, self.project_member_context,
self.project_reader_context]
self.project_or_system_reader_authorized_contexts = [
self.legacy_admin_context, self.system_admin_context,
self.project_admin_context, self.project_foo_context,
self.project_reader_context, self.project_member_context
self.project_admin_context, self.system_member_context,
self.system_reader_context, self.project_reader_context,
self.project_member_context, self.project_foo_context
]
self.admin_or_owner_unauthorized_contexts = [
self.system_member_context, self.system_reader_context,
self.project_or_system_reader_unauthorized_contexts = [
self.system_foo_context,
self.other_project_member_context
]
# Check that admin is able to show the instance actions
# events.
self.admin_authorized_contexts = [
self.legacy_admin_context,
self.system_admin_context,
self.project_admin_context
]
# Check that non-admin is not able to show the instance
# actions events.
self.admin_unauthorized_contexts = [
self.system_member_context,
self.system_reader_context,
self.system_foo_context,
self.project_member_context,
self.other_project_member_context,
self.project_foo_context,
self.project_reader_context
]
def _set_policy_rules(self, overwrite=True):
rules = {'os_compute_api:os-instance-actions': '@'}
rules = {ia_policies.BASE_POLICY_NAME % 'show': '@'}
policy.set_rules(oslo_policy.Rules.from_dict(rules),
overwrite=overwrite)
def test_index_instance_action_policy(self):
rule_name = "os_compute_api:os-instance-actions"
self.common_policy_check(self.admin_or_owner_authorized_contexts,
self.admin_or_owner_unauthorized_contexts,
rule_name, self.controller.index,
self.req, self.instance['uuid'])
rule_name = ia_policies.BASE_POLICY_NAME % "list"
self.common_policy_check(
self.project_or_system_reader_authorized_contexts,
self.project_or_system_reader_unauthorized_contexts,
rule_name, self.controller.index,
self.req, self.instance['uuid'])
@mock.patch('nova.compute.api.InstanceActionAPI.action_get_by_request_id')
def test_show_instance_action_policy(self, mock_action_get):
fake_action = self.fake_actions[FAKE_UUID][FAKE_REQUEST_ID]
mock_action_get.return_value = fake_action
rule_name = "os_compute_api:os-instance-actions"
self.common_policy_check(self.admin_or_owner_authorized_contexts,
self.admin_or_owner_unauthorized_contexts,
rule_name, self.controller.show,
self.req, self.instance['uuid'],
fake_action['request_id'])
rule_name = ia_policies.BASE_POLICY_NAME % "show"
self.common_policy_check(
self.project_or_system_reader_authorized_contexts,
self.project_or_system_reader_unauthorized_contexts,
rule_name, self.controller.show,
self.req, self.instance['uuid'], fake_action['request_id'])
@mock.patch('nova.objects.InstanceActionEventList.get_by_action')
@mock.patch('nova.objects.InstanceAction.get_by_request_id')
def test_show_instance_action_policy_with_events(
self, mock_get_action, mock_get_events):
"""Test to ensure skip checking policy rule
os_compute_api:os-instance-actions.
'os_compute_api:os-instance-actions:show'.
"""
fake_action = self.fake_actions[FAKE_UUID][FAKE_REQUEST_ID]
mock_get_action.return_value = fake_action
@ -129,10 +126,12 @@ class InstanceActionsPolicyTest(base.BasePolicyTest):
copy.deepcopy(fake_action))
self._set_policy_rules(overwrite=False)
rule_name = "os_compute_api:os-instance-actions:events"
rule_name = ia_policies.BASE_POLICY_NAME % "events"
authorize_res, unauthorize_res = self.common_policy_check(
self.admin_authorized_contexts, self.admin_unauthorized_contexts,
rule_name, self.controller.show, self.req, self.instance['uuid'],
self.system_reader_authorized_contexts,
self.system_reader_unauthorized_contexts,
rule_name, self.controller.show,
self.req, self.instance['uuid'],
fake_action['request_id'], fatal=False)
for action in authorize_res:
@ -146,6 +145,62 @@ class InstanceActionsPolicyTest(base.BasePolicyTest):
self.assertNotIn('events', action['instanceAction'])
class InstanceActionsDeprecatedPolicyTest(base.BasePolicyTest):
"""Test os-instance-actions APIs Deprecated policies.
This lass checks if deprecated policy rules are overridden
by user on policy.json file then they still work because
oslo.policy add deprecated rules in logical OR condition
and enforce them for policy checks if overridden.
"""
def setUp(self):
super(InstanceActionsDeprecatedPolicyTest, self).setUp()
self.controller = instance_actions_v21.InstanceActionsController()
self.admin_or_owner_req = fakes.HTTPRequest.blank('')
self.admin_or_owner_req.environ[
'nova.context'] = self.project_admin_context
self.reader_req = fakes.HTTPRequest.blank('')
self.reader_req.environ['nova.context'] = self.project_reader_context
self.deprecated_policy = ia_policies.ROOT_POLICY
# Overridde rule with different checks than defaults so that we can
# verify the rule overridden case.
override_rules = {self.deprecated_policy:
base_policy.RULE_ADMIN_OR_OWNER}
# NOTE(brinzhang): Only override the deprecated rule in policy file
# so that we can verify if overridden checks are considered by
# oslo.policy.
# Oslo.policy will consider the overridden rules if:
# 1. overridden deprecated rule's checks are different than defaults
# 2. new rules are not present in policy file
self.policy = self.useFixture(policy_fixture.OverridePolicyFixture(
rules_in_file=override_rules))
@mock.patch('nova.compute.api.InstanceActionAPI.actions_get')
@mock.patch('nova.api.openstack.common.get_instance')
def test_deprecated_policy_overridden_rule_is_checked(
self, mock_instance_get, mock_actions_get):
# Test to verify if deprecatd overridden policy is working.
instance = fake_instance.fake_instance_obj(
self.admin_or_owner_req.environ['nova.context'])
# Check for success as admin_or_owner role. Deprecated rule
# has been overridden with admin checks in policy.json
# If admin role pass it means overridden rule is enforced by
# olso.policy because new default is system reader and the old
# default is admin.
self.controller.index(self.admin_or_owner_req, instance['uuid'])
# check for failure with reader context.
exc = self.assertRaises(exception.PolicyNotAuthorized,
self.controller.index,
self.reader_req,
instance['uuid'])
self.assertEqual(
"Policy doesn't allow os_compute_api:os-instance-actions:list "
"to be performed.", exc.format_message())
class InstanceActionsScopeTypePolicyTest(InstanceActionsPolicyTest):
"""Test os-instance-actions APIs policies with system scope enabled.
@ -161,16 +216,65 @@ class InstanceActionsScopeTypePolicyTest(InstanceActionsPolicyTest):
super(InstanceActionsScopeTypePolicyTest, self).setUp()
self.flags(enforce_scope=True, group="oslo_policy")
# Check that system admin is able to get the
# Check that system reader is able to get the
# instance action events
self.admin_authorized_contexts = [
self.system_admin_context]
# Check that non-system or non-admin is not able to
self.system_reader_authorized_contexts = [
self.system_admin_context, self.system_member_context,
self.system_reader_context]
# Check that non-system-reader is not able to
# get the instance action events
self.admin_unauthorized_contexts = [
self.legacy_admin_context, self.system_member_context,
self.system_reader_context, self.system_foo_context,
self.system_reader_unauthorized_contexts = [
self.system_foo_context, self.legacy_admin_context,
self.project_admin_context, self.project_member_context,
self.other_project_member_context,
self.project_foo_context, self.project_reader_context
]
class InstanceActionsNoLegacyPolicyTest(InstanceActionsPolicyTest):
"""Test os-instance-actions APIs policies with system scope enabled,
and no more deprecated rules that allow the legacy admin API to
access system_admin_or_owner APIs.
"""
without_deprecated_rules = True
rules_without_deprecation = {
ia_policies.BASE_POLICY_NAME % 'list':
base_policy.PROJECT_READER_OR_SYSTEM_READER,
ia_policies.BASE_POLICY_NAME % 'show':
base_policy.PROJECT_READER_OR_SYSTEM_READER,
ia_policies.BASE_POLICY_NAME % 'events':
base_policy.SYSTEM_READER}
def setUp(self):
super(InstanceActionsNoLegacyPolicyTest, self).setUp()
self.flags(enforce_scope=True, group="oslo_policy")
# Check that system reader are able to get the
# instance action events.
self.system_reader_authorized_contexts = [
self.system_admin_context, self.system_reader_context,
self.system_member_context]
# Check that non-system-reader are not able to
# get the instance action events
self.system_reader_unauthorized_contexts = [
self.project_admin_context,
self.system_foo_context, self.legacy_admin_context,
self.other_project_member_context,
self.project_foo_context, self.project_member_context,
self.project_reader_context]
# Check that system or projct reader is able to
# show the instance actions events.
self.project_or_system_reader_authorized_contexts = [
self.system_admin_context,
self.project_admin_context, self.system_member_context,
self.system_reader_context, self.project_reader_context,
self.project_member_context,
]
# Check that non-system or non-project reader is not able to
# show the instance actions events.
self.project_or_system_reader_unauthorized_contexts = [
self.legacy_admin_context, self.project_foo_context,
self.system_foo_context, self.other_project_member_context
]

View File

@ -426,7 +426,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
"os_compute_api:os-flavor-extra-specs:show",
"os_compute_api:os-floating-ip-pools",
"os_compute_api:os-floating-ips",
"os_compute_api:os-instance-actions",
"os_compute_api:limits",
"os_compute_api:os-multinic",
"os_compute_api:os-networks:view",
@ -464,6 +463,8 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
"os_compute_api:os-volumes-attachments:show",
"os_compute_api:os-attach-interfaces:list",
"os_compute_api:os-attach-interfaces:show",
"os_compute_api:os-instance-actions:list",
"os_compute_api:os-instance-actions:show",
)
self.allow_nobody_rules = (