Add Allowed Address Pair/Delete buttons are only visible to admin

Initially In Queens, when logging into Horizon in context
of a _member_ in any project, the "Add Allowed Address Pair"
and "Delete" buttons in "Network" -> "Networks" ->
<select a network> -> "Ports" -> <select a port> ->
"Allowed Address Pairs" are not visible.

When accessing the same panel in context of a project where the user
has the admin role, the "Add" and "Delete" button is visible and
functional.

This patch fixes the above issue by updating the policy_target.

Change-Id: Ia7db6c2881c72580a156eb97b11bb51f295cfbce
Closes-Bug: #1794421
(cherry picked from commit 87b57dfe71)
This commit is contained in:
Shilpa Devharakar 2019-08-22 14:29:41 +05:30
parent 8f989ac62c
commit 83855762fb
3 changed files with 186 additions and 0 deletions

View File

@ -15,6 +15,7 @@
import collections
from django.test.utils import override_settings
from django.urls import reverse
import mock
@ -613,3 +614,50 @@ class NetworkPortTests(test.BaseAdminViewTests):
self._check_is_extension_supported(
{'network-ip-availability': 1,
'mac-learning': 1})
@override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
@test.create_mocks({api.neutron: ('port_get',
'network_get',
'is_extension_supported')})
def test_add_allowed_address_pair_button_shown(self):
port = self.ports.first()
url = reverse('horizon:project:networks:ports:addallowedaddresspairs',
args=[port.id])
classes = 'btn data-table-action btn-default ajax-modal'
link_name = "Add Allowed Address Pair"
expected_string = \
'<a id="allowed_address_pairs__action_AddAllowedAddressPair" ' \
'class="%s" href="%s" title="Add Allowed Address Pair">' \
'<span class="fa fa-plus"></span> %s</a>' \
% (classes, url, link_name)
res = self.client.get(reverse('horizon:project:networks:ports:detail',
args=[port.id]))
self.assertTemplateUsed(res, 'horizon/common/_detail_tab_group.html')
self.assertIn(expected_string, res.context_data['tab_group'].render())
@override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
@test.create_mocks({api.neutron: ('port_get',
'network_get',
'port_update',
'is_extension_supported')})
def test_delete_address_pair_button_shown(self):
port = self.ports.first()
classes = 'data-table-action btn-danger btn'
expected_string = \
'<button data-batch-action="true" ' \
'id="allowed_address_pairs__action_delete" ' \
'class="%s" name="action" help_text="This action cannot be ' \
'undone." type="submit" value="allowed_address_pairs__delete">' \
'<span class="fa fa-trash"></span>' \
' Delete</button>' \
% (classes)
res = self.client.get(reverse(
'horizon:project:networks:ports:detail', args=[port.id]))
self.assertTemplateUsed(res, 'horizon/common/_detail_tab_group.html')
self.assertIn(expected_string, res.context_data['tab_group'].render())

View File

@ -39,6 +39,14 @@ class AddAllowedAddressPair(policy.PolicyTargetMixin, tables.LinkAction):
("network", "update_port:allowed_address_pairs"),
)
def get_policy_target(self, request, datum=None):
policy_target = super(AddAllowedAddressPair, self).\
get_policy_target(request, datum)
policy_target["network:tenant_id"] = (
self.table.kwargs['port'].tenant_id)
return policy_target
def get_link_url(self, port=None):
if port:
return reverse(self.url, args=(port.id,))
@ -68,6 +76,14 @@ class DeleteAllowedAddressPair(tables.DeleteAction):
("network", "update_port:allowed_address_pairs"),
)
def get_policy_target(self, request, datum=None):
policy_target = super(DeleteAllowedAddressPair, self).\
get_policy_target(request, datum)
policy_target["network:tenant_id"] = (
self.table.kwargs['port'].tenant_id)
return policy_target
def delete(self, request, ip_address):
try:
port_id = self.table.kwargs['port_id']

View File

@ -16,10 +16,13 @@
import collections
import copy
from django.test.utils import override_settings
from django.urls import reverse
import mock
from openstack_auth import utils as auth_utils
from horizon.workflows import views
from openstack_dashboard import api
@ -288,6 +291,66 @@ class NetworkPortTests(test.TestCase):
self.mock_network_get.assert_called_once_with(
test.IsHttpRequest(), network.id)
@override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
@test.create_mocks({api.neutron: ('port_get',
'network_get',
'is_extension_supported')})
def test_add_allowed_address_pair_button_shown_to_network_owner(self):
port = self.ports.first()
url = reverse('horizon:project:networks:ports:addallowedaddresspairs',
args=[port.id])
classes = 'btn data-table-action btn-default ajax-modal'
link_name = "Add Allowed Address Pair"
expected_string = \
'<a id="allowed_address_pairs__action_AddAllowedAddressPair" ' \
'class="%s" href="%s" title="Add Allowed Address Pair">' \
'<span class="fa fa-plus"></span> %s</a>' \
% (classes, url, link_name)
res = self.client.get(reverse('horizon:project:networks:ports:detail',
args=[port.id]))
self.assertTemplateUsed(res, 'horizon/common/_detail_tab_group.html')
self.assertIn(expected_string, res.context_data['tab_group'].render())
@override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
@test.create_mocks({api.neutron: ('port_get',
'network_get',
'is_extension_supported')})
def test_add_allowed_address_pair_button_disabled_to_other_tenant(self):
# Current user tenant_id is 1 so select port whose tenant_id is
# other than 1 for checking "Add Allowed Address Pair" button is not
# displayed on the screen.
user = auth_utils.get_user(self.request)
# select port such that tenant_id is different from user's tenant_id.
port = [p for p in self.ports.list()
if p.tenant_id != user.tenant_id][0]
self._stub_is_extension_supported(
{'allowed-address-pairs': False, 'mac-learning': False})
with mock.patch('openstack_auth.utils.get_user', return_value=user):
url = reverse(
'horizon:project:networks:ports:addallowedaddresspairs',
args=[port.id])
classes = 'btn data-table-action btn-default ajax-modal'
link_name = "Add Allowed Address Pair"
expected_string = \
'<a id="allowed_address_pairs__action_AddAllowedAddressPair" ' \
'class="%s" href="%s" title="Add Allowed Address Pair">' \
'<span class="fa fa-plus"></span> %s</a>' \
% (classes, url, link_name)
res = self.client.get(reverse(
'horizon:project:networks:ports:detail', args=[port.id]))
self.assertNotIn(
expected_string, res.context_data['tab_group'].render())
@test.create_mocks({api.neutron: ('port_get',
'port_update')})
def test_port_add_allowed_address_pair(self):
@ -345,6 +408,65 @@ class NetworkPortTests(test.TestCase):
self.assertFormErrors(res, 1)
self.assertContains(res, "Incorrect format for IP address")
@override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
@test.create_mocks({api.neutron: ('port_get',
'network_get',
'port_update',
'is_extension_supported')})
def test_delete_address_pair_button_shown_to_network_owner(self):
port = self.ports.first()
classes = 'data-table-action btn-danger btn'
expected_string = \
'<button data-batch-action="true" ' \
'id="allowed_address_pairs__action_delete" ' \
'class="%s" name="action" help_text="This action cannot be ' \
'undone." type="submit" value="allowed_address_pairs__delete">' \
'<span class="fa fa-trash"></span>' \
' Delete</button>' \
% (classes)
res = self.client.get(reverse(
'horizon:project:networks:ports:detail', args=[port.id]))
self.assertTemplateUsed(res, 'horizon/common/_detail_tab_group.html')
self.assertIn(expected_string, res.context_data['tab_group'].render())
@override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
@test.create_mocks({api.neutron: ('port_get',
'network_get',
'is_extension_supported')})
def test_delete_address_pair_button_disabled_to_other_tenant(self):
# Current user tenant_id is 1 so select port whose tenant_id is
# other than 1 for checking "Delete Allowed Address Pair" button is
# not displayed on the screen.
user = auth_utils.get_user(self.request)
# select port such that tenant_id is different from user's tenant_id.
port = [p for p in self.ports.list()
if p.tenant_id != user.tenant_id][0]
self._stub_is_extension_supported(
{'allowed-address-pairs': False, 'mac-learning': False})
with mock.patch('openstack_auth.utils.get_user', return_value=user):
classes = 'data-table-action btn-danger btn'
expected_string = \
'<button data-batch-action="true" ' \
'id="allowed_address_pairs__action_delete" ' \
'class="%s" name="action" help_text="This action cannot be ' \
'undone." type="submit" ' \
'value="allowed_address_pairs__delete">' \
'<span class="fa fa-trash"></span>' \
' Delete</button>' % (classes)
res = self.client.get(reverse(
'horizon:project:networks:ports:detail', args=[port.id]))
self.assertNotIn(
expected_string, res.context_data['tab_group'].render())
@test.create_mocks({api.neutron: ('port_get',
'is_extension_supported',
'port_update')})