Merge "Add new default roles in os-services API policies"
This commit is contained in:
commit
3e7e2530f1
|
@ -64,7 +64,6 @@ class ServiceController(wsgi.Controller):
|
|||
api_services = ('nova-osapi_compute', 'nova-metadata')
|
||||
|
||||
context = req.environ['nova.context']
|
||||
context.can(services_policies.BASE_POLICY_NAME)
|
||||
|
||||
cell_down_support = api_version_request.is_supported(
|
||||
req, min_version=PARTIAL_CONSTRUCT_FOR_CELL_DOWN_MIN_VERSION)
|
||||
|
@ -218,7 +217,6 @@ class ServiceController(wsgi.Controller):
|
|||
def _perform_action(self, req, id, body, actions):
|
||||
"""Calculate action dictionary dependent on provided fields"""
|
||||
context = req.environ['nova.context']
|
||||
context.can(services_policies.BASE_POLICY_NAME)
|
||||
|
||||
try:
|
||||
action = actions[id]
|
||||
|
@ -233,7 +231,7 @@ class ServiceController(wsgi.Controller):
|
|||
def delete(self, req, id):
|
||||
"""Deletes the specified service."""
|
||||
context = req.environ['nova.context']
|
||||
context.can(services_policies.BASE_POLICY_NAME)
|
||||
context.can(services_policies.BASE_POLICY_NAME % 'delete')
|
||||
|
||||
if api_version_request.is_supported(
|
||||
req, min_version=UUID_FOR_ID_MIN_VERSION):
|
||||
|
@ -348,6 +346,8 @@ class ServiceController(wsgi.Controller):
|
|||
"""Return a list of all running services. Filter by host & service
|
||||
name
|
||||
"""
|
||||
context = req.environ['nova.context']
|
||||
context.can(services_policies.BASE_POLICY_NAME % 'list')
|
||||
if api_version_request.is_supported(req, min_version='2.11'):
|
||||
_services = self._get_services_list(req, ['forced_down'])
|
||||
else:
|
||||
|
@ -367,6 +367,8 @@ class ServiceController(wsgi.Controller):
|
|||
service ID passed on the path, just the action, for example
|
||||
PUT /os-services/disable.
|
||||
"""
|
||||
context = req.environ['nova.context']
|
||||
context.can(services_policies.BASE_POLICY_NAME % 'update')
|
||||
if api_version_request.is_supported(req, min_version='2.11'):
|
||||
actions = self.actions.copy()
|
||||
actions["force-down"] = self._forced_down
|
||||
|
@ -393,7 +395,7 @@ class ServiceController(wsgi.Controller):
|
|||
|
||||
# Validate the request context against the policy.
|
||||
context = req.environ['nova.context']
|
||||
context.can(services_policies.BASE_POLICY_NAME)
|
||||
context.can(services_policies.BASE_POLICY_NAME % 'update')
|
||||
|
||||
# Get the service by uuid.
|
||||
try:
|
||||
|
|
|
@ -18,49 +18,67 @@ from oslo_policy import policy
|
|||
from nova.policies import base
|
||||
|
||||
|
||||
BASE_POLICY_NAME = 'os_compute_api:os-services'
|
||||
BASE_POLICY_NAME = 'os_compute_api:os-services:%s'
|
||||
DEPRECATED_SERVICE_POLICY = policy.DeprecatedRule(
|
||||
'os_compute_api:os-services',
|
||||
base.RULE_ADMIN_API,
|
||||
)
|
||||
|
||||
DEPRECATED_REASON = """
|
||||
Since Ussuri release, nova API policies are introducing new default roles
|
||||
with scope_type capabilities. These new changes improve the security level
|
||||
and manageability. New policies are more rich in term of handling access
|
||||
at system and project level token with read, write roles.
|
||||
Start using the new policies and enable the scope checks via config option
|
||||
``nova.conf [oslo_policy] enforce_scope=True`` which is False by default.
|
||||
Old policies are marked as deprecated and silently going to be ignored
|
||||
in nova 23.0.0 (OpenStack W) release
|
||||
"""
|
||||
|
||||
services_policies = [
|
||||
policy.DocumentedRuleDefault(
|
||||
BASE_POLICY_NAME,
|
||||
base.RULE_ADMIN_API,
|
||||
"List all running Compute services in a region, enables or disable "
|
||||
"scheduling for a Compute service, logs disabled Compute service "
|
||||
"information, set or unset forced_down flag for the compute service "
|
||||
"and delete a Compute service",
|
||||
[
|
||||
name=BASE_POLICY_NAME % 'list',
|
||||
check_str=base.SYSTEM_READER,
|
||||
description="List all running Compute services in a region.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/os-services'
|
||||
},
|
||||
{
|
||||
'method': 'PUT',
|
||||
'path': '/os-services/enable'
|
||||
},
|
||||
{
|
||||
'method': 'PUT',
|
||||
'path': '/os-services/disable'
|
||||
},
|
||||
{
|
||||
'method': 'PUT',
|
||||
'path': '/os-services/disable-log-reason'
|
||||
},
|
||||
{
|
||||
'method': 'PUT',
|
||||
'path': '/os-services/force-down'
|
||||
},
|
||||
}
|
||||
],
|
||||
scope_types=['system'],
|
||||
deprecated_rule=DEPRECATED_SERVICE_POLICY,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since='20.0.0'),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=BASE_POLICY_NAME % 'update',
|
||||
check_str=base.SYSTEM_ADMIN,
|
||||
description="Update a Compute service.",
|
||||
operations=[
|
||||
{
|
||||
# Added in microversion 2.53.
|
||||
'method': 'PUT',
|
||||
'path': '/os-services/{service_id}'
|
||||
},
|
||||
],
|
||||
scope_types=['system'],
|
||||
deprecated_rule=DEPRECATED_SERVICE_POLICY,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since='20.0.0'),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=BASE_POLICY_NAME % 'delete',
|
||||
check_str=base.SYSTEM_ADMIN,
|
||||
description="Delete a Compute service.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'DELETE',
|
||||
'path': '/os-services/{service_id}'
|
||||
}
|
||||
],
|
||||
scope_types=['system']),
|
||||
scope_types=['system'],
|
||||
deprecated_rule=DEPRECATED_SERVICE_POLICY,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since='20.0.0'),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1201,7 +1201,7 @@ class ServicesTestV253(test.TestCase):
|
|||
|
||||
def test_update_policy_failed(self):
|
||||
"""Tests that policy is checked with microversion 2.53."""
|
||||
rule_name = "os_compute_api:os-services"
|
||||
rule_name = "os_compute_api:os-services:update"
|
||||
self.policy.set_rules({rule_name: "project_id:non_fake"})
|
||||
exc = self.assertRaises(
|
||||
exception.PolicyNotAuthorized,
|
||||
|
|
|
@ -77,7 +77,9 @@ policy_data = """
|
|||
"os_compute_api:os-server-groups:index": "",
|
||||
"os_compute_api:os-server-groups:create": "",
|
||||
"os_compute_api:os-server-groups:delete": "",
|
||||
"os_compute_api:os-services": "",
|
||||
"os_compute_api:os-services:list": "",
|
||||
"os_compute_api:os-services:update": "",
|
||||
"os_compute_api:os-services:delete": "",
|
||||
"os_compute_api:os-shelve:shelve": "",
|
||||
"os_compute_api:os-shelve:shelve_offload": "",
|
||||
"os_compute_api:os-simple-tenant-usage:show": "",
|
||||
|
|
|
@ -14,8 +14,11 @@
|
|||
import mock
|
||||
|
||||
from nova.api.openstack.compute import services as services_v21
|
||||
from nova import exception
|
||||
from nova.policies import base as base_policy
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
from nova.tests.unit.policies import base
|
||||
from nova.tests.unit import policy_fixture
|
||||
|
||||
|
||||
class ServicesPolicyTest(base.BasePolicyTest):
|
||||
|
@ -43,8 +46,26 @@ class ServicesPolicyTest(base.BasePolicyTest):
|
|||
self.project_foo_context, self.project_reader_context
|
||||
]
|
||||
|
||||
# Check that system scoped admin, member and reader are able to
|
||||
# read the service data.
|
||||
# NOTE(gmann): Until old default rule which is admin_api is
|
||||
# deprecated and not removed, project admin and legacy admin
|
||||
# will be able to read the service data. This make sure that existing
|
||||
# tokens will keep working even we have changed this policy defaults
|
||||
# to reader role.
|
||||
self.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 read the service
|
||||
# data
|
||||
self.reader_unauthorized_contexts = [
|
||||
self.system_foo_context, self.other_project_member_context,
|
||||
self.project_foo_context, self.project_member_context,
|
||||
self.project_reader_context]
|
||||
|
||||
def test_delete_service_policy(self):
|
||||
rule_name = "os_compute_api:os-services"
|
||||
rule_name = "os_compute_api:os-services:delete"
|
||||
with mock.patch('nova.compute.api.HostAPI.service_get_by_id'):
|
||||
self.common_policy_check(self.admin_authorized_contexts,
|
||||
self.admin_unauthorized_contexts,
|
||||
|
@ -52,15 +73,15 @@ class ServicesPolicyTest(base.BasePolicyTest):
|
|||
self.req, 1)
|
||||
|
||||
def test_index_service_policy(self):
|
||||
rule_name = "os_compute_api:os-services"
|
||||
rule_name = "os_compute_api:os-services:list"
|
||||
with mock.patch('nova.compute.api.HostAPI.service_get_all'):
|
||||
self.common_policy_check(self.admin_authorized_contexts,
|
||||
self.admin_unauthorized_contexts,
|
||||
self.common_policy_check(self.reader_authorized_contexts,
|
||||
self.reader_unauthorized_contexts,
|
||||
rule_name, self.controller.index,
|
||||
self.req)
|
||||
|
||||
def test_old_update_service_policy(self):
|
||||
rule_name = "os_compute_api:os-services"
|
||||
rule_name = "os_compute_api:os-services:update"
|
||||
body = {'host': 'host1', 'binary': 'nova-compute'}
|
||||
update = 'nova.compute.api.HostAPI.service_update_by_host_and_binary'
|
||||
with mock.patch(update):
|
||||
|
@ -70,7 +91,7 @@ class ServicesPolicyTest(base.BasePolicyTest):
|
|||
self.req, 'enable', body=body)
|
||||
|
||||
def test_update_service_policy(self):
|
||||
rule_name = "os_compute_api:os-services"
|
||||
rule_name = "os_compute_api:os-services:update"
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'', version=services_v21.UUID_FOR_ID_MIN_VERSION)
|
||||
service = self.start_service(
|
||||
|
@ -109,3 +130,67 @@ class ServicesScopeTypePolicyTest(ServicesPolicyTest):
|
|||
self.other_project_member_context,
|
||||
self.project_foo_context, self.project_reader_context
|
||||
]
|
||||
|
||||
# Check that system admin, member and reader are able to read the
|
||||
# service data
|
||||
self.reader_authorized_contexts = [
|
||||
self.system_admin_context, self.system_member_context,
|
||||
self.system_reader_context]
|
||||
# Check that non-system or non-reader are not able to read the service
|
||||
# data
|
||||
self.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 ServicesDeprecatedPolicyTest(base.BasePolicyTest):
|
||||
"""Test os-services APIs Deprecated policies.
|
||||
|
||||
This class 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(ServicesDeprecatedPolicyTest, self).setUp()
|
||||
self.controller = services_v21.ServiceController()
|
||||
self.member_req = fakes.HTTPRequest.blank('')
|
||||
self.member_req.environ['nova.context'] = self.project_member_context
|
||||
self.reader_req = fakes.HTTPRequest.blank('')
|
||||
self.reader_req.environ['nova.context'] = self.project_reader_context
|
||||
self.deprecated_policy = "os_compute_api:os-services"
|
||||
# Overridde rule with different checks than defaults so that we can
|
||||
# verify the rule overridden case.
|
||||
override_rules = {self.deprecated_policy: base_policy.PROJECT_MEMBER}
|
||||
# NOTE(gmann): 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))
|
||||
|
||||
def test_deprecated_policy_overridden_rule_is_checked(self):
|
||||
# Test to verify if deprecatd overridden policy is working.
|
||||
|
||||
# check for success as member role. Deprecated rule
|
||||
# has been overridden with member checks in policy.json
|
||||
# If member role pass it means overridden rule is enforced by
|
||||
# olso.policy because new default is system admin and the old
|
||||
# default is admin.
|
||||
with mock.patch('nova.compute.api.HostAPI.service_get_by_id'):
|
||||
self.controller.index(self.member_req)
|
||||
|
||||
# check for failure with reader context.
|
||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.controller.index, self.reader_req)
|
||||
self.assertEqual(
|
||||
"Policy doesn't allow os_compute_api:os-services:list to be"
|
||||
" performed.",
|
||||
exc.format_message())
|
||||
|
|
|
@ -130,3 +130,42 @@ class RoleBasedPolicyFixture(RealPolicyFixture):
|
|||
self.policy_file = os.path.join(self.policy_dir.path, 'policy.json')
|
||||
with open(self.policy_file, 'w') as f:
|
||||
jsonutils.dump(policy, f)
|
||||
|
||||
|
||||
class OverridePolicyFixture(RealPolicyFixture):
|
||||
"""Load the set of requested rules into policy file
|
||||
This overrides the policy with the requested rules only into
|
||||
policy file. This fixture is to verify the use case where operator
|
||||
has overridden the policy rules in policy file means default policy
|
||||
not used. One example is when policy rules are deprecated. In that case
|
||||
tests can use this fixture and verify if deprecated rules are overridden
|
||||
then does nova code enforce the overridden rules not only defaults.
|
||||
As per oslo.policy deprecattion feature, if deprecated rule is overridden
|
||||
in policy file then, overridden check is used to verify the policy.
|
||||
Example of usage:
|
||||
|
||||
self.deprecated_policy = "os_compute_api:os-services"
|
||||
# set check_str as different than defaults to verify the
|
||||
# rule overridden case.
|
||||
override_rules = {self.deprecated_policy: 'is_admin:True'}
|
||||
# NOTE(gmann): 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 checks are different than defaults
|
||||
# 2. new rules for deprecated rules are not present in policy file
|
||||
self.policy = self.useFixture(policy_fixture.OverridePolicyFixture(
|
||||
rules_in_file=override_rules))
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, rules_in_file, *args, **kwargs):
|
||||
self.rules_in_file = rules_in_file
|
||||
super(OverridePolicyFixture, self).__init__(*args, **kwargs)
|
||||
|
||||
def _prepare_policy(self):
|
||||
self.policy_dir = self.useFixture(fixtures.TempDir())
|
||||
self.policy_file = os.path.join(self.policy_dir.path,
|
||||
'policy.json')
|
||||
with open(self.policy_file, 'w') as f:
|
||||
jsonutils.dump(self.rules_in_file, f)
|
||||
CONF.set_override('policy_dirs', [], group='oslo_policy')
|
||||
|
|
|
@ -349,7 +349,8 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
|||
"os_compute_api:os-quota-sets:update",
|
||||
"os_compute_api:os-quota-sets:delete",
|
||||
"os_compute_api:os-server-diagnostics",
|
||||
"os_compute_api:os-services",
|
||||
"os_compute_api:os-services:update",
|
||||
"os_compute_api:os-services:delete",
|
||||
"os_compute_api:os-shelve:shelve_offload",
|
||||
"os_compute_api:os-simple-tenant-usage:list",
|
||||
"os_compute_api:os-availability-zone:detail",
|
||||
|
@ -453,6 +454,10 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
|||
|
||||
self.allow_all_rules = (
|
||||
"os_compute_api:os-quota-sets:defaults",
|
||||
)
|
||||
|
||||
self.system_reader_rules = (
|
||||
"os_compute_api:os-services:list",
|
||||
)
|
||||
|
||||
def test_all_rules_in_sample_file(self):
|
||||
|
@ -492,5 +497,5 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
|||
'system_admin_or_owner', 'system_or_project_reader')
|
||||
result = set(rules.keys()) - set(self.admin_only_rules +
|
||||
self.admin_or_owner_rules +
|
||||
self.allow_all_rules + special_rules)
|
||||
self.allow_all_rules + self.system_reader_rules + special_rules)
|
||||
self.assertEqual(set([]), result)
|
||||
|
|
Loading…
Reference in New Issue