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:
		
							
								
								
									
										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,7 +78,7 @@ 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
 | 
				
			||||||
@@ -114,7 +114,7 @@ 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,11 +104,21 @@ def enforce(context, action, target):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    init()
 | 
					    init()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return _ENFORCER.enforce(action, target, context.to_dict(),
 | 
					    try:
 | 
				
			||||||
 | 
					        return _ENFORCER.authorize(action, target, context.to_dict(),
 | 
				
			||||||
                                   do_raise=True,
 | 
					                                   do_raise=True,
 | 
				
			||||||
                                   exc=PolicyNotAuthorized,
 | 
					                                   exc=PolicyNotAuthorized,
 | 
				
			||||||
                                   action=action)
 | 
					                                   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):
 | 
				
			||||||
    """Whether or not roles contains 'admin' role according to policy setting.
 | 
					    """Whether or not roles contains 'admin' role according to policy setting.
 | 
				
			||||||
@@ -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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user