From d93722d379c503f16239dbf345b341c679c7210f Mon Sep 17 00:00:00 2001 From: Brian DeHamer Date: Wed, 5 Feb 2014 14:01:17 -0800 Subject: [PATCH] adding policy check for neutron Policy checks for all actions on firewalls, loadbalancers, network_topology, networks, routers and vpn panels. Co-Authored-By: Lin Hua Cheng Change-Id: Id12257d3200f8af6ff590fd576c4cb6e414b455a Implements: blueprint network-rbac --- openstack_dashboard/conf/neutron_policy.json | 172 ++++++++++++++++++ .../dashboards/admin/networks/ports/tables.py | 15 ++ .../admin/networks/subnets/tables.py | 35 ++++ .../dashboards/admin/networks/tables.py | 15 ++ .../dashboards/admin/routers/tables.py | 7 + .../dashboards/project/firewalls/tables.py | 61 +++++++ .../project/loadbalancers/tables.py | 14 ++ .../templates/network_topology/index.html | 6 + .../project/network_topology/views.py | 20 ++ .../project/networks/ports/tables.py | 7 + .../project/networks/subnets/tables.py | 22 +++ .../dashboards/project/networks/tables.py | 22 +++ .../routers/extensions/routerrules/tables.py | 14 ++ .../project/routers/ports/tables.py | 14 ++ .../dashboards/project/routers/tables.py | 22 +++ .../dashboards/project/vpn/tables.py | 12 ++ .../local/local_settings.py.example | 1 + openstack_dashboard/settings.py | 1 + 18 files changed, 460 insertions(+) create mode 100644 openstack_dashboard/conf/neutron_policy.json diff --git a/openstack_dashboard/conf/neutron_policy.json b/openstack_dashboard/conf/neutron_policy.json new file mode 100644 index 0000000000..3947312cee --- /dev/null +++ b/openstack_dashboard/conf/neutron_policy.json @@ -0,0 +1,172 @@ +{ + "context_is_admin": "role:admin", + "admin_or_owner": "rule:context_is_admin or project_id:%(project_id)s", + "admin_or_network_owner": "rule:context_is_admin or project_id:%(network:project_id)s", + "admin_only": "rule:context_is_admin", + "regular_user": "", + "shared": "field:networks:shared=True", + "shared_firewalls": "field:firewalls:shared=True", + "external": "field:networks:router:external=True", + "default": "rule:admin_or_owner", + + "subnets:private:read": "rule:admin_or_owner", + "subnets:private:write": "rule:admin_or_owner", + "subnets:shared:read": "rule:regular_user", + "subnets:shared:write": "rule:admin_only", + + "create_subnet": "rule:admin_or_network_owner", + "get_subnet": "rule:admin_or_owner or rule:shared", + "update_subnet": "rule:admin_or_network_owner", + "delete_subnet": "rule:admin_or_network_owner", + + "create_network": "", + "get_network": "rule:admin_or_owner or rule:shared or rule:external", + "get_network:router:external": "rule:regular_user", + "get_network:segments": "rule:admin_only", + "get_network:provider:network_type": "rule:admin_only", + "get_network:provider:physical_network": "rule:admin_only", + "get_network:provider:segmentation_id": "rule:admin_only", + "get_network:queue_id": "rule:admin_only", + "create_network:shared": "rule:admin_only", + "create_network:router:external": "rule:admin_only", + "create_network:segments": "rule:admin_only", + "create_network:provider:network_type": "rule:admin_only", + "create_network:provider:physical_network": "rule:admin_only", + "create_network:provider:segmentation_id": "rule:admin_only", + "update_network": "rule:admin_or_owner", + "update_network:segments": "rule:admin_only", + "update_network:provider:network_type": "rule:admin_only", + "update_network:provider:physical_network": "rule:admin_only", + "update_network:provider:segmentation_id": "rule:admin_only", + "delete_network": "rule:admin_or_owner", + + "create_port": "", + "create_port:mac_address": "rule:admin_or_network_owner", + "create_port:fixed_ips": "rule:admin_or_network_owner", + "create_port:port_security_enabled": "rule:admin_or_network_owner", + "create_port:binding:host_id": "rule:admin_only", + "create_port:binding:profile": "rule:admin_only", + "create_port:mac_learning_enabled": "rule:admin_or_network_owner", + "get_port": "rule:admin_or_owner", + "get_port:queue_id": "rule:admin_only", + "get_port:binding:vif_type": "rule:admin_only", + "get_port:binding:capabilities": "rule:admin_only", + "get_port:binding:host_id": "rule:admin_only", + "get_port:binding:profile": "rule:admin_only", + "update_port": "rule:admin_or_owner", + "update_port:fixed_ips": "rule:admin_or_network_owner", + "update_port:port_security_enabled": "rule:admin_or_network_owner", + "update_port:binding:host_id": "rule:admin_only", + "update_port:binding:profile": "rule:admin_only", + "update_port:mac_learning_enabled": "rule:admin_or_network_owner", + "delete_port": "rule:admin_or_owner", + + "create_router:external_gateway_info:enable_snat": "rule:admin_only", + "update_router:external_gateway_info:enable_snat": "rule:admin_only", + + "create_ikepolicy": "rule:admin_or_owner", + "update_ikepolicy": "rule:admin_or_owner", + "delete_ikepolicy": "rule:admin_or_owner", + + "create_ipsecpolicy": "rule:admin_or_owner", + "update_ipsecpolicy": "rule:admin_or_owner", + "delete_ipsecpolicy": "rule:admin_or_owner", + + "create_vpnservice": "rule:admin_or_owner", + "update_vpnservice": "rule:admin_or_owner", + "delete_vpnservice": "rule:admin_or_owner", + + "create_ipsec_site_connection": "rule:admin_or_owner", + "update_ipsec_site_connection": "rule:admin_or_owner", + "delete_ipsec_site_connection": "rule:admin_or_owner", + + "create_firewall": "", + "get_firewall": "rule:admin_or_owner", + "create_firewall:shared": "rule:admin_only", + "get_firewall:shared": "rule:admin_only", + "update_firewall": "rule:admin_or_owner", + "delete_firewall": "rule:admin_or_owner", + + "create_firewall_policy": "", + "get_firewall_policy": "rule:admin_or_owner or rule:shared_firewalls", + "create_firewall_policy:shared": "rule:admin_or_owner", + "update_firewall_policy": "rule:admin_or_owner", + "delete_firewall_policy": "rule:admin_or_owner", + + "create_firewall_rule": "", + "get_firewall_rule": "rule:admin_or_owner or rule:shared_firewalls", + "create_firewall_rule:shared": "rule:admin_or_owner", + "get_firewall_rule:shared": "rule:admin_or_owner", + "update_firewall_rule": "rule:admin_or_owner", + "delete_firewall_rule": "rule:admin_or_owner", + "insert_rule": "rule:admin_or_owner", + "remove_rule": "rule:admin_or_owner", + + "create_qos_queue": "rule:admin_only", + "get_qos_queue": "rule:admin_only", + + "update_agent": "rule:admin_only", + "delete_agent": "rule:admin_only", + "get_agent": "rule:admin_only", + + "create_dhcp-network": "rule:admin_only", + "delete_dhcp-network": "rule:admin_only", + "get_dhcp-networks": "rule:admin_only", + "create_l3-router": "rule:admin_only", + "delete_l3-router": "rule:admin_only", + "get_l3-routers": "rule:admin_only", + "get_dhcp-agents": "rule:admin_only", + "get_l3-agents": "rule:admin_only", + "get_loadbalancer-agent": "rule:admin_only", + "get_loadbalancer-pools": "rule:admin_only", + + "create_pool": "rule:admin_or_owner", + "update_pool": "rule:admin_or_owner", + "delete_pool": "rule:admin_or_owner", + + "create_vip": "rule:admin_or_owner", + "update_vip": "rule:admin_or_owner", + "delete_vip": "rule:admin_or_owner", + + "create_member": "rule:admin_or_owner", + "update_member": "rule:admin_or_owner", + "delete_member": "rule:admin_or_owner", + + "create_health_monitor": "rule:admin_or_owner", + "update_health_monitor": "rule:admin_or_owner", + "delete_health_monitor": "rule:admin_or_owner", + + "create_pool_health_monitor": "rule:admin_or_owner", + "delete_pool_health_monitor": "rule:admin_or_owner", + + "create_router": "rule:regular_user", + "get_router": "rule:admin_or_owner", + "update_router": "rule:admin_or_owner", + "add_router_interface": "rule:admin_or_owner", + "remove_router_interface": "rule:admin_or_owner", + "delete_router": "rule:admin_or_owner", + + "create_floatingip": "rule:regular_user", + "update_floatingip": "rule:admin_or_owner", + "delete_floatingip": "rule:admin_or_owner", + "get_floatingip": "rule:admin_or_owner", + + "create_network_profile": "rule:admin_only", + "update_network_profile": "rule:admin_only", + "delete_network_profile": "rule:admin_only", + "get_network_profiles": "", + "get_network_profile": "", + "update_policy_profiles": "rule:admin_only", + "get_policy_profiles": "", + "get_policy_profile": "", + + "create_metering_label": "rule:admin_only", + "delete_metering_label": "rule:admin_only", + "get_metering_label": "rule:admin_only", + + "create_metering_label_rule": "rule:admin_only", + "delete_metering_label_rule": "rule:admin_only", + "get_metering_label_rule": "rule:admin_only", + + "get_service_provider": "rule:regular_user" +} diff --git a/openstack_dashboard/dashboards/admin/networks/ports/tables.py b/openstack_dashboard/dashboards/admin/networks/ports/tables.py index 6f3c16e8a4..08a7c9b698 100644 --- a/openstack_dashboard/dashboards/admin/networks/ports/tables.py +++ b/openstack_dashboard/dashboards/admin/networks/ports/tables.py @@ -33,6 +33,13 @@ LOG = logging.getLogger(__name__) class DeletePort(tables.DeleteAction): data_type_singular = _("Port") data_type_plural = _("Ports") + policy_rules = (("network", "delete_port"),) + + 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 delete(self, request, obj_id): try: @@ -51,6 +58,7 @@ class CreatePort(tables.LinkAction): verbose_name = _("Create Port") url = "horizon:admin:networks:addport" classes = ("ajax-modal", "btn-create") + policy_rules = (("network", "create_port"),) def get_link_url(self, datum=None): network_id = self.table.kwargs['network_id'] @@ -62,6 +70,13 @@ class UpdatePort(tables.LinkAction): verbose_name = _("Edit Port") url = "horizon:admin:networks:editport" classes = ("ajax-modal", "btn-edit") + policy_rules = (("network", "update_port"),) + + 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, port): network_id = self.table.kwargs['network_id'] diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/tables.py b/openstack_dashboard/dashboards/admin/networks/subnets/tables.py index 099d667ed5..bcbf94d7c1 100644 --- a/openstack_dashboard/dashboards/admin/networks/subnets/tables.py +++ b/openstack_dashboard/dashboards/admin/networks/subnets/tables.py @@ -21,6 +21,7 @@ from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import tables +from horizon.utils import memoized from openstack_dashboard import api @@ -31,6 +32,13 @@ LOG = logging.getLogger(__name__) class DeleteSubnet(tables.DeleteAction): data_type_singular = _("Subnet") data_type_plural = _("Subnets") + policy_rules = (("network", "delete_subnet"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"network:project_id": project_id} def delete(self, request, obj_id): try: @@ -49,6 +57,14 @@ class CreateSubnet(tables.LinkAction): verbose_name = _("Create Subnet") url = "horizon:admin:networks:addsubnet" classes = ("ajax-modal", "btn-create") + policy_rules = (("network", "create_subnet"),) + + def get_policy_target(self, request, datum=None): + project_id = None + network = self.table._get_network() + if network: + project_id = getattr(network, 'tenant_id', None) + return {"network:project_id": project_id} def get_link_url(self, datum=None): network_id = self.table.kwargs['network_id'] @@ -60,6 +76,13 @@ class UpdateSubnet(tables.LinkAction): verbose_name = _("Edit Subnet") url = "horizon:admin:networks:editsubnet" classes = ("ajax-modal", "btn-edit") + policy_rules = (("network", "update_subnet"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"network:project_id": project_id} def get_link_url(self, subnet): network_id = self.table.kwargs['network_id'] @@ -76,6 +99,18 @@ class SubnetsTable(tables.DataTable): def get_object_display(self, subnet): return subnet.id + @memoized.memoized_method + def _get_network(self): + try: + network_id = self.kwargs['network_id'] + network = api.neutron.network_get(self.request, network_id) + network.set_id_as_name_if_empty(length=0) + except Exception: + msg = _('Unable to retrieve details for network "%s".') \ + % (network_id) + exceptions.handle(self.request, msg, redirect=self.failure_url) + return network + class Meta: name = "subnets" verbose_name = _("Subnets") diff --git a/openstack_dashboard/dashboards/admin/networks/tables.py b/openstack_dashboard/dashboards/admin/networks/tables.py index a1fcef0be3..4eed5c5800 100644 --- a/openstack_dashboard/dashboards/admin/networks/tables.py +++ b/openstack_dashboard/dashboards/admin/networks/tables.py @@ -34,6 +34,13 @@ LOG = logging.getLogger(__name__) class DeleteNetwork(tables.DeleteAction): data_type_singular = _("Network") data_type_plural = _("Networks") + policy_rules = (("network", "delete_network"),) + + 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 delete(self, request, obj_id): try: @@ -50,6 +57,7 @@ class CreateNetwork(tables.LinkAction): verbose_name = _("Create Network") url = "horizon:admin:networks:create" classes = ("ajax-modal", "btn-create") + policy_rules = (("network", "create_network"),) class EditNetwork(tables.LinkAction): @@ -57,6 +65,13 @@ class EditNetwork(tables.LinkAction): verbose_name = _("Edit Network") url = "horizon:admin:networks:update" classes = ("ajax-modal", "btn-edit") + policy_rules = (("network", "update_network"),) + + 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_subnets(network): diff --git a/openstack_dashboard/dashboards/admin/routers/tables.py b/openstack_dashboard/dashboards/admin/routers/tables.py index 6855837439..71d6ae9497 100644 --- a/openstack_dashboard/dashboards/admin/routers/tables.py +++ b/openstack_dashboard/dashboards/admin/routers/tables.py @@ -24,6 +24,13 @@ from openstack_dashboard.dashboards.project.routers import tables as r_tables class DeleteRouter(r_tables.DeleteRouter): redirect_url = "horizon:admin:routers:index" + policy_rules = (("network", "delete_router"),) + + 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 delete(self, request, obj_id): search_opts = {'device_owner': 'network:router_interface', diff --git a/openstack_dashboard/dashboards/project/firewalls/tables.py b/openstack_dashboard/dashboards/project/firewalls/tables.py index 01b5c64ecb..b3a7805466 100644 --- a/openstack_dashboard/dashboards/project/firewalls/tables.py +++ b/openstack_dashboard/dashboards/project/firewalls/tables.py @@ -27,6 +27,7 @@ class AddRuleLink(tables.LinkAction): verbose_name = _("Add Rule") url = "horizon:project:firewalls:addrule" classes = ("ajax-modal", "btn-create",) + policy_rules = (("network", "create_firewall_rule"),) class AddPolicyLink(tables.LinkAction): @@ -34,6 +35,7 @@ class AddPolicyLink(tables.LinkAction): verbose_name = _("Add Policy") url = "horizon:project:firewalls:addpolicy" classes = ("ajax-modal", "btn-addpolicy",) + policy_rules = (("network", "create_firewall_policy"),) class AddFirewallLink(tables.LinkAction): @@ -41,6 +43,7 @@ class AddFirewallLink(tables.LinkAction): verbose_name = _("Create Firewall") url = "horizon:project:firewalls:addfirewall" classes = ("ajax-modal", "btn-addfirewall",) + policy_rules = (("network", "create_firewall"),) class DeleteRuleLink(tables.DeleteAction): @@ -49,6 +52,13 @@ class DeleteRuleLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("Rule") data_type_plural = _("Rules") + policy_rules = (("network", "delete_firewall_rule"),) + + 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} class DeletePolicyLink(tables.DeleteAction): @@ -57,6 +67,13 @@ class DeletePolicyLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("Policy") data_type_plural = _("Policies") + policy_rules = (("network", "delete_firewall_policy"),) + + 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} class DeleteFirewallLink(tables.DeleteAction): @@ -65,12 +82,26 @@ class DeleteFirewallLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("Firewall") data_type_plural = _("Firewalls") + policy_rules = (("network", "delete_firewall"),) + + 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} class UpdateRuleLink(tables.LinkAction): name = "updaterule" verbose_name = _("Edit Rule") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_firewall_rule"),) + + 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, rule): base_url = reverse("horizon:project:firewalls:updaterule", @@ -82,6 +113,13 @@ class UpdatePolicyLink(tables.LinkAction): name = "updatepolicy" verbose_name = _("Edit Policy") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_firewall_policy"),) + + 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, policy): base_url = reverse("horizon:project:firewalls:updatepolicy", @@ -93,6 +131,13 @@ class UpdateFirewallLink(tables.LinkAction): name = "updatefirewall" verbose_name = _("Edit Firewall") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_firewall"),) + + 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, firewall): base_url = reverse("horizon:project:firewalls:updatefirewall", @@ -104,6 +149,14 @@ class InsertRuleToPolicyLink(tables.LinkAction): name = "insertrule" verbose_name = _("Insert Rule") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "get_firewall_policy"), + ("network", "insert_rule"),) + + 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, policy): base_url = reverse("horizon:project:firewalls:insertrule", @@ -115,6 +168,14 @@ class RemoveRuleFromPolicyLink(tables.LinkAction): name = "removerule" verbose_name = _("Remove Rule") classes = ("ajax-modal", "btn-danger",) + policy_rules = (("network", "get_firewall_policy"), + ("network", "remove_rule"),) + + 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, policy): base_url = reverse("horizon:project:firewalls:removerule", diff --git a/openstack_dashboard/dashboards/project/loadbalancers/tables.py b/openstack_dashboard/dashboards/project/loadbalancers/tables.py index c24dcb2620..807a554057 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/tables.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/tables.py @@ -31,12 +31,14 @@ class AddPoolLink(tables.LinkAction): verbose_name = _("Add Pool") url = "horizon:project:loadbalancers:addpool" classes = ("ajax-modal", "btn-create",) + policy_rules = (("network", "create_pool"),) class AddVipLink(tables.LinkAction): name = "addvip" verbose_name = _("Add VIP") classes = ("ajax-modal", "btn-create",) + policy_rules = (("network", "create_vip"),) def get_link_url(self, pool): base_url = reverse("horizon:project:loadbalancers:addvip", @@ -54,6 +56,7 @@ class AddMemberLink(tables.LinkAction): verbose_name = _("Add Member") url = "horizon:project:loadbalancers:addmember" classes = ("ajax-modal", "btn-create",) + policy_rules = (("network", "create_member"),) class AddMonitorLink(tables.LinkAction): @@ -61,6 +64,7 @@ class AddMonitorLink(tables.LinkAction): verbose_name = _("Add Monitor") url = "horizon:project:loadbalancers:addmonitor" classes = ("ajax-modal", "btn-create",) + policy_rules = (("network", "create_health_monitor"),) class DeleteVipLink(tables.DeleteAction): @@ -69,6 +73,7 @@ class DeleteVipLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("VIP") data_type_plural = _("VIPs") + policy_rules = (("network", "delete_vip"),) def allowed(self, request, datum=None): if datum and not datum.vip_id: @@ -82,6 +87,7 @@ class DeletePoolLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("Pool") data_type_plural = _("Pools") + policy_rules = (("network", "delete_pool"),) def allowed(self, request, datum=None): if datum and datum.vip_id: @@ -95,6 +101,7 @@ class DeleteMonitorLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("Monitor") data_type_plural = _("Monitors") + policy_rules = (("network", "delete_health_monitor"),) class DeleteMemberLink(tables.DeleteAction): @@ -103,12 +110,14 @@ class DeleteMemberLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("Member") data_type_plural = _("Members") + policy_rules = (("network", "delete_member"),) class UpdatePoolLink(tables.LinkAction): name = "updatepool" verbose_name = _("Edit Pool") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_pool"),) def get_link_url(self, pool): base_url = reverse("horizon:project:loadbalancers:updatepool", @@ -120,6 +129,7 @@ class UpdateVipLink(tables.LinkAction): name = "updatevip" verbose_name = _("Edit VIP") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_vip"),) def get_link_url(self, pool): base_url = reverse("horizon:project:loadbalancers:updatevip", @@ -136,6 +146,7 @@ class UpdateMemberLink(tables.LinkAction): name = "updatemember" verbose_name = _("Edit Member") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_member"),) def get_link_url(self, member): base_url = reverse("horizon:project:loadbalancers:updatemember", @@ -147,6 +158,7 @@ class UpdateMonitorLink(tables.LinkAction): name = "updatemonitor" verbose_name = _("Edit Monitor") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_health_monitor"),) def get_link_url(self, monitor): base_url = reverse("horizon:project:loadbalancers:updatemonitor", @@ -167,6 +179,7 @@ class AddPMAssociationLink(tables.LinkAction): verbose_name = _("Associate Monitor") url = "horizon:project:loadbalancers:addassociation" classes = ("ajax-modal", "btn-create",) + policy_rules = (("network", "create_pool_health_monitor"),) def allowed(self, request, datum=None): try: @@ -187,6 +200,7 @@ class DeletePMAssociationLink(tables.LinkAction): verbose_name = _("Disassociate Monitor") url = "horizon:project:loadbalancers:deleteassociation" classes = ("ajax-modal", "btn-delete", "btn-danger") + policy_rules = (("network", "delete_pool_health_monitor"),) def allowed(self, request, datum=None): if datum and not datum['health_monitors']: diff --git a/openstack_dashboard/dashboards/project/network_topology/templates/network_topology/index.html b/openstack_dashboard/dashboards/project/network_topology/templates/network_topology/index.html index 3d471c9894..c4706c2843 100644 --- a/openstack_dashboard/dashboards/project/network_topology/templates/network_topology/index.html +++ b/openstack_dashboard/dashboards/project/network_topology/templates/network_topology/index.html @@ -24,9 +24,15 @@
+ {% if launch_instance_allowed %} {%trans "Launch Instance" %} + {% endif %} + {% if create_network_allowed %} {%trans "Create Network" %} + {% endif %} + {% if create_router_allowed %} {%trans "Create Router" %} + {% endif %}
diff --git a/openstack_dashboard/dashboards/project/network_topology/views.py b/openstack_dashboard/dashboards/project/network_topology/views.py index db9f58f98d..cbd59d5e43 100644 --- a/openstack_dashboard/dashboards/project/network_topology/views.py +++ b/openstack_dashboard/dashboards/project/network_topology/views.py @@ -91,6 +91,26 @@ class RouterDetailView (r_views.DetailView): class NetworkTopologyView (TemplateView): template_name = 'project/network_topology/index.html' + def _has_permission(self, policy): + has_permission = True + policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None) + + if policy_check: + has_permission = policy_check(policy, self.request) + + return has_permission + + def get_context_data(self, **kwargs): + context = super(NetworkTopologyView, self).get_context_data(**kwargs) + + context['launch_instance_allowed'] = self._has_permission( + (("compute", "compute:create"),)) + context['create_network_allowed'] = self._has_permission( + (("network", "create_network"),)) + context['create_router_allowed'] = self._has_permission( + (("network", "create_router"),)) + return context + class JSONView(View): def add_resource_url(self, view, resources): diff --git a/openstack_dashboard/dashboards/project/networks/ports/tables.py b/openstack_dashboard/dashboards/project/networks/ports/tables.py index 3b87c37a00..59ff8b6563 100644 --- a/openstack_dashboard/dashboards/project/networks/ports/tables.py +++ b/openstack_dashboard/dashboards/project/networks/ports/tables.py @@ -41,6 +41,13 @@ class UpdatePort(tables.LinkAction): verbose_name = _("Edit Port") url = "horizon:project:networks:editport" classes = ("ajax-modal", "btn-edit") + policy_rules = (("network", "update_port"),) + + 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, port): network_id = self.table.kwargs['network_id'] diff --git a/openstack_dashboard/dashboards/project/networks/subnets/tables.py b/openstack_dashboard/dashboards/project/networks/subnets/tables.py index 40139cdffe..145c9f2bb3 100644 --- a/openstack_dashboard/dashboards/project/networks/subnets/tables.py +++ b/openstack_dashboard/dashboards/project/networks/subnets/tables.py @@ -45,6 +45,13 @@ class CheckNetworkEditable(object): class DeleteSubnet(CheckNetworkEditable, tables.DeleteAction): data_type_singular = _("Subnet") data_type_plural = _("Subnets") + policy_rules = (("network", "delete_subnet"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"network:project_id": project_id} def delete(self, request, obj_id): try: @@ -63,6 +70,14 @@ class CreateSubnet(CheckNetworkEditable, tables.LinkAction): verbose_name = _("Create Subnet") url = "horizon:project:networks:addsubnet" classes = ("ajax-modal", "btn-create") + policy_rules = (("network", "create_subnet"),) + + def get_policy_target(self, request, datum=None): + project_id = None + network = self.table._get_network() + if network: + project_id = getattr(network, 'tenant_id', None) + return {"network:project_id": project_id} def get_link_url(self, datum=None): network_id = self.table.kwargs['network_id'] @@ -74,6 +89,13 @@ class UpdateSubnet(CheckNetworkEditable, tables.LinkAction): verbose_name = _("Edit Subnet") url = "horizon:project:networks:editsubnet" classes = ("ajax-modal", "btn-edit") + policy_rules = (("network", "update_subnet"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"network:project_id": project_id} def get_link_url(self, subnet): network_id = self.table.kwargs['network_id'] diff --git a/openstack_dashboard/dashboards/project/networks/tables.py b/openstack_dashboard/dashboards/project/networks/tables.py index 880906588e..37fe386b9a 100644 --- a/openstack_dashboard/dashboards/project/networks/tables.py +++ b/openstack_dashboard/dashboards/project/networks/tables.py @@ -42,6 +42,13 @@ class CheckNetworkEditable(object): class DeleteNetwork(CheckNetworkEditable, tables.DeleteAction): data_type_singular = _("Network") data_type_plural = _("Networks") + policy_rules = (("network", "delete_network"),) + + 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 delete(self, request, network_id): try: @@ -67,6 +74,7 @@ class CreateNetwork(tables.LinkAction): verbose_name = _("Create Network") url = "horizon:project:networks:create" classes = ("ajax-modal", "btn-create") + policy_rules = (("network", "create_network"),) class EditNetwork(CheckNetworkEditable, tables.LinkAction): @@ -74,6 +82,13 @@ class EditNetwork(CheckNetworkEditable, tables.LinkAction): verbose_name = _("Edit Network") url = "horizon:project:networks:update" classes = ("ajax-modal", "btn-edit") + policy_rules = (("network", "update_network"),) + + 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} class CreateSubnet(CheckNetworkEditable, tables.LinkAction): @@ -81,6 +96,13 @@ class CreateSubnet(CheckNetworkEditable, tables.LinkAction): verbose_name = _("Add Subnet") url = "horizon:project:networks:addsubnet" classes = ("ajax-modal", "btn-create") + policy_rules = (("network", "create_subnet"),) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"network:project_id": project_id} def get_subnets(network): diff --git a/openstack_dashboard/dashboards/project/routers/extensions/routerrules/tables.py b/openstack_dashboard/dashboards/project/routers/extensions/routerrules/tables.py index 903e5b8d41..de6a479fe9 100644 --- a/openstack_dashboard/dashboards/project/routers/extensions/routerrules/tables.py +++ b/openstack_dashboard/dashboards/project/routers/extensions/routerrules/tables.py @@ -32,6 +32,13 @@ class AddRouterRule(tables.LinkAction): verbose_name = _("Add Router Rule") url = "horizon:project:routers:addrouterrule" classes = ("ajax-modal", "btn-create") + policy_rules = (("network", "update_router"),) + + 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, datum=None): router_id = self.table.kwargs['router_id'] @@ -42,6 +49,13 @@ class RemoveRouterRule(tables.DeleteAction): data_type_singular = _("Router Rule") data_type_plural = _("Router Rules") failure_url = 'horizon:project:routers:detail' + policy_rules = (("network", "update_router"),) + + 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 delete(self, request, obj_id): router_id = self.table.kwargs['router_id'] diff --git a/openstack_dashboard/dashboards/project/routers/ports/tables.py b/openstack_dashboard/dashboards/project/routers/ports/tables.py index 44e62b711a..9370f6330a 100644 --- a/openstack_dashboard/dashboards/project/routers/ports/tables.py +++ b/openstack_dashboard/dashboards/project/routers/ports/tables.py @@ -42,6 +42,13 @@ class AddInterface(tables.LinkAction): verbose_name = _("Add Interface") url = "horizon:project:routers:addinterface" classes = ("ajax-modal", "btn-create") + policy_rules = (("network", "add_router_interface"),) + + 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, datum=None): router_id = self.table.kwargs['router_id'] @@ -52,6 +59,13 @@ class RemoveInterface(tables.DeleteAction): data_type_singular = _("Interface") data_type_plural = _("Interfaces") failure_url = 'horizon:project:routers:detail' + policy_rules = (("network", "remove_router_interface"),) + + 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 delete(self, request, obj_id): try: diff --git a/openstack_dashboard/dashboards/project/routers/tables.py b/openstack_dashboard/dashboards/project/routers/tables.py index eb7d764ae8..2efd3639f1 100644 --- a/openstack_dashboard/dashboards/project/routers/tables.py +++ b/openstack_dashboard/dashboards/project/routers/tables.py @@ -33,6 +33,13 @@ class DeleteRouter(tables.DeleteAction): data_type_singular = _("Router") data_type_plural = _("Routers") redirect_url = "horizon:project:routers:index" + policy_rules = (("network", "delete_router"),) + + 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 delete(self, request, obj_id): obj = self.table.get_object_by_id(obj_id) @@ -59,6 +66,7 @@ class CreateRouter(tables.LinkAction): verbose_name = _("Create Router") url = "horizon:project:routers:create" classes = ("ajax-modal", "btn-create") + policy_rules = (("network", "create_router"),) class SetGateway(tables.LinkAction): @@ -66,6 +74,13 @@ class SetGateway(tables.LinkAction): verbose_name = _("Set Gateway") url = "horizon:project:routers:setgateway" classes = ("ajax-modal", "btn-camera") + policy_rules = (("network", "update_router"),) + + 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, datum=None): if datum.external_gateway_info: @@ -81,6 +96,13 @@ class ClearGateway(tables.BatchAction): data_type_plural = _("Gateways") classes = ('btn-danger', 'btn-cleargateway') redirect_url = "horizon:project:routers:index" + policy_rules = (("network", "update_router"),) + + 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 action(self, request, obj_id): obj = self.table.get_object_by_id(obj_id) diff --git a/openstack_dashboard/dashboards/project/vpn/tables.py b/openstack_dashboard/dashboards/project/vpn/tables.py index b3884428c4..0c60fbf9ef 100644 --- a/openstack_dashboard/dashboards/project/vpn/tables.py +++ b/openstack_dashboard/dashboards/project/vpn/tables.py @@ -33,6 +33,7 @@ class AddIKEPolicyLink(tables.LinkAction): verbose_name = _("Add IKE Policy") url = "horizon:project:vpn:addikepolicy" classes = ("ajax-modal", "btn-addikepolicy",) + policy_rules = (("network", "create_ikepolicy"),) class AddIPSecPolicyLink(tables.LinkAction): @@ -40,6 +41,7 @@ class AddIPSecPolicyLink(tables.LinkAction): verbose_name = _("Add IPSec Policy") url = "horizon:project:vpn:addipsecpolicy" classes = ("ajax-modal", "btn-addipsecpolicy",) + policy_rules = (("network", "create_ipsecpolicy"),) class AddVPNServiceLink(tables.LinkAction): @@ -47,6 +49,7 @@ class AddVPNServiceLink(tables.LinkAction): verbose_name = _("Add VPN Service") url = "horizon:project:vpn:addvpnservice" classes = ("ajax-modal", "btn-addvpnservice",) + policy_rules = (("network", "create_vpnservice"),) class AddIPSecSiteConnectionLink(tables.LinkAction): @@ -54,6 +57,7 @@ class AddIPSecSiteConnectionLink(tables.LinkAction): verbose_name = _("Add IPSec Site Connection") url = "horizon:project:vpn:addipsecsiteconnection" classes = ("ajax-modal", "btn-addipsecsiteconnection",) + policy_rules = (("network", "create_ipsec_site_connection"),) class DeleteVPNServiceLink(tables.DeleteAction): @@ -62,6 +66,7 @@ class DeleteVPNServiceLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("VPN Service") data_type_plural = _("VPN Services") + policy_rules = (("network", "delete_vpnservice"),) def allowed(self, request, datum=None): if datum and datum.ipsecsiteconns: @@ -75,6 +80,7 @@ class DeleteIKEPolicyLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("IKE Policy") data_type_plural = _("IKE Policies") + policy_rules = (("network", "delete_ikepolicy"),) def allowed(self, request, datum=None): if datum and datum.ipsecsiteconns: @@ -88,6 +94,7 @@ class DeleteIPSecPolicyLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("IPSec Policy") data_type_plural = _("IPSec Policies") + policy_rules = (("network", "delete_ipsecpolicy"),) def allowed(self, request, datum=None): if datum and datum.ipsecsiteconns: @@ -101,12 +108,14 @@ class DeleteIPSecSiteConnectionLink(tables.DeleteAction): action_past = _("Scheduled deletion of %(data_type)s") data_type_singular = _("IPSec Site Connection") data_type_plural = _("IPSec Site Connections") + policy_rules = (("network", "delete_ipsec_site_connection"),) class UpdateVPNServiceLink(tables.LinkAction): name = "update_vpnservice" verbose_name = _("Edit VPN Service") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_vpnservice"),) def get_link_url(self, vpnservice): return reverse("horizon:project:vpn:update_vpnservice", @@ -122,6 +131,7 @@ class UpdateIKEPolicyLink(tables.LinkAction): name = "updateikepolicy" verbose_name = _("Edit IKE Policy") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_ikepolicy"),) def get_link_url(self, ikepolicy): return reverse("horizon:project:vpn:update_ikepolicy", @@ -135,6 +145,7 @@ class UpdateIPSecPolicyLink(tables.LinkAction): name = "updateipsecpolicy" verbose_name = _("Edit IPSec Policy") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_ipsecpolicy"),) def get_link_url(self, ipsecpolicy): return reverse("horizon:project:vpn:update_ipsecpolicy", @@ -148,6 +159,7 @@ class UpdateIPSecSiteConnectionLink(tables.LinkAction): name = "updateipsecsiteconnection" verbose_name = _("Edit Connection") classes = ("ajax-modal", "btn-update",) + policy_rules = (("network", "update_ipsec_site_connection"),) def get_link_url(self, ipsecsiteconnection): return reverse("horizon:project:vpn:update_ipsecsiteconnection", diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example index acf1fd44a6..800c0174b0 100644 --- a/openstack_dashboard/local/local_settings.py.example +++ b/openstack_dashboard/local/local_settings.py.example @@ -257,6 +257,7 @@ TIME_ZONE = "UTC" # 'volume': 'cinder_policy.json', # 'image': 'glance_policy.json', # 'orchestration': 'heat_policy.json', +# 'network': 'neutron_policy.json', #} # Trove user and database extension support. By default support for diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py index 1d305d28fd..f9e8dbe463 100644 --- a/openstack_dashboard/settings.py +++ b/openstack_dashboard/settings.py @@ -207,6 +207,7 @@ POLICY_FILES = { 'volume': 'cinder_policy.json', 'image': 'glance_policy.json', 'orchestration': 'heat_policy.json', + 'network': 'neutron_policy.json', } SECRET_KEY = None