Implement secure RBAC
The default policy will been replaced with one which aligns with the Secure-RBAC scopes and roles. Since ironic-inspector is a tool used only by system-level admins, only the ``system`` scope is supported, and the only roles in the policy rules are ``admin`` and ``reader``. The is_admin and is_observer rules are deprecated for removal, and every rule which refers to them are deprecated in favor of the system-scoped equivalent (system_scope:all with role:admin or role:reader) No unit tests covered the existing policy, these are now covered by test_acl.TestACLDeprecated. Change-Id: I4d038245c6b97b1504fb47eeec78ad3f9e5a897c
This commit is contained in:
parent
5c79d7552a
commit
c9e312f8b4
@ -16,6 +16,7 @@ import sys
|
|||||||
|
|
||||||
from oslo_concurrency import lockutils
|
from oslo_concurrency import lockutils
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_log import versionutils
|
||||||
from oslo_policy import opts
|
from oslo_policy import opts
|
||||||
from oslo_policy import policy
|
from oslo_policy import policy
|
||||||
|
|
||||||
@ -23,23 +24,52 @@ CONF = cfg.CONF
|
|||||||
|
|
||||||
_ENFORCER = None
|
_ENFORCER = None
|
||||||
|
|
||||||
|
|
||||||
# TODO(gmann): Remove setting the default value of config policy_file
|
# TODO(gmann): Remove setting the default value of config policy_file
|
||||||
# once oslo_policy change the default value to 'policy.yaml'.
|
# once oslo_policy change the default value to 'policy.yaml'.
|
||||||
# https://github.com/openstack/oslo.policy/blob/a626ad12fe5a3abd49d70e3e5b95589d279ab578/oslo_policy/opts.py#L49
|
# https://github.com/openstack/oslo.policy/blob/a626ad12fe5a3abd49d70e3e5b95589d279ab578/oslo_policy/opts.py#L49
|
||||||
DEFAULT_POLICY_FILE = 'policy.yaml'
|
DEFAULT_POLICY_FILE = 'policy.yaml'
|
||||||
opts.set_defaults(cfg.CONF, DEFAULT_POLICY_FILE)
|
opts.set_defaults(cfg.CONF, DEFAULT_POLICY_FILE)
|
||||||
|
|
||||||
|
# Generic policy check string for system administrators. These are the people
|
||||||
|
# who need the highest level of authorization to operate the deployment.
|
||||||
|
# They're allowed to create, read, update, or delete any system-specific
|
||||||
|
# resource. They can also operate on project-specific resources where
|
||||||
|
# applicable (e.g., cleaning up baremetal hosts)
|
||||||
|
SYSTEM_ADMIN = 'role:admin and system_scope:all'
|
||||||
|
|
||||||
|
# Generic policy check string for system users who don't require all the
|
||||||
|
# authorization that system administrators typically have. This persona, or
|
||||||
|
# check string, typically isn't used by default, but it's existence it useful
|
||||||
|
# in the event a deployment wants to offload some administrative action from
|
||||||
|
# system administrator to system members
|
||||||
|
SYSTEM_MEMBER = 'role:member and system_scope:all'
|
||||||
|
|
||||||
|
# Generic policy check string for read-only access to system-level resources.
|
||||||
|
# This persona is useful for someone who needs access for auditing or even
|
||||||
|
# support. These uses are also able to view project-specific resources where
|
||||||
|
# applicable (e.g., listing all volumes in the deployment, regardless of the
|
||||||
|
# project they belong to).
|
||||||
|
SYSTEM_READER = 'role:reader and system_scope:all'
|
||||||
|
|
||||||
|
deprecated_node_reason = """
|
||||||
|
The inspector API is now aware of system scope and default roles.
|
||||||
|
"""
|
||||||
|
|
||||||
default_policies = [
|
default_policies = [
|
||||||
policy.RuleDefault(
|
policy.RuleDefault(
|
||||||
'is_admin',
|
'is_admin',
|
||||||
'role:admin or role:administrator or role:baremetal_admin',
|
'role:admin or role:administrator or role:baremetal_admin',
|
||||||
description='Full read/write API access'),
|
description='Full read/write API access',
|
||||||
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY),
|
||||||
policy.RuleDefault(
|
policy.RuleDefault(
|
||||||
'is_observer',
|
'is_observer',
|
||||||
'role:baremetal_observer',
|
'role:baremetal_observer',
|
||||||
description='Read-only API access'),
|
description='Read-only API access',
|
||||||
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY),
|
||||||
policy.RuleDefault(
|
policy.RuleDefault(
|
||||||
'public_api',
|
'public_api',
|
||||||
'is_public_api:True',
|
'is_public_api:True',
|
||||||
@ -66,67 +96,126 @@ api_version_policies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
deprecated_introspection_status = policy.DeprecatedRule(
|
||||||
|
name='introspection:status',
|
||||||
|
check_str='rule:is_admin or rule:is_observer'
|
||||||
|
)
|
||||||
|
deprecated_introspection_start = policy.DeprecatedRule(
|
||||||
|
name='introspection:start',
|
||||||
|
check_str='rule:is_admin'
|
||||||
|
)
|
||||||
|
deprecated_introspection_abort = policy.DeprecatedRule(
|
||||||
|
name='introspection:abort',
|
||||||
|
check_str='rule:is_admin'
|
||||||
|
)
|
||||||
|
deprecated_introspection_data = policy.DeprecatedRule(
|
||||||
|
name='introspection:data',
|
||||||
|
check_str='rule:is_admin'
|
||||||
|
)
|
||||||
|
deprecated_introspection_reapply = policy.DeprecatedRule(
|
||||||
|
name='introspection:reapply',
|
||||||
|
check_str='rule:is_admin'
|
||||||
|
)
|
||||||
|
deprecated_introspection_rule_get = policy.DeprecatedRule(
|
||||||
|
name='introspection:rule:get',
|
||||||
|
check_str='rule:is_admin'
|
||||||
|
)
|
||||||
|
deprecated_introspection_rule_delete = policy.DeprecatedRule(
|
||||||
|
name='introspection:rule:delete',
|
||||||
|
check_str='rule:is_admin'
|
||||||
|
)
|
||||||
|
deprecated_introspection_rule_create = policy.DeprecatedRule(
|
||||||
|
name='introspection:rule:create',
|
||||||
|
check_str='rule:is_admin'
|
||||||
|
)
|
||||||
|
|
||||||
introspection_policies = [
|
introspection_policies = [
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
'introspection:continue',
|
name='introspection:continue',
|
||||||
'rule:public_api',
|
check_str='rule:public_api',
|
||||||
'Ramdisk callback to continue introspection',
|
description='Ramdisk callback to continue introspection',
|
||||||
[{'path': '/continue', 'method': 'POST'}]
|
operations=[{'path': '/continue', 'method': 'POST'}],
|
||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
'introspection:status',
|
name='introspection:status',
|
||||||
'rule:is_admin or rule:is_observer',
|
check_str=SYSTEM_READER,
|
||||||
'Get introspection status',
|
description='Get introspection status',
|
||||||
[{'path': '/introspection', 'method': 'GET'},
|
operations=[{'path': '/introspection', 'method': 'GET'},
|
||||||
{'path': '/introspection/{node_id}', 'method': 'GET'}]
|
{'path': '/introspection/{node_id}', 'method': 'GET'}],
|
||||||
|
deprecated_rule=deprecated_introspection_status,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
'introspection:start',
|
name='introspection:start',
|
||||||
'rule:is_admin',
|
check_str=SYSTEM_ADMIN,
|
||||||
'Start introspection',
|
description='Start introspection',
|
||||||
[{'path': '/introspection/{node_id}', 'method': 'POST'}]
|
operations=[{'path': '/introspection/{node_id}', 'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_introspection_start,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
'introspection:abort',
|
name='introspection:abort',
|
||||||
'rule:is_admin',
|
check_str=SYSTEM_ADMIN,
|
||||||
'Abort introspection',
|
description='Abort introspection',
|
||||||
[{'path': '/introspection/{node_id}/abort', 'method': 'POST'}]
|
operations=[{'path': '/introspection/{node_id}/abort',
|
||||||
|
'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_introspection_abort,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
'introspection:data',
|
name='introspection:data',
|
||||||
'rule:is_admin',
|
check_str=SYSTEM_ADMIN,
|
||||||
'Get introspection data',
|
description='Get introspection data',
|
||||||
[{'path': '/introspection/{node_id}/data', 'method': 'GET'}]
|
operations=[{'path': '/introspection/{node_id}/data',
|
||||||
|
'method': 'GET'}],
|
||||||
|
deprecated_rule=deprecated_introspection_data,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
'introspection:reapply',
|
name='introspection:reapply',
|
||||||
'rule:is_admin',
|
check_str=SYSTEM_ADMIN,
|
||||||
'Reapply introspection on stored data',
|
description='Reapply introspection on stored data',
|
||||||
[{'path': '/introspection/{node_id}/data/unprocessed',
|
operations=[{'path': '/introspection/{node_id}/data/unprocessed',
|
||||||
'method': 'POST'}]
|
'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_introspection_reapply,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
rule_policies = [
|
rule_policies = [
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
'introspection:rule:get',
|
name='introspection:rule:get',
|
||||||
'rule:is_admin',
|
check_str=SYSTEM_ADMIN,
|
||||||
'Get introspection rule(s)',
|
description='Get introspection rule(s)',
|
||||||
[{'path': '/rules', 'method': 'GET'},
|
operations=[{'path': '/rules', 'method': 'GET'},
|
||||||
{'path': '/rules/{rule_id}', 'method': 'GET'}]
|
{'path': '/rules/{rule_id}', 'method': 'GET'}],
|
||||||
|
deprecated_rule=deprecated_introspection_rule_get,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
'introspection:rule:delete',
|
name='introspection:rule:delete',
|
||||||
'rule:is_admin',
|
check_str=SYSTEM_ADMIN,
|
||||||
'Delete introspection rule(s)',
|
description='Delete introspection rule(s)',
|
||||||
[{'path': '/rules', 'method': 'DELETE'},
|
operations=[{'path': '/rules', 'method': 'DELETE'},
|
||||||
{'path': '/rules/{rule_id}', 'method': 'DELETE'}]
|
{'path': '/rules/{rule_id}', 'method': 'DELETE'}],
|
||||||
|
deprecated_rule=deprecated_introspection_rule_delete,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
),
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
'introspection:rule:create',
|
name='introspection:rule:create',
|
||||||
'rule:is_admin',
|
check_str=SYSTEM_ADMIN,
|
||||||
'Create introspection rule',
|
description='Create introspection rule',
|
||||||
[{'path': '/rules', 'method': 'POST'}]
|
operations=[{'path': '/rules', 'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_introspection_rule_create,
|
||||||
|
deprecated_reason=deprecated_node_reason,
|
||||||
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
438
ironic_inspector/test/unit/test_acl.py
Normal file
438
ironic_inspector/test/unit/test_acl.py
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
# 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 datetime
|
||||||
|
from unittest import mock
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import fixtures
|
||||||
|
from keystoneauth1.fixture import v3 as v3_token
|
||||||
|
from keystonemiddleware import auth_token
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_context import context as oslo_context
|
||||||
|
import oslo_messaging as messaging
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
from ironic_inspector.common import rpc
|
||||||
|
import ironic_inspector.conf
|
||||||
|
from ironic_inspector import introspection_state as istate
|
||||||
|
from ironic_inspector import main
|
||||||
|
from ironic_inspector import node_cache
|
||||||
|
from ironic_inspector.test import base as test_base
|
||||||
|
|
||||||
|
CONF = ironic_inspector.conf.CONF
|
||||||
|
|
||||||
|
|
||||||
|
# Tokens for RBAC policy tests
|
||||||
|
ADMIN_TOKEN = uuid.uuid4().hex
|
||||||
|
admin_context = oslo_context.RequestContext(
|
||||||
|
user_id=ADMIN_TOKEN,
|
||||||
|
roles=['admin', 'member', 'reader'],
|
||||||
|
)
|
||||||
|
|
||||||
|
MEMBER_TOKEN = uuid.uuid4().hex
|
||||||
|
member_context = oslo_context.RequestContext(
|
||||||
|
user_id=MEMBER_TOKEN,
|
||||||
|
roles=['member', 'reader'],
|
||||||
|
)
|
||||||
|
|
||||||
|
READER_TOKEN = uuid.uuid4().hex
|
||||||
|
reader_context = oslo_context.RequestContext(
|
||||||
|
user_id=READER_TOKEN,
|
||||||
|
roles=['reader'],
|
||||||
|
)
|
||||||
|
|
||||||
|
NO_ROLE_TOKEN = uuid.uuid4().hex
|
||||||
|
no_role_context = oslo_context.RequestContext(
|
||||||
|
user_id=READER_TOKEN,
|
||||||
|
roles=[],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tokens for deprecated policy tests
|
||||||
|
BM_ADMIN_TOKEN = uuid.uuid4().hex
|
||||||
|
bm_admin_context = oslo_context.RequestContext(
|
||||||
|
user_id=BM_ADMIN_TOKEN,
|
||||||
|
roles=['baremetal_admin'],
|
||||||
|
)
|
||||||
|
BM_OBSERVER_TOKEN = uuid.uuid4().hex
|
||||||
|
bm_observer_context = oslo_context.RequestContext(
|
||||||
|
user_id=BM_OBSERVER_TOKEN,
|
||||||
|
roles=['baremetal_observer'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
USERS = {
|
||||||
|
ADMIN_TOKEN: admin_context.to_dict(),
|
||||||
|
MEMBER_TOKEN: member_context.to_dict(),
|
||||||
|
READER_TOKEN: reader_context.to_dict(),
|
||||||
|
NO_ROLE_TOKEN: no_role_context.to_dict(),
|
||||||
|
BM_ADMIN_TOKEN: bm_admin_context.to_dict(),
|
||||||
|
BM_OBSERVER_TOKEN: bm_observer_context.to_dict(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BasePolicyTest(test_base.BaseTest):
|
||||||
|
|
||||||
|
def init_app(self):
|
||||||
|
CONF.set_override('auth_strategy', 'keystone')
|
||||||
|
main._app.testing = True
|
||||||
|
self.app = main.get_app().test_client()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(BasePolicyTest, self).setUp()
|
||||||
|
self.init_app()
|
||||||
|
self.uuid = uuidutils.generate_uuid()
|
||||||
|
self.rpc_get_client_mock = self.useFixture(
|
||||||
|
fixtures.MockPatchObject(rpc, 'get_client', autospec=True)).mock
|
||||||
|
self.client_mock = mock.MagicMock(spec=messaging.RPCClient)
|
||||||
|
self.rpc_get_client_mock.return_value = self.client_mock
|
||||||
|
|
||||||
|
self.fake_token = None
|
||||||
|
mock_auth = mock.patch.object(
|
||||||
|
auth_token.AuthProtocol, 'process_request',
|
||||||
|
autospec=True)
|
||||||
|
self.mock_auth = mock_auth.start()
|
||||||
|
self.addCleanup(mock_auth.stop)
|
||||||
|
self.mock_auth.side_effect = self._fake_process_request
|
||||||
|
|
||||||
|
mock_get = mock.patch.object(node_cache, 'get_node', autospec=True)
|
||||||
|
get = mock_get.start()
|
||||||
|
self.addCleanup(mock_get.stop)
|
||||||
|
get.return_value = node_cache.NodeInfo(
|
||||||
|
uuid=self.uuid,
|
||||||
|
started_at=datetime.datetime(1, 1, 1),
|
||||||
|
state=istate.States.processing)
|
||||||
|
|
||||||
|
def _fake_process_request(self, request, meow):
|
||||||
|
if self.fake_token:
|
||||||
|
request.user_token_valid = True
|
||||||
|
request.user_token = True
|
||||||
|
# is this right?!?
|
||||||
|
request.token_info = self.fake_token
|
||||||
|
request.auth_token = v3_token.Token(
|
||||||
|
user_id=self.fake_token['user'])
|
||||||
|
else:
|
||||||
|
# Because of this, the user will always get a 403 in testing, even
|
||||||
|
# if the API would normally return a 401 if a token is valid
|
||||||
|
request.user_token_valid = False
|
||||||
|
|
||||||
|
def set_token(self, token):
|
||||||
|
self.fake_token = USERS[token]
|
||||||
|
headers = {
|
||||||
|
'X-Auth-Token': token,
|
||||||
|
'X-Roles': ','.join(self.fake_token['roles'])
|
||||||
|
}
|
||||||
|
if cfg.CONF.oslo_policy.enforce_scope:
|
||||||
|
headers['OpenStack-System-Scope'] = 'all'
|
||||||
|
return headers
|
||||||
|
|
||||||
|
def assert_status(self, status_code, token, request_func, path, data=None):
|
||||||
|
headers = self.set_token(token)
|
||||||
|
res = request_func(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(status_code, res.status_code)
|
||||||
|
|
||||||
|
|
||||||
|
class TestACLDeprecated(BasePolicyTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestACLDeprecated, self).setUp()
|
||||||
|
cfg.CONF.set_override('enforce_scope', False, group='oslo_policy')
|
||||||
|
cfg.CONF.set_override('enforce_new_defaults', False,
|
||||||
|
group='oslo_policy')
|
||||||
|
|
||||||
|
def test_root_baremetal_admin(self):
|
||||||
|
self.assert_status(200, BM_ADMIN_TOKEN, self.app.get, '/')
|
||||||
|
self.assert_status(200, BM_ADMIN_TOKEN, self.app.get, '/v1')
|
||||||
|
|
||||||
|
def test_root_baremetal_observer(self):
|
||||||
|
self.assert_status(200, BM_OBSERVER_TOKEN, self.app.get, '/')
|
||||||
|
self.assert_status(200, BM_OBSERVER_TOKEN, self.app.get, '/v1')
|
||||||
|
|
||||||
|
def test_root_system_no_role(self):
|
||||||
|
self.assert_status(200, NO_ROLE_TOKEN, self.app.get, '/')
|
||||||
|
self.assert_status(200, NO_ROLE_TOKEN, self.app.get, '/v1')
|
||||||
|
|
||||||
|
def test_introspect_baremetal_admin(self):
|
||||||
|
self.assert_status(202, BM_ADMIN_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s' % self.uuid)
|
||||||
|
|
||||||
|
def test_introspect_baremetal_observer(self):
|
||||||
|
self.assert_status(403, BM_OBSERVER_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/abort' % self.uuid)
|
||||||
|
|
||||||
|
def test_abort_baremetal_admin(self):
|
||||||
|
self.assert_status(202, BM_ADMIN_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/abort' % self.uuid)
|
||||||
|
|
||||||
|
def test_abort_baremetal_observer(self):
|
||||||
|
self.assert_status(403, BM_OBSERVER_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/abort' % self.uuid)
|
||||||
|
|
||||||
|
def test_status_baremetal_admin(self):
|
||||||
|
self.assert_status(200, BM_ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s' % self.uuid)
|
||||||
|
|
||||||
|
def test_status_baremetal_observer(self):
|
||||||
|
self.assert_status(200, BM_OBSERVER_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s' % self.uuid)
|
||||||
|
|
||||||
|
def test_list_baremetal_admin(self):
|
||||||
|
self.assert_status(200, BM_ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection')
|
||||||
|
|
||||||
|
def test_list_baremetal_observer(self):
|
||||||
|
self.assert_status(200, BM_OBSERVER_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection')
|
||||||
|
|
||||||
|
def test_data_baremetal_admin(self):
|
||||||
|
self.assert_status(404, BM_ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s/data' % self.uuid)
|
||||||
|
|
||||||
|
def test_data_baremetal_observer(self):
|
||||||
|
self.assert_status(403, BM_OBSERVER_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s/data' % self.uuid)
|
||||||
|
|
||||||
|
def test_data_unprocessed_baremetal_admin(self):
|
||||||
|
self.assert_status(400, BM_ADMIN_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/data/unprocessed' % self.uuid,
|
||||||
|
data={'foo': 'bar'})
|
||||||
|
|
||||||
|
def test_data_unprocessed_baremetal_observer(self):
|
||||||
|
self.assert_status(403, BM_OBSERVER_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/data/unprocessed' % self.uuid,
|
||||||
|
data={'foo': 'bar'})
|
||||||
|
|
||||||
|
def test_rule_list_baremetal_admin(self):
|
||||||
|
self.assert_status(200, BM_ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_list_baremetal_observer(self):
|
||||||
|
self.assert_status(403, BM_OBSERVER_TOKEN, self.app.get,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_get_baremetal_admin(self):
|
||||||
|
self.assert_status(404, BM_ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_get_baremetal_observer(self):
|
||||||
|
self.assert_status(403, BM_OBSERVER_TOKEN, self.app.get,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_delete_all_baremetal_admin(self):
|
||||||
|
self.assert_status(204, BM_ADMIN_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_delete_all_baremetal_observer(self):
|
||||||
|
self.assert_status(403, BM_OBSERVER_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_delete_baremetal_admin(self):
|
||||||
|
self.assert_status(404, BM_ADMIN_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_delete_baremetal_observer(self):
|
||||||
|
self.assert_status(403, BM_OBSERVER_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_create_baremetal_admin(self):
|
||||||
|
self.assert_status(500, BM_ADMIN_TOKEN, self.app.post,
|
||||||
|
'/v1/rules',
|
||||||
|
data={
|
||||||
|
'uuid': self.uuid,
|
||||||
|
'conditions': 'cond',
|
||||||
|
'actions': 'act'
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_rule_create_baremetal_observer(self):
|
||||||
|
self.assert_status(403, BM_OBSERVER_TOKEN, self.app.post,
|
||||||
|
'/v1/rules',
|
||||||
|
data={
|
||||||
|
'uuid': self.uuid,
|
||||||
|
'conditions': 'cond',
|
||||||
|
'actions': 'act'
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class TestRBACScoped(BasePolicyTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestRBACScoped, self).setUp()
|
||||||
|
cfg.CONF.set_override('enforce_scope', True, group='oslo_policy')
|
||||||
|
cfg.CONF.set_override('enforce_new_defaults', True,
|
||||||
|
group='oslo_policy')
|
||||||
|
|
||||||
|
def test_root_system_admin(self):
|
||||||
|
self.assert_status(200, ADMIN_TOKEN, self.app.get, '/')
|
||||||
|
self.assert_status(200, ADMIN_TOKEN, self.app.get, '/v1')
|
||||||
|
|
||||||
|
def test_root_system_member(self):
|
||||||
|
self.assert_status(200, MEMBER_TOKEN, self.app.get, '/')
|
||||||
|
self.assert_status(200, MEMBER_TOKEN, self.app.get, '/v1')
|
||||||
|
|
||||||
|
def test_root_system_reader(self):
|
||||||
|
self.assert_status(200, READER_TOKEN, self.app.get, '/')
|
||||||
|
self.assert_status(200, READER_TOKEN, self.app.get, '/v1')
|
||||||
|
|
||||||
|
def test_root_system_no_role(self):
|
||||||
|
self.assert_status(200, NO_ROLE_TOKEN, self.app.get, '/')
|
||||||
|
self.assert_status(200, NO_ROLE_TOKEN, self.app.get, '/v1')
|
||||||
|
|
||||||
|
def test_introspect_system_admin(self):
|
||||||
|
self.assert_status(202, ADMIN_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s' % self.uuid)
|
||||||
|
|
||||||
|
def test_introspect_system_member(self):
|
||||||
|
self.assert_status(403, MEMBER_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s' % self.uuid)
|
||||||
|
|
||||||
|
def test_introspect_system_reader(self):
|
||||||
|
self.assert_status(403, READER_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/abort' % self.uuid)
|
||||||
|
|
||||||
|
def test_abort_system_admin(self):
|
||||||
|
self.assert_status(202, ADMIN_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/abort' % self.uuid)
|
||||||
|
|
||||||
|
def test_abort_system_member(self):
|
||||||
|
self.assert_status(403, MEMBER_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/abort' % self.uuid)
|
||||||
|
|
||||||
|
def test_abort_system_reader(self):
|
||||||
|
self.assert_status(403, READER_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/abort' % self.uuid)
|
||||||
|
|
||||||
|
def test_status_system_admin(self):
|
||||||
|
self.assert_status(200, ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s' % self.uuid)
|
||||||
|
|
||||||
|
def test_status_system_member(self):
|
||||||
|
self.assert_status(200, MEMBER_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s' % self.uuid)
|
||||||
|
|
||||||
|
def test_status_system_reader(self):
|
||||||
|
self.assert_status(200, READER_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s' % self.uuid)
|
||||||
|
|
||||||
|
def test_list_system_admin(self):
|
||||||
|
self.assert_status(200, ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection')
|
||||||
|
|
||||||
|
def test_list_system_member(self):
|
||||||
|
self.assert_status(200, MEMBER_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection')
|
||||||
|
|
||||||
|
def test_list_system_reader(self):
|
||||||
|
self.assert_status(200, READER_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection')
|
||||||
|
|
||||||
|
def test_data_system_admin(self):
|
||||||
|
self.assert_status(404, ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s/data' % self.uuid)
|
||||||
|
|
||||||
|
def test_data_system_member(self):
|
||||||
|
self.assert_status(403, MEMBER_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s/data' % self.uuid)
|
||||||
|
|
||||||
|
def test_data_system_reader(self):
|
||||||
|
self.assert_status(403, READER_TOKEN, self.app.get,
|
||||||
|
'/v1/introspection/%s/data' % self.uuid)
|
||||||
|
|
||||||
|
def test_data_unprocessed_system_admin(self):
|
||||||
|
self.assert_status(400, ADMIN_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/data/unprocessed' % self.uuid,
|
||||||
|
data={'foo': 'bar'})
|
||||||
|
|
||||||
|
def test_data_unprocessed_system_member(self):
|
||||||
|
self.assert_status(403, MEMBER_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/data/unprocessed' % self.uuid,
|
||||||
|
data={'foo': 'bar'})
|
||||||
|
|
||||||
|
def test_data_unprocessed_system_reader(self):
|
||||||
|
self.assert_status(403, READER_TOKEN, self.app.post,
|
||||||
|
'/v1/introspection/%s/data/unprocessed' % self.uuid,
|
||||||
|
data={'foo': 'bar'})
|
||||||
|
|
||||||
|
def test_rule_list_system_admin(self):
|
||||||
|
self.assert_status(200, ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_list_system_member(self):
|
||||||
|
self.assert_status(403, MEMBER_TOKEN, self.app.get,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_list_system_reader(self):
|
||||||
|
self.assert_status(403, READER_TOKEN, self.app.get,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_get_system_admin(self):
|
||||||
|
self.assert_status(404, ADMIN_TOKEN, self.app.get,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_get_system_member(self):
|
||||||
|
self.assert_status(403, MEMBER_TOKEN, self.app.get,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_get_system_reader(self):
|
||||||
|
self.assert_status(403, READER_TOKEN, self.app.get,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_delete_all_system_admin(self):
|
||||||
|
self.assert_status(204, ADMIN_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_delete_all_system_member(self):
|
||||||
|
self.assert_status(403, MEMBER_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_delete_all_system_reader(self):
|
||||||
|
self.assert_status(403, READER_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules')
|
||||||
|
|
||||||
|
def test_rule_delete_system_admin(self):
|
||||||
|
self.assert_status(404, ADMIN_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_delete_system_member(self):
|
||||||
|
self.assert_status(403, MEMBER_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_delete_system_reader(self):
|
||||||
|
self.assert_status(403, READER_TOKEN, self.app.delete,
|
||||||
|
'/v1/rules/foo')
|
||||||
|
|
||||||
|
def test_rule_create_system_admin(self):
|
||||||
|
self.assert_status(500, ADMIN_TOKEN, self.app.post,
|
||||||
|
'/v1/rules',
|
||||||
|
data={
|
||||||
|
'uuid': self.uuid,
|
||||||
|
'conditions': 'cond',
|
||||||
|
'actions': 'act'
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_rule_create_system_member(self):
|
||||||
|
self.assert_status(403, MEMBER_TOKEN, self.app.post,
|
||||||
|
'/v1/rules',
|
||||||
|
data={
|
||||||
|
'uuid': self.uuid,
|
||||||
|
'conditions': 'cond',
|
||||||
|
'actions': 'act'
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_rule_create_system_reader(self):
|
||||||
|
self.assert_status(403, READER_TOKEN, self.app.post,
|
||||||
|
'/v1/rules',
|
||||||
|
data={
|
||||||
|
'uuid': self.uuid,
|
||||||
|
'conditions': 'cond',
|
||||||
|
'actions': 'act'
|
||||||
|
})
|
19
releasenotes/notes/secure-rbac-0d4fcbc865d45858.yaml
Normal file
19
releasenotes/notes/secure-rbac-0d4fcbc865d45858.yaml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The default policy will been replaced with one which aligns with the
|
||||||
|
Secure-RBAC scopes and roles. Since ironic-inspector is a tool used only
|
||||||
|
by system-level admins, only the ``system`` scope is supported, and the
|
||||||
|
only roles in the policy rules are ``admin`` and ``reader``.
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The new policy is only enforced when ``[oslo_policy]`` config is changed to
|
||||||
|
``enforce_new_defaults=True`` and ``enforce_scope=True``, otherwise the
|
||||||
|
existing deprecated policy is used. User accounts which rely on having
|
||||||
|
the ``baremetal_admin`` or ``baremetal_observer`` roles will need to
|
||||||
|
have system-scoped ``admin`` or ``reader`` roles to use the API when the
|
||||||
|
new policy is enforced.
|
||||||
|
deprecations:
|
||||||
|
- |
|
||||||
|
The previous policy is still enforced by default, but is now deprecated
|
||||||
|
and will be removed in a future release.
|
Loading…
Reference in New Issue
Block a user