Add new default roles in os-hypervisors policies

This adds new defaults roles in os-hypervisors API policies.
This policy is default to SYSTEM_READER role.

Policy rules are made more granular to adopt the new defaults.

Also add tests to simulates the future where we drop the deprecation
fall back in the policy by overriding the rules with a version where
there are no deprecated rule options. Operators can do the same by
adding overrides in their policy files that match the default but
stop the rule deprecation fallback from happening.

Partial implement blueprint policy-defaults-refresh

Change-Id: I548e49bd3e51eb51de922e97f3340363b8b94e50
This commit is contained in:
Ghanshyam Mann 2020-03-30 14:47:16 -05:00
parent 258e38bd71
commit 1fbaff1770
6 changed files with 171 additions and 113 deletions

View File

@ -122,7 +122,6 @@ class HypervisorsController(wsgi.Controller):
:param links: If True, return links in the response for paging.
"""
context = req.environ['nova.context']
context.can(hv_policies.BASE_POLICY_NAME)
# The 2.53 microversion moves the search and servers routes into
# GET /os-hypervisors and GET /os-hypervisors/detail with query
@ -221,6 +220,8 @@ class HypervisorsController(wsgi.Controller):
return self._index(req)
def _index(self, req, limit=None, marker=None, links=False):
context = req.environ['nova.context']
context.can(hv_policies.BASE_POLICY_NAME % 'list')
return self._get_hypervisors(req, detail=False, limit=limit,
marker=marker, links=links)
@ -251,6 +252,8 @@ class HypervisorsController(wsgi.Controller):
return self._detail(req)
def _detail(self, req, limit=None, marker=None, links=False):
context = req.environ['nova.context']
context.can(hv_policies.BASE_POLICY_NAME % 'list-detail')
return self._get_hypervisors(req, detail=True, limit=limit,
marker=marker, links=links)
@ -302,7 +305,7 @@ class HypervisorsController(wsgi.Controller):
def _show(self, req, id, with_servers=False):
context = req.environ['nova.context']
context.can(hv_policies.BASE_POLICY_NAME)
context.can(hv_policies.BASE_POLICY_NAME % 'show')
self._validate_id(req, id)
@ -324,7 +327,7 @@ class HypervisorsController(wsgi.Controller):
@wsgi.expected_errors((400, 404, 501))
def uptime(self, req, id):
context = req.environ['nova.context']
context.can(hv_policies.BASE_POLICY_NAME)
context.can(hv_policies.BASE_POLICY_NAME % 'uptime')
self._validate_id(req, id)
@ -362,7 +365,7 @@ class HypervisorsController(wsgi.Controller):
index and detail methods.
"""
context = req.environ['nova.context']
context.can(hv_policies.BASE_POLICY_NAME)
context.can(hv_policies.BASE_POLICY_NAME % 'search')
hypervisors = self._get_compute_nodes_by_name_pattern(context, id)
try:
return dict(hypervisors=[
@ -386,7 +389,7 @@ class HypervisorsController(wsgi.Controller):
GET /os-hypervisors index and detail methods.
"""
context = req.environ['nova.context']
context.can(hv_policies.BASE_POLICY_NAME)
context.can(hv_policies.BASE_POLICY_NAME % 'servers')
compute_nodes = self._get_compute_nodes_by_name_pattern(context, id)
hypervisors = []
for compute_node in compute_nodes:
@ -405,6 +408,6 @@ class HypervisorsController(wsgi.Controller):
@wsgi.expected_errors(())
def statistics(self, req):
context = req.environ['nova.context']
context.can(hv_policies.BASE_POLICY_NAME)
context.can(hv_policies.BASE_POLICY_NAME % 'statistics')
stats = self.host_api.compute_node_statistics(context)
return dict(hypervisor_statistics=stats)

View File

@ -18,55 +18,121 @@ from oslo_policy import policy
from nova.policies import base
BASE_POLICY_NAME = 'os_compute_api:os-hypervisors'
BASE_POLICY_NAME = 'os_compute_api:os-hypervisors:%s'
DEPRECATED_POLICY = policy.DeprecatedRule(
'os_compute_api:os-hypervisors',
base.RULE_ADMIN_API,
)
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.
"""
hypervisors_policies = [
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME,
check_str=base.RULE_ADMIN_API,
description="""Policy rule for hypervisor related APIs.
This rule will be checked for the following APIs:
List all hypervisors, list all hypervisors with details, show
summary statistics for all hypervisors over all compute nodes,
show details for a hypervisor, show the uptime of a hypervisor,
search hypervisor by hypervisor_hostname pattern and list all
servers on hypervisors that can match the provided
hypervisor_hostname pattern.""",
name=BASE_POLICY_NAME % 'list',
check_str=base.SYSTEM_READER,
description="List all hypervisors.",
operations=[
{
'path': '/os-hypervisors',
'method': 'GET'
},
],
scope_types=['system'],
deprecated_rule=DEPRECATED_POLICY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since='21.0.0'),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'list-detail',
check_str=base.SYSTEM_READER,
description="List all hypervisors with details",
operations=[
{
'path': '/os-hypervisors/details',
'method': 'GET'
},
],
scope_types=['system'],
deprecated_rule=DEPRECATED_POLICY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since='21.0.0'),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'statistics',
check_str=base.SYSTEM_READER,
description="Show summary statistics for all hypervisors "
"over all compute nodes.",
operations=[
{
'path': '/os-hypervisors/statistics',
'method': 'GET'
},
],
scope_types=['system'],
deprecated_rule=DEPRECATED_POLICY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since='21.0.0'),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'show',
check_str=base.SYSTEM_READER,
description="Show details for a hypervisor.",
operations=[
{
'path': '/os-hypervisors/{hypervisor_id}',
'method': 'GET'
},
],
scope_types=['system'],
deprecated_rule=DEPRECATED_POLICY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since='21.0.0'),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'uptime',
check_str=base.SYSTEM_READER,
description="Show the uptime of a hypervisor.",
operations=[
{
'path': '/os-hypervisors/{hypervisor_id}/uptime',
'method': 'GET'
},
],
scope_types=['system'],
deprecated_rule=DEPRECATED_POLICY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since='21.0.0'),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'search',
check_str=base.SYSTEM_READER,
description="Search hypervisor by hypervisor_hostname pattern.",
operations=[
{
'path': '/os-hypervisors/{hypervisor_hostname_pattern}/search',
'method': 'GET'
},
],
scope_types=['system'],
deprecated_rule=DEPRECATED_POLICY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since='21.0.0'),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'servers',
check_str=base.SYSTEM_READER,
description="List all servers on hypervisors that can match "
"the provided hypervisor_hostname pattern.",
operations=[
{
'path':
'/os-hypervisors/{hypervisor_hostname_pattern}/servers',
'method': 'GET'
}
],
scope_types=['system']
scope_types=['system'],
deprecated_rule=DEPRECATED_POLICY,
deprecated_reason=DEPRECATED_REASON,
deprecated_since='21.0.0',
),
]

View File

@ -236,7 +236,6 @@ class HypervisorsTestV21(test.NoDBTestCase):
def setUp(self):
super(HypervisorsTestV21, self).setUp()
self._set_up_controller()
self.rule_hyp_show = "os_compute_api:os-hypervisors"
host_api = self.controller.host_api
host_api.compute_node_get_all = mock.MagicMock(
@ -306,11 +305,6 @@ class HypervisorsTestV21(test.NoDBTestCase):
self.assertEqual(dict(hypervisors=self.INDEX_HYPER_DICTS), result)
def test_index_non_admin(self):
req = self._get_request(False)
self.assertRaises(exception.PolicyNotAuthorized,
self.controller.index, req)
def test_index_compute_host_not_found(self):
"""Tests that if a service is deleted but the compute node is not we
don't fail when listing hypervisors.
@ -387,11 +381,6 @@ class HypervisorsTestV21(test.NoDBTestCase):
self.assertEqual(dict(hypervisors=self.DETAIL_HYPERS_DICTS), result)
def test_detail_non_admin(self):
req = self._get_request(False)
self.assertRaises(exception.PolicyNotAuthorized,
self.controller.detail, req)
def test_detail_compute_host_not_found(self):
"""Tests that if a service is deleted but the compute node is not we
don't fail when listing hypervisors.
@ -514,12 +503,6 @@ class HypervisorsTestV21(test.NoDBTestCase):
self.assertEqual(dict(hypervisor=self.DETAIL_HYPERS_DICTS[0]), result)
def test_show_non_admin(self):
req = self._get_request(False)
self.assertRaises(exception.PolicyNotAuthorized,
self.controller.show, req,
self._get_hyper_id())
def test_uptime_noid(self):
req = self._get_request(True)
hyper_id = uuids.hyper3 if self.expect_uuid_for_id else '3'
@ -553,12 +536,6 @@ class HypervisorsTestV21(test.NoDBTestCase):
req = self._get_request(True)
self.assertRaises(exc.HTTPNotFound, self.controller.uptime, req, 'abc')
def test_uptime_non_admin(self):
req = self._get_request(False)
self.assertRaises(exception.PolicyNotAuthorized,
self.controller.uptime, req,
self.TEST_HYPERS_OBJ[0].id)
def test_uptime_hypervisor_down(self):
with mock.patch.object(self.controller.host_api, 'get_host_uptime',
side_effect=exception.ComputeServiceUnavailable(host='dummy')
@ -603,12 +580,6 @@ class HypervisorsTestV21(test.NoDBTestCase):
self.assertEqual(dict(hypervisors=self.INDEX_HYPER_DICTS), result)
def test_search_non_admin(self):
req = self._get_request(False)
self.assertRaises(exception.PolicyNotAuthorized,
self.controller.search, req,
self.TEST_HYPERS_OBJ[0].id)
def test_search_non_exist(self):
with mock.patch.object(self.controller.host_api,
'compute_node_search_by_hypervisor',
@ -673,12 +644,6 @@ class HypervisorsTestV21(test.NoDBTestCase):
req, '115')
self.assertEqual(1, mock_node_search.call_count)
def test_servers_non_admin(self):
req = self._get_request(False)
self.assertRaises(exception.PolicyNotAuthorized,
self.controller.servers, req,
self.TEST_HYPERS_OBJ[0].id)
def test_servers_with_non_integer_hypervisor_id(self):
with mock.patch.object(self.controller.host_api,
'compute_node_search_by_hypervisor',
@ -716,11 +681,6 @@ class HypervisorsTestV21(test.NoDBTestCase):
running_vms=4,
disk_available_least=200)), result)
def test_statistics_non_admin(self):
req = self._get_request(False)
self.assertRaises(exception.PolicyNotAuthorized,
self.controller.statistics, req)
class HypervisorsTestV228(HypervisorsTestV21):
api_version = '2.28'
@ -1080,12 +1040,6 @@ class HypervisorsTestV253(HypervisorsTestV252):
self.assertRaises(exc.HTTPNotFound, self.controller.index, req)
s.assert_called_once_with(req.environ['nova.context'], 'shenzhen')
def test_servers_non_admin(self):
"""There is no reason to test this for 2.53 since the
/os-hypervisors/servers route is deprecated.
"""
pass
def test_servers_non_id(self):
"""There is no reason to test this for 2.53 since the
/os-hypervisors/servers route is deprecated.
@ -1164,12 +1118,6 @@ class HypervisorsTestV253(HypervisorsTestV252):
"""
pass
def test_search_non_admin(self):
"""There is no reason to test this for 2.53 since the
/os-hypervisors/search route is deprecated.
"""
pass
def test_search_unmapped(self):
"""This is already tested with test_index_compute_host_not_mapped."""
pass

View File

@ -58,6 +58,13 @@ policy_data = """
"os_compute_api:os-instance-actions:events:details": "",
"os_compute_api:os-instance-usage-audit-log:list": "",
"os_compute_api:os-instance-usage-audit-log:show": "",
"os_compute_api:os-hypervisors:list": "",
"os_compute_api:os-hypervisors:list-detail": "",
"os_compute_api:os-hypervisors:statistics": "",
"os_compute_api:os-hypervisors:show": "",
"os_compute_api:os-hypervisors:uptime": "",
"os_compute_api:os-hypervisors:search": "",
"os_compute_api:os-hypervisors:servers": "",
"os_compute_api:os-lock-server:lock": "",
"os_compute_api:os-lock-server:unlock": "",

View File

@ -13,6 +13,7 @@
import mock
from nova.api.openstack.compute import hypervisors
from nova.policies import base as base_policy
from nova.policies import hypervisors as hv_policies
from nova.tests.unit.api.openstack import fakes
from nova.tests.unit.policies import base
@ -35,68 +36,72 @@ class HypervisorsPolicyTest(base.BasePolicyTest):
self.controller.host_api.service_get_by_compute_host = mock.MagicMock()
self.controller.host_api.compute_node_get = mock.MagicMock()
# Check that admin is able to perform operations
# on hypervisors.
self.admin_authorized_contexts = [
self.legacy_admin_context, self.system_admin_context,
# Check that system scoped admin, member and reader are able to
# perform operations on hypervisors.
# 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 agent 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-admin is not able to perform operations
# on hypervisors.
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
]
# Check that non-system-reader are not able to perform operations
# on hypervisors
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_list_hypervisors_policy(self):
rule_name = hv_policies.BASE_POLICY_NAME
self.common_policy_check(self.admin_authorized_contexts,
self.admin_unauthorized_contexts,
rule_name = hv_policies.BASE_POLICY_NAME % 'list'
self.common_policy_check(self.reader_authorized_contexts,
self.reader_unauthorized_contexts,
rule_name, self.controller.index,
self.req)
def test_list_details_hypervisors_policy(self):
rule_name = hv_policies.BASE_POLICY_NAME
self.common_policy_check(self.admin_authorized_contexts,
self.admin_unauthorized_contexts,
rule_name = hv_policies.BASE_POLICY_NAME % 'list-detail'
self.common_policy_check(self.reader_authorized_contexts,
self.reader_unauthorized_contexts,
rule_name, self.controller.detail,
self.req)
def test_show_hypervisors_policy(self):
rule_name = hv_policies.BASE_POLICY_NAME
self.common_policy_check(self.admin_authorized_contexts,
self.admin_unauthorized_contexts,
rule_name = hv_policies.BASE_POLICY_NAME % 'show'
self.common_policy_check(self.reader_authorized_contexts,
self.reader_unauthorized_contexts,
rule_name, self.controller.show,
self.req, 11111)
@mock.patch('nova.compute.api.HostAPI.get_host_uptime')
def test_uptime_hypervisors_policy(self, mock_uptime):
rule_name = hv_policies.BASE_POLICY_NAME
self.common_policy_check(self.admin_authorized_contexts,
self.admin_unauthorized_contexts,
rule_name = hv_policies.BASE_POLICY_NAME % 'uptime'
self.common_policy_check(self.reader_authorized_contexts,
self.reader_unauthorized_contexts,
rule_name, self.controller.uptime,
self.req, 11111)
def test_search_hypervisors_policy(self):
rule_name = hv_policies.BASE_POLICY_NAME
self.common_policy_check(self.admin_authorized_contexts,
self.admin_unauthorized_contexts,
rule_name = hv_policies.BASE_POLICY_NAME % 'search'
self.common_policy_check(self.reader_authorized_contexts,
self.reader_unauthorized_contexts,
rule_name, self.controller.search,
self.req, 11111)
def test_servers_hypervisors_policy(self):
rule_name = hv_policies.BASE_POLICY_NAME
self.common_policy_check(self.admin_authorized_contexts,
self.admin_unauthorized_contexts,
rule_name = hv_policies.BASE_POLICY_NAME % 'servers'
self.common_policy_check(self.reader_authorized_contexts,
self.reader_unauthorized_contexts,
rule_name, self.controller.servers,
self.req, 11111)
@mock.patch('nova.compute.api.HostAPI.compute_node_statistics')
def test_statistics_hypervisors_policy(self, mock_statistics):
rule_name = hv_policies.BASE_POLICY_NAME
self.common_policy_check(self.admin_authorized_contexts,
self.admin_unauthorized_contexts,
rule_name = hv_policies.BASE_POLICY_NAME % 'statistics'
self.common_policy_check(self.reader_authorized_contexts,
self.reader_unauthorized_contexts,
rule_name, self.controller.statistics,
self.req)
@ -115,16 +120,39 @@ class HypervisorsScopeTypePolicyTest(HypervisorsPolicyTest):
super(HypervisorsScopeTypePolicyTest, self).setUp()
self.flags(enforce_scope=True, group="oslo_policy")
# Check that system admin is able to perform operations
# Check that system reader is able to perform operations
# on hypervisors.
self.admin_authorized_contexts = [
self.system_admin_context]
# Check that non-system-admin is not able to perform operations
self.reader_authorized_contexts = [
self.system_admin_context, self.system_member_context,
self.system_reader_context]
# Check that non-system-reader is not able to perform operations
# on hypervisors.
self.admin_unauthorized_contexts = [
self.legacy_admin_context, self.system_member_context,
self.system_reader_context, self.project_admin_context,
self.reader_unauthorized_contexts = [
self.legacy_admin_context, self.project_admin_context,
self.system_foo_context, self.project_member_context,
self.other_project_member_context,
self.project_foo_context, self.project_reader_context
]
class HypervisorsNoLegacyPolicyTest(HypervisorsScopeTypePolicyTest):
"""Test Hypervisors APIs policies with system scope enabled,
and no more deprecated rules.
"""
without_deprecated_rules = True
rules_without_deprecation = {
hv_policies.BASE_POLICY_NAME % 'list':
base_policy.SYSTEM_READER,
hv_policies.BASE_POLICY_NAME % 'list-detail':
base_policy.SYSTEM_READER,
hv_policies.BASE_POLICY_NAME % 'show':
base_policy.SYSTEM_READER,
hv_policies.BASE_POLICY_NAME % 'statistics':
base_policy.SYSTEM_READER,
hv_policies.BASE_POLICY_NAME % 'uptime':
base_policy.SYSTEM_READER,
hv_policies.BASE_POLICY_NAME % 'search':
base_policy.SYSTEM_READER,
hv_policies.BASE_POLICY_NAME % 'servers':
base_policy.SYSTEM_READER,
}

View File

@ -343,7 +343,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
"os_compute_api:os-flavor-manage:update",
"os_compute_api:os-flavor-manage:delete",
"os_compute_api:os-hosts",
"os_compute_api:os-hypervisors",
"os_compute_api:os-instance-actions:events",
"os_compute_api:os-lock-server:unlock:unlock_override",
"os_compute_api:os-migrate-server:migrate",
@ -461,6 +460,13 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
"os_compute_api:os-instance-usage-audit-log:list",
"os_compute_api:os-instance-usage-audit-log:show",
"os_compute_api:os-agents:list",
"os_compute_api:os-hypervisors:list",
"os_compute_api:os-hypervisors:list-detail",
"os_compute_api:os-hypervisors:show",
"os_compute_api:os-hypervisors:statistics",
"os_compute_api:os-hypervisors:uptime",
"os_compute_api:os-hypervisors:search",
"os_compute_api:os-hypervisors:servers",
)
self.system_reader_or_owner_rules = (