Deprecate old policy engine and restrict access
This commit does two different changes: it changes the policy engine to oslo_policy and restrict access to sysinv API to users of projects 'admin' or 'services'. The policy engine deprecated is the one present in the file "sysinv/sysinv/sysinv/sysinv/openstack/common/policy.py" (780 lines). This file is no longer used by this repository and was not deleted because it is used by other repositories, like starlingx/update. The library oslo_policy is used in its place. In fact, the deprecated engine seems to be an ancient version of oslo_policy. The library oslo_policy changed the default format of configuration files from JSON to YAML, so the configuration files named "policy.json" were changed to "policy.yaml". The file that initializes and wraps oslo_policy ("sysinv/sysinv/sysinv/sysinv/common/policy.py") contains the minimal implementation to use this library. The access to sysinv API, before this commit, was restricted to users with role "admin" or "administrator" from any project. This commit restricts the access to users with role "admin" of projects "admin" or "services". This change should not cause problems, because role "administrator" doesn't exist and because all users from Starlingx are from projects "admin" or "services". This change is needed to avoid access from admin users of other projects. To test custom policy rules set in the file "/etc/sysinv/policy.yaml", it will be used the Service Parameter API actions create/apply/modify/ delete/get (commands "system service-parameter-[add/apply/modify/delete/ list]". To test default policy for sysinv API commands, it will be used the command to change the system description (PATCH "/v1/isystems", command "system modify --description='test'"). On test plan, these commands will be reffered as "test commands". Any change in the file "/etc/sysinv/policy.yaml" is detected by policy engine and rules are updated. Test Plan: PASS: Successfully deploy an AIO-SX using an Debian image with this commit present. Successfully create, through openstack CLI, the users: 'testreader' with role 'reader' in project 'admin', 'adminsvc' with role 'admin' in project 'services' and 'otheradmin' with role 'admin' in project 'notadminproject'. Create openrc files for all new users. Note: the other user that will be used is the already existing 'admin' with role 'admin' in project 'admin'. PASS: In the deployed AIO-SX, check the behavior of test commands through different users: for "admin" and "adminsvc" users, all commands are successful; for user "testreader", only "service-parameter-list" command is successful and for user "otheradmin" no command is successful. PASS: In the deployed AIO-SX, add the following lines in file "/etc/sysinv/policy.yaml": config_api:service_parameter:add: role:reader config_api:service_parameter:apply: role:reader config_api:service_parameter:delete: role:reader config_api:service_parameter:get: role:reader config_api:service_parameter:modify: role:reader and check the behavior of test commands through different users: for "admin" and "adminsvc" users, all commands are successful; for users "testreader" and "otheradmin", all commands are successful except the change in the system description ("system modify --description='test'"). PASS: In the deployed AIO-SX, to assert that public API works without authentication, execute the commands: "curl -v http://<MGMT_IP>:6385/v1/" and "curl -v http://<MGMT_IP>:6385/v1/isystems/mgmtvlan" and verify that they are accepted and that the HTTP response is 200, and execute the commands: "curl -v http://<MGMT_IP>:6385/v1/isystems/" and "curl -v http://<MGMT_IP>:6385/v1/service_parameter" and verify that they are rejected and that the HTTP response is 401. PASS: Repeat all tests above changing the deploy to AIO-DX using an CentOS image. PASS: Successfully execute Debian AIO-SX daily regression and sanity tests using an image containing this change. Story: 2010149 Task: 45984 Signed-off-by: Joao Victor Portal <Joao.VictorPortal@windriver.com> Change-Id: Id7aa387e154afb1441a8484b076cdc97f2fc46cb
This commit is contained in:
parent
986c61d0f8
commit
9aee309999
@ -108,7 +108,7 @@ function cleanup_sysinv {
|
||||
pip_uninstall sysinv
|
||||
|
||||
sudo rm -f $SYSINV_ETC_GOENABLEDD/sysinv_goenabled_check.sh
|
||||
sudo rm -f $SYSINV_CONF_DIR/policy.json
|
||||
sudo rm -f $SYSINV_CONF_DIR/policy.yaml
|
||||
sudo rm -f $SYSINV_ETC_MOTDD/10-system
|
||||
sudo rm -f $SYSINV_CONF_DIR/upgrades/delete_load.sh
|
||||
sudo rm -f $STX_OCF_ROOT/resource.d/platform/sysinv-api
|
||||
@ -247,7 +247,7 @@ function install_sysinv {
|
||||
sudo install -d -m 755 $SYSINV_ETC_GOENABLEDD
|
||||
sudo install -p -D -m 755 $SYSINV_DIR/etc/sysinv/sysinv_goenabled_check.sh $SYSINV_ETC_GOENABLEDD/sysinv_goenabled_check.sh
|
||||
sudo install -d -m 755 $SYSINV_CONF_DIR
|
||||
sudo install -p -D -m 755 $SYSINV_DIR/etc/sysinv/policy.json $SYSINV_CONF_DIR/policy.json
|
||||
sudo install -p -D -m 755 $SYSINV_DIR/etc/sysinv/policy.yaml $SYSINV_CONF_DIR/policy.yaml
|
||||
sudo install -d -m 755 $SYSINV_ETC_MOTDD
|
||||
sudo install -p -D -m 755 $SYSINV_DIR/etc/sysinv/motd-system $SYSINV_ETC_MOTDD/10-system
|
||||
sudo install -d -m 755 $SYSINV_CONF_DIR/upgrades
|
||||
|
@ -44,6 +44,7 @@ Requires: python2-oslo-config
|
||||
Requires: python2-oslo-concurrency
|
||||
Requires: python2-oslo-db
|
||||
Requires: python2-oslo-log
|
||||
Requires: python2-oslo-policy
|
||||
Requires: python2-oslo-rootwrap
|
||||
Requires: python2-oslo-serialization
|
||||
Requires: python2-oslo-service
|
||||
@ -89,7 +90,7 @@ install -d -m 755 %{buildroot}%{local_etc_goenabledd}
|
||||
install -p -D -m 755 etc/sysinv/sysinv_goenabled_check.sh %{buildroot}%{local_etc_goenabledd}/sysinv_goenabled_check.sh
|
||||
|
||||
install -d -m 755 %{buildroot}%{local_etc_sysinv}
|
||||
install -p -D -m 755 etc/sysinv/policy.json %{buildroot}%{local_etc_sysinv}/policy.json
|
||||
install -p -D -m 755 etc/sysinv/policy.yaml %{buildroot}%{local_etc_sysinv}/policy.yaml
|
||||
|
||||
install -p -D -m 644 etc/sysinv/crushmap-storage-model.txt %{buildroot}%{local_etc_sysinv}/crushmap-storage-model.txt
|
||||
install -p -D -m 644 etc/sysinv/crushmap-controller-model.txt %{buildroot}%{local_etc_sysinv}/crushmap-controller-model.txt
|
||||
|
@ -31,6 +31,7 @@ Build-Depends-Indep:
|
||||
python3-oslo.log,
|
||||
python3-oslo.db,
|
||||
python3-oslo.messaging,
|
||||
python3-oslo.policy,
|
||||
python3-oslo.rootwrap,
|
||||
python3-oslo.service,
|
||||
python3-oslo.utils,
|
||||
@ -105,6 +106,7 @@ Depends: ${python3:Depends}, ${misc:Depends},
|
||||
python3-oslo.log,
|
||||
python3-oslo.db,
|
||||
python3-oslo.messaging,
|
||||
python3-oslo.policy,
|
||||
python3-oslo.rootwrap,
|
||||
python3-oslo.service,
|
||||
python3-oslo.utils,
|
||||
|
@ -1,4 +1,4 @@
|
||||
etc/sysinv/policy.json etc/sysinv
|
||||
etc/sysinv/policy.yaml etc/sysinv
|
||||
etc/sysinv/crushmap-storage-model.txt etc/sysinv
|
||||
etc/sysinv/crushmap-controller-model.txt etc/sysinv
|
||||
etc/sysinv/crushmap-aio-sx.txt etc/sysinv
|
||||
|
@ -47,6 +47,7 @@ Requires: python2-oslo.config
|
||||
Requires: python2-oslo.concurrency
|
||||
Requires: python2-oslo.db
|
||||
Requires: python2-oslo.log
|
||||
Requires: python2-oslo.policy
|
||||
Requires: python2-oslo.rootwrap
|
||||
Requires: python2-oslo.serialization
|
||||
Requires: python2-oslo.service
|
||||
@ -89,7 +90,7 @@ install -d -m 755 %{buildroot}%{local_etc_goenabledd}
|
||||
install -p -D -m 755 etc/sysinv/sysinv_goenabled_check.sh %{buildroot}%{local_etc_goenabledd}/sysinv_goenabled_check.sh
|
||||
|
||||
install -d -m 755 %{buildroot}%{local_etc_sysinv}
|
||||
install -p -D -m 644 etc/sysinv/policy.json %{buildroot}%{local_etc_sysinv}/policy.json
|
||||
install -p -D -m 644 etc/sysinv/policy.yaml %{buildroot}%{local_etc_sysinv}/policy.yaml
|
||||
|
||||
install -p -D -m 644 etc/sysinv/crushmap-storage-model.txt %{buildroot}%{local_etc_sysinv}/crushmap-storage-model.txt
|
||||
install -p -D -m 644 etc/sysinv/crushmap-controller-model.txt %{buildroot}%{local_etc_sysinv}/crushmap-controller-model.txt
|
||||
|
@ -21,5 +21,5 @@ graft doc
|
||||
graft etc
|
||||
include sysinv/db/sqlalchemy/migrate_repo/migrate.cfg
|
||||
include sysinv/openstack/common/config/generator.py
|
||||
include sysinv/tests/policy.json
|
||||
include sysinv/tests/policy.yaml
|
||||
graft tools
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"admin": "role:admin or role:administrator",
|
||||
"admin_api": "is_admin:True",
|
||||
"default": "rule:admin_api"
|
||||
}
|
11
sysinv/sysinv/sysinv/etc/sysinv/policy.yaml
Normal file
11
sysinv/sysinv/sysinv/etc/sysinv/policy.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
# The commented lines below contains the default values for presented rules.
|
||||
|
||||
# admin_in_system_projects: role:admin and (project_name:admin or project_name:services)
|
||||
# reader_in_system_projects: role:reader and (project_name:admin or project_name:services)
|
||||
|
||||
# config_api:service_parameter:add: rule:admin_in_system_projects
|
||||
# config_api:service_parameter:apply: rule:admin_in_system_projects
|
||||
# config_api:service_parameter:delete: rule:admin_in_system_projects
|
||||
# config_api:service_parameter:get: rule:reader_in_system_projects
|
||||
# config_api:service_parameter:modify: rule:admin_in_system_projects
|
@ -59,7 +59,7 @@
|
||||
#
|
||||
|
||||
# JSON file representing policy (string value)
|
||||
#policy_file=policy.json
|
||||
#policy_file=policy.yaml
|
||||
|
||||
# Rule checked when requested rule is not found (string value)
|
||||
#policy_default_rule=default
|
||||
|
@ -52,3 +52,4 @@ python-barbicanclient
|
||||
rfc3986
|
||||
importlib-metadata>=3.3.0;python_version=="3.6"
|
||||
importlib-resources==5.2.2;python_version=="3.6"
|
||||
oslo.policy # Apache-2.0
|
@ -27,7 +27,6 @@ from sysinv.api.controllers.v1 import link
|
||||
from sysinv.api.controllers.v1 import types
|
||||
from sysinv.api.controllers.v1 import utils
|
||||
from sysinv.api.controllers.v1.query import Query
|
||||
from sysinv.api.policies import base as base_policy
|
||||
from sysinv.api.policies import service_parameter as sp_policy
|
||||
from sysinv import objects
|
||||
from sysinv.common import constants
|
||||
@ -827,21 +826,16 @@ class ServiceParameterController(rest.RestController):
|
||||
|
||||
def enforce_policy(self, method_name, request):
|
||||
"""Check policy rules for each action of this controller."""
|
||||
context = request.context
|
||||
context_dict = request.context.to_dict()
|
||||
if method_name == "apply":
|
||||
policy.enforce(context, sp_policy.POLICY_ROOT % "apply",
|
||||
{'project_name': base_policy.ADMIN_PROJECT_NAME})
|
||||
policy.authorize(sp_policy.POLICY_ROOT % "apply", {}, context_dict)
|
||||
elif method_name == "delete":
|
||||
policy.enforce(context, sp_policy.POLICY_ROOT % "delete",
|
||||
{'project_name': base_policy.ADMIN_PROJECT_NAME})
|
||||
policy.authorize(sp_policy.POLICY_ROOT % "delete", {}, context_dict)
|
||||
elif method_name in ["get_all", "get_one"]:
|
||||
policy.enforce(context, sp_policy.POLICY_ROOT % "get",
|
||||
{'project_name': base_policy.ADMIN_PROJECT_NAME})
|
||||
policy.authorize(sp_policy.POLICY_ROOT % "get", {}, context_dict)
|
||||
elif method_name == "patch":
|
||||
policy.enforce(context, sp_policy.POLICY_ROOT % "modify",
|
||||
{'project_name': base_policy.ADMIN_PROJECT_NAME})
|
||||
policy.authorize(sp_policy.POLICY_ROOT % "modify", {}, context_dict)
|
||||
elif method_name == "post":
|
||||
policy.enforce(context, sp_policy.POLICY_ROOT % "add",
|
||||
{'project_name': base_policy.ADMIN_PROJECT_NAME})
|
||||
policy.authorize(sp_policy.POLICY_ROOT % "add", {}, context_dict)
|
||||
else:
|
||||
raise exception.PolicyNotFound()
|
||||
|
@ -31,11 +31,12 @@ from oslo_utils import uuidutils
|
||||
from pecan import hooks
|
||||
|
||||
from sysinv._i18n import _
|
||||
from sysinv.api.policies import base as base_policy
|
||||
from sysinv.common import context
|
||||
from sysinv.common import utils
|
||||
from sysinv.conductor import rpcapi
|
||||
from sysinv.db import api as dbapi
|
||||
from sysinv.openstack.common import policy
|
||||
from sysinv.common import policy
|
||||
from webob import exc
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
@ -141,7 +142,7 @@ class ContextHook(hooks.PecanHook):
|
||||
domain_id = state.request.headers.get('X-User-Domain-Id')
|
||||
domain_name = state.request.headers.get('X-User-Domain-Name')
|
||||
auth_token = state.request.headers.get('X-Auth-Token', None)
|
||||
creds = {'roles': state.request.headers.get('X-Roles', '').split(',')}
|
||||
roles = state.request.headers.get('X-Roles', '').split(',')
|
||||
catalog_header = state.request.headers.get('X-Service-Catalog')
|
||||
service_catalog = None
|
||||
if catalog_header:
|
||||
@ -151,7 +152,12 @@ class ContextHook(hooks.PecanHook):
|
||||
raise webob.exc.HTTPInternalServerError(
|
||||
_('Invalid service catalog json.'))
|
||||
|
||||
is_admin = policy.check('admin', state.request.headers, creds)
|
||||
credentials = {
|
||||
'project_name': project_name,
|
||||
'roles': roles
|
||||
}
|
||||
is_admin = policy.authorize(base_policy.ADMIN_IN_SYSTEM_PROJECTS, {},
|
||||
credentials, do_raise=False)
|
||||
|
||||
utils.safe_rstrip(state.request.path, '/')
|
||||
is_public_api = state.request.environ.get('is_public_api', False)
|
||||
@ -165,7 +171,7 @@ class ContextHook(hooks.PecanHook):
|
||||
is_admin=is_admin,
|
||||
is_public_api=is_public_api,
|
||||
project_name=project_name,
|
||||
roles=creds['roles'],
|
||||
roles=roles,
|
||||
service_catalog=service_catalog
|
||||
)
|
||||
|
||||
@ -182,11 +188,16 @@ class AccessPolicyHook(hooks.PecanHook):
|
||||
def before(self, state):
|
||||
controller = state.controller.__self__
|
||||
if hasattr(controller, 'enforce_policy'):
|
||||
try:
|
||||
controller_method = state.controller.__name__
|
||||
controller.enforce_policy(controller_method, state.request)
|
||||
except Exception:
|
||||
raise exc.HTTPForbidden()
|
||||
else:
|
||||
context = state.request.context
|
||||
is_admin_api = policy.check('admin_api', {}, context.to_dict())
|
||||
is_admin_api = policy.authorize(
|
||||
base_policy.ADMIN_IN_SYSTEM_PROJECTS, {},
|
||||
context.to_dict(), do_raise=False)
|
||||
if not is_admin_api and not context.is_public_api:
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
|
@ -14,49 +14,24 @@
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
ADMIN_PROJECT_NAME = 'admin'
|
||||
ADMIN_IN_SPECIFIC_PROJECT = 'rule:admin_in_specific_project'
|
||||
READER_IN_SPECIFIC_PROJECT = 'rule:reader_in_specific_project'
|
||||
from oslo_policy import policy
|
||||
|
||||
|
||||
class RuleDefault(object):
|
||||
"""Class used to represent a policy rule.
|
||||
|
||||
:param name: The name of the policy.
|
||||
:param check_str: The string that represents the policy.
|
||||
:param description: A brief description of the policy.
|
||||
"""
|
||||
def __init__(self, name, check_str, description):
|
||||
self.name = name
|
||||
self.check_str = check_str
|
||||
self.description = description
|
||||
ADMIN_IN_SYSTEM_PROJECTS = 'admin_in_system_projects'
|
||||
READER_IN_SYSTEM_PROJECTS = 'reader_in_system_projects'
|
||||
|
||||
|
||||
base_rules = [
|
||||
RuleDefault(
|
||||
name='admin',
|
||||
check_str='role:admin or role:administrator',
|
||||
policy.RuleDefault(
|
||||
name=ADMIN_IN_SYSTEM_PROJECTS,
|
||||
check_str='role:admin and (project_name:admin or ' +
|
||||
'project_name:services)',
|
||||
description="Base rule.",
|
||||
),
|
||||
RuleDefault(
|
||||
name='admin_api',
|
||||
check_str='is_admin:True',
|
||||
description="Base rule.",
|
||||
),
|
||||
RuleDefault(
|
||||
name='default',
|
||||
check_str='rule:admin_api',
|
||||
description="Base rule.",
|
||||
),
|
||||
RuleDefault(
|
||||
name='admin_in_specific_project',
|
||||
check_str='role:admin and project_name:%(project_name)s',
|
||||
description="Base rule.",
|
||||
),
|
||||
RuleDefault(
|
||||
name='reader_in_specific_project',
|
||||
check_str='role:reader and project_name:%(project_name)s',
|
||||
description="Base rule.",
|
||||
policy.RuleDefault(
|
||||
name=READER_IN_SYSTEM_PROJECTS,
|
||||
check_str='role:reader and (project_name:admin or ' +
|
||||
'project_name:services)',
|
||||
description="Base rule."
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -14,36 +14,71 @@
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from oslo_policy import policy
|
||||
from sysinv.api.policies import base
|
||||
|
||||
POLICY_ROOT = 'config_api:service_parameter:%s'
|
||||
|
||||
|
||||
service_parameter_rules = [
|
||||
base.RuleDefault(
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'add',
|
||||
check_str=base.ADMIN_IN_SPECIFIC_PROJECT,
|
||||
check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS,
|
||||
description="Add a Service Parameter.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': '/v1/service_parameter'
|
||||
}
|
||||
]
|
||||
),
|
||||
base.RuleDefault(
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'apply',
|
||||
check_str=base.ADMIN_IN_SPECIFIC_PROJECT,
|
||||
check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS,
|
||||
description="Apply Service Parameters.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': '/v1/service_parameter/apply'
|
||||
}
|
||||
]
|
||||
),
|
||||
base.RuleDefault(
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'delete',
|
||||
check_str=base.ADMIN_IN_SPECIFIC_PROJECT,
|
||||
check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS,
|
||||
description="Delete a Service Parameter.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'DELETE',
|
||||
'path': '/v1/service_parameter/{parameter_id}'
|
||||
}
|
||||
]
|
||||
),
|
||||
base.RuleDefault(
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'get',
|
||||
check_str=base.READER_IN_SPECIFIC_PROJECT,
|
||||
check_str='rule:' + base.READER_IN_SYSTEM_PROJECTS,
|
||||
description="Get Service Parameters.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/v1/service_parameter'
|
||||
},
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/v1/service_parameter/{parameter_id}'
|
||||
}
|
||||
]
|
||||
),
|
||||
base.RuleDefault(
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'modify',
|
||||
check_str=base.ADMIN_IN_SPECIFIC_PROJECT,
|
||||
check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS,
|
||||
description="Modify Service Parameter value.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'PATCH',
|
||||
'path': '/v1/service_parameter/{parameter_id}'
|
||||
}
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -20,6 +20,7 @@ from pecan import hooks
|
||||
from oslo_context import context as base_context
|
||||
from oslo_utils import encodeutils
|
||||
|
||||
from sysinv.api.policies import base as base_policy
|
||||
from sysinv.common import policy
|
||||
|
||||
ALLOWED_WITHOUT_AUTH = '/'
|
||||
@ -75,8 +76,8 @@ class RequestContext(base_context.RequestContext):
|
||||
|
||||
# Check user is admin or not
|
||||
if is_admin is None:
|
||||
self.is_admin = policy.enforce(self, 'context_is_admin',
|
||||
target={'project': self.project},
|
||||
self.is_admin = policy.authorize(
|
||||
base_policy.ADMIN_IN_SYSTEM_PROJECTS, {}, self.to_dict(),
|
||||
do_raise=False)
|
||||
else:
|
||||
self.is_admin = is_admin
|
||||
|
@ -1,7 +1,4 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
# Copyright (c) 2022 Wind River Systems, Inc.
|
||||
#
|
||||
# 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
|
||||
@ -14,121 +11,45 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Policy Engine For Sysinv."""
|
||||
|
||||
import os.path
|
||||
|
||||
from oslo_config import cfg
|
||||
from sysinv._i18n import _
|
||||
from oslo_policy import policy
|
||||
from sysinv.api import policies as controller_policies
|
||||
from sysinv.common import exception
|
||||
from sysinv.common import utils
|
||||
from sysinv.openstack.common import policy
|
||||
|
||||
|
||||
policy_opts = [
|
||||
cfg.StrOpt('policy_file',
|
||||
default='policy.json',
|
||||
help=_('JSON file representing policy')),
|
||||
cfg.StrOpt('policy_default_rule',
|
||||
default='default',
|
||||
help=_('Rule checked when requested rule is not found')),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(policy_opts)
|
||||
|
||||
_POLICY_PATH = None
|
||||
_POLICY_CACHE = {}
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
def reset():
|
||||
global _POLICY_PATH
|
||||
global _POLICY_CACHE
|
||||
_POLICY_PATH = None
|
||||
_POLICY_CACHE = {}
|
||||
policy.reset()
|
||||
"""Discard current Enforcer object."""
|
||||
global _ENFORCER
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
def init():
|
||||
global _POLICY_PATH
|
||||
global _POLICY_CACHE
|
||||
if not _POLICY_PATH:
|
||||
_POLICY_PATH = CONF.policy_file
|
||||
if not os.path.exists(_POLICY_PATH):
|
||||
_POLICY_PATH = CONF.find_file(_POLICY_PATH)
|
||||
if not _POLICY_PATH:
|
||||
raise exception.ConfigNotFound(message=CONF.policy_file)
|
||||
utils.read_cached_file(_POLICY_PATH, _POLICY_CACHE,
|
||||
reload_func=_set_rules)
|
||||
def init(policy_file='policy.yaml'):
|
||||
"""Init an Enforcer class.
|
||||
|
||||
:param policy_file: Custom policy file to be used.
|
||||
|
||||
def _set_rules(data):
|
||||
default_rule = CONF.policy_default_rule
|
||||
rules = policy.Rules.load_rules(data, default_rule,
|
||||
controller_policies.list_rules())
|
||||
policy.set_rules(rules)
|
||||
|
||||
|
||||
def enforce(context, action, target, do_raise=True):
|
||||
"""Verifies that the action is valid on the target in this context.
|
||||
|
||||
:param context: sysinv context
|
||||
:param action: string representing the action to be checked
|
||||
this should be colon separated for clarity.
|
||||
i.e. ``compute:create_instance``,
|
||||
``compute:attach_volume``,
|
||||
``volume:attach_volume``
|
||||
:param target: dictionary representing the object of the action
|
||||
for object creation this should be a dictionary representing the
|
||||
location of the object e.g. ``{'project_id': context.project_id}``
|
||||
:param do_raise: if True (the default), raises PolicyNotAuthorized;
|
||||
if False, returns False
|
||||
|
||||
:raises sysinv.exception.PolicyNotAuthorized: if verification fails
|
||||
and do_raise is True.
|
||||
|
||||
:return: returns a non-False value (not necessarily "True") if
|
||||
authorized, and the exact value False if not authorized and
|
||||
do_raise is False.
|
||||
:return: Returns a Enforcer instance.
|
||||
"""
|
||||
global _ENFORCER
|
||||
if not _ENFORCER:
|
||||
# https://docs.openstack.org/oslo.policy/latest/user/usage.html
|
||||
_ENFORCER = policy.Enforcer(CONF,
|
||||
policy_file=policy_file,
|
||||
default_rule='default',
|
||||
use_conf=True,
|
||||
overwrite=True)
|
||||
_ENFORCER.register_defaults(controller_policies.list_rules())
|
||||
return _ENFORCER
|
||||
|
||||
|
||||
def authorize(rule, target, creds, do_raise=True):
|
||||
"""A wrapper around 'authorize' from 'oslo_policy.policy'."""
|
||||
init()
|
||||
|
||||
credentials = context.to_dict()
|
||||
|
||||
# Add the exception arguments if asked to do a raise
|
||||
extra = {}
|
||||
if do_raise:
|
||||
extra.update(exc=exception.PolicyNotAuthorized, action=action)
|
||||
|
||||
return policy.check(action, target, credentials, **extra)
|
||||
|
||||
|
||||
def check_is_admin(context):
|
||||
"""Whether or not role contains 'admin' role according to policy setting.
|
||||
|
||||
"""
|
||||
init()
|
||||
|
||||
credentials = context.to_dict()
|
||||
target = credentials
|
||||
|
||||
return policy.check('context_is_admin', target, credentials)
|
||||
|
||||
|
||||
@policy.register('context_is_admin')
|
||||
class IsAdminCheck(policy.Check):
|
||||
"""An explicit check for is_admin."""
|
||||
|
||||
def __init__(self, kind, match):
|
||||
"""Initialize the check."""
|
||||
|
||||
self.expected = (match.lower() == 'true')
|
||||
|
||||
super(IsAdminCheck, self).__init__(kind, str(self.expected))
|
||||
|
||||
def __call__(self, target, creds):
|
||||
"""Determine whether is_admin matches the requested value."""
|
||||
|
||||
return creds['is_admin'] == self.expected
|
||||
return _ENFORCER.authorize(rule, target, creds, do_raise=do_raise)
|
||||
|
@ -16,6 +16,10 @@
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
IMPORTANT: this implementation was deprecated by oslo_policy and it is no longer
|
||||
used in this repository. This file was not deleted because it is used by other
|
||||
repositories.
|
||||
|
||||
Common Policy Engine Implementation
|
||||
|
||||
Policies can be expressed in one of two forms: A list of lists, or a
|
||||
|
@ -47,8 +47,6 @@ class FunctionalTest(base.TestCase):
|
||||
def setUp(self):
|
||||
super(FunctionalTest, self).setUp()
|
||||
cfg.CONF.set_override("auth_version", "v2.0", group=acl.OPT_GROUP_NAME)
|
||||
cfg.CONF.set_override("policy_file",
|
||||
self.path_get('tests/policy.json'))
|
||||
self.app = self._make_app()
|
||||
self.dbapi = dbapi.get_instance()
|
||||
self.context = sysinv_context.RequestContext(is_admin=True)
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
|
||||
policy_data = """
|
||||
{
|
||||
"admin_api": "role:admin",
|
||||
"admin_or_owner": "is_admin:True or project_id:%(project_id)s",
|
||||
"is_admin": "role:admin or role:administrator",
|
||||
"default": "rule:admin_or_owner"
|
||||
}
|
||||
---
|
||||
admin_api: "role:admin"
|
||||
admin_or_owner: "is_admin:True or project_id:%(project_id)s"
|
||||
is_admin: "role:admin or role:administrator"
|
||||
default: "rule:admin_or_owner"
|
||||
|
||||
"""
|
||||
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"admin_api": "is_admin:True",
|
||||
"admin_or_owner": "is_admin:True or project_id:%(project_id)s",
|
||||
"is_admin": "role:admin or role:administrator",
|
||||
"default": "rule:admin_or_owner",
|
||||
}
|
11
sysinv/sysinv/sysinv/sysinv/tests/policy.yaml
Normal file
11
sysinv/sysinv/sysinv/sysinv/tests/policy.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
# The commented lines below contains the default values for presented rules.
|
||||
|
||||
# admin_in_system_projects: role:admin and (project_name:admin or project_name:services)
|
||||
# reader_in_system_projects: role:reader and (project_name:admin or project_name:services)
|
||||
|
||||
# config_api:service_parameter:add: rule:admin_in_system_projects
|
||||
# config_api:service_parameter:apply: rule:admin_in_system_projects
|
||||
# config_api:service_parameter:delete: rule:admin_in_system_projects
|
||||
# config_api:service_parameter:get: rule:reader_in_system_projects
|
||||
# config_api:service_parameter:modify: rule:admin_in_system_projects
|
@ -18,7 +18,6 @@ import fixtures
|
||||
from oslo_config import cfg
|
||||
|
||||
from sysinv.common import policy as sysinv_policy
|
||||
from sysinv.openstack.common import policy as common_policy
|
||||
from sysinv.tests import fake_policy
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -30,15 +29,9 @@ class PolicyFixture(fixtures.Fixture):
|
||||
super(PolicyFixture, self).setUp()
|
||||
self.policy_dir = self.useFixture(fixtures.TempDir())
|
||||
self.policy_file_name = os.path.join(self.policy_dir.path,
|
||||
'policy.json')
|
||||
'policy.yaml')
|
||||
with open(self.policy_file_name, 'w') as policy_file:
|
||||
policy_file.write(fake_policy.policy_data)
|
||||
CONF.set_override('policy_file', self.policy_file_name)
|
||||
sysinv_policy.reset()
|
||||
sysinv_policy.init()
|
||||
sysinv_policy.init(self.policy_file_name)
|
||||
self.addCleanup(sysinv_policy.reset)
|
||||
|
||||
def set_rules(self, rules):
|
||||
common_policy.set_rules(common_policy.Rules(
|
||||
dict((k, common_policy.parse_rule(v))
|
||||
for k, v in rules.items())))
|
||||
|
Loading…
Reference in New Issue
Block a user