diff --git a/doc/source/configuration/settings.rst b/doc/source/configuration/settings.rst index 0d577c1bcb..7c84f656f7 100644 --- a/doc/source/configuration/settings.rst +++ b/doc/source/configuration/settings.rst @@ -2656,3 +2656,16 @@ generated ``kubeconfig`` file. .. seealso:: `KUBECONFIG_ENABLED`_ to enable the ``kubeconfig`` file generation. + + +SYSTEM_SCOPE_SERVICES +--------------------- +.. versionadded:: 21.1.0(Yoga) + +Default: ``[]`` + +A list of names of services for which the system scope token should be used. +If empty, system scope will be removed from the context switching menu. If not +empty, the context switching menu will show a "system scope" option, and the +admin panels for the services listed will be moved to that context, no longer +showing up in the project context. diff --git a/openstack_dashboard/dashboards/admin/aggregates/panel.py b/openstack_dashboard/dashboards/admin/aggregates/panel.py index 4d553ea875..315bef2e05 100644 --- a/openstack_dashboard/dashboards/admin/aggregates/panel.py +++ b/openstack_dashboard/dashboards/admin/aggregates/panel.py @@ -12,6 +12,7 @@ import logging +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -25,3 +26,9 @@ class Aggregates(horizon.Panel): slug = 'aggregates' policy_rules = (("compute", "compute_extension:aggregates"),) permissions = ('openstack.services.compute',) + + def allowed(self, context): + if (('compute' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/defaults/panel.py b/openstack_dashboard/dashboards/admin/defaults/panel.py index 9dfee0267d..2d95973881 100644 --- a/openstack_dashboard/dashboards/admin/defaults/panel.py +++ b/openstack_dashboard/dashboards/admin/defaults/panel.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -22,3 +23,9 @@ class Defaults(horizon.Panel): slug = 'defaults' policy_rules = (("compute", "context_is_admin"), ("volume", "context_is_admin"),) + + def allowed(self, context): + if (('compute' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/flavors/panel.py b/openstack_dashboard/dashboards/admin/flavors/panel.py index 772e73aed9..3cf3203b43 100644 --- a/openstack_dashboard/dashboards/admin/flavors/panel.py +++ b/openstack_dashboard/dashboards/admin/flavors/panel.py @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -26,3 +27,9 @@ class Flavors(horizon.Panel): slug = 'flavors' permissions = ('openstack.services.compute',) policy_rules = (("compute", "context_is_admin"),) + + def allowed(self, context): + if (('compute' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/floating_ips/panel.py b/openstack_dashboard/dashboards/admin/floating_ips/panel.py index 9d117b0773..3a8e5b80aa 100644 --- a/openstack_dashboard/dashboards/admin/floating_ips/panel.py +++ b/openstack_dashboard/dashboards/admin/floating_ips/panel.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -30,3 +31,9 @@ class AdminFloatingIps(horizon.Panel): def can_register(): return setting_utils.get_dict_config( 'OPENSTACK_NEUTRON_NETWORK', 'enable_router') + + def allowed(self, context): + if (('network' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/hypervisors/panel.py b/openstack_dashboard/dashboards/admin/hypervisors/panel.py index 5b29a5ec41..d02097bf91 100644 --- a/openstack_dashboard/dashboards/admin/hypervisors/panel.py +++ b/openstack_dashboard/dashboards/admin/hypervisors/panel.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -22,3 +23,9 @@ class Hypervisors(horizon.Panel): slug = 'hypervisors' permissions = ('openstack.services.compute',) policy_rules = (("compute", "os_compute_api:os-hypervisors"),) + + def allowed(self, context): + if (('compute' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/images/panel.py b/openstack_dashboard/dashboards/admin/images/panel.py index 2f6505ba97..b6f9a55ff0 100644 --- a/openstack_dashboard/dashboards/admin/images/panel.py +++ b/openstack_dashboard/dashboards/admin/images/panel.py @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -27,3 +28,9 @@ class Images(horizon.Panel): permissions = ('openstack.services.image',) policy_rules = ((("image", "context_is_admin"), ("image", "get_images")),) + + def allowed(self, context): + if (('compute' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/info/panel.py b/openstack_dashboard/dashboards/admin/info/panel.py index d314a8fd4e..9aa04aff24 100644 --- a/openstack_dashboard/dashboards/admin/info/panel.py +++ b/openstack_dashboard/dashboards/admin/info/panel.py @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -27,3 +28,9 @@ class Info(horizon.Panel): policy_rules = (("compute", "context_is_admin"), ("volume", "context_is_admin"), ("network", "context_is_admin"),) + + def allowed(self, context): + if (('compute' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/instances/panel.py b/openstack_dashboard/dashboards/admin/instances/panel.py index b0eb711694..0a4c21b977 100644 --- a/openstack_dashboard/dashboards/admin/instances/panel.py +++ b/openstack_dashboard/dashboards/admin/instances/panel.py @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -27,3 +28,9 @@ class Instances(horizon.Panel): permissions = ('openstack.services.compute',) policy_rules = ((("compute", "context_is_admin"), ("compute", "os_compute_api:servers:detail")),) + + def allowed(self, context): + if (('compute' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/metadata_defs/panel.py b/openstack_dashboard/dashboards/admin/metadata_defs/panel.py index 3baa4b1e17..32d8f78926 100644 --- a/openstack_dashboard/dashboards/admin/metadata_defs/panel.py +++ b/openstack_dashboard/dashboards/admin/metadata_defs/panel.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -30,3 +31,9 @@ class MetadataDefinitions(horizon.Panel): @staticmethod def can_register(): return glance.VERSIONS.active >= 2 + + def allowed(self, context): + if (('image' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/networks/panel.py b/openstack_dashboard/dashboards/admin/networks/panel.py index f1ce7638ee..821ce2101b 100644 --- a/openstack_dashboard/dashboards/admin/networks/panel.py +++ b/openstack_dashboard/dashboards/admin/networks/panel.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -22,3 +23,9 @@ class Networks(horizon.Panel): slug = 'networks' permissions = ('openstack.services.network',) policy_rules = (("network", "context_is_admin"),) + + def allowed(self, context): + if (('network' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/overview/panel.py b/openstack_dashboard/dashboards/admin/overview/panel.py index afd55aa397..b7f67a6733 100644 --- a/openstack_dashboard/dashboards/admin/overview/panel.py +++ b/openstack_dashboard/dashboards/admin/overview/panel.py @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -30,5 +31,11 @@ class Overview(horizon.Panel): ('compute', 'context_is_admin')),) permissions = ('openstack.services.compute',) + def allowed(self, context): + if (('compute' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) + dashboard.Admin.register(Overview) diff --git a/openstack_dashboard/dashboards/admin/rbac_policies/panel.py b/openstack_dashboard/dashboards/admin/rbac_policies/panel.py index 18a7a6f57f..b3198db399 100644 --- a/openstack_dashboard/dashboards/admin/rbac_policies/panel.py +++ b/openstack_dashboard/dashboards/admin/rbac_policies/panel.py @@ -12,6 +12,7 @@ import logging +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -29,6 +30,9 @@ class RBACPolicies(horizon.Panel): policy_rules = (("network", "context_is_admin"),) def allowed(self, context): + if (('network' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False request = context['request'] try: return ( diff --git a/openstack_dashboard/dashboards/admin/routers/panel.py b/openstack_dashboard/dashboards/admin/routers/panel.py index 3dc5444b1a..304737e24f 100644 --- a/openstack_dashboard/dashboards/admin/routers/panel.py +++ b/openstack_dashboard/dashboards/admin/routers/panel.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -29,3 +30,9 @@ class Routers(horizon.Panel): def can_register(): return setting_utils.get_dict_config( 'OPENSTACK_NEUTRON_NETWORK', 'enable_router') + + def allowed(self, context): + if (('network' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().allowed(context) diff --git a/openstack_dashboard/dashboards/admin/trunks/panel.py b/openstack_dashboard/dashboards/admin/trunks/panel.py index 8d5f702a02..b312c72f59 100644 --- a/openstack_dashboard/dashboards/admin/trunks/panel.py +++ b/openstack_dashboard/dashboards/admin/trunks/panel.py @@ -14,6 +14,7 @@ import logging +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -30,6 +31,9 @@ class Trunks(horizon.Panel): policy_rules = (("trunk", "context_is_admin"),) def allowed(self, context): + if (('network' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False request = context['request'] try: return ( diff --git a/openstack_dashboard/dashboards/identity/dashboard.py b/openstack_dashboard/dashboards/identity/dashboard.py index 63d61bf56f..9e2893fffd 100644 --- a/openstack_dashboard/dashboards/identity/dashboard.py +++ b/openstack_dashboard/dashboards/identity/dashboard.py @@ -13,6 +13,7 @@ # under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ import horizon @@ -22,5 +23,11 @@ class Identity(horizon.Dashboard): name = _("Identity") slug = "identity" + def can_access(self, context): + if (('identity' in settings.SYSTEM_SCOPE_SERVICES) != + bool(context['request'].user.system_scoped)): + return False + return super().can_access(context) + horizon.register(Identity) diff --git a/openstack_dashboard/defaults.py b/openstack_dashboard/defaults.py index 30d7785b89..4ed8d1fc58 100644 --- a/openstack_dashboard/defaults.py +++ b/openstack_dashboard/defaults.py @@ -551,3 +551,8 @@ REST_API_ADDITIONAL_SETTINGS = [] KUBECONFIG_ENABLED = False KUBECONFIG_KUBERNETES_URL = "" KUBECONFIG_CERTIFICATE_AUTHORITY_DATA = "" + + +# Services may require a System Scope token for certain operations. This +# settings enables the use of the system scope token on per-service basis. +SYSTEM_SCOPE_SERVICES = [] diff --git a/openstack_dashboard/templates/header/_context_selection.html b/openstack_dashboard/templates/header/_context_selection.html index e75b2962f4..080b8a3818 100644 --- a/openstack_dashboard/templates/header/_context_selection.html +++ b/openstack_dashboard/templates/header/_context_selection.html @@ -31,8 +31,8 @@ {% endif %} - {% is_system_user as system_user %} - {% if system_user %} + {% show_systems as system_scope_enabled %} + {% if system_scope_enabled %}
  • {% show_system_list %}
  • diff --git a/openstack_dashboard/templatetags/context_selection.py b/openstack_dashboard/templatetags/context_selection.py index 08cf1b903c..afcdaa933f 100644 --- a/openstack_dashboard/templatetags/context_selection.py +++ b/openstack_dashboard/templatetags/context_selection.py @@ -42,7 +42,9 @@ def is_multidomain(): @register.simple_tag(takes_context=True) -def is_system_user(context): +def show_systems(context): + if not settings.SYSTEM_SCOPE_SERVICES: + return False try: request = context['request'] except KeyError: diff --git a/releasenotes/notes/feature-system-scope-a88a07b7f414b3d6.yaml b/releasenotes/notes/feature-system-scope-a88a07b7f414b3d6.yaml new file mode 100644 index 0000000000..dc02a760ed --- /dev/null +++ b/releasenotes/notes/feature-system-scope-a88a07b7f414b3d6.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Horizon can now use a system scope token when performing admin operations. + To enable that, a new setting, SYSTEM_SCOPE_SERVICES, has to list the + OpenStack services for which this feature is to be enabled. When that + setting is not empty, a new option, "system scope" will appear in the + context switching menu, and the panels for the listed services will be + moved into that context in the main menu.