diff --git a/cloudkitty/common/policies/base.py b/cloudkitty/common/policies/base.py index 0fe48476..9866afe2 100644 --- a/cloudkitty/common/policies/base.py +++ b/cloudkitty/common/policies/base.py @@ -19,6 +19,24 @@ RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner' ROLE_ADMIN = 'role:admin' UNPROTECTED = '' +DEPRECATED_REASON = """ +CloudKitty API policies are introducing new default roles with scope_type +capabilities. Old policies are deprecated and silently going to be ignored +in future release. +""" + +DEPRECATED_ADMIN_OR_OWNER_POLICY = policy.DeprecatedRule( + name=RULE_ADMIN_OR_OWNER, + check_str='is_admin:True or ' + '(role:admin and is_admin_project:True) or ' + 'project_id:%(project_id)s', + deprecated_reason=DEPRECATED_REASON, + deprecated_since='22.0.0' +) + +PROJECT_MEMBER_OR_ADMIN = 'rule:project_member_or_admin' +PROJECT_READER_OR_ADMIN = 'rule:project_reader_or_admin' + rules = [ policy.RuleDefault( name='context_is_admin', @@ -27,10 +45,33 @@ rules = [ name='admin_or_owner', check_str='is_admin:True or ' '(role:admin and is_admin_project:True) or ' - 'project_id:%(project_id)s'), + 'project_id:%(project_id)s', + deprecated_for_removal=True, + deprecated_reason=DEPRECATED_REASON, + deprecated_since='22.0.0'), policy.RuleDefault( name='default', - check_str=UNPROTECTED) + check_str=UNPROTECTED), + policy.RuleDefault( + "project_member_api", + "role:member and project_id:%(project_id)s", + "Default rule for Project level non admin APIs.", + deprecated_rule=DEPRECATED_ADMIN_OR_OWNER_POLICY), + policy.RuleDefault( + "project_reader_api", + "role:reader and project_id:%(project_id)s", + "Default rule for Project level read only APIs.", + deprecated_rule=DEPRECATED_ADMIN_OR_OWNER_POLICY), + policy.RuleDefault( + "project_member_or_admin", + "rule:project_member_api or rule:context_is_admin", + "Default rule for Project Member or admin APIs.", + deprecated_rule=DEPRECATED_ADMIN_OR_OWNER_POLICY), + policy.RuleDefault( + "project_reader_or_admin", + "rule:project_reader_api or rule:context_is_admin", + "Default rule for Project reader or admin APIs.", + deprecated_rule=DEPRECATED_ADMIN_OR_OWNER_POLICY) ] diff --git a/cloudkitty/common/policies/v1/collector.py b/cloudkitty/common/policies/v1/collector.py index afedc6c1..194aa351 100644 --- a/cloudkitty/common/policies/v1/collector.py +++ b/cloudkitty/common/policies/v1/collector.py @@ -23,13 +23,15 @@ collector_policies = [ check_str=base.ROLE_ADMIN, description='Return the list of every services mapped to a collector.', operations=[{'path': '/v1/collector/mappings', - 'method': 'LIST'}]), + 'method': 'LIST'}], + scope_types=['project']), 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'}]), + 'method': 'GET'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='collector:manage_mapping', check_str=base.ROLE_ADMIN, @@ -37,19 +39,22 @@ collector_policies = [ operations=[{'path': '/v1/collector/mappings', 'method': 'POST'}, {'path': '/v1/collector/mappings/{service_id}', - 'method': 'DELETE'}]), + 'method': 'DELETE'}], + scope_types=['project']), 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'}]), + 'method': 'GET'}], + scope_types=['project']), 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'}]) + 'method': 'PUT'}], + scope_types=['project']) ] diff --git a/cloudkitty/common/policies/v1/info.py b/cloudkitty/common/policies/v1/info.py index 838f088a..42661b74 100644 --- a/cloudkitty/common/policies/v1/info.py +++ b/cloudkitty/common/policies/v1/info.py @@ -23,31 +23,36 @@ info_policies = [ check_str=base.UNPROTECTED, description='List available services information in Cloudkitty.', operations=[{'path': '/v1/info/services', - 'method': 'LIST'}]), + 'method': 'LIST'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='info:get_service_info', check_str=base.UNPROTECTED, description='Get specified service information.', operations=[{'path': '/v1/info/services/{metric_id}', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='info:list_metrics_info', check_str=base.UNPROTECTED, description='List available metrics information in Cloudkitty.', operations=[{'path': '/v1/info/metrics', - 'method': 'LIST'}]), + 'method': 'LIST'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='info:get_metric_info', check_str=base.UNPROTECTED, description='Get specified metric information.', operations=[{'path': '/v1/info/metrics/{metric_id}', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='info:get_config', check_str=base.UNPROTECTED, description='Get current configuration in Cloudkitty.', operations=[{'path': '/v1/info/config', - 'method': 'GET'}]) + 'method': 'GET'}], + scope_types=['project']) ] diff --git a/cloudkitty/common/policies/v1/rating.py b/cloudkitty/common/policies/v1/rating.py index 3e164e36..a91e1f04 100644 --- a/cloudkitty/common/policies/v1/rating.py +++ b/cloudkitty/common/policies/v1/rating.py @@ -23,32 +23,37 @@ rating_policies = [ check_str=base.ROLE_ADMIN, description='Return the list of loaded modules in Cloudkitty.', operations=[{'path': '/v1/rating/modules', - 'method': 'LIST'}]), + 'method': 'LIST'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='rating:get_module', check_str=base.ROLE_ADMIN, description='Get specified module.', operations=[{'path': '/v1/rating/modules/{module_id}', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), 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'}]), + 'method': 'PUT'}], + scope_types=['project']), 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'}]), + 'method': 'POST'}], + scope_types=['project']), 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'}]) + 'method': 'GET'}], + scope_types=['project']) ] diff --git a/cloudkitty/common/policies/v1/report.py b/cloudkitty/common/policies/v1/report.py index ae06dd90..1a65cc6c 100644 --- a/cloudkitty/common/policies/v1/report.py +++ b/cloudkitty/common/policies/v1/report.py @@ -23,19 +23,22 @@ report_policies = [ check_str=base.ROLE_ADMIN, description='Return the list of rated tenants.', operations=[{'path': '/v1/report/tenants', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='report:get_summary', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.PROJECT_READER_OR_ADMIN, description='Return the summary to pay for a given period.', operations=[{'path': '/v1/report/summary', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='report:get_total', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.PROJECT_READER_OR_ADMIN, description='Return the amount to pay for a given period.', operations=[{'path': '/v1/report/total', - 'method': 'GET'}]) + 'method': 'GET'}], + scope_types=['project']) ] diff --git a/cloudkitty/common/policies/v1/storage.py b/cloudkitty/common/policies/v1/storage.py index 47d92079..afa8e948 100644 --- a/cloudkitty/common/policies/v1/storage.py +++ b/cloudkitty/common/policies/v1/storage.py @@ -20,11 +20,12 @@ from cloudkitty.common.policies import base storage_policies = [ policy.DocumentedRuleDefault( name='storage:list_data_frames', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.PROJECT_READER_OR_ADMIN, description='Return a list of rated resources for a time period ' 'and a tenant.', operations=[{'path': '/v1/storage/dataframes', - 'method': 'GET'}]) + 'method': 'GET'}], + scope_types=['project']) ] diff --git a/cloudkitty/common/policies/v2/dataframes.py b/cloudkitty/common/policies/v2/dataframes.py index 0fcaf540..a17bdc24 100644 --- a/cloudkitty/common/policies/v2/dataframes.py +++ b/cloudkitty/common/policies/v2/dataframes.py @@ -23,13 +23,15 @@ dataframes_policies = [ check_str=base.ROLE_ADMIN, description='Add one or several DataFrames', operations=[{'path': '/v2/dataframes', - 'method': 'POST'}]), + 'method': 'POST'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='dataframes:get', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.PROJECT_READER_OR_ADMIN, description='Get DataFrames', operations=[{'path': '/v2/dataframes', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), ] diff --git a/cloudkitty/common/policies/v2/rating.py b/cloudkitty/common/policies/v2/rating.py index 24ff5dd1..3d3b496b 100644 --- a/cloudkitty/common/policies/v2/rating.py +++ b/cloudkitty/common/policies/v2/rating.py @@ -23,19 +23,22 @@ rating_policies = [ check_str=base.ROLE_ADMIN, description='Returns the list of loaded modules in Cloudkitty.', operations=[{'path': '/v2/rating/modules', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='v2_rating:get_module', check_str=base.ROLE_ADMIN, description='Get specified module.', operations=[{'path': '/v2/rating/modules/{module_id}', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='v2_rating:update_module', check_str=base.ROLE_ADMIN, description='Change the state and priority of a module.', operations=[{'path': '/v2/rating/modules/{module_id}', - 'method': 'PUT'}]) + 'method': 'PUT'}], + scope_types=['project']) ] diff --git a/cloudkitty/common/policies/v2/scope.py b/cloudkitty/common/policies/v2/scope.py index c2d28eb8..56e9973e 100644 --- a/cloudkitty/common/policies/v2/scope.py +++ b/cloudkitty/common/policies/v2/scope.py @@ -23,25 +23,29 @@ scope_policies = [ check_str=base.ROLE_ADMIN, description='Get the state of one or several scopes', operations=[{'path': '/v2/scope', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='scope:reset_state', check_str=base.ROLE_ADMIN, description='Reset the state of one or several scopes', operations=[{'path': '/v2/scope', - 'method': 'PUT'}]), + 'method': 'PUT'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='scope:patch_state', check_str=base.ROLE_ADMIN, description='Enables operators to patch a storage scope', operations=[{'path': '/v2/scope', - 'method': 'PATCH'}]), + 'method': 'PATCH'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='scope:post_state', check_str=base.ROLE_ADMIN, description='Enables operators to create a storage scope', operations=[{'path': '/v2/scope', - 'method': 'POST'}]), + 'method': 'POST'}], + scope_types=['project']), ] diff --git a/cloudkitty/common/policies/v2/summary.py b/cloudkitty/common/policies/v2/summary.py index 74c25166..51109732 100644 --- a/cloudkitty/common/policies/v2/summary.py +++ b/cloudkitty/common/policies/v2/summary.py @@ -19,10 +19,11 @@ from cloudkitty.common.policies import base example_policies = [ policy.DocumentedRuleDefault( name='summary:get_summary', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.PROJECT_READER_OR_ADMIN, description='Get a rating summary', operations=[{'path': '/v2/summary', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), ] diff --git a/cloudkitty/common/policies/v2/tasks.py b/cloudkitty/common/policies/v2/tasks.py index f2d4ee44..4dc37553 100644 --- a/cloudkitty/common/policies/v2/tasks.py +++ b/cloudkitty/common/policies/v2/tasks.py @@ -22,13 +22,15 @@ schedule_policies = [ check_str=base.ROLE_ADMIN, description='Schedule a scope for reprocessing', operations=[{'path': '/v2/task/reprocesses', - 'method': 'POST'}]), + 'method': 'POST'}], + scope_types=['project']), policy.DocumentedRuleDefault( name='schedule:get_task_reprocesses', check_str=base.ROLE_ADMIN, description='Get reprocessing schedule tasks for scopes.', operations=[{'path': '/v2/task/reprocesses', - 'method': 'GET'}]), + 'method': 'GET'}], + scope_types=['project']), ] diff --git a/releasenotes/notes/secure-rbac-defaults-5bb903323634a94c.yaml b/releasenotes/notes/secure-rbac-defaults-5bb903323634a94c.yaml new file mode 100644 index 00000000..3899f0c0 --- /dev/null +++ b/releasenotes/notes/secure-rbac-defaults-5bb903323634a94c.yaml @@ -0,0 +1,17 @@ +--- +features: + - | + The CloudKitty policies implemented the scope concept and new default roles + (``admin``, ``member``, and ``reader``) provided by keystone. +upgrade: + - | + All the policies implement the ``scope_type`` and new defaults. + + * **Scope** + + Each policy is protected with ``project`` ``scope_type``. + + * **New Defaults (Admin, Member and Reader)** + + Policies are default to Admin, Member and Reader roles. Old roles are + also supported. There is no change in the legacy admin access.