Policy in code
This patch introduces the implementation for registering default policy rules in code. Default rules are defined under cloudkitty.common.policies. Each API's policies are defined in a sub-folder under that path and __init__.py contains all the default policies in code which are registered in the ``init`` enforcer function in cloudkitty/common/policy.py. This commit does the following: - Creates the ``policies`` module that contains all the default policies in code. - Adds the base policy rules into code (context_is_admin, admin_or_owner and default rules). - Add policies in code for current APIs - Add a tox env to generate default policy sample file - Delete policy.json from repo as policies in code will be used. Change-Id: I257e8cefc2b699fc979c717531cd9ba77233d94b Implements: blueprint policy-in-code
This commit is contained in:
parent
43e1999e9d
commit
7eca672645
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,6 +25,7 @@ cloudkitty.egg-info
|
|||||||
|
|
||||||
# Configuration file
|
# Configuration file
|
||||||
etc/cloudkitty/cloudkitty.conf.sample
|
etc/cloudkitty/cloudkitty.conf.sample
|
||||||
|
etc/cloudkitty/policy.yaml.sample
|
||||||
|
|
||||||
# Installer logs
|
# Installer logs
|
||||||
pip-log.txt
|
pip-log.txt
|
||||||
|
@ -39,7 +39,7 @@ class MappingController(rest.RestController):
|
|||||||
|
|
||||||
:param service: Name of the service to filter on.
|
:param service: Name of the service to filter on.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'collector:get_mapping', {})
|
policy.authorize(pecan.request.context, 'collector:get_mapping', {})
|
||||||
try:
|
try:
|
||||||
mapping = self._db.get_mapping(service)
|
mapping = self._db.get_mapping(service)
|
||||||
return collector_models.ServiceToCollectorMapping(
|
return collector_models.ServiceToCollectorMapping(
|
||||||
@ -55,7 +55,7 @@ class MappingController(rest.RestController):
|
|||||||
:param collector: Filter on the collector name.
|
:param collector: Filter on the collector name.
|
||||||
:return: Service to collector mappings collection.
|
:return: Service to collector mappings collection.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'collector:list_mappings', {})
|
policy.authorize(pecan.request.context, 'collector:list_mappings', {})
|
||||||
mappings = [collector_models.ServiceToCollectorMapping(
|
mappings = [collector_models.ServiceToCollectorMapping(
|
||||||
**mapping.as_dict())
|
**mapping.as_dict())
|
||||||
for mapping in self._db.list_mappings(collector)]
|
for mapping in self._db.list_mappings(collector)]
|
||||||
@ -71,7 +71,7 @@ class MappingController(rest.RestController):
|
|||||||
:param collector: Name of the collector to apply mapping on.
|
:param collector: Name of the collector to apply mapping on.
|
||||||
:param service: Name of the service to apply mapping on.
|
:param service: Name of the service to apply mapping on.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'collector:manage_mapping', {})
|
policy.authorize(pecan.request.context, 'collector:manage_mapping', {})
|
||||||
new_mapping = self._db.set_mapping(service, collector)
|
new_mapping = self._db.set_mapping(service, collector)
|
||||||
return collector_models.ServiceToCollectorMapping(
|
return collector_models.ServiceToCollectorMapping(
|
||||||
service=new_mapping.service,
|
service=new_mapping.service,
|
||||||
@ -85,7 +85,7 @@ class MappingController(rest.RestController):
|
|||||||
|
|
||||||
:param service: Name of the service to filter on.
|
:param service: Name of the service to filter on.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'collector:manage_mapping', {})
|
policy.authorize(pecan.request.context, 'collector:manage_mapping', {})
|
||||||
try:
|
try:
|
||||||
self._db.delete_mapping(service)
|
self._db.delete_mapping(service)
|
||||||
except db_api.NoSuchMapping as e:
|
except db_api.NoSuchMapping as e:
|
||||||
@ -105,7 +105,7 @@ class CollectorStateController(rest.RestController):
|
|||||||
:param name: Name of the collector.
|
:param name: Name of the collector.
|
||||||
:return: State of the collector.
|
:return: State of the collector.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'collector:get_state', {})
|
policy.authorize(pecan.request.context, 'collector:get_state', {})
|
||||||
enabled = self._db.get_state('collector_{}'.format(name))
|
enabled = self._db.get_state('collector_{}'.format(name))
|
||||||
collector = collector_models.CollectorInfos(name=name,
|
collector = collector_models.CollectorInfos(name=name,
|
||||||
enabled=enabled)
|
enabled=enabled)
|
||||||
@ -121,7 +121,7 @@ class CollectorStateController(rest.RestController):
|
|||||||
:param infos: New state informations of the collector.
|
:param infos: New state informations of the collector.
|
||||||
:return: State of the collector.
|
:return: State of the collector.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'collector:update_state', {})
|
policy.authorize(pecan.request.context, 'collector:update_state', {})
|
||||||
enabled = self._db.set_state('collector_{}'.format(name),
|
enabled = self._db.set_state('collector_{}'.format(name),
|
||||||
infos.enabled)
|
infos.enabled)
|
||||||
collector = collector_models.CollectorInfos(name=name,
|
collector = collector_models.CollectorInfos(name=name,
|
||||||
|
@ -44,7 +44,7 @@ class ServiceInfoController(rest.RestController):
|
|||||||
|
|
||||||
:return: List of every services.
|
:return: List of every services.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'info:list_services_info', {})
|
policy.authorize(pecan.request.context, 'info:list_services_info', {})
|
||||||
services_info_list = []
|
services_info_list = []
|
||||||
for service, metadata in METADATA.items():
|
for service, metadata in METADATA.items():
|
||||||
info = metadata.copy()
|
info = metadata.copy()
|
||||||
@ -60,7 +60,7 @@ class ServiceInfoController(rest.RestController):
|
|||||||
|
|
||||||
:param service_name: name of the service.
|
:param service_name: name of the service.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'info:get_service_info', {})
|
policy.authorize(pecan.request.context, 'info:get_service_info', {})
|
||||||
try:
|
try:
|
||||||
info = METADATA[service_name].copy()
|
info = METADATA[service_name].copy()
|
||||||
info['service_id'] = service_name
|
info['service_id'] = service_name
|
||||||
@ -81,7 +81,7 @@ class InfoController(rest.RestController):
|
|||||||
})
|
})
|
||||||
def config(self):
|
def config(self):
|
||||||
"""Return current configuration."""
|
"""Return current configuration."""
|
||||||
policy.enforce(pecan.request.context, 'info:get_config', {})
|
policy.authorize(pecan.request.context, 'info:get_config', {})
|
||||||
info = {}
|
info = {}
|
||||||
info["collect"] = ck_utils.get_metrics_conf(CONF.collect.metrics_conf)
|
info["collect"] = ck_utils.get_metrics_conf(CONF.collect.metrics_conf)
|
||||||
return info
|
return info
|
||||||
|
@ -63,7 +63,7 @@ class ModulesController(rest.RestController, RatingModulesMixin):
|
|||||||
def route(self, *args):
|
def route(self, *args):
|
||||||
route = args[0]
|
route = args[0]
|
||||||
if route.startswith('/v1/module_config'):
|
if route.startswith('/v1/module_config'):
|
||||||
policy.enforce(pecan.request.context, 'rating:module_config', {})
|
policy.authorize(pecan.request.context, 'rating:module_config', {})
|
||||||
|
|
||||||
super(ModulesController, self).route(*args)
|
super(ModulesController, self).route(*args)
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ class ModulesController(rest.RestController, RatingModulesMixin):
|
|||||||
|
|
||||||
:return: name of every loaded modules.
|
:return: name of every loaded modules.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'rating:list_modules', {})
|
policy.authorize(pecan.request.context, 'rating:list_modules', {})
|
||||||
|
|
||||||
modules_list = []
|
modules_list = []
|
||||||
lock = lockutils.lock('rating-modules')
|
lock = lockutils.lock('rating-modules')
|
||||||
@ -92,7 +92,7 @@ class ModulesController(rest.RestController, RatingModulesMixin):
|
|||||||
|
|
||||||
:return: CloudKittyModule
|
:return: CloudKittyModule
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'rating:get_module', {})
|
policy.authorize(pecan.request.context, 'rating:get_module', {})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
lock = lockutils.lock('rating-modules')
|
lock = lockutils.lock('rating-modules')
|
||||||
@ -114,7 +114,7 @@ class ModulesController(rest.RestController, RatingModulesMixin):
|
|||||||
:param module_id: name of the module to modify
|
:param module_id: name of the module to modify
|
||||||
:param module: CloudKittyModule object describing the new desired state
|
:param module: CloudKittyModule object describing the new desired state
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'rating:update_module', {})
|
policy.authorize(pecan.request.context, 'rating:update_module', {})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
lock = lockutils.lock('rating-modules')
|
lock = lockutils.lock('rating-modules')
|
||||||
@ -194,7 +194,7 @@ class RatingController(rest.RestController):
|
|||||||
:param res_data: List of resource descriptions.
|
:param res_data: List of resource descriptions.
|
||||||
:return: Total price for these descriptions.
|
:return: Total price for these descriptions.
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'rating:quote', {})
|
policy.authorize(pecan.request.context, 'rating:quote', {})
|
||||||
|
|
||||||
client = pecan.request.rpc_client.prepare(namespace='rating')
|
client = pecan.request.rpc_client.prepare(namespace='rating')
|
||||||
res_dict = {}
|
res_dict = {}
|
||||||
@ -212,7 +212,7 @@ class RatingController(rest.RestController):
|
|||||||
"""Trigger a rating module list reload.
|
"""Trigger a rating module list reload.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'rating:module_config', {})
|
policy.authorize(pecan.request.context, 'rating:module_config', {})
|
||||||
self.modules.reload_extensions()
|
self.modules.reload_extensions()
|
||||||
self.module_config.reload_extensions()
|
self.module_config.reload_extensions()
|
||||||
self.module_config.expose_modules()
|
self.module_config.expose_modules()
|
||||||
|
@ -46,7 +46,7 @@ class ReportController(rest.RestController):
|
|||||||
"""Return the list of rated tenants.
|
"""Return the list of rated tenants.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
policy.enforce(pecan.request.context, 'report:list_tenants', {})
|
policy.authorize(pecan.request.context, 'report:list_tenants', {})
|
||||||
|
|
||||||
if not begin:
|
if not begin:
|
||||||
begin = ck_utils.get_month_start()
|
begin = ck_utils.get_month_start()
|
||||||
@ -78,8 +78,8 @@ class ReportController(rest.RestController):
|
|||||||
else:
|
else:
|
||||||
tenant_context = pecan.request.context.tenant
|
tenant_context = pecan.request.context.tenant
|
||||||
tenant_id = tenant_context if not tenant_id else tenant_id
|
tenant_id = tenant_context if not tenant_id else tenant_id
|
||||||
policy.enforce(pecan.request.context, 'report:get_total',
|
policy.authorize(pecan.request.context, 'report:get_total',
|
||||||
{"tenant_id": tenant_id})
|
{"tenant_id": tenant_id})
|
||||||
|
|
||||||
storage = pecan.request.storage_backend
|
storage = pecan.request.storage_backend
|
||||||
# FIXME(sheeprine): We should filter on user id.
|
# FIXME(sheeprine): We should filter on user id.
|
||||||
@ -114,8 +114,8 @@ class ReportController(rest.RestController):
|
|||||||
else:
|
else:
|
||||||
tenant_context = pecan.request.context.tenant
|
tenant_context = pecan.request.context.tenant
|
||||||
tenant_id = tenant_context if not tenant_id else tenant_id
|
tenant_id = tenant_context if not tenant_id else tenant_id
|
||||||
policy.enforce(pecan.request.context, 'report:get_summary',
|
policy.authorize(pecan.request.context, 'report:get_summary',
|
||||||
{"tenant_id": tenant_id})
|
{"tenant_id": tenant_id})
|
||||||
storage = pecan.request.storage_backend
|
storage = pecan.request.storage_backend
|
||||||
|
|
||||||
summarymodels = []
|
summarymodels = []
|
||||||
|
@ -48,7 +48,7 @@ class DataFramesController(rest.RestController):
|
|||||||
:return: Collection of DataFrame objects.
|
:return: Collection of DataFrame objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
policy.enforce(pecan.request.context, 'storage:list_data_frames', {})
|
policy.authorize(pecan.request.context, 'storage:list_data_frames', {})
|
||||||
|
|
||||||
if not begin:
|
if not begin:
|
||||||
begin = ck_utils.get_month_start()
|
begin = ck_utils.get_month_start()
|
||||||
|
34
cloudkitty/common/policies/__init__.py
Normal file
34
cloudkitty/common/policies/__init__.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Copyright 2017 GohighSec.
|
||||||
|
# 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 cloudkitty.common.policies import base
|
||||||
|
from cloudkitty.common.policies import collector
|
||||||
|
from cloudkitty.common.policies import info
|
||||||
|
from cloudkitty.common.policies import rating
|
||||||
|
from cloudkitty.common.policies import report
|
||||||
|
from cloudkitty.common.policies import storage
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return itertools.chain(
|
||||||
|
base.list_rules(),
|
||||||
|
collector.list_rules(),
|
||||||
|
info.list_rules(),
|
||||||
|
rating.list_rules(),
|
||||||
|
report.list_rules(),
|
||||||
|
storage.list_rules()
|
||||||
|
)
|
36
cloudkitty/common/policies/base.py
Normal file
36
cloudkitty/common/policies/base.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Copyright 2017 GohighSec.
|
||||||
|
# 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
|
||||||
|
|
||||||
|
RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
|
||||||
|
ROLE_ADMIN = 'role:admin'
|
||||||
|
UNPROTECTED = ''
|
||||||
|
|
||||||
|
rules = [
|
||||||
|
policy.RuleDefault(
|
||||||
|
name='context_is_admin',
|
||||||
|
check_str='role:admin'),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name='admin_or_owner',
|
||||||
|
check_str='is_admin:True or tenant:%(tenant_id)s'),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name='default',
|
||||||
|
check_str=UNPROTECTED)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return rules
|
57
cloudkitty/common/policies/collector.py
Normal file
57
cloudkitty/common/policies/collector.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Copyright 2017 GohighSec.
|
||||||
|
# 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 cloudkitty.common.policies import base
|
||||||
|
|
||||||
|
collector_policies = [
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='collector:list_mappings',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Return the list of every services mapped to a collector.',
|
||||||
|
operations=[{'path': '/v1/collector/mappings',
|
||||||
|
'method': 'LIST'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='collector:get_mapping',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Return a service to collector mapping.',
|
||||||
|
operations=[{'path': '/v1/collector/mappings/{service_id}',
|
||||||
|
'method': 'GET'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='collector:manage_mapping',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Manage a service to collector mapping.',
|
||||||
|
operations=[{'path': '/v1/collector/mappings',
|
||||||
|
'method': 'POST'},
|
||||||
|
{'path': '/v1/collector/mappings/{service_id}',
|
||||||
|
'method': 'DELETE'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='collector:get_state',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Query the enable state of a collector.',
|
||||||
|
operations=[{'path': '/v1/collector/states/{collector_id}',
|
||||||
|
'method': 'GET'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='collector:update_state',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Set the enable state of a collector.',
|
||||||
|
operations=[{'path': '/v1/collector/states/{collector_id}',
|
||||||
|
'method': 'PUT'}])
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return collector_policies
|
43
cloudkitty/common/policies/info.py
Normal file
43
cloudkitty/common/policies/info.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Copyright 2017 GohighSec.
|
||||||
|
# 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 cloudkitty.common.policies import base
|
||||||
|
|
||||||
|
info_policies = [
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='info:list_services_info',
|
||||||
|
check_str=base.UNPROTECTED,
|
||||||
|
description='List available services information in Cloudkitty.',
|
||||||
|
operations=[{'path': '/v1/info/services',
|
||||||
|
'method': 'LIST'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='info:get_service_info',
|
||||||
|
check_str=base.UNPROTECTED,
|
||||||
|
description='Get specified service information.',
|
||||||
|
operations=[{'path': '/v1/info/services/{service_id}',
|
||||||
|
'method': 'GET'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='info:get_config',
|
||||||
|
check_str=base.UNPROTECTED,
|
||||||
|
description='Get current configuration in Cloudkitty.',
|
||||||
|
operations=[{'path': '/v1/info/config',
|
||||||
|
'method': 'GET'}])
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return info_policies
|
56
cloudkitty/common/policies/rating.py
Normal file
56
cloudkitty/common/policies/rating.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Copyright 2017 GohighSec.
|
||||||
|
# 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 cloudkitty.common.policies import base
|
||||||
|
|
||||||
|
rating_policies = [
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='rating:list_modules',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Reture the list of loaded modules in Cloudkitty.',
|
||||||
|
operations=[{'path': '/v1/rating/modules',
|
||||||
|
'method': 'LIST'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='rating:get_module',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Get specified module.',
|
||||||
|
operations=[{'path': '/v1/rating/modules/{module_id}',
|
||||||
|
'method': 'GET'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='rating:update_module',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Change the state and priority of a module.',
|
||||||
|
operations=[{'path': '/v1/rating/modules/{module_id}',
|
||||||
|
'method': 'PUT'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='rating:quote',
|
||||||
|
check_str=base.UNPROTECTED,
|
||||||
|
description='Get an instant quote based on multiple resource '
|
||||||
|
'descriptions.',
|
||||||
|
operations=[{'path': '/v1/rating/quote',
|
||||||
|
'method': 'POST'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='rating:module_config',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Trigger a rating module list reload.',
|
||||||
|
operations=[{'path': '/v1/rating/reload_modules',
|
||||||
|
'method': 'GET'}])
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return rating_policies
|
43
cloudkitty/common/policies/report.py
Normal file
43
cloudkitty/common/policies/report.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Copyright 2017 GohighSec.
|
||||||
|
# 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 cloudkitty.common.policies import base
|
||||||
|
|
||||||
|
report_policies = [
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='report:list_tenants',
|
||||||
|
check_str=base.ROLE_ADMIN,
|
||||||
|
description='Return the list of rated tenants.',
|
||||||
|
operations=[{'path': '/v1/report/tenants',
|
||||||
|
'method': 'GET'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='report:get_summary',
|
||||||
|
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||||
|
description='Return the summary to pay for a given period.',
|
||||||
|
operations=[{'path': '/v1/report/summary',
|
||||||
|
'method': 'GET'}]),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='report:get_total',
|
||||||
|
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||||
|
description='Return the amount to pay for a given period.',
|
||||||
|
operations=[{'path': '/v1/report/total',
|
||||||
|
'method': 'GET'}])
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return report_policies
|
32
cloudkitty/common/policies/storage.py
Normal file
32
cloudkitty/common/policies/storage.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Copyright 2017 GohighSec.
|
||||||
|
# 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 cloudkitty.common.policies import base
|
||||||
|
|
||||||
|
storage_policies = [
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='storage:list_data_frames',
|
||||||
|
check_str=base.UNPROTECTED,
|
||||||
|
description='Return a list of rated resources for a time period '
|
||||||
|
'and a tenant.',
|
||||||
|
operations=[{'path': '/v1/storage/dataframes',
|
||||||
|
'method': 'GET'}])
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return storage_policies
|
@ -15,13 +15,28 @@
|
|||||||
|
|
||||||
# Borrowed from cinder (cinder/policy.py)
|
# Borrowed from cinder (cinder/policy.py)
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import sys
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_policy import opts as policy_opts
|
||||||
from oslo_policy import policy
|
from oslo_policy import policy
|
||||||
|
from oslo_utils import excutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from cloudkitty.common import policies
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
policy_opts.set_defaults(cfg.CONF, 'policy.json')
|
||||||
|
|
||||||
_ENFORCER = None
|
_ENFORCER = None
|
||||||
|
# oslo_policy will read the policy configuration file again when the file
|
||||||
|
# is changed in runtime so the old policy rules will be saved to
|
||||||
|
# saved_file_rules and used to compare with new rules to determine the
|
||||||
|
# rules whether were updated.
|
||||||
|
saved_file_rules = []
|
||||||
|
|
||||||
|
|
||||||
# TODO(gpocentek): provide a proper parent class to handle such exceptions
|
# TODO(gpocentek): provide a proper parent class to handle such exceptions
|
||||||
@ -37,13 +52,37 @@ class PolicyNotAuthorized(Exception):
|
|||||||
return six.text_type(self.msg)
|
return six.text_type(self.msg)
|
||||||
|
|
||||||
|
|
||||||
|
def reset():
|
||||||
|
global _ENFORCER
|
||||||
|
if _ENFORCER:
|
||||||
|
_ENFORCER.clear()
|
||||||
|
_ENFORCER = None
|
||||||
|
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
global _ENFORCER
|
global _ENFORCER
|
||||||
|
global saved_file_rules
|
||||||
if not _ENFORCER:
|
if not _ENFORCER:
|
||||||
_ENFORCER = policy.Enforcer(CONF)
|
_ENFORCER = policy.Enforcer(CONF)
|
||||||
|
register_rules(_ENFORCER)
|
||||||
|
|
||||||
|
# Only the rules which are loaded from file may be changed.
|
||||||
|
current_file_rules = _ENFORCER.file_rules
|
||||||
|
current_file_rules = _serialize_rules(current_file_rules)
|
||||||
|
|
||||||
|
# Checks whether the rules are updated in the runtime
|
||||||
|
if saved_file_rules != current_file_rules:
|
||||||
|
saved_file_rules = copy.deepcopy(current_file_rules)
|
||||||
|
|
||||||
|
|
||||||
def enforce(context, action, target):
|
def _serialize_rules(rules):
|
||||||
|
"""Serialize all the Rule object as string."""
|
||||||
|
result = [(rule_name, str(rule))
|
||||||
|
for rule_name, rule in rules.items()]
|
||||||
|
return sorted(result, key=lambda rule: rule[0])
|
||||||
|
|
||||||
|
|
||||||
|
def authorize(context, action, target):
|
||||||
"""Verifies that the action is valid on the target in this context.
|
"""Verifies that the action is valid on the target in this context.
|
||||||
|
|
||||||
:param context: cloudkitty context
|
:param context: cloudkitty context
|
||||||
@ -65,10 +104,20 @@ def enforce(context, action, target):
|
|||||||
|
|
||||||
init()
|
init()
|
||||||
|
|
||||||
return _ENFORCER.enforce(action, target, context.to_dict(),
|
try:
|
||||||
do_raise=True,
|
return _ENFORCER.authorize(action, target, context.to_dict(),
|
||||||
exc=PolicyNotAuthorized,
|
do_raise=True,
|
||||||
action=action)
|
exc=PolicyNotAuthorized,
|
||||||
|
action=action)
|
||||||
|
|
||||||
|
except policy.PolicyNotRegistered:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.exception('Policy not registered')
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.error('Policy check for %(action)s failed with credentials '
|
||||||
|
'%(credentials)s',
|
||||||
|
{'action': action, 'credentials': context.to_dict()})
|
||||||
|
|
||||||
|
|
||||||
def check_is_admin(roles):
|
def check_is_admin(roles):
|
||||||
@ -87,4 +136,28 @@ def check_is_admin(roles):
|
|||||||
target = {'project_id': ''}
|
target = {'project_id': ''}
|
||||||
credentials = {'roles': roles}
|
credentials = {'roles': roles}
|
||||||
|
|
||||||
return _ENFORCER.enforce('context_is_admin', target, credentials)
|
return _ENFORCER.authorize('context_is_admin', target, credentials)
|
||||||
|
|
||||||
|
|
||||||
|
def register_rules(enforcer):
|
||||||
|
enforcer.register_defaults(policies.list_rules())
|
||||||
|
|
||||||
|
|
||||||
|
def get_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 Cloudkitty 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='cloudkitty')
|
||||||
|
init()
|
||||||
|
return _ENFORCER
|
||||||
|
@ -142,7 +142,7 @@ class RatingRestControllerBase(rest.RestController):
|
|||||||
@pecan.expose()
|
@pecan.expose()
|
||||||
def _route(self, args, request):
|
def _route(self, args, request):
|
||||||
try:
|
try:
|
||||||
policy.enforce(request.context, 'rating:module_config', {})
|
policy.authorize(request.context, 'rating:module_config', {})
|
||||||
except policy.PolicyNotAuthorized as e:
|
except policy.PolicyNotAuthorized as e:
|
||||||
pecan.abort(403, six.text_type(e))
|
pecan.abort(403, six.text_type(e))
|
||||||
|
|
||||||
|
122
cloudkitty/tests/test_policy.py
Normal file
122
cloudkitty/tests/test_policy.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
# Copyright (c) 2017 GohighSec.
|
||||||
|
# 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 os.path
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_config import fixture as config_fixture
|
||||||
|
from oslo_context import context
|
||||||
|
from oslo_policy import policy as oslo_policy
|
||||||
|
|
||||||
|
from cloudkitty.common import policy
|
||||||
|
from cloudkitty import tests
|
||||||
|
from cloudkitty import utils
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class PolicyFileTestCase(tests.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(PolicyFileTestCase, self).setUp()
|
||||||
|
self.context = context.RequestContext('fake', 'fake', roles=['member'])
|
||||||
|
self.target = {}
|
||||||
|
self.fixture = self.useFixture(config_fixture.Config(CONF))
|
||||||
|
self.addCleanup(policy.reset)
|
||||||
|
CONF(args=[], project='cloudkitty', default_config_files=[])
|
||||||
|
|
||||||
|
def test_modified_policy_reloads(self):
|
||||||
|
with utils.tempdir() as tmpdir:
|
||||||
|
tmpfilename = os.path.join(tmpdir, 'policy')
|
||||||
|
self.fixture.config(policy_file=tmpfilename, group='oslo_policy')
|
||||||
|
rule = oslo_policy.RuleDefault('example:test', "")
|
||||||
|
policy.reset()
|
||||||
|
policy.init()
|
||||||
|
policy._ENFORCER.register_defaults([rule])
|
||||||
|
|
||||||
|
action = "example:test"
|
||||||
|
with open(tmpfilename, "w") as policyfile:
|
||||||
|
policyfile.write('{"example:test": ""}')
|
||||||
|
policy.authorize(self.context, action, self.target)
|
||||||
|
with open(tmpfilename, "w") as policyfile:
|
||||||
|
policyfile.write('{"example:test": "!"}')
|
||||||
|
policy._ENFORCER.load_rules(True)
|
||||||
|
self.assertRaises(policy.PolicyNotAuthorized,
|
||||||
|
policy.authorize,
|
||||||
|
self.context, action, self.target)
|
||||||
|
|
||||||
|
|
||||||
|
class PolicyTestCase(tests.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(PolicyTestCase, self).setUp()
|
||||||
|
rules = [
|
||||||
|
oslo_policy.RuleDefault("true", '@'),
|
||||||
|
oslo_policy.RuleDefault("test:allowed", '@'),
|
||||||
|
oslo_policy.RuleDefault("test:denied", "!"),
|
||||||
|
oslo_policy.RuleDefault("test:early_and_fail", "! and @"),
|
||||||
|
oslo_policy.RuleDefault("test:early_or_success", "@ or !"),
|
||||||
|
oslo_policy.RuleDefault("test:lowercase_admin",
|
||||||
|
"role:admin"),
|
||||||
|
oslo_policy.RuleDefault("test:uppercase_admin",
|
||||||
|
"role:ADMIN"),
|
||||||
|
]
|
||||||
|
CONF(args=[], project='cloudkitty', default_config_files=[])
|
||||||
|
# before a policy rule can be used, its default has to be registered.
|
||||||
|
policy.reset()
|
||||||
|
policy.init()
|
||||||
|
policy._ENFORCER.register_defaults(rules)
|
||||||
|
self.context = context.RequestContext('fake',
|
||||||
|
'fake',
|
||||||
|
roles=['member'])
|
||||||
|
self.target = {}
|
||||||
|
self.addCleanup(policy.reset)
|
||||||
|
|
||||||
|
def test_enforce_nonexistent_action_throws(self):
|
||||||
|
action = "test:noexist"
|
||||||
|
self.assertRaises(oslo_policy.PolicyNotRegistered, policy.authorize,
|
||||||
|
self.context, action, self.target)
|
||||||
|
|
||||||
|
def test_enforce_bad_action_throws(self):
|
||||||
|
action = "test:denied"
|
||||||
|
self.assertRaises(policy.PolicyNotAuthorized, policy.authorize,
|
||||||
|
self.context, action, self.target)
|
||||||
|
|
||||||
|
def test_enforce_bad_action_noraise(self):
|
||||||
|
action = "test:denied"
|
||||||
|
self.assertRaises(policy.PolicyNotAuthorized, policy.authorize,
|
||||||
|
self.context, action, self.target)
|
||||||
|
|
||||||
|
def test_enforce_good_action(self):
|
||||||
|
action = "test:allowed"
|
||||||
|
result = policy.authorize(self.context, action, self.target)
|
||||||
|
self.assertTrue(result)
|
||||||
|
|
||||||
|
def test_early_AND_authorization(self):
|
||||||
|
action = "test:early_and_fail"
|
||||||
|
self.assertRaises(policy.PolicyNotAuthorized, policy.authorize,
|
||||||
|
self.context, action, self.target)
|
||||||
|
|
||||||
|
def test_early_OR_authorization(self):
|
||||||
|
action = "test:early_or_success"
|
||||||
|
policy.authorize(self.context, action, self.target)
|
||||||
|
|
||||||
|
def test_ignore_case_role_check(self):
|
||||||
|
lowercase_action = "test:lowercase_admin"
|
||||||
|
uppercase_action = "test:uppercase_admin"
|
||||||
|
admin_context = context.RequestContext('admin',
|
||||||
|
'fake',
|
||||||
|
roles=['AdMiN'])
|
||||||
|
policy.authorize(admin_context, lowercase_action, self.target)
|
||||||
|
policy.authorize(admin_context, uppercase_action, self.target)
|
@ -22,12 +22,17 @@ We're mostly using oslo_utils for time calculations but we're encapsulating it
|
|||||||
to ease maintenance in case of library modifications.
|
to ease maintenance in case of library modifications.
|
||||||
"""
|
"""
|
||||||
import calendar
|
import calendar
|
||||||
|
import contextlib
|
||||||
import datetime
|
import datetime
|
||||||
|
import shutil
|
||||||
|
import six
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from six import moves
|
from six import moves
|
||||||
from stevedore import extension
|
from stevedore import extension
|
||||||
@ -257,3 +262,16 @@ def get_metrics_conf(conf_path):
|
|||||||
LOG.error(exc)
|
LOG.error(exc)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def tempdir(**kwargs):
|
||||||
|
tmpdir = tempfile.mkdtemp(**kwargs)
|
||||||
|
try:
|
||||||
|
yield tmpdir
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
except OSError as e:
|
||||||
|
LOG.debug('Could not remove tmpdir: %s',
|
||||||
|
six.text_type(e))
|
||||||
|
@ -132,7 +132,11 @@ function configure_cloudkitty {
|
|||||||
|
|
||||||
touch $CLOUDKITTY_CONF
|
touch $CLOUDKITTY_CONF
|
||||||
|
|
||||||
cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF_DIR/policy.json $CLOUDKITTY_CONF_DIR
|
# generate policy sample file
|
||||||
|
oslopolicy-sample-generator --config-file $CLOUDKITTY_DIR/etc/oslo-policy-generator/cloudkitty.conf --output-file $CLOUDKITTY_DIR/etc/cloudkitty/policy.yaml.sample
|
||||||
|
cp $CLOUDKITTY_DIR/etc/cloudkitty/policy.yaml.sample "$CLOUDKITTY_CONF_DIR/policy.yaml"
|
||||||
|
iniset $CLOUDKITTY_CONF oslo_policy policy_file 'policy.yaml'
|
||||||
|
|
||||||
cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF_DIR/api_paste.ini $CLOUDKITTY_CONF_DIR
|
cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF_DIR/api_paste.ini $CLOUDKITTY_CONF_DIR
|
||||||
cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF_DIR/metrics.yml $CLOUDKITTY_CONF_DIR
|
cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF_DIR/metrics.yml $CLOUDKITTY_CONF_DIR
|
||||||
iniset_rpc_backend cloudkitty $CLOUDKITTY_CONF DEFAULT
|
iniset_rpc_backend cloudkitty $CLOUDKITTY_CONF DEFAULT
|
||||||
|
@ -57,6 +57,8 @@ extensions = ['sphinx.ext.coverage',
|
|||||||
'sphinxcontrib.pecanwsme.rest',
|
'sphinxcontrib.pecanwsme.rest',
|
||||||
'sphinxcontrib.httpdomain',
|
'sphinxcontrib.httpdomain',
|
||||||
'openstackdocstheme',
|
'openstackdocstheme',
|
||||||
|
'oslo_policy.sphinxext',
|
||||||
|
'oslo_policy.sphinxpolicygen',
|
||||||
]
|
]
|
||||||
|
|
||||||
# openstackdocstheme options
|
# openstackdocstheme options
|
||||||
@ -65,6 +67,9 @@ bug_project = 'cloudkitty'
|
|||||||
bug_tag = ''
|
bug_tag = ''
|
||||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||||
|
|
||||||
|
policy_generator_config_file = '../../etc/oslo-policy-generator/cloudkitty.conf'
|
||||||
|
sample_policy_basename = '_static/cloudkitty'
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
# templates_path = []
|
# templates_path = []
|
||||||
|
|
||||||
|
12
doc/source/configuration/policy.rst
Normal file
12
doc/source/configuration/policy.rst
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
====================
|
||||||
|
Policy configuration
|
||||||
|
====================
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The following is an overview of all available policies in Cloudkitty. For a sample
|
||||||
|
configuration file, refer to :doc:`samples/policy-yaml`.
|
||||||
|
|
||||||
|
.. show-policy::
|
||||||
|
:config-file: ../../etc/oslo-policy-generator/cloudkitty.conf
|
11
doc/source/configuration/samples/index.rst
Normal file
11
doc/source/configuration/samples/index.rst
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
==========================
|
||||||
|
Sample configuration files
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Configuration files can alter how cloudkitty behaves at runtime and by default
|
||||||
|
are located in ``/etc/cloudkitty/``. Links to sample configuration files can be
|
||||||
|
found below:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
policy-yaml.rst
|
8
doc/source/configuration/samples/policy-yaml.rst
Normal file
8
doc/source/configuration/samples/policy-yaml.rst
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
===========
|
||||||
|
policy.yaml
|
||||||
|
===========
|
||||||
|
|
||||||
|
Use the ``policy.yaml`` file to define additional access controls that apply to
|
||||||
|
the Rating service:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../_static/policy.yaml.sample
|
15
doc/source/sample_policy.rst
Normal file
15
doc/source/sample_policy.rst
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
========================
|
||||||
|
Cloudkitty Sample Policy
|
||||||
|
========================
|
||||||
|
|
||||||
|
The following is a sample Cloudkitty policy file that has been auto-generated
|
||||||
|
from default policy values in code. If you're using the default policies, then
|
||||||
|
the maintenance of this file is not necessary, and it should not be copied into
|
||||||
|
a deployment. Doing so will result in duplicate policy definitions. It is here
|
||||||
|
to help explain which policy operations protect specific Cloudkitty APIs, but it
|
||||||
|
is not suggested to copy and paste into a deployment unless you're planning on
|
||||||
|
providing a different policy for an operation that is not the default.
|
||||||
|
|
||||||
|
The sample policy file can also be viewed in `file form <_static/policy.yaml.sample>`_.
|
||||||
|
|
||||||
|
.. literalinclude:: _static/policy.yaml.sample
|
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"context_is_admin": "role:admin",
|
|
||||||
"admin_or_owner": "is_admin:True or tenant:%(tenant_id)s",
|
|
||||||
"default": "",
|
|
||||||
|
|
||||||
"info:list_services_info": "",
|
|
||||||
"info:get_service_info": "",
|
|
||||||
"info:get_config":"",
|
|
||||||
|
|
||||||
"rating:list_modules": "role:admin",
|
|
||||||
"rating:get_module": "role:admin",
|
|
||||||
"rating:update_module": "role:admin",
|
|
||||||
"rating:quote": "",
|
|
||||||
|
|
||||||
"report:list_tenants": "role:admin",
|
|
||||||
"report:get_summary": "rule:admin_or_owner",
|
|
||||||
"report:get_total": "rule:admin_or_owner",
|
|
||||||
|
|
||||||
"collector:list_mappings": "role:admin",
|
|
||||||
"collector:get_mapping": "role:admin",
|
|
||||||
"collector:manage_mapping": "role:admin",
|
|
||||||
"collector:get_state": "role:admin",
|
|
||||||
"collector:update_state": "role:admin",
|
|
||||||
|
|
||||||
"storage:list_data_frames": "",
|
|
||||||
|
|
||||||
"rating:module_config": "role:admin"
|
|
||||||
}
|
|
3
etc/oslo-policy-generator/cloudkitty.conf
Normal file
3
etc/oslo-policy-generator/cloudkitty.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
output_file = etc/cloudkitty/policy.yaml.sample
|
||||||
|
namespace = cloudkitty
|
@ -32,6 +32,12 @@ console_scripts =
|
|||||||
wsgi_scripts =
|
wsgi_scripts =
|
||||||
cloudkitty-api = cloudkitty.api.app:build_wsgi_app
|
cloudkitty-api = cloudkitty.api.app:build_wsgi_app
|
||||||
|
|
||||||
|
oslo.policy.enforcer =
|
||||||
|
cloudkitty = cloudkitty.common.policy:get_enforcer
|
||||||
|
|
||||||
|
oslo.policy.policies =
|
||||||
|
cloudkitty = cloudkitty.common.policies:list_rules
|
||||||
|
|
||||||
oslo.config.opts =
|
oslo.config.opts =
|
||||||
cloudkitty.common.config = cloudkitty.common.config:list_opts
|
cloudkitty.common.config = cloudkitty.common.config:list_opts
|
||||||
|
|
||||||
|
3
tox.ini
3
tox.ini
@ -34,6 +34,9 @@ commands =
|
|||||||
commands =
|
commands =
|
||||||
oslo-config-generator --config-file etc/oslo-config-generator/cloudkitty.conf
|
oslo-config-generator --config-file etc/oslo-config-generator/cloudkitty.conf
|
||||||
|
|
||||||
|
[testenv:genpolicy]
|
||||||
|
commands = oslopolicy-sample-generator --config-file=etc/oslo-policy-generator/cloudkitty.conf
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
commands = python setup.py build_sphinx
|
commands = python setup.py build_sphinx
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user