Add test to ensure policy is always authorized
This adds new unit tests to ensure that all API methods decorated with @expose.expose are also calling policy.authorize() and context.to_policy_values() within the method body. This is done by patching sys.modules to replace the @expose decorator with a wrapper that records a reference to the exposed method. Then, all API modules' (chassis, node, port, etc.) sources are reloaded within the unit test, causing the test classes own decorator to be called, and references to every @expose'd method to be recorded in the test class instance. The test case then iterates over that list of function references, and inspects the python source of each one to determine if it contains a call to the policy.authorize and context.to_policy_values methods. An error is raised if it does not. This approach is rather brutish, but without being able to invoke @authorize as a decorator (*) I have not found a better way than inspecting the source. (*) we can't invoke @authorize as a decorator because decorators are evaluated at module compile time, but @authorize requires run-time context available only from the Pecan run-time magic accessor pecan.request.context. Co-Authored-By: Vladyslav Drok <vdrok@mirantis.com> Change-Id: Iebfd9183dcbd49dbef78398c07327a347a41976e
This commit is contained in:
parent
633abbeff8
commit
f6b28b3f25
78
ironic/tests/unit/api/v1/test_expose.py
Normal file
78
ironic/tests/unit/api/v1/test_expose.py
Normal file
@ -0,0 +1,78 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import imp
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
|
||||
import mock
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from ironic.tests import base as test_base
|
||||
|
||||
|
||||
class TestExposedAPIMethodsCheckPolicy(test_base.TestCase):
|
||||
"""Ensure that all exposed HTTP endpoints call authorize."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestExposedAPIMethodsCheckPolicy, self).setUp()
|
||||
self.original_method = sys.modules['ironic.api.expose'].expose
|
||||
self.exposed_methods = set()
|
||||
|
||||
def expose_and_track(*args, **kwargs):
|
||||
def wrap(f):
|
||||
if f not in self.exposed_methods:
|
||||
self.exposed_methods.add(f)
|
||||
e = self.original_method(*args, **kwargs)
|
||||
return e(f)
|
||||
return wrap
|
||||
|
||||
p = mock.patch('ironic.api.expose.expose', expose_and_track)
|
||||
p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _test(self, module):
|
||||
module_path = os.path.abspath(sys.modules[module].__file__)
|
||||
# NOTE(vdrok): coverage runs on compiled .pyc files, which breaks
|
||||
# load_source. Strip c and o letters from the end of the module path,
|
||||
# just in case someone tries to use .pyo or .pyc for whatever reason
|
||||
imp.load_source(uuidutils.generate_uuid(), module_path.rstrip('co'))
|
||||
|
||||
for func in self.exposed_methods:
|
||||
src = inspect.getsource(func)
|
||||
self.assertTrue('policy.authorize' in src,
|
||||
'policy.authorize call not found in exposed '
|
||||
'method %s' % func)
|
||||
self.assertTrue('context.to_policy_values' in src,
|
||||
'context.to_policy_values call not found in '
|
||||
'exposed method %s' % func)
|
||||
|
||||
def test_chasis_api_policy(self):
|
||||
self._test('ironic.api.controllers.v1.chassis')
|
||||
|
||||
def test_driver_api_policy(self):
|
||||
self._test('ironic.api.controllers.v1.driver')
|
||||
|
||||
def test_node_api_policy(self):
|
||||
self._test('ironic.api.controllers.v1.node')
|
||||
|
||||
def test_port_api_policy(self):
|
||||
self._test('ironic.api.controllers.v1.port')
|
||||
|
||||
def test_portgroup_api_policy(self):
|
||||
self._test('ironic.api.controllers.v1.portgroup')
|
||||
|
||||
def test_ramdisk_api_policy(self):
|
||||
self._test('ironic.api.controllers.v1.ramdisk')
|
Loading…
x
Reference in New Issue
Block a user