diff --git a/etc/heat/policy.json b/etc/heat/policy.json index 9fbf21a804..4ecac36d1c 100644 --- a/etc/heat/policy.json +++ b/etc/heat/policy.json @@ -25,16 +25,6 @@ "cloudwatch:PutMetricData": "", "cloudwatch:SetAlarmState": "rule:deny_stack_user", - "actions:action": "rule:deny_stack_user", - "build_info:build_info": "rule:deny_stack_user", - "events:index": "rule:deny_stack_user", - "events:show": "rule:deny_stack_user", - "resource:index": "rule:deny_stack_user", - "resource:metadata": "", - "resource:signal": "", - "resource:mark_unhealthy": "rule:deny_stack_user", - "resource:show": "rule:deny_stack_user", - "software_configs:global_index": "rule:deny_everybody", "software_configs:index": "rule:deny_stack_user", "software_configs:create": "rule:deny_stack_user", @@ -45,7 +35,5 @@ "software_deployments:show": "rule:deny_stack_user", "software_deployments:update": "rule:deny_stack_user", "software_deployments:delete": "rule:deny_stack_user", - "software_deployments:metadata": "", - - "service:index": "rule:context_is_admin" + "software_deployments:metadata": "" } diff --git a/heat/api/openstack/v1/actions.py b/heat/api/openstack/v1/actions.py index fcc4a5236c..2b058e72c9 100644 --- a/heat/api/openstack/v1/actions.py +++ b/heat/api/openstack/v1/actions.py @@ -26,7 +26,8 @@ class ActionController(object): Implements the API for stack actions """ - # Define request scope (must match what is in policy.json) + # Define request scope (must match what is in policy.json or policies in + # code) REQUEST_SCOPE = 'actions' ACTIONS = ( @@ -41,7 +42,7 @@ class ActionController(object): self.options = options self.rpc_client = rpc_client.EngineClient() - @util.identified_stack + @util.registered_identified_stack def action(self, req, identity, body=None): """Performs a specified action on a stack. diff --git a/heat/api/openstack/v1/build_info.py b/heat/api/openstack/v1/build_info.py index 5172a13a83..2743f621b2 100644 --- a/heat/api/openstack/v1/build_info.py +++ b/heat/api/openstack/v1/build_info.py @@ -24,14 +24,15 @@ class BuildInfoController(object): Returns build information for current app. """ - # Define request scope (must match what is in policy.json) + # Define request scope (must match what is in policy.json or policies in + # code) REQUEST_SCOPE = 'build_info' def __init__(self, options): self.options = options self.rpc_client = rpc_client.EngineClient() - @util.policy_enforce + @util.registered_policy_enforce def build_info(self, req): engine_revision = self.rpc_client.get_revision(req.context) build_info = { diff --git a/heat/api/openstack/v1/events.py b/heat/api/openstack/v1/events.py index 9cfc14f3f8..465d0ab458 100644 --- a/heat/api/openstack/v1/events.py +++ b/heat/api/openstack/v1/events.py @@ -84,7 +84,8 @@ class EventController(object): Implements the API actions. """ - # Define request scope (must match what is in policy.json) + # Define request scope (must match what is in policy.json or policies in + # code) REQUEST_SCOPE = 'events' def __init__(self, options): @@ -106,7 +107,7 @@ class EventController(object): return [format_event(req, e, keys) for e in events] - @util.identified_stack + @util.registered_identified_stack def index(self, req, identity, resource_name=None): """Lists summary information for all events.""" whitelist = { @@ -149,7 +150,7 @@ class EventController(object): return {'events': events} - @util.identified_stack + @util.registered_identified_stack def show(self, req, identity, resource_name, event_id): """Gets detailed information for an event.""" diff --git a/heat/api/openstack/v1/resources.py b/heat/api/openstack/v1/resources.py index ab056b0e53..dc01e53780 100644 --- a/heat/api/openstack/v1/resources.py +++ b/heat/api/openstack/v1/resources.py @@ -75,7 +75,8 @@ class ResourceController(object): Implements the API actions. """ - # Define request scope (must match what is in policy.json) + # Define request scope (must match what is in policy.json or policies in + # code) REQUEST_SCOPE = 'resource' def __init__(self, options): @@ -92,7 +93,7 @@ class ResourceController(object): else: return default - @util.identified_stack + @util.registered_identified_stack def index(self, req, identity): """Lists information for all resources.""" @@ -131,7 +132,7 @@ class ResourceController(object): return {'resources': [format_resource(req, res) for res in res_list]} - @util.identified_stack + @util.registered_identified_stack def show(self, req, identity, resource_name): """Gets detailed information for a resource.""" @@ -146,7 +147,7 @@ class ResourceController(object): return {'resource': format_resource(req, res)} - @util.identified_stack + @util.registered_identified_stack def metadata(self, req, identity, resource_name): """Gets metadata information for a resource.""" @@ -156,14 +157,14 @@ class ResourceController(object): return {rpc_api.RES_METADATA: res[rpc_api.RES_METADATA]} - @util.identified_stack + @util.registered_identified_stack def signal(self, req, identity, resource_name, body=None): self.rpc_client.resource_signal(req.context, stack_identity=identity, resource_name=resource_name, details=body) - @util.identified_stack + @util.registered_identified_stack def mark_unhealthy(self, req, identity, resource_name, body): """Mark a resource as healthy or unhealthy.""" data = dict() diff --git a/heat/api/openstack/v1/services.py b/heat/api/openstack/v1/services.py index d311a3972b..c51167b7c6 100644 --- a/heat/api/openstack/v1/services.py +++ b/heat/api/openstack/v1/services.py @@ -25,14 +25,15 @@ from heat.rpc import client as rpc_client class ServiceController(object): """WSGI controller for reporting the heat engine status in Heat v1 API.""" - # Define request scope (must match what is in policy.json) + # Define request scope (must match what is in policy.json or policies in + # code) REQUEST_SCOPE = 'service' def __init__(self, options): self.options = options self.rpc_client = rpc_client.EngineClient() - @util.policy_enforce + @util.registered_policy_enforce def index(self, req): try: services = self.rpc_client.list_services(req.context) diff --git a/heat/policies/__init__.py b/heat/policies/__init__.py index 2707432bbd..1826d792cf 100644 --- a/heat/policies/__init__.py +++ b/heat/policies/__init__.py @@ -13,14 +13,24 @@ import itertools +from heat.policies import actions from heat.policies import base +from heat.policies import build_info +from heat.policies import events +from heat.policies import resource from heat.policies import resource_types +from heat.policies import service from heat.policies import stacks def list_rules(): return itertools.chain( base.list_rules(), - stacks.list_rules(), + actions.list_rules(), + build_info.list_rules(), + events.list_rules(), + resource.list_rules(), resource_types.list_rules(), + service.list_rules(), + stacks.list_rules(), ) diff --git a/heat/policies/actions.py b/heat/policies/actions.py new file mode 100644 index 0000000000..4dd45fcb01 --- /dev/null +++ b/heat/policies/actions.py @@ -0,0 +1,37 @@ +# 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 heat.policies import base + +POLICY_ROOT = 'actions:%s' + +actions_policies = [ + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'action', + check_str=base.RULE_DENY_STACK_USER, + description='Performs non-lifecycle operations on the stack ' + '(Snapshot, Resume, Cancel update, or check stack resources).', + operations=[ + { + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/' + 'actions', + 'method': 'POST' + } + ] + ) +] + + +def list_rules(): + return actions_policies diff --git a/heat/policies/build_info.py b/heat/policies/build_info.py new file mode 100644 index 0000000000..066bf7bdb8 --- /dev/null +++ b/heat/policies/build_info.py @@ -0,0 +1,35 @@ +# 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 heat.policies import base + +POLICY_ROOT = 'build_info:%s' + +build_info_policies = [ + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'build_info', + check_str=base.RULE_DENY_STACK_USER, + description='Show build information.', + operations=[ + { + 'path': '/v1/{tenant_id}/build_info', + 'method': 'GET' + } + ] + ) +] + + +def list_rules(): + return build_info_policies diff --git a/heat/policies/events.py b/heat/policies/events.py new file mode 100644 index 0000000000..b6c1f21fae --- /dev/null +++ b/heat/policies/events.py @@ -0,0 +1,48 @@ +# 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 heat.policies import base + +POLICY_ROOT = 'events:%s' + +events_policies = [ + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'index', + check_str=base.RULE_DENY_STACK_USER, + description='List events.', + operations=[ + { + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/' + 'events', + 'method': 'GET' + } + ] + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'show', + check_str=base.RULE_DENY_STACK_USER, + description='Show event.', + operations=[ + { + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/' + 'resources/{resource_name}/events/{event_id}', + 'method': 'GET' + } + ] + ) +] + + +def list_rules(): + return events_policies diff --git a/heat/policies/resource.py b/heat/policies/resource.py new file mode 100644 index 0000000000..8be1c2a40f --- /dev/null +++ b/heat/policies/resource.py @@ -0,0 +1,84 @@ +# 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 heat.policies import base + +POLICY_ROOT = 'resource:%s' + +resource_policies = [ + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'index', + check_str=base.RULE_DENY_STACK_USER, + description='List resources.', + operations=[ + { + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/' + 'resources', + 'method': 'GET' + } + ] + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'metadata', + check_str=base.RULE_ALLOW_EVERYBODY, + description='Show resource metadata.', + operations=[ + { + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/' + 'resources/{resource_name}/metadata', + 'method': 'GET' + } + ] + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'signal', + check_str=base.RULE_ALLOW_EVERYBODY, + description='Signal resource.', + operations=[ + { + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/' + 'resources/{resource_name}/signal', + 'method': 'POST' + } + ] + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'mark_unhealthy', + check_str=base.RULE_DENY_STACK_USER, + description='Mark resource as unhealthy.', + operations=[ + { + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/' + 'resources/{resource_name_or_physical_id}', + 'method': 'PATCH' + } + ] + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'show', + check_str=base.RULE_DENY_STACK_USER, + description='Show resource.', + operations=[ + { + 'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/' + 'resources/{resource_name}', + 'method': 'GET' + } + ] + ) +] + + +def list_rules(): + return resource_policies diff --git a/heat/policies/service.py b/heat/policies/service.py new file mode 100644 index 0000000000..9bf86a6960 --- /dev/null +++ b/heat/policies/service.py @@ -0,0 +1,27 @@ +# 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 heat.policies import base + +POLICY_ROOT = 'service:%s' + +service_policies = [ + policy.RuleDefault( + name=POLICY_ROOT % 'index', + check_str=base.RULE_CONTEXT_IS_ADMIN) +] + + +def list_rules(): + return service_policies