From 4d8c573b1d772cf02dd8496ef7599b266d3114f6 Mon Sep 17 00:00:00 2001 From: Brian DeHamer Date: Tue, 7 Jan 2014 15:09:52 -0800 Subject: [PATCH] adding policy checks for nova instance actions Adding policy rule checks for all Nova instance actions (on both the project and admin dashboards). Change-Id: Ibf419ecf6624cf3315ddb10daee0542f7d8d2c3e Implements: blueprint compute-rbac --- openstack_dashboard/conf/nova_policy.json | 11 ++ .../dashboards/admin/instances/tables.py | 15 ++ .../dashboards/project/instances/tables.py | 131 +++++++++++++++++- 3 files changed, 155 insertions(+), 2 deletions(-) diff --git a/openstack_dashboard/conf/nova_policy.json b/openstack_dashboard/conf/nova_policy.json index b1d7e6706e..487e46c118 100644 --- a/openstack_dashboard/conf/nova_policy.json +++ b/openstack_dashboard/conf/nova_policy.json @@ -9,11 +9,22 @@ "compute:create:attach_network": "", "compute:create:attach_volume": "", "compute:create:forced_host": "is_admin:True", + "compute:delete": "rule:default", "compute:get_all": "", "compute:get_all_tenants": "", + "compute:reboot": "rule:default", + "compute:rebuild": "rule:default", + "compute:snapshot": "rule:default", + "compute:start": "rule:default", + "compute:stop": "rule:default", "compute:unlock_override": "rule:admin_api", "compute:attach_volume" : "rule:default", "compute:detach_volume" : "rule:default", + "compute:update": "rule:default", + + "compute:resize": "rule:default", + "compute:confirm_resize": "rule:default", + "compute:revert_resize": "rule:default", "compute:shelve": "", "compute:shelve_offload": "", diff --git a/openstack_dashboard/dashboards/admin/instances/tables.py b/openstack_dashboard/dashboards/admin/instances/tables.py index da25da7cd0..8a1f8d1ee9 100644 --- a/openstack_dashboard/dashboards/admin/instances/tables.py +++ b/openstack_dashboard/dashboards/admin/instances/tables.py @@ -37,6 +37,13 @@ class MigrateInstance(tables.BatchAction): data_type_singular = _("Instance") data_type_plural = _("Instances") classes = ("btn-migrate", "btn-danger") + policy_rules = (("compute", "compute_extension:admin_actions:migrate"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): return ((instance.status in project_tables.ACTIVE_STATES @@ -52,6 +59,14 @@ class LiveMigrateInstance(tables.LinkAction): verbose_name = _("Live Migrate Instance") url = "horizon:admin:instances:live_migrate" classes = ("ajax-modal", "btn-migrate", "btn-danger") + policy_rules = ( + ("compute", "compute_extension:admin_actions:migrateLive"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): return ((instance.status in project_tables.ACTIVE_STATES) diff --git a/openstack_dashboard/dashboards/project/instances/tables.py b/openstack_dashboard/dashboards/project/instances/tables.py index 77b6b0a10e..3c89ce50cb 100644 --- a/openstack_dashboard/dashboards/project/instances/tables.py +++ b/openstack_dashboard/dashboards/project/instances/tables.py @@ -17,6 +17,7 @@ import logging +from django.conf import settings # noqa from django.core import urlresolvers from django import shortcuts from django import template @@ -77,6 +78,13 @@ class TerminateInstance(tables.BatchAction): data_type_singular = _("Instance") data_type_plural = _("Instances") classes = ('btn-danger', 'btn-terminate') + policy_rules = (("compute", "compute:delete"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance=None): return True @@ -92,6 +100,13 @@ class RebootInstance(tables.BatchAction): data_type_singular = _("Instance") data_type_plural = _("Instances") classes = ('btn-danger', 'btn-reboot') + policy_rules = (("compute", "compute:reboot"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance=None): if instance is not None: @@ -132,9 +147,19 @@ class TogglePause(tables.BatchAction): self.paused = instance.status == "PAUSED" if self.paused: self.current_present_action = UNPAUSE + policy = (("compute", "compute_extension:admin_actions:unpause"),) else: self.current_present_action = PAUSE - return ((instance.status in ACTIVE_STATES or self.paused) + policy = (("compute", "compute_extension:admin_actions:pause"),) + + has_permission = True + policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None) + if policy_check: + has_permission = policy_check(policy, request, + target={'project_id': getattr(instance, 'tenant_id', None)}) + + return (has_permission + and (instance.status in ACTIVE_STATES or self.paused) and not is_deleting(instance)) def action(self, request, obj_id): @@ -164,9 +189,19 @@ class ToggleSuspend(tables.BatchAction): self.suspended = instance.status == "SUSPENDED" if self.suspended: self.current_present_action = RESUME + policy = (("compute", "compute_extension:admin_actions:resume"),) else: self.current_present_action = SUSPEND - return ((instance.status in ACTIVE_STATES or self.suspended) + policy = (("compute", "compute_extension:admin_actions:suspend"),) + + has_permission = True + policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None) + if policy_check: + has_permission = policy_check(policy, request, + target={'project_id': getattr(instance, 'tenant_id', None)}) + + return (has_permission + and (instance.status in ACTIVE_STATES or self.suspended) and not is_deleting(instance)) def action(self, request, obj_id): @@ -183,6 +218,7 @@ class LaunchLink(tables.LinkAction): verbose_name = _("Launch Instance") url = "horizon:project:instances:launch" classes = ("btn-launch", "ajax-modal") + policy_rules = (("compute", "compute:create"),) def allowed(self, request, datum): try: @@ -217,6 +253,13 @@ class EditInstance(tables.LinkAction): verbose_name = _("Edit Instance") url = "horizon:project:instances:update" classes = ("ajax-modal", "btn-edit") + policy_rules = (("compute", "compute:update"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def get_link_url(self, project): return self._get_link_url(project, 'instance_info') @@ -248,6 +291,13 @@ class CreateSnapshot(tables.LinkAction): verbose_name = _("Create Snapshot") url = "horizon:project:images_and_snapshots:snapshots:create" classes = ("ajax-modal", "btn-camera") + policy_rules = (("compute", "compute:snapshot"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance=None): return instance.status in SNAPSHOT_READY_STATES \ @@ -259,6 +309,13 @@ class ConsoleLink(tables.LinkAction): verbose_name = _("Console") url = "horizon:project:instances:detail" classes = ("btn-console",) + policy_rules = (("compute", "compute_extension:consoles"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance=None): return instance.status in ACTIVE_STATES and not is_deleting(instance) @@ -275,6 +332,13 @@ class LogLink(tables.LinkAction): verbose_name = _("View Log") url = "horizon:project:instances:detail" classes = ("btn-log",) + policy_rules = (("compute", "compute_extension:console_output"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance=None): return instance.status in ACTIVE_STATES and not is_deleting(instance) @@ -291,6 +355,13 @@ class ResizeLink(tables.LinkAction): verbose_name = _("Resize Instance") url = "horizon:project:instances:resize" classes = ("ajax-modal", "btn-resize") + policy_rules = (("compute", "compute:resize"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def get_link_url(self, project): return self._get_link_url(project, 'flavor_choice') @@ -310,6 +381,13 @@ class ConfirmResize(tables.Action): name = "confirm" verbose_name = _("Confirm Resize/Migrate") classes = ("btn-confirm", "btn-action-required") + policy_rules = (("compute", "compute:confirm_resize"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): return instance.status == 'VERIFY_RESIZE' @@ -322,6 +400,13 @@ class RevertResize(tables.Action): name = "revert" verbose_name = _("Revert Resize/Migrate") classes = ("btn-revert", "btn-action-required") + policy_rules = (("compute", "compute:revert_resize"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): return instance.status == 'VERIFY_RESIZE' @@ -335,6 +420,13 @@ class RebuildInstance(tables.LinkAction): verbose_name = _("Rebuild Instance") classes = ("btn-rebuild", "ajax-modal") url = "horizon:project:instances:rebuild" + policy_rules = (("compute", "compute:rebuild"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): return ((instance.status in ACTIVE_STATES @@ -351,6 +443,13 @@ class AssociateIP(tables.LinkAction): verbose_name = _("Associate Floating IP") url = "horizon:project:access_and_security:floating_ips:associate" classes = ("ajax-modal", "btn-associate") + policy_rules = (("compute", "network:associate_floating_ip"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): if api.network.floating_ip_simple_associate_supported(request): @@ -370,6 +469,13 @@ class SimpleAssociateIP(tables.Action): name = "associate-simple" verbose_name = _("Associate Floating IP") classes = ("btn-associate-simple",) + policy_rules = (("compute", "network:associate_floating_ip"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): if not api.network.floating_ip_simple_associate_supported(request): @@ -398,6 +504,13 @@ class SimpleDisassociateIP(tables.Action): name = "disassociate" verbose_name = _("Disassociate Floating IP") classes = ("btn-danger", "btn-disassociate",) + policy_rules = (("compute", "network:disassociate_floating_ip"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): if not conf.HORIZON_CONFIG["simple_ip_management"]: @@ -472,6 +585,13 @@ class StartInstance(tables.BatchAction): action_past = _("Started") data_type_singular = _("Instance") data_type_plural = _("Instances") + policy_rules = (("compute", "compute:start"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): return instance.status in ("SHUTDOWN", "SHUTOFF", "CRASHED") @@ -487,6 +607,13 @@ class StopInstance(tables.BatchAction): data_type_singular = _("Instance") data_type_plural = _("Instances") classes = ('btn-danger',) + policy_rules = (("compute", "compute:stop"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} def allowed(self, request, instance): return ((get_power_state(instance)