From 083bf79107588fedda6df2e846c916e362d95f15 Mon Sep 17 00:00:00 2001 From: Andrew Bogott Date: Tue, 4 May 2021 21:06:13 -0500 Subject: [PATCH] Add a ton of policy checks In some cases there isn't a perfect relationship between UI widgets and API endpoints but this should be pretty close. Story: 2008897 Task: 42463 Change-Id: I8966035fe8e754bf31367535b08dc46856f5afac --- ...port-custom-policies-b7a47657ff92bacb.yaml | 6 ++++ .../content/backup_strategies/tables.py | 3 ++ .../content/database_backups/tables.py | 5 ++++ .../content/database_clusters/tables.py | 9 ++++++ .../content/database_configurations/tables.py | 4 +++ .../content/databases/logs/tables.py | 5 ++++ trove_dashboard/content/databases/tables.py | 30 +++++++++++++++++++ 7 files changed, 62 insertions(+) create mode 100644 releasenotes/notes/support-custom-policies-b7a47657ff92bacb.yaml diff --git a/releasenotes/notes/support-custom-policies-b7a47657ff92bacb.yaml b/releasenotes/notes/support-custom-policies-b7a47657ff92bacb.yaml new file mode 100644 index 00000000..d2b17c9f --- /dev/null +++ b/releasenotes/notes/support-custom-policies-b7a47657ff92bacb.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Dashboard features now check the 'databases' policy file. If the + corresponding API is not available to the current user, this UI element + will not display or will appear disabled. diff --git a/trove_dashboard/content/backup_strategies/tables.py b/trove_dashboard/content/backup_strategies/tables.py index b0b21e3b..0e49efff 100644 --- a/trove_dashboard/content/backup_strategies/tables.py +++ b/trove_dashboard/content/backup_strategies/tables.py @@ -26,9 +26,12 @@ class CreateBackupStrategy(tables.LinkAction): url = "horizon:project:backup_strategies:create" classes = ("ajax-modal", "btn-create") icon = "camera" + policy_rules = (("database", "backup_strategy:create"), ) class DeleteBackupStrategy(tables.DeleteAction): + policy_rules = (("database", "backup_strategy:delete"), ) + @staticmethod def action_present(count): return ngettext_lazy( diff --git a/trove_dashboard/content/database_backups/tables.py b/trove_dashboard/content/database_backups/tables.py index 1ec9edac..48d6d907 100644 --- a/trove_dashboard/content/database_backups/tables.py +++ b/trove_dashboard/content/database_backups/tables.py @@ -58,6 +58,7 @@ class LaunchLink(tables.LinkAction): url = "horizon:project:database_backups:create" classes = ("ajax-modal", "btn-create") icon = "camera" + policy_rules = (("database", "backup:create"), ) class RestoreLink(tables.LinkAction): @@ -66,6 +67,7 @@ class RestoreLink(tables.LinkAction): url = "horizon:project:databases:launch" classes = ("ajax-modal",) icon = "cloud-upload" + policy_rules = (("database", "backup:show"), ) def allowed(self, request, backup=None): return backup.status in ['COMPLETED', 'RESTORED'] @@ -80,6 +82,7 @@ class DownloadBackup(tables.LinkAction): verbose_name = _("Download Backup") url = 'horizon:project:containers:object_download' classes = ("btn-download",) + policy_rules = (("database", "backup:show"), ) def get_link_url(self, datum): ref = datum.locationRef.split('/') @@ -99,6 +102,8 @@ class DownloadBackup(tables.LinkAction): class DeleteBackup(tables.DeleteAction): + policy_rules = (("database", "backup:delete"), ) + @staticmethod def action_present(count): return ngettext_lazy( diff --git a/trove_dashboard/content/database_clusters/tables.py b/trove_dashboard/content/database_clusters/tables.py index 111259cd..0f14eff3 100644 --- a/trove_dashboard/content/database_clusters/tables.py +++ b/trove_dashboard/content/database_clusters/tables.py @@ -42,6 +42,7 @@ class DeleteCluster(tables.BatchAction): icon = "remove" classes = ('btn-danger',) help_text = _("Deleted cluster is not recoverable.") + policy_rules = (("database", "cluster:delete"), ) @staticmethod def action_present(count): @@ -69,12 +70,14 @@ class LaunchLink(tables.LinkAction): url = "horizon:project:database_clusters:launch" classes = ("btn-launch", "ajax-modal") icon = "cloud-upload" + policy_rules = (("database", "cluster:create"), ) class ClusterGrow(tables.LinkAction): name = "cluster_grow" verbose_name = _("Grow Cluster") url = "horizon:project:database_clusters:cluster_grow_details" + policy_rules = (("database", "cluster:action"), ) def allowed(self, request, cluster=None): if (cluster and cluster.task["name"] == 'NONE' and @@ -87,6 +90,7 @@ class ClusterShrink(tables.LinkAction): name = "cluster_shrink" verbose_name = _("Shrink Cluster") url = "horizon:project:database_clusters:cluster_shrink_details" + policy_rules = (("database", "cluster:action"), ) def allowed(self, request, cluster=None): if (cluster and cluster.task["name"] == 'NONE' and @@ -100,6 +104,7 @@ class ResetPassword(tables.LinkAction): verbose_name = _("Reset Root Password") url = "horizon:project:database_clusters:reset_password" classes = ("ajax-modal",) + policy_rules = (("database", "cluster:action"), ) def allowed(self, request, cluster=None): if (cluster and cluster.task["name"] == 'NONE' and @@ -241,6 +246,7 @@ class ClusterShrinkAction(tables.BatchAction): classes = ('btn-danger',) success_url = 'horizon:project:database_clusters:index' help_text = _("Shrinking a cluster is not recoverable.") + policy_rules = (("database", "cluster:delete"), ) @staticmethod def action_present(count): @@ -305,6 +311,7 @@ class ClusterGrowAddInstance(tables.LinkAction): verbose_name = _("Add Instance") url = "horizon:project:database_clusters:add_instance" classes = ("ajax-modal",) + policy_rules = (("database", "cluster:action"), ) def get_link_url(self): return urls.reverse( @@ -313,6 +320,7 @@ class ClusterGrowAddInstance(tables.LinkAction): class ClusterGrowRemoveInstance(tables.BatchAction): name = "cluster_grow_remove_instance" + policy_rules = (("database", "cluster:action"), ) @staticmethod def action_present(count): @@ -387,6 +395,7 @@ class ClusterGrowAction(tables.Action): verbose_name_plural = _("Grow Cluster") requires_input = False icon = "plus" + policy_rules = (("database", "cluster:action"), ) def handle(self, table, request, obj_ids): if not table.data: diff --git a/trove_dashboard/content/database_configurations/tables.py b/trove_dashboard/content/database_configurations/tables.py index 989becca..07dd92eb 100644 --- a/trove_dashboard/content/database_configurations/tables.py +++ b/trove_dashboard/content/database_configurations/tables.py @@ -33,11 +33,13 @@ class CreateConfiguration(tables.LinkAction): url = "horizon:project:database_configurations:create" classes = ('ajax-modal', ) icon = "plus" + policy_rules = (("database", "configuration:create"),) class DeleteConfiguration(tables.DeleteAction): data_type_singular = _("Configuration Group") data_type_plural = _("Configuration Groups") + policy_rules = (("database", "configuration:delete"),) @staticmethod def action_present(count): @@ -87,6 +89,7 @@ class AddParameter(tables.LinkAction): url = "horizon:project:database_configurations:add" classes = ('ajax-modal', ) icon = "plus" + policy_rules = (("database", "configuration:edit"),) def get_link_url(self, datum=None): configuration_id = self.table.kwargs['configuration_id'] @@ -98,6 +101,7 @@ class ApplyChanges(tables.Action): verbose_name = _("Apply Changes") verbose_name_plural = _("Apply Changes") icon = "pencil" + policy_rules = (("database", "configuration:edit"),) def __init__(self, **kwargs): super(ApplyChanges, self).__init__(**kwargs) diff --git a/trove_dashboard/content/databases/logs/tables.py b/trove_dashboard/content/databases/logs/tables.py index a1a6ccfe..024fe791 100644 --- a/trove_dashboard/content/databases/logs/tables.py +++ b/trove_dashboard/content/databases/logs/tables.py @@ -39,6 +39,7 @@ class PublishLog(tables.BatchAction): ) name = "publish_log" + policy_rules = (("database", "instance:guest_log_list"),) def action(self, request, obj_id): instance_id = self.table.kwargs['instance_id'] @@ -63,6 +64,7 @@ class DiscardLog(tables.BatchAction): ) name = "discard_log" + policy_rules = (("database", "instance:guest_log_list"),) def action(self, request, obj_id): instance_id = self.table.kwargs['instance_id'] @@ -87,6 +89,7 @@ class EnableLog(tables.BatchAction): ) name = "enable_log" + policy_rules = (("database", "instance:guest_log_list"),) def action(self, request, obj_id): instance_id = self.table.kwargs['instance_id'] @@ -111,6 +114,7 @@ class DisableLog(tables.BatchAction): ) name = "disable_log" + policy_rules = (("database", "instance:guest_log_list"),) def action(self, request, obj_id): instance_id = self.table.kwargs['instance_id'] @@ -126,6 +130,7 @@ class ViewLog(tables.LinkAction): name = "view_log" verbose_name = _("View Log") url = "horizon:project:databases:logs:log_contents" + policy_rules = (("database", "instance:guest_log_list"),) def get_link_url(self, datum): instance_id = self.table.kwargs['instance_id'] diff --git a/trove_dashboard/content/databases/tables.py b/trove_dashboard/content/databases/tables.py index e28ed75f..4dfcfa26 100644 --- a/trove_dashboard/content/databases/tables.py +++ b/trove_dashboard/content/databases/tables.py @@ -42,6 +42,7 @@ ACTIVE_STATES = ("ACTIVE", "HEALTHY",) class DeleteInstance(tables.DeleteAction): help_text = _("Deleted instances are not recoverable.") + policy_rules = (("database", "instance:delete"),) @staticmethod def action_present(count): @@ -66,6 +67,7 @@ class DeleteInstance(tables.DeleteAction): class RestartInstance(tables.BatchAction): help_text = _("Restarted instances will lose any data not" " saved in persistent storage.") + policy_rules = (("database", "instance:restart"),) @staticmethod def action_present(count): @@ -103,6 +105,7 @@ class DetachReplica(tables.BatchAction): u"Detach Replicas", count ) + policy_rules = (("database", "instance:eject_replica_source"),) @staticmethod def action_past(count): @@ -128,6 +131,7 @@ class PromoteToReplicaSource(tables.LinkAction): verbose_name = _("Promote to Replica Source") url = "horizon:project:databases:promote_to_replica_source" classes = ("ajax-modal", "btn-promote-to-replica-source") + policy_rules = (("database", "instance:promote_to_replica_source"),) def allowed(self, request, instance=None): return (instance.status in ACTIVE_STATES and @@ -157,6 +161,7 @@ class EjectReplicaSource(tables.BatchAction): name = "eject_replica_source" classes = ('btn-danger', 'btn-eject-replica-source') + policy_rules = (("database", "instance:eject_replica_source"),) def _allowed(self, request, instance=None): return (instance.status != 'PROMOTE' and @@ -185,6 +190,7 @@ class GrantAccess(tables.BatchAction): name = "grant_access" classes = ('btn-grant-access') + policy_rules = (("database", "instance:extension:user_access:update"),) def allowed(self, request, instance=None): if instance: @@ -219,6 +225,7 @@ class RevokeAccess(tables.BatchAction): name = "revoke_access" classes = ('btn-revoke-access') + policy_rules = (("database", "instance:extension:user_access:delete"),) def allowed(self, request, instance=None): if instance: @@ -246,6 +253,7 @@ def parse_host_param(request): class AccessTable(tables.DataTable): dbname = tables.Column("name", verbose_name=_("Name")) + access = tables.Column( "access", verbose_name=_("Accessible"), @@ -265,6 +273,7 @@ class ManageAccess(tables.LinkAction): verbose_name = _("Manage Access") url = "horizon:project:databases:access_detail" icon = "pencil" + policy_rules = (("database", "instance:extension:user_access:update"),) def allowed(self, request, instance=None): instance = self.table.kwargs['instance'] @@ -284,6 +293,7 @@ class CreateUser(tables.LinkAction): url = "horizon:project:databases:create_user" classes = ("ajax-modal",) icon = "plus" + policy_rules = (("database", "instance:extension:user:create"),) def allowed(self, request, instance=None): instance = self.table.kwargs['instance'] @@ -301,6 +311,7 @@ class EditUser(tables.LinkAction): url = "horizon:project:databases:edit_user" classes = ("ajax-modal",) icon = "pencil" + policy_rules = (("database", "instance:extension:user:update"),) def allowed(self, request, instance=None): instance = self.table.kwargs['instance'] @@ -322,6 +333,8 @@ def has_user_add_perm(request): class DeleteUser(tables.DeleteAction): + policy_rules = (("database", "instance:extension:user:delete"),) + @staticmethod def action_present(count): return ngettext_lazy( @@ -350,6 +363,7 @@ class CreateDatabase(tables.LinkAction): url = "horizon:project:databases:create_database" classes = ("ajax-modal",) icon = "plus" + policy_rules = (("database", "instance:extension:database:create"),) def allowed(self, request, database=None): instance = self.table.kwargs['instance'] @@ -369,6 +383,8 @@ def has_database_add_perm(request): class DeleteDatabase(tables.DeleteAction): + policy_rules = (("database", "instance:extension:database:delete"),) + @staticmethod def action_present(count): return ngettext_lazy( @@ -400,6 +416,7 @@ class LaunchLink(tables.LinkAction): url = "horizon:project:databases:launch" classes = ("ajax-modal", "btn-launch") icon = "cloud-upload" + policy_rules = (("database", "instance:create"),) class CreateBackup(tables.LinkAction): @@ -408,6 +425,7 @@ class CreateBackup(tables.LinkAction): url = "horizon:project:database_backups:create" classes = ("ajax-modal",) icon = "camera" + policy_rules = (("database", "backup:create"),) def allowed(self, request, instance=None): return (instance.status in ACTIVE_STATES and @@ -423,6 +441,7 @@ class ResizeVolume(tables.LinkAction): verbose_name = _("Resize Volume") url = "horizon:project:databases:resize_volume" classes = ("ajax-modal", "btn-resize") + policy_rules = (("database", "instance:resize_volume"),) def allowed(self, request, instance=None): return instance.status in ACTIVE_STATES @@ -437,6 +456,7 @@ class ResizeInstance(tables.LinkAction): verbose_name = _("Resize Instance") url = "horizon:project:databases:resize_instance" classes = ("ajax-modal", "btn-resize") + policy_rules = (("database", "instance:resize_flavor"),) def allowed(self, request, instance=None): return ((instance.status in ACTIVE_STATES or @@ -452,6 +472,7 @@ class AttachConfiguration(tables.LinkAction): verbose_name = _("Attach Configuration Group") url = "horizon:project:databases:attach_config" classes = ("btn-attach-config", "ajax-modal") + policy_rules = (("database", "instance:update"),) def allowed(self, request, instance=None): return (instance.status in ACTIVE_STATES and @@ -477,6 +498,7 @@ class DetachConfiguration(tables.BatchAction): name = "detach_configuration" classes = ('btn-danger', 'btn-detach-config') + policy_rules = (("database", "instance:update"),) def allowed(self, request, instance=None): return (instance.status in ACTIVE_STATES and @@ -489,6 +511,7 @@ class DetachConfiguration(tables.BatchAction): class EnableRootAction(tables.Action): name = "enable_root_action" verbose_name = _("Enable Root") + policy_rules = (("database", "instance:extension:root:create"),) def handle(self, table, request, obj_ids): try: @@ -502,6 +525,7 @@ class EnableRootAction(tables.Action): class DisableRootAction(tables.Action): name = "disable_root_action" verbose_name = _("Disable Root") + policy_rules = (("database", "instance:extension:root:delete"),) def allowed(self, request, instance): enabled = api.trove.root_show(request, instance.id) @@ -521,6 +545,9 @@ class ManageRoot(tables.LinkAction): name = "manage_root_action" verbose_name = _("Manage Root Access") url = "horizon:project:databases:manage_root" + policy_rules = (("database", "instance:extension:root:index"), + ("database", "instance:extension:root:create"), + ("database", "instance:extension:root:delete")) def allowed(self, request, instance): return instance.status in ACTIVE_STATES @@ -532,6 +559,7 @@ class ManageRoot(tables.LinkAction): class ManageRootTable(tables.DataTable): name = tables.Column('name', verbose_name=_('Instance Name')) + policy_rules = (("database", "instance:extension:root:index"), ) enabled = tables.Column('enabled', verbose_name=_('Has Root Ever Been Enabled'), filters=(d_filters.yesno, d_filters.capfirst), @@ -659,6 +687,7 @@ class StopDatabase(tables.BatchAction): name = "stop_database" help_text = _("Stop database service inside an instance.") action_type = "danger" + policy_rules = (("database", "instance:extension:database:delete"), ) @staticmethod def action_present(count): @@ -688,6 +717,7 @@ class UpdateInstance(tables.LinkAction): verbose_name = _("Update Instance") url = "horizon:project:databases:edit_instance" classes = ("btn-attach-config", "ajax-modal") + policy_rules = (("database", "instance:update"), ) def allowed(self, request, instance=None): return (instance.status in ACTIVE_STATES)