From ee92fab5103e1f77bac237460f36eb28e325adb3 Mon Sep 17 00:00:00 2001 From: Brian DeHamer Date: Fri, 17 Jan 2014 16:19:57 -0800 Subject: [PATCH] policy checks for nova access/security actions Adding policy rule checks for all Nova access and security actions. Co-Authored-By: Lin Hua Cheng Change-Id: I9f4e9209606999e5529e5ba068640d607b817f56 Implements: blueprint compute-rbac --- .../access_and_security/api_access/tables.py | 1 + .../floating_ips/tables.py | 41 +++++++++-- .../access_and_security/keypairs/tables.py | 3 + .../security_groups/tables.py | 73 +++++++++++++++++++ 4 files changed, 111 insertions(+), 7 deletions(-) diff --git a/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py b/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py index 666e24f3f6..c3348deb50 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py +++ b/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py @@ -35,6 +35,7 @@ class DownloadEC2(tables.LinkAction): verbose_name_plural = _("Download EC2 Credentials") classes = ("btn-download",) url = "horizon:project:access_and_security:api_access:ec2" + policy_rules = (("compute", "compute_extension:certificates"),) class DownloadOpenRC(tables.LinkAction): diff --git a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py index 76a1cef6cb..c0c674cb90 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py +++ b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py @@ -17,6 +17,7 @@ import logging +from django.conf import settings from django.core import urlresolvers from django import shortcuts from django.utils.http import urlencode @@ -34,6 +35,8 @@ from openstack_dashboard.utils import filters LOG = logging.getLogger(__name__) +POLICY_CHECK = getattr(settings, "POLICY_CHECK_FUNCTION", lambda p, r: True) + class AllocateIP(tables.LinkAction): name = "allocate" @@ -55,7 +58,14 @@ class AllocateIP(tables.LinkAction): self.verbose_name = _("Allocate IP To Project") classes = [c for c in self.classes if c != "disabled"] self.classes = classes - return True + + if api.base.is_service_enabled(request, "network"): + policy = (("network", "create_floatingip"),) + else: + policy = (("compute", "compute_extension:floating_ips"), + ("compute", "network:allocate_floating_ip"),) + + return POLICY_CHECK(policy, request) class ReleaseIPs(tables.BatchAction): @@ -66,6 +76,15 @@ class ReleaseIPs(tables.BatchAction): data_type_plural = _("Floating IPs") classes = ('btn-danger', 'btn-release') + def allowed(self, request, fip=None): + if api.base.is_service_enabled(request, "network"): + policy = (("network", "delete_floatingip"),) + else: + policy = (("compute", "compute_extension:floating_ips"), + ("compute", "network:release_floating_ip"),) + + return POLICY_CHECK(policy, request) + def action(self, request, obj_id): api.network.tenant_floating_ip_release(request, obj_id) @@ -77,9 +96,13 @@ class AssociateIP(tables.LinkAction): classes = ("ajax-modal", "btn-associate") def allowed(self, request, fip): - if fip.port_id: - return False - return True + if api.base.is_service_enabled(request, "network"): + policy = (("network", "update_floatingip"),) + else: + policy = (("compute", "compute_extension:floating_ips"), + ("compute", "network:associate_floating_ip"),) + + return not fip.port_id and POLICY_CHECK(policy, request) def get_link_url(self, datum): base_url = urlresolvers.reverse(self.url) @@ -93,9 +116,13 @@ class DisassociateIP(tables.Action): classes = ("btn-disassociate", "btn-danger") def allowed(self, request, fip): - if fip.port_id: - return True - return False + if api.base.is_service_enabled(request, "network"): + policy = (("network", "update_floatingip"),) + else: + policy = (("compute", "compute_extension:floating_ips"), + ("compute", "network:disassociate_floating_ip"),) + + return fip.port_id and POLICY_CHECK(policy, request) def single(self, table, request, obj_id): try: diff --git a/openstack_dashboard/dashboards/project/access_and_security/keypairs/tables.py b/openstack_dashboard/dashboards/project/access_and_security/keypairs/tables.py index 56a6af3fb1..304328cd28 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/keypairs/tables.py +++ b/openstack_dashboard/dashboards/project/access_and_security/keypairs/tables.py @@ -26,6 +26,7 @@ from openstack_dashboard.usage import quotas class DeleteKeyPairs(tables.DeleteAction): data_type_singular = _("Key Pair") data_type_plural = _("Key Pairs") + policy_rules = (("compute", "compute_extension:keypairs:delete"),) def delete(self, request, obj_id): api.nova.keypair_delete(request, obj_id) @@ -36,6 +37,7 @@ class ImportKeyPair(tables.LinkAction): verbose_name = _("Import Key Pair") url = "horizon:project:access_and_security:keypairs:import" classes = ("ajax-modal", "btn-upload") + policy_rules = (("compute", "compute_extension:keypairs:create"),) class CreateKeyPair(tables.LinkAction): @@ -43,6 +45,7 @@ class CreateKeyPair(tables.LinkAction): verbose_name = _("Create Key Pair") url = "horizon:project:access_and_security:keypairs:create" classes = ("ajax-modal", "btn-create") + policy_rules = (("compute", "compute_extension:keypairs:create"),) def allowed(self, request, keypair=None): usages = quotas.tenant_quota_usages(request) diff --git a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py index 6bc08abc1a..74fa58fb36 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py +++ b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py @@ -24,11 +24,30 @@ from openstack_dashboard import api from openstack_dashboard.utils import filters +POLICY_CHECK = getattr(settings, "POLICY_CHECK_FUNCTION", + lambda policy, request, target: True) + + class DeleteGroup(tables.DeleteAction): data_type_singular = _("Security Group") data_type_plural = _("Security Groups") + 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, security_group=None): + policy_target = self.get_policy_target(request, security_group) + if api.base.is_service_enabled(request, "network"): + policy = (("network", "delete_security_group"),) + else: + policy = (("compute", "compute_extension:security_groups"),) + + if not POLICY_CHECK(policy, request, policy_target): + return False + if not security_group: return True return security_group.name != 'default' @@ -43,6 +62,14 @@ class CreateGroup(tables.LinkAction): url = "horizon:project:access_and_security:security_groups:create" classes = ("ajax-modal", "btn-create") + def allowed(self, request, security_group=None): + if api.base.is_service_enabled(request, "network"): + policy = (("network", "create_security_group"),) + else: + policy = (("compute", "compute_extension:security_groups"),) + + return POLICY_CHECK(policy, request, target={}) + class EditGroup(tables.LinkAction): name = "edit" @@ -50,7 +77,22 @@ class EditGroup(tables.LinkAction): url = "horizon:project:access_and_security:security_groups:update" classes = ("ajax-modal", "btn-edit") + 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, security_group=None): + policy_target = self.get_policy_target(request, security_group) + if api.base.is_service_enabled(request, "network"): + policy = (("network", "update_security_group"),) + else: + policy = (("compute", "compute_extension:security_groups"),) + + if not POLICY_CHECK(policy, request, policy_target): + return False + if not security_group: return True return security_group.name != 'default' @@ -62,6 +104,21 @@ class ManageRules(tables.LinkAction): url = "horizon:project:access_and_security:security_groups:detail" classes = ("btn-edit") + 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, security_group=None): + policy_target = self.get_policy_target(request, security_group) + if api.base.is_service_enabled(request, "network"): + policy = (("network", "get_security_group"),) + else: + policy = (("compute", "compute_extension:security_groups"),) + + return POLICY_CHECK(policy, request, policy_target) + class SecurityGroupsTable(tables.DataTable): name = tables.Column("name", verbose_name=_("Name")) @@ -83,6 +140,14 @@ class CreateRule(tables.LinkAction): url = "horizon:project:access_and_security:security_groups:add_rule" classes = ("ajax-modal", "btn-create") + def allowed(self, request, security_group_rule=None): + if api.base.is_service_enabled(request, "network"): + policy = (("network", "create_security_group_rule"),) + else: + policy = (("compute", "compute_extension:security_groups"),) + + return POLICY_CHECK(policy, request, target={}) + def get_link_url(self): return reverse(self.url, args=[self.table.kwargs['security_group_id']]) @@ -91,6 +156,14 @@ class DeleteRule(tables.DeleteAction): data_type_singular = _("Rule") data_type_plural = _("Rules") + def allowed(self, request, security_group_rule=None): + if api.base.is_service_enabled(request, "network"): + policy = (("network", "delete_security_group_rule"),) + else: + policy = (("compute", "compute_extension:security_groups"),) + + return POLICY_CHECK(policy, request, target={}) + def delete(self, request, obj_id): api.network.security_group_rule_delete(request, obj_id)