From 39a1828ddfd67f23d32f176d043274d6561677c3 Mon Sep 17 00:00:00 2001 From: Takamasa Takenaka Date: Wed, 17 Nov 2021 15:10:17 -0300 Subject: [PATCH] Use policy_rules for user role assignment and group tabs This patch is ported from upstream: https://review.opendev.org/c/openstack/horizon/+/775014 https://review.opendev.org/c/openstack/horizon/+/783307 Test Plan: PASS: Confirm tabs "Role assignments" and "Groups" are invisible for non admin user. PASS: Confirm tabs "Role assignments" and "Groups" are visible for admin user PASS: All pass horizon.test.unit.tabs.test_tabs unit test Partial-Bug: 1951302 Signed-off-by: Takamasa Takenaka Change-Id: I073f526ad08017ef6f23b738a1da6d5e2fcf757a --- ...-user-role-assignment-and-group-tabs.patch | 159 ++++++++++++++++++ .../centos/python-django-horizon.spec | 1 + 2 files changed, 160 insertions(+) create mode 100644 openstack/python-horizon/centos/files/0002-Use-policy_rules-for-user-role-assignment-and-group-tabs.patch diff --git a/openstack/python-horizon/centos/files/0002-Use-policy_rules-for-user-role-assignment-and-group-tabs.patch b/openstack/python-horizon/centos/files/0002-Use-policy_rules-for-user-role-assignment-and-group-tabs.patch new file mode 100644 index 00000000..e0ece7a7 --- /dev/null +++ b/openstack/python-horizon/centos/files/0002-Use-policy_rules-for-user-role-assignment-and-group-tabs.patch @@ -0,0 +1,159 @@ +From 245e2b9b2f18e316410067804c48ae63b0f20320 Mon Sep 17 00:00:00 2001 +From: Takamsa Takenaka +Date: Fri, 12 Nov 2021 12:17:19 -0500 +Subject: [PATCH] Use policy_rules for user role assignment and group tabs + +This patch is ported from the following patches: +- https://review.opendev.org/c/openstack/horizon/+/775014 +- https://review.opendev.org/c/openstack/horizon/+/783307 + +Signed-off-by: Takamsa Takenaka +--- + horizon/tabs/base.py | 14 +++++++-- + horizon/test/unit/tabs/test_tabs.py | 30 +++++++++++++++++-- + .../dashboards/identity/users/tabs.py | 2 ++ + 3 files changed, 40 insertions(+), 6 deletions(-) + +diff --git a/horizon/tabs/base.py b/horizon/tabs/base.py +index 67847bf10..108d35eeb 100644 +--- a/horizon/tabs/base.py ++++ b/horizon/tabs/base.py +@@ -26,6 +26,7 @@ from django.utils import module_loading + + from horizon import exceptions + from horizon.utils import html ++from horizon.utils import settings as utils_settings + + LOG = logging.getLogger(__name__) + +@@ -310,8 +311,9 @@ class Tab(html.HTMLElement): + preload = True + _active = None + permissions = [] ++ policy_rules = None + +- def __init__(self, tab_group, request=None): ++ def __init__(self, tab_group, request=None, policy_rules=None): + super(Tab, self).__init__() + # Priority: constructor, class-defined, fallback + if not self.name: +@@ -325,6 +327,7 @@ class Tab(html.HTMLElement): + self._allowed = self.allowed(request) and ( + self._has_permissions(request)) + self._enabled = self.enabled(request) ++ self.policy_rules = policy_rules or [] + + def __repr__(self): + return "<%s: %s>" % (self.__class__.__name__, self.slug) +@@ -442,9 +445,14 @@ class Tab(html.HTMLElement): + + Tab instances can override this method to specify conditions under + which this tab should not be shown at all by returning ``False``. +- +- The default behavior is to return ``True`` for all cases. + """ ++ if not self.policy_rules: ++ return True ++ ++ policy_check = utils_settings.import_setting("POLICY_CHECK_FUNCTION") ++ ++ if policy_check: ++ return policy_check(self.policy_rules, request) + return True + + def post(self, request, *args, **kwargs): +diff --git a/horizon/test/unit/tabs/test_tabs.py b/horizon/test/unit/tabs/test_tabs.py +index 6c50e401b..358bf77c9 100644 +--- a/horizon/test/unit/tabs/test_tabs.py ++++ b/horizon/test/unit/tabs/test_tabs.py +@@ -67,9 +67,16 @@ class TabDisallowed(BaseTestTab): + return False + + ++class TabWithPolicy(BaseTestTab): ++ slug = "tab_with_policy" ++ name = "tab only visible to admin" ++ template_name = "_tab.html" ++ policy_rules = (("compute", "role:admin"),) ++ ++ + class Group(horizon_tabs.TabGroup): + slug = "tab_group" +- tabs = (TabOne, TabDelayed, TabDisabled, TabDisallowed) ++ tabs = (TabOne, TabDelayed, TabDisabled, TabDisallowed, TabWithPolicy) + sticky = True + + def tabs_not_available(self): +@@ -128,15 +135,19 @@ class TabWithTableView(horizon_tabs.TabbedTableView): + + + class TabTests(test.TestCase): ++ @override_settings(POLICY_CHECK_FUNCTION=lambda *args: True) + def test_tab_group_basics(self): + tg = Group(self.request) + + # Test tab instantiation/attachment to tab group, and get_tabs method + tabs = tg.get_tabs() + # "tab_disallowed" should NOT be in this list. ++ # "tab_with_policy" should be present, since our policy check ++ # always passes + self.assertQuerysetEqual(tabs, ['', + '', +- '']) ++ '', ++ '']) + # Test get_id + self.assertEqual("tab_group", tg.get_id()) + # get_default_classes +@@ -151,6 +162,19 @@ class TabTests(test.TestCase): + # Test get_selected_tab is None w/o GET input + self.assertIsNone(tg.get_selected_tab()) + ++ @override_settings(POLICY_CHECK_FUNCTION=lambda *args: False) ++ def test_failed_tab_policy(self): ++ tg = Group(self.request) ++ ++ # Test tab instantiation/attachment to tab group, and get_tabs method ++ tabs = tg.get_tabs() ++ # "tab_disallowed" should NOT be in this list, it's not allowed ++ # "tab_with_policy" should also not be present as its ++ # policy check failed ++ self.assertQuerysetEqual(tabs, ['', ++ '', ++ '']) ++ + @test.update_settings( + HORIZON_CONFIG={'extra_tabs': { + 'horizon.test.unit.tabs.test_tabs.GroupWithConfig': ( +@@ -253,7 +277,7 @@ class TabTests(test.TestCase): + # tab group + output = tg.render() + res = http.HttpResponse(output.strip()) +- self.assertContains(res, "