315 lines
12 KiB
Python
315 lines
12 KiB
Python
# Copyright (c) 2011 OpenStack Foundation
|
|
# 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.
|
|
|
|
"""Policy Engine For Ironic."""
|
|
|
|
import sys
|
|
|
|
from oslo_concurrency import lockutils
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
from oslo_policy import policy
|
|
|
|
from iotronic.common import exception
|
|
from iotronic.common.i18n import _LW
|
|
|
|
_ENFORCER = None
|
|
CONF = cfg.CONF
|
|
LOG = log.getLogger(__name__)
|
|
|
|
default_policies = [
|
|
# Legacy setting, don't remove. Likely to be overridden by operators who
|
|
# forget to update their policy.json configuration file.
|
|
# This gets rolled into the new "is_admin" rule below.
|
|
policy.RuleDefault('admin_api',
|
|
'role:admin or role:administrator',
|
|
description='Legacy rule for cloud admin access'),
|
|
# is_public_api is set in the environment from AuthTokenMiddleware
|
|
policy.RuleDefault('public_api',
|
|
'is_public_api:True',
|
|
description='Internal flag for public API routes'),
|
|
|
|
policy.RuleDefault('is_admin',
|
|
'rule:admin_api',
|
|
description='Full read/write API access'),
|
|
policy.RuleDefault('is_admin_iot_project',
|
|
'role:admin_iot_project',
|
|
description='Full read/write API access'),
|
|
policy.RuleDefault('is_manager_iot_project',
|
|
'role:manager_iot_project',
|
|
description='Full read/write API access'),
|
|
policy.RuleDefault('is_user_iot',
|
|
'role:user_iot',
|
|
description='Full read/write API access'),
|
|
policy.RuleDefault('is_owner',
|
|
'user:%(owner)s',
|
|
description='full access to the owner'),
|
|
policy.RuleDefault('admin_or_owner',
|
|
'rule:is_admin or rule:is_owner',
|
|
description='full access to the owner or the admin'),
|
|
policy.RuleDefault('is_iot_member',
|
|
'rule:is_admin_iot_project '
|
|
'or rule:is_manager_iot_project or rule:is_user_iot',
|
|
description='define a member on iot context'),
|
|
]
|
|
|
|
# NOTE(deva): to follow policy-in-code spec, we define defaults for
|
|
# the granular policies in code, rather than in policy.json.
|
|
# All of these may be overridden by configuration, but we can
|
|
# depend on their existence throughout the code.
|
|
|
|
board_policies = [
|
|
policy.RuleDefault('iot:board:get',
|
|
'rule:is_admin or rule:is_iot_member',
|
|
description='Retrieve Board records'),
|
|
policy.RuleDefault('iot:board:create',
|
|
'rule:is_admin_iot_project',
|
|
description='Create Board records'),
|
|
policy.RuleDefault('iot:board:delete',
|
|
'rule:is_admin or rule:is_admin_iot_project '
|
|
'or rule:is_manager_iot_project',
|
|
description='Delete Board records'),
|
|
policy.RuleDefault('iot:board:update',
|
|
'rule:is_admin or rule:is_admin_iot_project '
|
|
'or rule:is_manager_iot_project',
|
|
description='Update Board records'),
|
|
|
|
]
|
|
|
|
plugin_policies = [
|
|
policy.RuleDefault('iot:plugin:get',
|
|
'rule:is_admin or rule:is_iot_member',
|
|
description='Retrieve Plugin records'),
|
|
policy.RuleDefault('iot:plugin:create',
|
|
'rule:is_iot_member',
|
|
description='Create Plugin records'),
|
|
policy.RuleDefault('iot:plugin:get_one', 'rule:admin_or_owner',
|
|
description='Retrieve a Plugin record'),
|
|
policy.RuleDefault('iot:plugin:delete', 'rule:admin_or_owner',
|
|
description='Delete Plugin records'),
|
|
policy.RuleDefault('iot:plugin:update', 'rule:admin_or_owner',
|
|
description='Update Plugin records'),
|
|
|
|
]
|
|
|
|
injection_plugin_policies = [
|
|
policy.RuleDefault('iot:plugin_on_board:get',
|
|
'rule:admin_or_owner',
|
|
description='Retrieve Plugin records'),
|
|
policy.RuleDefault('iot:plugin_remove:delete', 'rule:admin_or_owner',
|
|
description='Delete Plugin records'),
|
|
|
|
policy.RuleDefault('iot:plugin_action:post',
|
|
'rule:admin_or_owner',
|
|
description='Create Plugin records'),
|
|
policy.RuleDefault('iot:plugin_inject:put', 'rule:admin_or_owner',
|
|
description='Retrieve a Plugin record'),
|
|
|
|
]
|
|
|
|
service_policies = [
|
|
policy.RuleDefault('iot:service:get',
|
|
'rule:is_admin or rule:is_iot_member',
|
|
description='Retrieve Service records'),
|
|
policy.RuleDefault('iot:service:create',
|
|
'rule:is_iot_member',
|
|
description='Create Service records'),
|
|
policy.RuleDefault('iot:service:get_one', 'rule:admin_or_owner',
|
|
description='Retrieve a Service record'),
|
|
policy.RuleDefault('iot:service:delete', 'rule:admin_or_owner',
|
|
description='Delete Service records'),
|
|
policy.RuleDefault('iot:service:update', 'rule:admin_or_owner',
|
|
description='Update Service records'),
|
|
|
|
]
|
|
|
|
port_on_board_policies = [
|
|
policy.RuleDefault('iot:port_on_board:get',
|
|
'rule:is_admin or rule:is_iot_member',
|
|
description='Retrieve Service records'),
|
|
policy.RuleDefault('iot:port_on_board:create',
|
|
'rule:is_iot_member',
|
|
description='Create Service records'),
|
|
policy.RuleDefault('iot:port_on_board:get_one', 'rule:admin_or_owner',
|
|
description='Retrieve a Service record'),
|
|
policy.RuleDefault('iot:port_on_board:delete', 'rule:admin_or_owner',
|
|
description='Delete Service records'),
|
|
policy.RuleDefault('iot:port_on_board:update', 'rule:admin_or_owner',
|
|
description='Update Service records'),
|
|
|
|
]
|
|
|
|
exposed_service_policies = [
|
|
policy.RuleDefault('iot:service_on_board:get',
|
|
'rule:admin_or_owner',
|
|
description='Retrieve Service records'),
|
|
policy.RuleDefault('iot:service_remove:delete', 'rule:admin_or_owner',
|
|
description='Delete Service records'),
|
|
policy.RuleDefault('iot:service_action:post',
|
|
'rule:admin_or_owner',
|
|
description='Create Service records'),
|
|
policy.RuleDefault('iot:service_inject:put', 'rule:admin_or_owner',
|
|
description='Retrieve a Service record'),
|
|
|
|
]
|
|
|
|
fleet_policies = [
|
|
policy.RuleDefault('iot:fleet:get',
|
|
'rule:is_admin or rule:is_iot_member',
|
|
description='Retrieve Fleet records'),
|
|
policy.RuleDefault('iot:fleet:create',
|
|
'rule:is_iot_member',
|
|
description='Create Fleet records'),
|
|
policy.RuleDefault('iot:fleet:get_one', 'rule:admin_or_owner',
|
|
description='Retrieve a Fleet record'),
|
|
policy.RuleDefault('iot:fleet:delete', 'rule:admin_or_owner',
|
|
description='Delete Fleet records'),
|
|
policy.RuleDefault('iot:fleet:update', 'rule:admin_or_owner',
|
|
description='Update Fleet records'),
|
|
|
|
]
|
|
|
|
|
|
def list_policies():
|
|
policies = (default_policies
|
|
+ board_policies
|
|
+ plugin_policies
|
|
+ injection_plugin_policies
|
|
+ service_policies
|
|
+ exposed_service_policies
|
|
+ port_on_board_policies
|
|
+ fleet_policies
|
|
)
|
|
return policies
|
|
|
|
|
|
@lockutils.synchronized('policy_enforcer')
|
|
def init_enforcer(policy_file=None, rules=None,
|
|
default_rule=None, use_conf=True):
|
|
"""Synchronously initializes the policy enforcer
|
|
|
|
:param policy_file: Custom policy file to use, if none is specified,
|
|
`CONF.oslo_policy.policy_file` will be used.
|
|
:param rules: Default dictionary / Rules to use. It will be
|
|
considered just in the first instantiation.
|
|
:param default_rule: Default rule to use,
|
|
CONF.oslo_policy.policy_default_rule will
|
|
be used if none is specified.
|
|
:param use_conf: Whether to load rules from config file.
|
|
|
|
"""
|
|
global _ENFORCER
|
|
|
|
if _ENFORCER:
|
|
return
|
|
|
|
# NOTE(deva): Register defaults for policy-in-code here so that they are
|
|
# loaded exactly once - when this module-global is initialized.
|
|
# Defining these in the relevant API modules won't work
|
|
# because API classes lack singletons and don't use globals.
|
|
_ENFORCER = policy.Enforcer(CONF, policy_file=policy_file,
|
|
rules=rules,
|
|
default_rule=default_rule,
|
|
use_conf=use_conf)
|
|
|
|
_ENFORCER.register_defaults(list_policies())
|
|
|
|
|
|
def get_enforcer():
|
|
"""Provides access to the single instance of Policy enforcer."""
|
|
|
|
if not _ENFORCER:
|
|
init_enforcer()
|
|
|
|
return _ENFORCER
|
|
|
|
|
|
def get_oslo_policy_enforcer():
|
|
# This method is for use by oslopolicy CLI scripts. Those scripts need the
|
|
# 'output-file' and 'namespace' options, but having those in sys.argv means
|
|
# loading the Ironic config options will fail as those are not expected to
|
|
# be present. So we pass in an arg list with those stripped out.
|
|
|
|
conf_args = []
|
|
# Start at 1 because cfg.CONF expects the equivalent of sys.argv[1:]
|
|
i = 1
|
|
while i < len(sys.argv):
|
|
if sys.argv[i].strip('-') in ['namespace', 'output-file']:
|
|
i += 2
|
|
continue
|
|
conf_args.append(sys.argv[i])
|
|
i += 1
|
|
|
|
cfg.CONF(conf_args, project='ironic')
|
|
|
|
return get_enforcer()
|
|
|
|
|
|
# NOTE(deva): We can't call these methods from within decorators because the
|
|
# 'target' and 'creds' parameter must be fetched from the call time
|
|
# context-local pecan.request magic variable, but decorators are compiled
|
|
# at module-load time.
|
|
|
|
|
|
def authorize(rule, target, creds, *args, **kwargs):
|
|
"""A shortcut for policy.Enforcer.authorize()
|
|
|
|
Checks authorization of a rule against the target and credentials, and
|
|
raises an exception if the rule is not defined.
|
|
Always returns true if CONF.auth_strategy == noauth.
|
|
|
|
Beginning with the Newton cycle, this should be used in place of 'enforce'.
|
|
"""
|
|
if CONF.auth_strategy == 'noauth':
|
|
return True
|
|
enforcer = get_enforcer()
|
|
|
|
try:
|
|
return enforcer.authorize(rule, target, creds, do_raise=True,
|
|
*args, **kwargs)
|
|
except policy.PolicyNotAuthorized:
|
|
raise exception.HTTPForbidden(resource=rule)
|
|
|
|
|
|
def check(rule, target, creds, *args, **kwargs):
|
|
"""A shortcut for policy.Enforcer.enforce()
|
|
|
|
Checks authorization of a rule against the target and credentials
|
|
and returns True or False.
|
|
"""
|
|
enforcer = get_enforcer()
|
|
return enforcer.enforce(rule, target, creds, *args, **kwargs)
|
|
|
|
|
|
def enforce(rule, target, creds, do_raise=False, exc=None, *args, **kwargs):
|
|
"""A shortcut for policy.Enforcer.enforce()
|
|
|
|
Checks authorization of a rule against the target and credentials.
|
|
Always returns true if CONF.auth_strategy == noauth.
|
|
|
|
"""
|
|
# NOTE(deva): this method is obsoleted by authorize(), but retained for
|
|
# backwards compatibility in case it has been used downstream.
|
|
# It may be removed in the Pike cycle.
|
|
LOG.warning(_LW(
|
|
"Deprecation warning: calls to ironic.common.policy.enforce() "
|
|
"should be replaced with authorize(). This method may be removed "
|
|
"in a future release."))
|
|
if CONF.auth_strategy == 'noauth':
|
|
return True
|
|
enforcer = get_enforcer()
|
|
return enforcer.enforce(rule, target, creds, do_raise=do_raise,
|
|
exc=exc, *args, **kwargs)
|