diff --git a/heat/api/openstack/v1/util.py b/heat/api/openstack/v1/util.py index 86df81eb6c..70a22d4208 100644 --- a/heat/api/openstack/v1/util.py +++ b/heat/api/openstack/v1/util.py @@ -29,12 +29,17 @@ def registered_policy_enforce(handler): """ @functools.wraps(handler) def handle_stack_method(controller, req, tenant_id, **kwargs): - if req.context.tenant_id != tenant_id and not req.context.is_admin: + _target = {"project_id": tenant_id} + + if req.context.tenant_id != tenant_id and not ( + req.context.is_admin or + req.context.system_scope == all): raise exc.HTTPForbidden() allowed = req.context.policy.enforce( context=req.context, action=handler.__name__, scope=controller.REQUEST_SCOPE, + target=_target, is_registered_policy=True) if not allowed: raise exc.HTTPForbidden() diff --git a/heat/policies/actions.py b/heat/policies/actions.py index 78865786e2..ef40337f18 100644 --- a/heat/policies/actions.py +++ b/heat/policies/actions.py @@ -10,29 +10,51 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from heat.policies import base POLICY_ROOT = 'actions:%s' +DEPRECATED_REASON = """ +The actions API now supports system scope and default roles. +""" -def _action_rule(action_name, description): - return policy.DocumentedRuleDefault( - name=POLICY_ROOT % action_name, - check_str='rule:%s' % (POLICY_ROOT % 'action'), - description=description, - operations=[{ - 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions', - 'method': 'POST', - }] - ) +deprecated_action = policy.DeprecatedRule( + name=POLICY_ROOT % 'action', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_snapshot = policy.DeprecatedRule( + name=POLICY_ROOT % 'snapshot', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_suspend = policy.DeprecatedRule( + name=POLICY_ROOT % 'suspend', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_resume = policy.DeprecatedRule( + name=POLICY_ROOT % 'resume', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_check = policy.DeprecatedRule( + name=POLICY_ROOT % 'check', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_cancel_update = policy.DeprecatedRule( + name=POLICY_ROOT % 'cancel_update', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_cancel_without_rollback = policy.DeprecatedRule( + name=POLICY_ROOT % 'cancel_without_rollback', + check_str=base.RULE_DENY_STACK_USER +) actions_policies = [ policy.DocumentedRuleDefault( name=POLICY_ROOT % 'action', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, description='Performs non-lifecycle operations on the stack ' '(Snapshot, Resume, Cancel update, or check stack resources). ' 'This is the default for all actions but can be overridden by more ' @@ -41,14 +63,88 @@ actions_policies = [ 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions', 'method': 'POST', }], + deprecated_rule=deprecated_action, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), - _action_rule('snapshot', 'Create stack snapshot.'), - _action_rule('suspend', 'Suspend a stack.'), - _action_rule('resume', 'Resume a suspended stack.'), - _action_rule('check', 'Check stack resources.'), - _action_rule('cancel_update', 'Cancel stack operation and roll back.'), - _action_rule('cancel_without_rollback', - 'Cancel stack operation without rolling back.'), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'snapshot', + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], + description='Create stack snapshot', + operations=[{ + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions', + 'method': 'POST', + }], + deprecated_rule=deprecated_snapshot, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'suspend', + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], + description='Suspend a stack.', + operations=[{ + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions', + 'method': 'POST', + }], + deprecated_rule=deprecated_suspend, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'resume', + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], + description='Resume a suspended stack.', + operations=[{ + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions', + 'method': 'POST', + }], + deprecated_rule=deprecated_resume, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'check', + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], + description='Check stack resources.', + operations=[{ + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions', + 'method': 'POST', + }], + deprecated_rule=deprecated_check, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'cancel_update', + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], + description='Cancel stack operation and roll back.', + operations=[{ + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions', + 'method': 'POST', + }], + deprecated_rule=deprecated_cancel_update, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'cancel_without_rollback', + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], + description='Cancel stack operation without rolling back.', + operations=[{ + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions', + 'method': 'POST', + }], + deprecated_rule=deprecated_cancel_without_rollback, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ) ] diff --git a/heat/policies/base.py b/heat/policies/base.py index 7f4d8643d9..cdc3b9f7d6 100644 --- a/heat/policies/base.py +++ b/heat/policies/base.py @@ -18,11 +18,45 @@ RULE_DENY_STACK_USER = 'rule:deny_stack_user' RULE_DENY_EVERYBODY = 'rule:deny_everybody' RULE_ALLOW_EVERYBODY = 'rule:allow_everybody' +# Check strings that embody common personas +SYSTEM_ADMIN = 'role:admin and system_scope:all' +SYSTEM_READER = 'role:reader and system_scope:all' +PROJECT_MEMBER = 'role:member and project_id:%(project_id)s' +PROJECT_READER = 'role:reader and project_id:%(project_id)s' + +# Heat personas +PROJECT_ADMIN = 'role:admin and project_id:%(project_id)s' +PROJECT_STACK_USER = 'role:heat_stack_user and project_id:%(project_id)s' + +# Composite check strings that are useful for policies that protect APIs that +# operate at different scopes. +SYSTEM_ADMIN_OR_PROJECT_MEMBER = ( + '(' + SYSTEM_ADMIN + ')' + ' or (' + PROJECT_MEMBER + ')' +) +SYSTEM_OR_PROJECT_READER = ( + '(' + SYSTEM_READER + ')' + ' or (' + PROJECT_READER + ')' +) +SYSTEM_ADMIN_OR_PROJECT_MEMBER_OR_STACK_USER = ( + '(' + SYSTEM_ADMIN + ')' + ' or (' + PROJECT_MEMBER + ')' + ' or (' + PROJECT_STACK_USER + ')' +) +SYSTEM_OR_PROJECT_READER_OR_STACK_USER = ( + '(' + SYSTEM_READER + ')' + ' or (' + PROJECT_READER + ')' + ' or (' + PROJECT_STACK_USER + ')' +) + rules = [ policy.RuleDefault( name="context_is_admin", - check_str="role:admin and is_admin_project:True", + check_str=( + "(role:admin and is_admin_project:True) OR " + "(" + SYSTEM_ADMIN + ")" + ), description="Decides what is required for the 'is_admin:True' check " "to succeed."), policy.RuleDefault( diff --git a/heat/policies/build_info.py b/heat/policies/build_info.py index 066bf7bdb8..ebcda36fca 100644 --- a/heat/policies/build_info.py +++ b/heat/policies/build_info.py @@ -10,23 +10,38 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from heat.policies import base +DEPRECATED_REASON = """ +The build API now supports system scope and default roles. +""" + POLICY_ROOT = 'build_info:%s' +deprecated_build_info = policy.DeprecatedRule( + name=POLICY_ROOT % 'build_info', + check_str=base.RULE_DENY_STACK_USER +) + + build_info_policies = [ policy.DocumentedRuleDefault( name=POLICY_ROOT % 'build_info', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Show build information.', operations=[ { 'path': '/v1/{tenant_id}/build_info', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_build_info, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ) ] diff --git a/heat/policies/cloudformation.py b/heat/policies/cloudformation.py index aa61fa9a00..4983ee1797 100644 --- a/heat/policies/cloudformation.py +++ b/heat/policies/cloudformation.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from heat.policies import base @@ -17,48 +18,170 @@ from heat.policies import base # These policies are for AWS CloudFormation-like APIs, so we won't list out # the URI paths in rules. +DEPRECATED_REASON = """ +The cloud formation API now supports system scope and default roles. +""" + POLICY_ROOT = 'cloudformation:%s' +deprecated_list_stacks = policy.DeprecatedRule( + name=POLICY_ROOT % 'ListStacks', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_create_stack = policy.DeprecatedRule( + name=POLICY_ROOT % 'CreateStack', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_describe_stacks = policy.DeprecatedRule( + name=POLICY_ROOT % 'DescribeStacks', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_delete_stack = policy.DeprecatedRule( + name=POLICY_ROOT % 'DeleteStack', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_update_stack = policy.DeprecatedRule( + name=POLICY_ROOT % 'UpdateStack', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_cancel_update_stack = policy.DeprecatedRule( + name=POLICY_ROOT % 'CancelUpdateStack', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_describe_stack_events = policy.DeprecatedRule( + name=POLICY_ROOT % 'DescribeStackEvents', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_validate_template = policy.DeprecatedRule( + name=POLICY_ROOT % 'ValidateTemplate', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_get_template = policy.DeprecatedRule( + name=POLICY_ROOT % 'GetTemplate', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_estimate_template_cost = policy.DeprecatedRule( + name=POLICY_ROOT % 'EstimateTemplateCost', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_describe_stack_resource = policy.DeprecatedRule( + name=POLICY_ROOT % 'DescribeStackResource', + check_str=base.RULE_ALLOW_EVERYBODY +) +deprecated_describe_stack_resources = policy.DeprecatedRule( + name=POLICY_ROOT % 'DescribeStackResources', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_list_stack_resources = policy.DeprecatedRule( + name=POLICY_ROOT % 'ListStackResources', + check_str=base.RULE_DENY_STACK_USER +) + cloudformation_policies = [ policy.RuleDefault( name=POLICY_ROOT % 'ListStacks', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_list_stacks, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'CreateStack', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_create_stack, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'DescribeStacks', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_describe_stacks, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'DeleteStack', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_delete_stack, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'UpdateStack', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_update_stack, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'CancelUpdateStack', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_cancel_update_stack, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'DescribeStackEvents', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_describe_stack_events, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'ValidateTemplate', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_validate_template, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'GetTemplate', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_get_template, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'EstimateTemplateCost', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_estimate_template_cost, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'DescribeStackResource', - check_str=base.RULE_ALLOW_EVERYBODY), + check_str=base.SYSTEM_OR_PROJECT_READER_OR_STACK_USER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_describe_stack_resource, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'DescribeStackResources', - check_str=base.RULE_DENY_STACK_USER), + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_describe_stack_resources, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ), policy.RuleDefault( name=POLICY_ROOT % 'ListStackResources', - check_str=base.RULE_DENY_STACK_USER) + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], + deprecated_rule=deprecated_list_stack_resources, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ) ] diff --git a/heat/policies/events.py b/heat/policies/events.py index b6c1f21fae..886d22a58d 100644 --- a/heat/policies/events.py +++ b/heat/policies/events.py @@ -10,16 +10,32 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from heat.policies import base POLICY_ROOT = 'events:%s' +DEPRECATED_REASON = """ +The events API now supports system scope and default roles. +""" + +deprecated_index = policy.DeprecatedRule( + name=POLICY_ROOT % 'index', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_show = policy.DeprecatedRule( + name=POLICY_ROOT % 'show', + check_str=base.RULE_DENY_STACK_USER +) + + events_policies = [ policy.DocumentedRuleDefault( name=POLICY_ROOT % 'index', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List events.', operations=[ { @@ -27,11 +43,15 @@ events_policies = [ 'events', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_index, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'show', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Show event.', operations=[ { @@ -39,7 +59,10 @@ events_policies = [ 'resources/{resource_name}/events/{event_id}', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_show, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ) ] diff --git a/heat/policies/resource.py b/heat/policies/resource.py index 8be1c2a40f..3d0fdcc7ef 100644 --- a/heat/policies/resource.py +++ b/heat/policies/resource.py @@ -10,16 +10,43 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from heat.policies import base POLICY_ROOT = 'resource:%s' +DEPRECATED_REASON = """ +The resources API now supports system scope and default roles. +""" + +deprecated_list_resources = policy.DeprecatedRule( + name=POLICY_ROOT % 'index', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_mark_unhealthy = policy.DeprecatedRule( + name=POLICY_ROOT % 'mark_unhealthy', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_show_resource = policy.DeprecatedRule( + name=POLICY_ROOT % 'show', + check_str=base.RULE_DENY_STACK_USER, +) +deprecated_metadata = policy.DeprecatedRule( + name=POLICY_ROOT % 'metadata', + check_str=base.RULE_ALLOW_EVERYBODY, +) +deprecated_signal = policy.DeprecatedRule( + name=POLICY_ROOT % 'signal', + check_str=base.RULE_ALLOW_EVERYBODY, +) + resource_policies = [ policy.DocumentedRuleDefault( name=POLICY_ROOT % 'index', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List resources.', operations=[ { @@ -27,11 +54,15 @@ resource_policies = [ 'resources', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_list_resources, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'metadata', - check_str=base.RULE_ALLOW_EVERYBODY, + check_str=base.SYSTEM_OR_PROJECT_READER_OR_STACK_USER, + scope_types=['system', 'project'], description='Show resource metadata.', operations=[ { @@ -39,11 +70,15 @@ resource_policies = [ 'resources/{resource_name}/metadata', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_metadata, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'signal', - check_str=base.RULE_ALLOW_EVERYBODY, + check_str=base.SYSTEM_OR_PROJECT_READER_OR_STACK_USER, + scope_types=['system', 'project'], description='Signal resource.', operations=[ { @@ -51,11 +86,15 @@ resource_policies = [ 'resources/{resource_name}/signal', 'method': 'POST' } - ] + ], + deprecated_rule=deprecated_signal, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'mark_unhealthy', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Mark resource as unhealthy.', operations=[ { @@ -63,11 +102,15 @@ resource_policies = [ 'resources/{resource_name_or_physical_id}', 'method': 'PATCH' } - ] + ], + deprecated_rule=deprecated_mark_unhealthy, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'show', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Show resource.', operations=[ { @@ -75,7 +118,10 @@ resource_policies = [ 'resources/{resource_name}', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_show_resource, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ) ] diff --git a/heat/policies/service.py b/heat/policies/service.py index 9bf86a6960..8e4e5d979c 100644 --- a/heat/policies/service.py +++ b/heat/policies/service.py @@ -10,16 +10,30 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from heat.policies import base +DEPRECATED_REASON = """ +The service API now supports system scope and default roles. +""" + POLICY_ROOT = 'service:%s' +deprecated_index = policy.DeprecatedRule( + name=POLICY_ROOT % 'index', + check_str=base.RULE_CONTEXT_IS_ADMIN +) + service_policies = [ policy.RuleDefault( name=POLICY_ROOT % 'index', - check_str=base.RULE_CONTEXT_IS_ADMIN) + check_str=base.SYSTEM_READER, + deprecated_rule=deprecated_index, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY + ) ] diff --git a/heat/policies/software_configs.py b/heat/policies/software_configs.py index 72f6f2c996..5de6535fb7 100644 --- a/heat/policies/software_configs.py +++ b/heat/policies/software_configs.py @@ -10,67 +10,113 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from heat.policies import base +DEPRECATED_REASON = """ +The software configuration API now support system scope and default roles. +""" + POLICY_ROOT = 'software_configs:%s' +deprecated_global_index = policy.DeprecatedRule( + name=POLICY_ROOT % 'global_index', + check_str=base.RULE_DENY_EVERYBODY +) +deprecated_index = policy.DeprecatedRule( + name=POLICY_ROOT % 'index', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_create = policy.DeprecatedRule( + name=POLICY_ROOT % 'create', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_show = policy.DeprecatedRule( + name=POLICY_ROOT % 'show', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_delete = policy.DeprecatedRule( + name=POLICY_ROOT % 'delete', + check_str=base.RULE_DENY_STACK_USER +) + software_configs_policies = [ policy.DocumentedRuleDefault( name=POLICY_ROOT % 'global_index', - check_str=base.RULE_DENY_EVERYBODY, + check_str=base.SYSTEM_READER, + scope_types=['system', 'project'], description='List configs globally.', operations=[ { 'path': '/v1/{tenant_id}/software_configs', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_global_index, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'index', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List configs.', operations=[ { 'path': '/v1/{tenant_id}/software_configs', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_index, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'create', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Create config.', operations=[ { 'path': '/v1/{tenant_id}/software_configs', 'method': 'POST' } - ] + ], + deprecated_rule=deprecated_create, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'show', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Show config details.', operations=[ { 'path': '/v1/{tenant_id}/software_configs/{config_id}', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_show, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'delete', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Delete config.', operations=[ { 'path': '/v1/{tenant_id}/software_configs/{config_id}', 'method': 'DELETE' } - ] + ], + deprecated_rule=deprecated_delete, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ) ] diff --git a/heat/policies/software_deployments.py b/heat/policies/software_deployments.py index 05f73d5867..a2dd0924a5 100644 --- a/heat/policies/software_deployments.py +++ b/heat/policies/software_deployments.py @@ -10,71 +10,119 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from heat.policies import base +DEPRECATED_REASON = """ +The software deployment API now supports system scope and default roles. +""" + POLICY_ROOT = 'software_deployments:%s' +deprecated_index = policy.DeprecatedRule( + name=POLICY_ROOT % 'index', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_create = policy.DeprecatedRule( + name=POLICY_ROOT % 'create', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_show = policy.DeprecatedRule( + name=POLICY_ROOT % 'show', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_update = policy.DeprecatedRule( + name=POLICY_ROOT % 'update', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_delete = policy.DeprecatedRule( + name=POLICY_ROOT % 'delete', + check_str=base.RULE_DENY_STACK_USER +) + + software_deployments_policies = [ policy.DocumentedRuleDefault( name=POLICY_ROOT % 'index', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List deployments.', operations=[ { 'path': '/v1/{tenant_id}/software_deployments', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_index, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'create', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Create deployment.', operations=[ { 'path': '/v1/{tenant_id}/software_deployments', 'method': 'POST' } - ] + ], + deprecated_rule=deprecated_create, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'show', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Show deployment details.', operations=[ { 'path': '/v1/{tenant_id}/software_deployments/{deployment_id}', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_show, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'update', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Update deployment.', operations=[ { 'path': '/v1/{tenant_id}/software_deployments/{deployment_id}', 'method': 'PUT' } - ] + ], + deprecated_rule=deprecated_update, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'delete', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Delete deployment.', operations=[ { 'path': '/v1/{tenant_id}/software_deployments/{deployment_id}', 'method': 'DELETE' } - ] + ], + deprecated_rule=deprecated_delete, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'metadata', - check_str=base.RULE_ALLOW_EVERYBODY, + check_str=base.SYSTEM_OR_PROJECT_READER_OR_STACK_USER, + scope_types=['system', 'project'], description='Show server configuration metadata.', operations=[ { diff --git a/heat/policies/stacks.py b/heat/policies/stacks.py index 7332a69a34..04f6988c56 100644 --- a/heat/policies/stacks.py +++ b/heat/policies/stacks.py @@ -10,16 +10,144 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from heat.policies import base +DEPRECATED_REASON = """ +The stack API now supports system scope and default roles. +""" + POLICY_ROOT = 'stacks:%s' +deprecated_abandon = policy.DeprecatedRule( + name=POLICY_ROOT % 'abandon', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_create = policy.DeprecatedRule( + name=POLICY_ROOT % 'create', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_delete = policy.DeprecatedRule( + name=POLICY_ROOT % 'delete', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_detail = policy.DeprecatedRule( + name=POLICY_ROOT % 'detail', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_export = policy.DeprecatedRule( + name=POLICY_ROOT % 'export', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_generate_template = policy.DeprecatedRule( + name=POLICY_ROOT % 'generate_template', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_global_index = policy.DeprecatedRule( + name=POLICY_ROOT % 'global_index', + check_str=base.RULE_DENY_EVERYBODY +) +deprecated_index = policy.DeprecatedRule( + name=POLICY_ROOT % 'index', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_list_resource_types = policy.DeprecatedRule( + name=POLICY_ROOT % 'list_resource_types', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_list_template_versions = policy.DeprecatedRule( + name=POLICY_ROOT % 'list_template_versions', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_list_template_functions = policy.DeprecatedRule( + name=POLICY_ROOT % 'list_template_functions', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_preview = policy.DeprecatedRule( + name=POLICY_ROOT % 'preview', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_resource_schema = policy.DeprecatedRule( + name=POLICY_ROOT % 'resource_schema', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_show = policy.DeprecatedRule( + name=POLICY_ROOT % 'show', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_template = policy.DeprecatedRule( + name=POLICY_ROOT % 'template', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_environment = policy.DeprecatedRule( + name=POLICY_ROOT % 'environment', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_files = policy.DeprecatedRule( + name=POLICY_ROOT % 'files', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_update = policy.DeprecatedRule( + name=POLICY_ROOT % 'update', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_update_patch = policy.DeprecatedRule( + name=POLICY_ROOT % 'update_patch', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_preview_update = policy.DeprecatedRule( + name=POLICY_ROOT % 'preview_update', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_preview_update_patch = policy.DeprecatedRule( + name=POLICY_ROOT % 'preview_update_patch', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_validate_template = policy.DeprecatedRule( + name=POLICY_ROOT % 'validate_template', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_snapshot = policy.DeprecatedRule( + name=POLICY_ROOT % 'snapshot', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_show_snapshot = policy.DeprecatedRule( + name=POLICY_ROOT % 'show_snapshot', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_delete_snapshot = policy.DeprecatedRule( + name=POLICY_ROOT % 'delete_snapshot', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_list_snapshots = policy.DeprecatedRule( + name=POLICY_ROOT % 'list_snapshots', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_restore_snapshot = policy.DeprecatedRule( + name=POLICY_ROOT % 'restore_snapshot', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_list_outputs = policy.DeprecatedRule( + name=POLICY_ROOT % 'list_outputs', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_show_output = policy.DeprecatedRule( + name=POLICY_ROOT % 'show_output', + check_str=base.RULE_DENY_STACK_USER +) +deprecated_lookup = policy.DeprecatedRule( + name=POLICY_ROOT % 'lookup', + check_str=base.RULE_ALLOW_EVERYBODY +) + + stacks_policies = [ policy.DocumentedRuleDefault( name=POLICY_ROOT % 'abandon', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Abandon stack.', operations=[ { @@ -27,44 +155,60 @@ stacks_policies = [ 'abandon', 'method': 'DELETE' } - ] + ], + deprecated_rule=deprecated_abandon, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'create', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Create stack.', operations=[ { 'path': '/v1/{tenant_id}/stacks', 'method': 'POST' } - ] + ], + deprecated_rule=deprecated_create, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'delete', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Delete stack.', operations=[ { 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}', 'method': 'DELETE' } - ] + ], + deprecated_rule=deprecated_delete, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'detail', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List stacks in detail.', operations=[ { 'path': '/v1/{tenant_id}/stacks', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_detail, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'export', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Export stack.', operations=[ { @@ -72,11 +216,15 @@ stacks_policies = [ 'export', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_export, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'generate_template', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Generate stack template.', operations=[ { @@ -84,55 +232,75 @@ stacks_policies = [ 'template', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_generate_template, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'global_index', - check_str=base.RULE_DENY_EVERYBODY, + check_str=base.SYSTEM_READER, + scope_types=['system', 'project'], description='List stacks globally.', operations=[ { 'path': '/v1/{tenant_id}/stacks', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_global_index, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'index', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List stacks.', operations=[ { 'path': '/v1/{tenant_id}/stacks', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_index, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'list_resource_types', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List resource types.', operations=[ { 'path': '/v1/{tenant_id}/resource_types', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_list_resource_types, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'list_template_versions', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List template versions.', operations=[ { 'path': '/v1/{tenant_id}/template_versions', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_list_template_versions, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'list_template_functions', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List template functions.', operations=[ { @@ -140,55 +308,75 @@ stacks_policies = [ '{template_version}/functions', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_list_template_functions, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'lookup', - check_str=base.RULE_ALLOW_EVERYBODY, + check_str=base.SYSTEM_OR_PROJECT_READER_OR_STACK_USER, + scope_types=['system', 'project'], description='Find stack.', operations=[ { 'path': '/v1/{tenant_id}/stacks/{stack_identity}', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_lookup, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'preview', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Preview stack.', operations=[ { 'path': '/v1/{tenant_id}/stacks/preview', 'method': 'POST' } - ] + ], + deprecated_rule=deprecated_preview, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'resource_schema', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Show resource type schema.', operations=[ { 'path': '/v1/{tenant_id}/resource_types/{type_name}', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_resource_schema, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'show', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Show stack.', operations=[ { 'path': '/v1/{tenant_id}/stacks/{stack_identity}', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_show, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'template', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Get stack template.', operations=[ { @@ -196,11 +384,15 @@ stacks_policies = [ 'template', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_template, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'environment', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Get stack environment.', operations=[ { @@ -208,11 +400,15 @@ stacks_policies = [ 'environment', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_environment, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'files', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Get stack files.', operations=[ { @@ -220,33 +416,45 @@ stacks_policies = [ 'files', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_files, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'update', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Update stack.', operations=[ { 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}', 'method': 'PUT' } - ] + ], + deprecated_rule=deprecated_update, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'update_patch', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Update stack (PATCH).', operations=[ { 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}', 'method': 'PATCH' } - ] + ], + deprecated_rule=deprecated_update_patch, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'preview_update', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Preview update stack.', operations=[ { @@ -254,11 +462,15 @@ stacks_policies = [ 'preview', 'method': 'PUT' } - ] + ], + deprecated_rule=deprecated_preview_update, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'preview_update_patch', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Preview update stack (PATCH).', operations=[ { @@ -266,22 +478,30 @@ stacks_policies = [ 'preview', 'method': 'PATCH' } - ] + ], + deprecated_rule=deprecated_preview_update_patch, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'validate_template', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Validate template.', operations=[ { 'path': '/v1/{tenant_id}/validate', 'method': 'POST' } - ] + ], + deprecated_rule=deprecated_validate_template, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'snapshot', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Snapshot Stack.', operations=[ { @@ -289,11 +509,15 @@ stacks_policies = [ 'snapshots', 'method': 'POST' } - ] + ], + deprecated_rule=deprecated_snapshot, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'show_snapshot', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Show snapshot.', operations=[ { @@ -301,11 +525,15 @@ stacks_policies = [ 'snapshots/{snapshot_id}', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_show_snapshot, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'delete_snapshot', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Delete snapshot.', operations=[ { @@ -313,11 +541,15 @@ stacks_policies = [ 'snapshots/{snapshot_id}', 'method': 'DELETE' } - ] + ], + deprecated_rule=deprecated_delete_snapshot, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'list_snapshots', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List snapshots.', operations=[ { @@ -325,11 +557,15 @@ stacks_policies = [ 'snapshots', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_list_snapshots, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'restore_snapshot', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER, + scope_types=['system', 'project'], description='Restore snapshot.', operations=[ { @@ -337,11 +573,15 @@ stacks_policies = [ 'snapshots/{snapshot_id}/restore', 'method': 'POST' } - ] + ], + deprecated_rule=deprecated_restore_snapshot, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'list_outputs', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='List outputs.', operations=[ { @@ -349,11 +589,15 @@ stacks_policies = [ 'outputs', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_list_outputs, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ), policy.DocumentedRuleDefault( name=POLICY_ROOT % 'show_output', - check_str=base.RULE_DENY_STACK_USER, + check_str=base.SYSTEM_OR_PROJECT_READER, + scope_types=['system', 'project'], description='Show outputs.', operations=[ { @@ -361,7 +605,10 @@ stacks_policies = [ 'outputs/{output_key}', 'method': 'GET' } - ] + ], + deprecated_rule=deprecated_show_output, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.WALLABY ) ] diff --git a/heat/tests/api/openstack_v1/test_stacks.py b/heat/tests/api/openstack_v1/test_stacks.py index 67cd34a1b2..48d1ebd619 100644 --- a/heat/tests/api/openstack_v1/test_stacks.py +++ b/heat/tests/api/openstack_v1/test_stacks.py @@ -471,7 +471,9 @@ class StackControllerTest(tools.ControllerTest, common.HeatTestCase): mock_enforce.assert_called_with(action='global_index', scope=self.controller.REQUEST_SCOPE, is_registered_policy=True, - context=self.context) + context=self.context, + target={"project_id": self.tenant} + ) def test_global_index_uses_admin_context(self, mock_enforce): rpc_client = self.controller.rpc_client @@ -1675,7 +1677,9 @@ class StackControllerTest(tools.ControllerTest, common.HeatTestCase): version='1.20' ) - def test_show_invalidtenant(self, mock_enforce): + # the test_show_invalidtenant for stacks is now dealt with srbac + # more generic approach + def test_deprecated_show_invalidtenant(self, mock_enforce): identity = identifier.HeatIdentifier('wibble', 'wordpress', '6') req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity) diff --git a/heat/tests/api/openstack_v1/tools.py b/heat/tests/api/openstack_v1/tools.py index 63a8b0521a..0f5247a587 100644 --- a/heat/tests/api/openstack_v1/tools.py +++ b/heat/tests/api/openstack_v1/tools.py @@ -119,6 +119,7 @@ class ControllerTest(object): action=self.action, context=self.context, scope=self.controller.REQUEST_SCOPE, + target={'project_id': self.tenant}, is_registered_policy=mock.ANY ) self.assertEqual(self.expected_request_count, diff --git a/heat/tests/policy/check_admin.json b/heat/tests/policy/check_admin.json deleted file mode 100644 index 96a15c83c3..0000000000 --- a/heat/tests/policy/check_admin.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "context_is_admin": "role:admin" -} diff --git a/heat/tests/policy/deny_stack_user.json b/heat/tests/policy/deny_stack_user.json deleted file mode 100644 index c20d2673fa..0000000000 --- a/heat/tests/policy/deny_stack_user.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "deny_stack_user": "not role:heat_stack_user", - "cloudformation:ListStacks": "rule:deny_stack_user", - "cloudformation:CreateStack": "rule:deny_stack_user", - "cloudformation:DescribeStacks": "rule:deny_stack_user", - "cloudformation:DeleteStack": "rule:deny_stack_user", - "cloudformation:UpdateStack": "rule:deny_stack_user", - "cloudformation:DescribeStackEvents": "rule:deny_stack_user", - "cloudformation:ValidateTemplate": "rule:deny_stack_user", - "cloudformation:GetTemplate": "rule:deny_stack_user", - "cloudformation:EstimateTemplateCost": "rule:deny_stack_user", - "cloudformation:DescribeStackResource": "", - "cloudformation:DescribeStackResources": "rule:deny_stack_user", - "cloudformation:ListStackResources": "rule:deny_stack_user", -} diff --git a/heat/tests/policy/notallowed.json b/heat/tests/policy/notallowed.json deleted file mode 100644 index 5346307e3c..0000000000 --- a/heat/tests/policy/notallowed.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "cloudformation:ListStacks": "!", - "cloudformation:CreateStack": "!", - "cloudformation:DescribeStacks": "!", - "cloudformation:DeleteStack": "!", - "cloudformation:UpdateStack": "!", - "cloudformation:DescribeStackEvents": "!", - "cloudformation:ValidateTemplate": "!", - "cloudformation:GetTemplate": "!", - "cloudformation:EstimateTemplateCost": "!", - "cloudformation:DescribeStackResource": "!", - "cloudformation:DescribeStackResources": "!", - "cloudformation:ListStackResources": "!" -} diff --git a/heat/tests/policy/resources.json b/heat/tests/policy/resources.json index 163fdb66ee..dc56a47785 100644 --- a/heat/tests/policy/resources.json +++ b/heat/tests/policy/resources.json @@ -1,7 +1,3 @@ { - "context_is_admin": "role:admin", - "resource_types:OS::Cinder::Quota": "!", - "resource_types:OS::Keystone::*": "rule:context_is_admin" - } diff --git a/heat/tests/policy/test_acl_personas.yaml b/heat/tests/policy/test_acl_personas.yaml new file mode 100644 index 0000000000..1fe5ce40a4 --- /dev/null +++ b/heat/tests/policy/test_acl_personas.yaml @@ -0,0 +1,241 @@ +actions_most_restricted: + scope: "actions" + actions: + - "snapshot" + - "suspend" + - "resume" + - "cancel_update" + - "cancel_without_rollback" + allowed: + - "system_admin" + - "project_member" + denied: + - "stack_user" + +actions_restricted: + scope: "actions" + actions: + - "check" + allowed: + - "system_reader" + - "project_reader" + denied: + - "stack_user" + +cloud_formation_most_restricted: + scope: "cloudformation" + actions: + - "ListStacks" + - "CreateStack" + - "DescribeStacks" + - "DeleteStack" + - "UpdateStack" + - "DescribeStackEvents" + - "ValidateTemplate" + - "GetTemplate" + - "EstimateTemplateCost" + - "DescribeStackResources" + allowed: + - "system_admin" + - "project_member" + denied: + - "stack_user" + +cloud_formation_restricted: + scope: "cloudformation" + actions: + - "DescribeStackResource" + allowed: + - "system_admin" + - "project_member" + +build_info_acl: + scope: "build_info" + actions: + - "build_info" + allowed: + - "system_reader" + - "project_reader" + denied: + - "stack_user" + +events_acl: + scope: "events" + actions: + - "index" + - "show" + allowed: + - "system_reader" + - "project_reader" + denied: + - "stack_user" + +resource_least_restricted: + scope: "resource" + actions: + - "metadata" + - "signal" + allowed: + - "system_reader" + - "system_reader" + - "stack_user" + +resource_restricted: + scope: "resource" + actions: + - "index" + - "show" + allowed: + - "system_reader" + - "project_reader" + denied: + - "stack_user" + +resource_most_restricted: + scope: "resource" + actions: + - "mark_unhealthy" + allowed: + - "system_admin" + - "project_member" + denied: + - "stack_user" + +service_acl: + scope: "service" + actions: + - "index" + allowed: + - "system_reader" + +software_configs_least_restricted: + scope: "software_configs" + actions: + - "global_index" + allowed: + - "system_reader" + +software_configs_most_restricted: + scope: "software_configs" + actions: + - "create" + - "delete" + allowed: + - "system_admin" + - "project_member" + denied: + - "stack_user" + +software_configs_restricted: + scope: "software_configs" + actions: + - "index" + - "create" + - "show" + allowed: + - "system_reader" + - "project_reader" + denied: + - "stack_user" + +software_deployments_most_restricted: + scope: "software_deployments" + actions: + - "create" + - "update" + - "delete" + allowed: + - "system_admin" + - "project_member" + denied: + - "stack_user" + +software_deployments_restricted: + scope: "software_deployments" + actions: + - "index" + - "show" + allowed: + - "system_reader" + - "project_reader" + denied: + - "stack_user" + + +software_deployments_least_restricted: + scope: "software_deployments" + actions: + - "metadata" + allowed: + - "stack_user" + +stacks_most_restricted: + scope: "stacks" + actions: + - "abandon" + - "create" + - "delete" + - "export" + - "generate_template" + - "update" + - "update_patch" + - "preview_update" + - "preview_update_patch" + - "validate_template" + - "snapshot" + - "delete_snapshot" + - "restore_snapshot" + allowed: + - "system_admin" + - "project_member" + denied: + - "stack_user" + +stacks_restricted: + scope: "stacks" + actions: + - "detail" + - "index" + - "list_resource_types" + - "list_template_versions" + - "list_template_functions" + - "preview" + - "resource_schema" + - "show" + - "template" + - "environment" + - "files" + - "show_snapshot" + - "list_snapshots" + - "list_outputs" + - "show_output" + allowed: + - "system_reader" + - "project_reader" + denied: + - "stack_user" + +stacks_restricted_index: + scope: "stacks" + actions: + - "global_index" + allowed: + - "system_admin" + +stacks_open: + scope: "stacks" + actions: + - "lookup" + allowed: + - "system_reader" + - "project_reader" + - "stack_user" + +create_stacks: + scope: "stacks" + actions: + - "create" + allowed: + - "system_admin" + - "project_admin" + - "project_member" diff --git a/heat/tests/policy/test_deprecated_access.yaml b/heat/tests/policy/test_deprecated_access.yaml new file mode 100644 index 0000000000..26af91c8b9 --- /dev/null +++ b/heat/tests/policy/test_deprecated_access.yaml @@ -0,0 +1,22 @@ +cloud_formation_restricted: + scope: "cloudformation" + actions: + - "DescribeStackResource" + allowed: + - "stack_user" + - "anyone" + +stacks_open: + scope: "stacks" + actions: + - "lookup" + allowed: + - "anyone" + +create_stacks: + scope: "stacks" + actions: + - "create" + allowed: + - "system_reader" + - "project_reader" diff --git a/heat/tests/test_common_policy.py b/heat/tests/test_common_policy.py index ee311a8095..eb2753c4ac 100644 --- a/heat/tests/test_common_policy.py +++ b/heat/tests/test_common_policy.py @@ -16,6 +16,8 @@ import os.path +import ddt + from oslo_config import fixture as config_fixture from oslo_policy import policy as base_policy @@ -27,17 +29,8 @@ from heat.tests import utils policy_path = os.path.dirname(os.path.realpath(__file__)) + "/policy/" +@ddt.ddt class TestPolicyEnforcer(common.HeatTestCase): - cfn_actions = ("ListStacks", "CreateStack", "DescribeStacks", - "DeleteStack", "UpdateStack", "DescribeStackEvents", - "ValidateTemplate", "GetTemplate", - "EstimateTemplateCost", "DescribeStackResource", - "DescribeStackResources") - - cw_actions = ("DeleteAlarms", "DescribeAlarmHistory", "DescribeAlarms", - "DescribeAlarmsForMetric", "DisableAlarmActions", - "EnableAlarmActions", "GetMetricStatistics", "ListMetrics", - "PutMetricAlarm", "PutMetricData", "SetAlarmState") def setUp(self): super(TestPolicyEnforcer, self).setUp(mock_resource_policy=False) @@ -47,44 +40,80 @@ class TestPolicyEnforcer(common.HeatTestCase): def get_policy_file(self, filename): return policy_path + filename - def test_policy_cfn_default(self): - enforcer = policy.Enforcer(scope='cloudformation') + def _get_context(self, persona): + if persona == "system_admin": + ctx = utils.dummy_system_admin_context() + elif persona == "system_reader": + ctx = utils.dummy_system_reader_context() + elif persona == "project_admin": + ctx = utils.dummy_context(roles=['admin', 'member', 'reader']) + elif persona == "project_member": + ctx = utils.dummy_context(roles=['member', 'reader']) + elif persona == "project_reader": + ctx = utils.dummy_context(roles=['reader']) + elif persona == "stack_user": + ctx = utils.dummy_context(roles=['heat_stack_user']) + elif persona == "anyone": + ctx = utils.dummy_context(roles=['foobar']) + else: + self.fail("Persona [{}] not found".format(persona)) + return ctx - ctx = utils.dummy_context(roles=[]) - for action in self.cfn_actions: - # Everything should be allowed - enforcer.enforce(ctx, action, is_registered_policy=True) + def _test_legacy_rbac_policies(self, **kwargs): + scope = kwargs.get("scope") + actions = kwargs.get("actions") + allowed_personas = kwargs.get("allowed", []) + denied_personas = kwargs.get("denied", []) + self._test_policy_allowed(scope, actions, allowed_personas) + self._test_policy_notallowed(scope, actions, denied_personas) - def test_policy_cfn_notallowed(self): - enforcer = policy.Enforcer( - scope='cloudformation', - policy_file=self.get_policy_file('notallowed.json')) + @ddt.file_data('policy/test_acl_personas.yaml') + @ddt.unpack + def test_legacy_rbac_policies(self, **kwargs): + self._test_legacy_rbac_policies(**kwargs) - ctx = utils.dummy_context(roles=[]) - for action in self.cfn_actions: - # Everything should raise the default exception.Forbidden - self.assertRaises(exception.Forbidden, enforcer.enforce, ctx, - action, {}, is_registered_policy=True) + @ddt.file_data('policy/test_deprecated_access.yaml') + @ddt.unpack + def test_deprecated_policies(self, **kwargs): + self._test_legacy_rbac_policies(**kwargs) - def test_policy_cfn_deny_stack_user(self): - enforcer = policy.Enforcer(scope='cloudformation') + @ddt.file_data('policy/test_acl_personas.yaml') + @ddt.unpack + def test_secure_rbac_policies(self, **kwargs): + self.fixture.config(group='oslo_policy', enforce_scope=True) + self.fixture.config(group='oslo_policy', enforce_new_defaults=True) + scope = kwargs.get("scope") + actions = kwargs.get("actions") + allowed_personas = kwargs.get("allowed", []) + denied_personas = kwargs.get("denied", []) + self._test_policy_allowed(scope, actions, allowed_personas) + self._test_policy_notallowed(scope, actions, denied_personas) - ctx = utils.dummy_context(roles=['heat_stack_user']) - for action in self.cfn_actions: - # Everything apart from DescribeStackResource should be Forbidden - if action == "DescribeStackResource": - enforcer.enforce(ctx, action, is_registered_policy=True) - else: - self.assertRaises(exception.Forbidden, enforcer.enforce, ctx, - action, {}, is_registered_policy=True) + def _test_policy_allowed(self, scope, actions, personas): + enforcer = policy.Enforcer(scope=scope) + for persona in personas: + ctx = self._get_context(persona) + for action in actions: + # Everything should be allowed + enforcer.enforce( + ctx, + action, + target={"project_id": "test_tenant_id"}, + is_registered_policy=True + ) - def test_policy_cfn_allow_non_stack_user(self): - enforcer = policy.Enforcer(scope='cloudformation') - - ctx = utils.dummy_context(roles=['not_a_stack_user']) - for action in self.cfn_actions: - # Everything should be allowed - enforcer.enforce(ctx, action, is_registered_policy=True) + def _test_policy_notallowed(self, scope, actions, personas): + enforcer = policy.Enforcer(scope=scope) + for persona in personas: + ctx = self._get_context(persona) + for action in actions: + # Everything should raise the default exception.Forbidden + self.assertRaises( + exception.Forbidden, + enforcer.enforce, ctx, + action, + target={"project_id": "test_tenant_id"}, + is_registered_policy=True) def test_set_rules_overwrite_true(self): enforcer = policy.Enforcer() diff --git a/heat/tests/utils.py b/heat/tests/utils.py index 854e7caf40..cf3e383f8c 100644 --- a/heat/tests/utils.py +++ b/heat/tests/utils.py @@ -90,6 +90,32 @@ def dummy_context(user='test_username', tenant_id='test_tenant_id', }) +def dummy_system_admin_context(): + """Return a heat.common.context.RequestContext for system-admin. + + :returns: an instance of heat.common.context.RequestContext + + """ + ctx = dummy_context(roles=['admin', 'member', 'reader']) + ctx.system_scope = 'all' + ctx.project_id = None + ctx.tenant_id = None + return ctx + + +def dummy_system_reader_context(): + """Return a heat.common.context.RequestContext for system-reader. + + :returns: an instance of heat.common.context.RequestContext + + """ + ctx = dummy_context(roles=['reader']) + ctx.system_scope = 'all' + ctx.project_id = None + ctx.tenant_id = None + return ctx + + def parse_stack(t, params=None, files=None, stack_name=None, stack_id=None, timeout_mins=None, cache_data=None, tags=None): diff --git a/lower-constraints.txt b/lower-constraints.txt index 582eccf49a..e3b2841bcc 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -16,6 +16,7 @@ contextlib2==0.5.5 coverage==4.0 croniter==0.3.4 cryptography==2.5 +ddt==1.4.1 debtcollector==1.19.0 decorator==4.3.0 deprecation==2.0 @@ -72,7 +73,7 @@ oslo.i18n==3.20.0 oslo.log==4.3.0 oslo.messaging==5.29.0 oslo.middleware==3.31.0 -oslo.policy==3.6.0 +oslo.policy==3.6.2 oslo.reports==1.18.0 oslo.serialization==2.25.0 oslo.service==1.24.0 diff --git a/releasenotes/notes/support-rbac-824a2d02c8746d3d.yaml b/releasenotes/notes/support-rbac-824a2d02c8746d3d.yaml new file mode 100644 index 0000000000..faaa3283c1 --- /dev/null +++ b/releasenotes/notes/support-rbac-824a2d02c8746d3d.yaml @@ -0,0 +1,15 @@ +--- +features: + - | + The default policies provided by heat api have been updated to add support + for default roles and system scope. This is part of a broader community + effort to support read-only roles and implement secure, consistent default + policies. + + Refer to `the Keystone documentation`__ for more information on the reason + for these changes. + + __ https://docs.openstack.org/keystone/latest/admin/service-api-protection.html +deprecations: + - | + The old default policy rules have been deprecated for removal in Xena cycle. diff --git a/requirements.txt b/requirements.txt index e416eb3c49..cd7d4c06d8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ pbr>=3.1.1 # Apache-2.0 Babel!=2.4.0,>=2.3.4 # BSD +ddt>=1.4.1 # MIT croniter>=0.3.4 # MIT License cryptography>=2.5 # BSD/Apache-2.0 debtcollector>=1.19.0 # Apache-2.0 @@ -23,7 +24,7 @@ oslo.i18n>=3.20.0 # Apache-2.0 oslo.log>=4.3.0 # Apache-2.0 oslo.messaging>=5.29.0 # Apache-2.0 oslo.middleware>=3.31.0 # Apache-2.0 -oslo.policy>=3.6.0 # Apache-2.0 +oslo.policy>=3.6.2 # Apache-2.0 oslo.reports>=1.18.0 # Apache-2.0 oslo.serialization>=2.25.0 # Apache-2.0 oslo.service!=1.28.1,>=1.24.0 # Apache-2.0