Implement policy in code
* Added policy in code as per community goals [1] for vnf packages. * Modified tox to generate policy sample json file. [1]: https://governance.openstack.org/tc/goals/queens/policy-in-code.html Partial-Implements: blueprint tosca-csar-mgmt-driver Co-Author: Neha Alhat <neha.alhat@nttdata.com> Co-Author: Bhagyashri Shewale <bhagyashri.shewale@nttdata.com> Change-Id: I7cedbca4abe41223e3f8d6211a74b4347299e9e5
This commit is contained in:
parent
354da78e0c
commit
34b661ad08
2
.gitignore
vendored
2
.gitignore
vendored
@ -28,7 +28,9 @@ subunit.log
|
||||
.eggs/
|
||||
.stestr/
|
||||
SP1_res.yaml
|
||||
etc/tacker/policy.yaml.sample
|
||||
|
||||
releasenotes/build
|
||||
etc/tacker/tacker.conf.sample
|
||||
doc/source/contributor/api
|
||||
doc/source/_static/tacker.policy.yaml.sample
|
||||
|
@ -200,16 +200,10 @@ function configure_tacker {
|
||||
|
||||
# server
|
||||
TACKER_API_PASTE_FILE=$TACKER_CONF_DIR/api-paste.ini
|
||||
TACKER_POLICY_FILE=$TACKER_CONF_DIR/policy.json
|
||||
|
||||
cp $TACKER_DIR/etc/tacker/api-paste.ini $TACKER_API_PASTE_FILE
|
||||
cp $TACKER_DIR/etc/tacker/policy.json $TACKER_POLICY_FILE
|
||||
|
||||
# allow tacker user to administer tacker to match tacker account
|
||||
sed -i 's/"context_is_admin": "role:admin"/"context_is_admin": "role:admin or user_name:tacker"/g' $TACKER_POLICY_FILE
|
||||
|
||||
iniset $TACKER_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
|
||||
iniset $TACKER_CONF DEFAULT policy_file $TACKER_POLICY_FILE
|
||||
|
||||
iniset $TACKER_CONF DEFAULT auth_strategy $TACKER_AUTH_STRATEGY
|
||||
_tacker_setup_keystone $TACKER_CONF keystone_authtoken
|
||||
|
@ -1,6 +1,5 @@
|
||||
[DEFAULT]
|
||||
auth_strategy = keystone
|
||||
policy_file = /etc/tacker/policy.json
|
||||
debug = True
|
||||
logging_exception_prefix = %(color)s%(asctime)s.%(msecs)03d TRACE %(name)s [01;35m%(instance)s[00m
|
||||
logging_debug_format_suffix = [00;33mfrom (pid=%(process)d) %(funcName)s %(pathname)s:%(lineno)d[00m
|
||||
|
@ -23,8 +23,15 @@ extensions = [
|
||||
'sphinxcontrib.apidoc',
|
||||
'stevedore.sphinxext',
|
||||
'openstackdocstheme',
|
||||
'oslo_policy.sphinxext',
|
||||
'oslo_policy.sphinxpolicygen',
|
||||
]
|
||||
|
||||
policy_generator_config_file = [
|
||||
('../../etc/tacker-policy-generator.conf', '_static/tacker'),
|
||||
]
|
||||
sample_policy_basename = '_static/tacker'
|
||||
|
||||
# sphinxcontrib.apidoc options
|
||||
apidoc_module_dir = '../../tacker'
|
||||
apidoc_output_dir = 'contributor/api'
|
||||
|
@ -31,3 +31,24 @@ The sample configuration can also be viewed in :download:`file form
|
||||
version of this documentation.
|
||||
|
||||
.. literalinclude:: /_extra/tacker.conf.sample
|
||||
|
||||
Policy
|
||||
------
|
||||
|
||||
Tacker, like most OpenStack projects, uses a policy language to restrict
|
||||
permissions on REST API actions.
|
||||
|
||||
* :doc:`Policy Reference <policy>`: A complete reference of all
|
||||
policy points in tacker and what they impact.
|
||||
|
||||
* :doc:`Sample Policy File <sample_policy>`: A sample tacker
|
||||
policy file with inline documentation.
|
||||
|
||||
.. # NOTE(bhagyashris): This is the section where we hide things that we don't
|
||||
# actually want in the table of contents but sphinx build would fail if
|
||||
# they aren't in the toctree somewhere.
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
policy
|
||||
sample_policy
|
||||
|
9
doc/source/configuration/policy.rst
Normal file
9
doc/source/configuration/policy.rst
Normal file
@ -0,0 +1,9 @@
|
||||
===============
|
||||
Tacker Policies
|
||||
===============
|
||||
|
||||
The following is an overview of all available policies in Tacker.
|
||||
For a sample configuration file, refer to :doc:`/configuration/sample_policy`.
|
||||
|
||||
.. show-policy::
|
||||
:config-file: etc/tacker-policy-generator.conf
|
16
doc/source/configuration/sample_policy.rst
Normal file
16
doc/source/configuration/sample_policy.rst
Normal file
@ -0,0 +1,16 @@
|
||||
=========================
|
||||
Sample Tacker Policy File
|
||||
=========================
|
||||
|
||||
The following is a sample tacker policy file for adaptation and use.
|
||||
|
||||
The sample policy can also be viewed in :download:`file form
|
||||
</_static/tacker.policy.yaml.sample>`.
|
||||
|
||||
.. important::
|
||||
|
||||
The sample policy file is auto-generated from tacker when this documentation
|
||||
is built. You must ensure your version of tacker matches the version of this
|
||||
documentation.
|
||||
|
||||
.. literalinclude:: /_static/tacker.policy.yaml.sample
|
3
etc/tacker-policy-generator.conf
Normal file
3
etc/tacker-policy-generator.conf
Normal file
@ -0,0 +1,3 @@
|
||||
[DEFAULT]
|
||||
output_file = etc/tacker/policy.yaml.sample
|
||||
namespace = tacker
|
7
etc/tacker/README-policy-yaml.txt
Normal file
7
etc/tacker/README-policy-yaml.txt
Normal file
@ -0,0 +1,7 @@
|
||||
Tacker
|
||||
======
|
||||
|
||||
To generate the sample tacker policy.yaml file, run the following command from
|
||||
the top level of the tacker directory:
|
||||
|
||||
tox -egenpolicy
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"context_is_admin": "role:admin",
|
||||
"admin_or_owner": "rule:context_is_admin or tenant_id:%(tenant_id)s",
|
||||
"admin_only": "rule:context_is_admin",
|
||||
"regular_user": "",
|
||||
"shared": "field:vims:shared=True",
|
||||
"default": "rule:admin_or_owner",
|
||||
|
||||
"get_vim": "rule:admin_or_owner or rule:shared"
|
||||
}
|
10
setup.cfg
10
setup.cfg
@ -25,7 +25,6 @@ packages =
|
||||
data_files =
|
||||
etc/tacker =
|
||||
etc/tacker/api-paste.ini
|
||||
etc/tacker/policy.json
|
||||
etc/tacker/rootwrap.conf
|
||||
etc/rootwrap.d =
|
||||
etc/tacker/rootwrap.d/tacker.filters
|
||||
@ -96,6 +95,15 @@ oslo.config.opts =
|
||||
mistral.actions =
|
||||
tacker.vim_ping_action = tacker.nfvo.workflows.vim_monitor.vim_ping_action:PingVimAction
|
||||
|
||||
oslo.policy.enforcer =
|
||||
tacker = tacker.policy:get_enforcer
|
||||
|
||||
oslo.policy.policies =
|
||||
# The sample policies will be ordered by entry point and then by list
|
||||
# returned from that entry point. If more control is desired split out each
|
||||
# list_rules method into a separate entry point rather than using the
|
||||
# aggregate method.
|
||||
tacker = tacker.policies:list_rules
|
||||
|
||||
[build_releasenotes]
|
||||
all_files = 1
|
||||
|
@ -43,8 +43,6 @@ core_opts = [
|
||||
help=_("The path for API extensions")),
|
||||
cfg.ListOpt('service_plugins', default=['nfvo', 'vnfm'],
|
||||
help=_("The service plugins Tacker will use")),
|
||||
cfg.StrOpt('policy_file', default="policy.json",
|
||||
help=_("The policy file to use")),
|
||||
cfg.StrOpt('auth_strategy', default='keystone',
|
||||
help=_("The type of authentication to use")),
|
||||
cfg.BoolOpt('allow_bulk', default=True,
|
||||
|
@ -23,6 +23,7 @@ from oslo_config import cfg
|
||||
from oslo_context import context as oslo_context
|
||||
from oslo_db.sqlalchemy import enginefacade
|
||||
|
||||
from tacker.common import exceptions
|
||||
from tacker.db import api as db_api
|
||||
from tacker import policy
|
||||
|
||||
@ -142,6 +143,34 @@ class ContextBase(oslo_context.RequestContext):
|
||||
|
||||
return context
|
||||
|
||||
def can(self, action, target=None, fatal=True):
|
||||
"""Verifies that the given action is valid on the target in this context.
|
||||
|
||||
:param action: string representing the action to be checked.
|
||||
: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}``.
|
||||
If None, then this default target will be considered:
|
||||
{'project_id': self.project_id, 'user_id': self.user_id}
|
||||
:param fatal: if False, will return False when an exception.Forbidden
|
||||
occurs.
|
||||
|
||||
:raises tacker.exception.Forbidden: if verification fails and fatal
|
||||
is True.
|
||||
|
||||
:return: returns a non-False value (not necessarily "True") if
|
||||
authorized and False if not authorized and fatal is False.
|
||||
"""
|
||||
if target is None:
|
||||
target = {'tenant_id': self.tenant_id,
|
||||
'user_id': self.user_id}
|
||||
try:
|
||||
return policy.authorize(self, action, target)
|
||||
except exceptions.Forbidden:
|
||||
if fatal:
|
||||
raise
|
||||
return False
|
||||
|
||||
|
||||
@enginefacade.transaction_context_provider
|
||||
class ContextBaseWithSession(ContextBase):
|
||||
|
27
tacker/policies/__init__.py
Normal file
27
tacker/policies/__init__.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2019 NTT DATA
|
||||
# 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 itertools
|
||||
|
||||
from tacker.policies import base
|
||||
from tacker.policies import vnf_package
|
||||
|
||||
|
||||
def list_rules():
|
||||
return itertools.chain(
|
||||
base.list_rules(),
|
||||
vnf_package.list_rules(),
|
||||
)
|
49
tacker/policies/base.py
Normal file
49
tacker/policies/base.py
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright (C) 2019 NTT DATA
|
||||
# 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.
|
||||
|
||||
from oslo_policy import policy
|
||||
|
||||
TACKER_API = 'os_nfv_orchestration_api'
|
||||
|
||||
RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
|
||||
RULE_ADMIN_API = 'rule:admin_only'
|
||||
RULE_ANY = '@'
|
||||
|
||||
rules = [
|
||||
policy.RuleDefault(
|
||||
"context_is_admin",
|
||||
"role:admin",
|
||||
"Decides what is required for the 'is_admin:True' check to succeed."),
|
||||
policy.RuleDefault(
|
||||
"admin_or_owner",
|
||||
"is_admin:True or tenant_id:%(tenant_id)s",
|
||||
"Default rule for most non-Admin APIs."),
|
||||
policy.RuleDefault(
|
||||
"admin_only",
|
||||
"is_admin:True",
|
||||
"Default rule for most Admin APIs."),
|
||||
policy.RuleDefault(
|
||||
"shared",
|
||||
"field:vims:shared=True",
|
||||
"Default rule for sharing vims."),
|
||||
policy.RuleDefault(
|
||||
"default",
|
||||
"rule:admin_or_owner",
|
||||
"Default rule for most non-Admin APIs.")
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return rules
|
91
tacker/policies/vnf_package.py
Normal file
91
tacker/policies/vnf_package.py
Normal file
@ -0,0 +1,91 @@
|
||||
# Copyright (C) 2019 NTT DATA
|
||||
# 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.
|
||||
|
||||
|
||||
from oslo_policy import policy
|
||||
|
||||
from tacker.policies import base
|
||||
|
||||
|
||||
VNFPKGM = 'os_nfv_orchestration_api:vnf_packages:%s'
|
||||
|
||||
rules = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFPKGM % 'create',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="Creates a vnf package.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': '/vnf_packages'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFPKGM % 'show',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="Show a vnf package.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/vnf_packages/{vnf_package_id}'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFPKGM % 'index',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="List all vnf packages.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/vnf_packages/'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFPKGM % 'delete',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="Delete a vnf package.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'DELETE',
|
||||
'path': '/vnf_packages/{vnf_package_id}'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFPKGM % 'upload_package_content',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="upload a vnf package content.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'PUT',
|
||||
'path': '/vnf_packages/{vnf_package_id}/'
|
||||
'package_content'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFPKGM % 'upload_from_uri',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="upload a vnf package content from uri.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': '/vnf_packages/{vnf_package_id}/package_content/'
|
||||
'upload_from_uri'
|
||||
}
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return rules
|
@ -28,6 +28,7 @@ import six
|
||||
from tacker._i18n import _
|
||||
from tacker.api.v1 import attributes
|
||||
from tacker.common import exceptions
|
||||
from tacker import policies
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -36,18 +37,6 @@ _ENFORCER = None
|
||||
ADMIN_CTX_POLICY = 'context_is_admin'
|
||||
|
||||
|
||||
_BASE_RULES = [
|
||||
policy.RuleDefault(
|
||||
ADMIN_CTX_POLICY,
|
||||
'role:admin',
|
||||
description='Rule for cloud admin access'),
|
||||
# policy.RuleDefault(
|
||||
# _ADVSVC_CTX_POLICY,
|
||||
# 'role:advsvc',
|
||||
# description='Rule for advanced service role access'),
|
||||
]
|
||||
|
||||
|
||||
def reset():
|
||||
global _ENFORCER
|
||||
if _ENFORCER:
|
||||
@ -61,8 +50,29 @@ def init(conf=cfg.CONF, policy_file=None):
|
||||
global _ENFORCER
|
||||
if not _ENFORCER:
|
||||
_ENFORCER = policy.Enforcer(conf, policy_file=policy_file)
|
||||
_ENFORCER.register_defaults(_BASE_RULES)
|
||||
_ENFORCER.load_rules(True)
|
||||
register_rules(_ENFORCER)
|
||||
_ENFORCER.load_rules()
|
||||
|
||||
|
||||
def authorize(context, action, target, do_raise=True, exc=None):
|
||||
|
||||
init()
|
||||
credentials = context.to_policy_values()
|
||||
if not exc:
|
||||
exc = exceptions.PolicyNotAuthorized
|
||||
try:
|
||||
result = _ENFORCER.authorize(action, target, credentials,
|
||||
do_raise=do_raise, exc=exc, action=action)
|
||||
except policy.PolicyNotRegistered:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.debug('Policy not registered')
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.debug('Policy check for %(action)s failed with credentials '
|
||||
'%(credentials)s',
|
||||
{'action': action, 'credentials': credentials})
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def refresh(policy_file=None):
|
||||
@ -427,6 +437,10 @@ def check_is_admin(context):
|
||||
return False
|
||||
|
||||
|
||||
def register_rules(enforcer):
|
||||
enforcer.register_defaults(policies.list_rules())
|
||||
|
||||
|
||||
def get_enforcer():
|
||||
# NOTE(amotoki): This was borrowed from nova/policy.py.
|
||||
# This method is for use by oslo.policy CLI scripts. Those scripts need the
|
||||
|
@ -29,7 +29,6 @@ class ConfigurationTest(base.BaseTestCase):
|
||||
self.assertEqual(9890, cfg.CONF.bind_port)
|
||||
self.assertEqual('api-paste.ini.test', cfg.CONF.api_paste_config)
|
||||
self.assertEqual('unit/extensions', cfg.CONF.api_extensions_path)
|
||||
self.assertEqual('policy.json', cfg.CONF.policy_file)
|
||||
self.assertEqual('keystone', cfg.CONF.auth_strategy)
|
||||
self.assertTrue(cfg.CONF.allow_bulk)
|
||||
self.assertFalse(cfg.CONF.allow_pagination)
|
||||
|
5
tox.ini
5
tox.ini
@ -60,6 +60,7 @@ commands = python ./tools/check_i18n.py ./tacker
|
||||
deps = -r{toxinidir}/doc/requirements.txt
|
||||
commands =
|
||||
sphinx-build -W -b html doc/source doc/build/html
|
||||
oslopolicy-sample-generator --config-file=etc/tacker-policy-generator.conf
|
||||
|
||||
[testenv:api-ref]
|
||||
deps = -r{toxinidir}/doc/requirements.txt
|
||||
@ -101,6 +102,10 @@ local-check-factory = tacker.hacking.checks.factory
|
||||
commands =
|
||||
oslo-config-generator --config-file=etc/config-generator.conf
|
||||
|
||||
[testenv:genpolicy]
|
||||
commands =
|
||||
oslopolicy-sample-generator --config-file=etc/tacker-policy-generator.conf
|
||||
|
||||
[testenv:lower-constraints]
|
||||
deps =
|
||||
-c{toxinidir}/lower-constraints.txt
|
||||
|
Loading…
Reference in New Issue
Block a user