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
This commit is contained in:
Andrew Bogott
2021-05-04 21:06:13 -05:00
committed by Lingxian Kong
parent 055a1a819f
commit 083bf79107
7 changed files with 62 additions and 0 deletions

View File

@@ -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.

View File

@@ -26,9 +26,12 @@ class CreateBackupStrategy(tables.LinkAction):
url = "horizon:project:backup_strategies:create" url = "horizon:project:backup_strategies:create"
classes = ("ajax-modal", "btn-create") classes = ("ajax-modal", "btn-create")
icon = "camera" icon = "camera"
policy_rules = (("database", "backup_strategy:create"), )
class DeleteBackupStrategy(tables.DeleteAction): class DeleteBackupStrategy(tables.DeleteAction):
policy_rules = (("database", "backup_strategy:delete"), )
@staticmethod @staticmethod
def action_present(count): def action_present(count):
return ngettext_lazy( return ngettext_lazy(

View File

@@ -58,6 +58,7 @@ class LaunchLink(tables.LinkAction):
url = "horizon:project:database_backups:create" url = "horizon:project:database_backups:create"
classes = ("ajax-modal", "btn-create") classes = ("ajax-modal", "btn-create")
icon = "camera" icon = "camera"
policy_rules = (("database", "backup:create"), )
class RestoreLink(tables.LinkAction): class RestoreLink(tables.LinkAction):
@@ -66,6 +67,7 @@ class RestoreLink(tables.LinkAction):
url = "horizon:project:databases:launch" url = "horizon:project:databases:launch"
classes = ("ajax-modal",) classes = ("ajax-modal",)
icon = "cloud-upload" icon = "cloud-upload"
policy_rules = (("database", "backup:show"), )
def allowed(self, request, backup=None): def allowed(self, request, backup=None):
return backup.status in ['COMPLETED', 'RESTORED'] return backup.status in ['COMPLETED', 'RESTORED']
@@ -80,6 +82,7 @@ class DownloadBackup(tables.LinkAction):
verbose_name = _("Download Backup") verbose_name = _("Download Backup")
url = 'horizon:project:containers:object_download' url = 'horizon:project:containers:object_download'
classes = ("btn-download",) classes = ("btn-download",)
policy_rules = (("database", "backup:show"), )
def get_link_url(self, datum): def get_link_url(self, datum):
ref = datum.locationRef.split('/') ref = datum.locationRef.split('/')
@@ -99,6 +102,8 @@ class DownloadBackup(tables.LinkAction):
class DeleteBackup(tables.DeleteAction): class DeleteBackup(tables.DeleteAction):
policy_rules = (("database", "backup:delete"), )
@staticmethod @staticmethod
def action_present(count): def action_present(count):
return ngettext_lazy( return ngettext_lazy(

View File

@@ -42,6 +42,7 @@ class DeleteCluster(tables.BatchAction):
icon = "remove" icon = "remove"
classes = ('btn-danger',) classes = ('btn-danger',)
help_text = _("Deleted cluster is not recoverable.") help_text = _("Deleted cluster is not recoverable.")
policy_rules = (("database", "cluster:delete"), )
@staticmethod @staticmethod
def action_present(count): def action_present(count):
@@ -69,12 +70,14 @@ class LaunchLink(tables.LinkAction):
url = "horizon:project:database_clusters:launch" url = "horizon:project:database_clusters:launch"
classes = ("btn-launch", "ajax-modal") classes = ("btn-launch", "ajax-modal")
icon = "cloud-upload" icon = "cloud-upload"
policy_rules = (("database", "cluster:create"), )
class ClusterGrow(tables.LinkAction): class ClusterGrow(tables.LinkAction):
name = "cluster_grow" name = "cluster_grow"
verbose_name = _("Grow Cluster") verbose_name = _("Grow Cluster")
url = "horizon:project:database_clusters:cluster_grow_details" url = "horizon:project:database_clusters:cluster_grow_details"
policy_rules = (("database", "cluster:action"), )
def allowed(self, request, cluster=None): def allowed(self, request, cluster=None):
if (cluster and cluster.task["name"] == 'NONE' and if (cluster and cluster.task["name"] == 'NONE' and
@@ -87,6 +90,7 @@ class ClusterShrink(tables.LinkAction):
name = "cluster_shrink" name = "cluster_shrink"
verbose_name = _("Shrink Cluster") verbose_name = _("Shrink Cluster")
url = "horizon:project:database_clusters:cluster_shrink_details" url = "horizon:project:database_clusters:cluster_shrink_details"
policy_rules = (("database", "cluster:action"), )
def allowed(self, request, cluster=None): def allowed(self, request, cluster=None):
if (cluster and cluster.task["name"] == 'NONE' and if (cluster and cluster.task["name"] == 'NONE' and
@@ -100,6 +104,7 @@ class ResetPassword(tables.LinkAction):
verbose_name = _("Reset Root Password") verbose_name = _("Reset Root Password")
url = "horizon:project:database_clusters:reset_password" url = "horizon:project:database_clusters:reset_password"
classes = ("ajax-modal",) classes = ("ajax-modal",)
policy_rules = (("database", "cluster:action"), )
def allowed(self, request, cluster=None): def allowed(self, request, cluster=None):
if (cluster and cluster.task["name"] == 'NONE' and if (cluster and cluster.task["name"] == 'NONE' and
@@ -241,6 +246,7 @@ class ClusterShrinkAction(tables.BatchAction):
classes = ('btn-danger',) classes = ('btn-danger',)
success_url = 'horizon:project:database_clusters:index' success_url = 'horizon:project:database_clusters:index'
help_text = _("Shrinking a cluster is not recoverable.") help_text = _("Shrinking a cluster is not recoverable.")
policy_rules = (("database", "cluster:delete"), )
@staticmethod @staticmethod
def action_present(count): def action_present(count):
@@ -305,6 +311,7 @@ class ClusterGrowAddInstance(tables.LinkAction):
verbose_name = _("Add Instance") verbose_name = _("Add Instance")
url = "horizon:project:database_clusters:add_instance" url = "horizon:project:database_clusters:add_instance"
classes = ("ajax-modal",) classes = ("ajax-modal",)
policy_rules = (("database", "cluster:action"), )
def get_link_url(self): def get_link_url(self):
return urls.reverse( return urls.reverse(
@@ -313,6 +320,7 @@ class ClusterGrowAddInstance(tables.LinkAction):
class ClusterGrowRemoveInstance(tables.BatchAction): class ClusterGrowRemoveInstance(tables.BatchAction):
name = "cluster_grow_remove_instance" name = "cluster_grow_remove_instance"
policy_rules = (("database", "cluster:action"), )
@staticmethod @staticmethod
def action_present(count): def action_present(count):
@@ -387,6 +395,7 @@ class ClusterGrowAction(tables.Action):
verbose_name_plural = _("Grow Cluster") verbose_name_plural = _("Grow Cluster")
requires_input = False requires_input = False
icon = "plus" icon = "plus"
policy_rules = (("database", "cluster:action"), )
def handle(self, table, request, obj_ids): def handle(self, table, request, obj_ids):
if not table.data: if not table.data:

View File

@@ -33,11 +33,13 @@ class CreateConfiguration(tables.LinkAction):
url = "horizon:project:database_configurations:create" url = "horizon:project:database_configurations:create"
classes = ('ajax-modal', ) classes = ('ajax-modal', )
icon = "plus" icon = "plus"
policy_rules = (("database", "configuration:create"),)
class DeleteConfiguration(tables.DeleteAction): class DeleteConfiguration(tables.DeleteAction):
data_type_singular = _("Configuration Group") data_type_singular = _("Configuration Group")
data_type_plural = _("Configuration Groups") data_type_plural = _("Configuration Groups")
policy_rules = (("database", "configuration:delete"),)
@staticmethod @staticmethod
def action_present(count): def action_present(count):
@@ -87,6 +89,7 @@ class AddParameter(tables.LinkAction):
url = "horizon:project:database_configurations:add" url = "horizon:project:database_configurations:add"
classes = ('ajax-modal', ) classes = ('ajax-modal', )
icon = "plus" icon = "plus"
policy_rules = (("database", "configuration:edit"),)
def get_link_url(self, datum=None): def get_link_url(self, datum=None):
configuration_id = self.table.kwargs['configuration_id'] configuration_id = self.table.kwargs['configuration_id']
@@ -98,6 +101,7 @@ class ApplyChanges(tables.Action):
verbose_name = _("Apply Changes") verbose_name = _("Apply Changes")
verbose_name_plural = _("Apply Changes") verbose_name_plural = _("Apply Changes")
icon = "pencil" icon = "pencil"
policy_rules = (("database", "configuration:edit"),)
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(ApplyChanges, self).__init__(**kwargs) super(ApplyChanges, self).__init__(**kwargs)

View File

@@ -39,6 +39,7 @@ class PublishLog(tables.BatchAction):
) )
name = "publish_log" name = "publish_log"
policy_rules = (("database", "instance:guest_log_list"),)
def action(self, request, obj_id): def action(self, request, obj_id):
instance_id = self.table.kwargs['instance_id'] instance_id = self.table.kwargs['instance_id']
@@ -63,6 +64,7 @@ class DiscardLog(tables.BatchAction):
) )
name = "discard_log" name = "discard_log"
policy_rules = (("database", "instance:guest_log_list"),)
def action(self, request, obj_id): def action(self, request, obj_id):
instance_id = self.table.kwargs['instance_id'] instance_id = self.table.kwargs['instance_id']
@@ -87,6 +89,7 @@ class EnableLog(tables.BatchAction):
) )
name = "enable_log" name = "enable_log"
policy_rules = (("database", "instance:guest_log_list"),)
def action(self, request, obj_id): def action(self, request, obj_id):
instance_id = self.table.kwargs['instance_id'] instance_id = self.table.kwargs['instance_id']
@@ -111,6 +114,7 @@ class DisableLog(tables.BatchAction):
) )
name = "disable_log" name = "disable_log"
policy_rules = (("database", "instance:guest_log_list"),)
def action(self, request, obj_id): def action(self, request, obj_id):
instance_id = self.table.kwargs['instance_id'] instance_id = self.table.kwargs['instance_id']
@@ -126,6 +130,7 @@ class ViewLog(tables.LinkAction):
name = "view_log" name = "view_log"
verbose_name = _("View Log") verbose_name = _("View Log")
url = "horizon:project:databases:logs:log_contents" url = "horizon:project:databases:logs:log_contents"
policy_rules = (("database", "instance:guest_log_list"),)
def get_link_url(self, datum): def get_link_url(self, datum):
instance_id = self.table.kwargs['instance_id'] instance_id = self.table.kwargs['instance_id']

View File

@@ -42,6 +42,7 @@ ACTIVE_STATES = ("ACTIVE", "HEALTHY",)
class DeleteInstance(tables.DeleteAction): class DeleteInstance(tables.DeleteAction):
help_text = _("Deleted instances are not recoverable.") help_text = _("Deleted instances are not recoverable.")
policy_rules = (("database", "instance:delete"),)
@staticmethod @staticmethod
def action_present(count): def action_present(count):
@@ -66,6 +67,7 @@ class DeleteInstance(tables.DeleteAction):
class RestartInstance(tables.BatchAction): class RestartInstance(tables.BatchAction):
help_text = _("Restarted instances will lose any data not" help_text = _("Restarted instances will lose any data not"
" saved in persistent storage.") " saved in persistent storage.")
policy_rules = (("database", "instance:restart"),)
@staticmethod @staticmethod
def action_present(count): def action_present(count):
@@ -103,6 +105,7 @@ class DetachReplica(tables.BatchAction):
u"Detach Replicas", u"Detach Replicas",
count count
) )
policy_rules = (("database", "instance:eject_replica_source"),)
@staticmethod @staticmethod
def action_past(count): def action_past(count):
@@ -128,6 +131,7 @@ class PromoteToReplicaSource(tables.LinkAction):
verbose_name = _("Promote to Replica Source") verbose_name = _("Promote to Replica Source")
url = "horizon:project:databases:promote_to_replica_source" url = "horizon:project:databases:promote_to_replica_source"
classes = ("ajax-modal", "btn-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): def allowed(self, request, instance=None):
return (instance.status in ACTIVE_STATES and return (instance.status in ACTIVE_STATES and
@@ -157,6 +161,7 @@ class EjectReplicaSource(tables.BatchAction):
name = "eject_replica_source" name = "eject_replica_source"
classes = ('btn-danger', 'btn-eject-replica-source') classes = ('btn-danger', 'btn-eject-replica-source')
policy_rules = (("database", "instance:eject_replica_source"),)
def _allowed(self, request, instance=None): def _allowed(self, request, instance=None):
return (instance.status != 'PROMOTE' and return (instance.status != 'PROMOTE' and
@@ -185,6 +190,7 @@ class GrantAccess(tables.BatchAction):
name = "grant_access" name = "grant_access"
classes = ('btn-grant-access') classes = ('btn-grant-access')
policy_rules = (("database", "instance:extension:user_access:update"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
if instance: if instance:
@@ -219,6 +225,7 @@ class RevokeAccess(tables.BatchAction):
name = "revoke_access" name = "revoke_access"
classes = ('btn-revoke-access') classes = ('btn-revoke-access')
policy_rules = (("database", "instance:extension:user_access:delete"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
if instance: if instance:
@@ -246,6 +253,7 @@ def parse_host_param(request):
class AccessTable(tables.DataTable): class AccessTable(tables.DataTable):
dbname = tables.Column("name", verbose_name=_("Name")) dbname = tables.Column("name", verbose_name=_("Name"))
access = tables.Column( access = tables.Column(
"access", "access",
verbose_name=_("Accessible"), verbose_name=_("Accessible"),
@@ -265,6 +273,7 @@ class ManageAccess(tables.LinkAction):
verbose_name = _("Manage Access") verbose_name = _("Manage Access")
url = "horizon:project:databases:access_detail" url = "horizon:project:databases:access_detail"
icon = "pencil" icon = "pencil"
policy_rules = (("database", "instance:extension:user_access:update"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
instance = self.table.kwargs['instance'] instance = self.table.kwargs['instance']
@@ -284,6 +293,7 @@ class CreateUser(tables.LinkAction):
url = "horizon:project:databases:create_user" url = "horizon:project:databases:create_user"
classes = ("ajax-modal",) classes = ("ajax-modal",)
icon = "plus" icon = "plus"
policy_rules = (("database", "instance:extension:user:create"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
instance = self.table.kwargs['instance'] instance = self.table.kwargs['instance']
@@ -301,6 +311,7 @@ class EditUser(tables.LinkAction):
url = "horizon:project:databases:edit_user" url = "horizon:project:databases:edit_user"
classes = ("ajax-modal",) classes = ("ajax-modal",)
icon = "pencil" icon = "pencil"
policy_rules = (("database", "instance:extension:user:update"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
instance = self.table.kwargs['instance'] instance = self.table.kwargs['instance']
@@ -322,6 +333,8 @@ def has_user_add_perm(request):
class DeleteUser(tables.DeleteAction): class DeleteUser(tables.DeleteAction):
policy_rules = (("database", "instance:extension:user:delete"),)
@staticmethod @staticmethod
def action_present(count): def action_present(count):
return ngettext_lazy( return ngettext_lazy(
@@ -350,6 +363,7 @@ class CreateDatabase(tables.LinkAction):
url = "horizon:project:databases:create_database" url = "horizon:project:databases:create_database"
classes = ("ajax-modal",) classes = ("ajax-modal",)
icon = "plus" icon = "plus"
policy_rules = (("database", "instance:extension:database:create"),)
def allowed(self, request, database=None): def allowed(self, request, database=None):
instance = self.table.kwargs['instance'] instance = self.table.kwargs['instance']
@@ -369,6 +383,8 @@ def has_database_add_perm(request):
class DeleteDatabase(tables.DeleteAction): class DeleteDatabase(tables.DeleteAction):
policy_rules = (("database", "instance:extension:database:delete"),)
@staticmethod @staticmethod
def action_present(count): def action_present(count):
return ngettext_lazy( return ngettext_lazy(
@@ -400,6 +416,7 @@ class LaunchLink(tables.LinkAction):
url = "horizon:project:databases:launch" url = "horizon:project:databases:launch"
classes = ("ajax-modal", "btn-launch") classes = ("ajax-modal", "btn-launch")
icon = "cloud-upload" icon = "cloud-upload"
policy_rules = (("database", "instance:create"),)
class CreateBackup(tables.LinkAction): class CreateBackup(tables.LinkAction):
@@ -408,6 +425,7 @@ class CreateBackup(tables.LinkAction):
url = "horizon:project:database_backups:create" url = "horizon:project:database_backups:create"
classes = ("ajax-modal",) classes = ("ajax-modal",)
icon = "camera" icon = "camera"
policy_rules = (("database", "backup:create"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
return (instance.status in ACTIVE_STATES and return (instance.status in ACTIVE_STATES and
@@ -423,6 +441,7 @@ class ResizeVolume(tables.LinkAction):
verbose_name = _("Resize Volume") verbose_name = _("Resize Volume")
url = "horizon:project:databases:resize_volume" url = "horizon:project:databases:resize_volume"
classes = ("ajax-modal", "btn-resize") classes = ("ajax-modal", "btn-resize")
policy_rules = (("database", "instance:resize_volume"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
return instance.status in ACTIVE_STATES return instance.status in ACTIVE_STATES
@@ -437,6 +456,7 @@ class ResizeInstance(tables.LinkAction):
verbose_name = _("Resize Instance") verbose_name = _("Resize Instance")
url = "horizon:project:databases:resize_instance" url = "horizon:project:databases:resize_instance"
classes = ("ajax-modal", "btn-resize") classes = ("ajax-modal", "btn-resize")
policy_rules = (("database", "instance:resize_flavor"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
return ((instance.status in ACTIVE_STATES or return ((instance.status in ACTIVE_STATES or
@@ -452,6 +472,7 @@ class AttachConfiguration(tables.LinkAction):
verbose_name = _("Attach Configuration Group") verbose_name = _("Attach Configuration Group")
url = "horizon:project:databases:attach_config" url = "horizon:project:databases:attach_config"
classes = ("btn-attach-config", "ajax-modal") classes = ("btn-attach-config", "ajax-modal")
policy_rules = (("database", "instance:update"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
return (instance.status in ACTIVE_STATES and return (instance.status in ACTIVE_STATES and
@@ -477,6 +498,7 @@ class DetachConfiguration(tables.BatchAction):
name = "detach_configuration" name = "detach_configuration"
classes = ('btn-danger', 'btn-detach-config') classes = ('btn-danger', 'btn-detach-config')
policy_rules = (("database", "instance:update"),)
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
return (instance.status in ACTIVE_STATES and return (instance.status in ACTIVE_STATES and
@@ -489,6 +511,7 @@ class DetachConfiguration(tables.BatchAction):
class EnableRootAction(tables.Action): class EnableRootAction(tables.Action):
name = "enable_root_action" name = "enable_root_action"
verbose_name = _("Enable Root") verbose_name = _("Enable Root")
policy_rules = (("database", "instance:extension:root:create"),)
def handle(self, table, request, obj_ids): def handle(self, table, request, obj_ids):
try: try:
@@ -502,6 +525,7 @@ class EnableRootAction(tables.Action):
class DisableRootAction(tables.Action): class DisableRootAction(tables.Action):
name = "disable_root_action" name = "disable_root_action"
verbose_name = _("Disable Root") verbose_name = _("Disable Root")
policy_rules = (("database", "instance:extension:root:delete"),)
def allowed(self, request, instance): def allowed(self, request, instance):
enabled = api.trove.root_show(request, instance.id) enabled = api.trove.root_show(request, instance.id)
@@ -521,6 +545,9 @@ class ManageRoot(tables.LinkAction):
name = "manage_root_action" name = "manage_root_action"
verbose_name = _("Manage Root Access") verbose_name = _("Manage Root Access")
url = "horizon:project:databases:manage_root" 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): def allowed(self, request, instance):
return instance.status in ACTIVE_STATES return instance.status in ACTIVE_STATES
@@ -532,6 +559,7 @@ class ManageRoot(tables.LinkAction):
class ManageRootTable(tables.DataTable): class ManageRootTable(tables.DataTable):
name = tables.Column('name', verbose_name=_('Instance Name')) name = tables.Column('name', verbose_name=_('Instance Name'))
policy_rules = (("database", "instance:extension:root:index"), )
enabled = tables.Column('enabled', enabled = tables.Column('enabled',
verbose_name=_('Has Root Ever Been Enabled'), verbose_name=_('Has Root Ever Been Enabled'),
filters=(d_filters.yesno, d_filters.capfirst), filters=(d_filters.yesno, d_filters.capfirst),
@@ -659,6 +687,7 @@ class StopDatabase(tables.BatchAction):
name = "stop_database" name = "stop_database"
help_text = _("Stop database service inside an instance.") help_text = _("Stop database service inside an instance.")
action_type = "danger" action_type = "danger"
policy_rules = (("database", "instance:extension:database:delete"), )
@staticmethod @staticmethod
def action_present(count): def action_present(count):
@@ -688,6 +717,7 @@ class UpdateInstance(tables.LinkAction):
verbose_name = _("Update Instance") verbose_name = _("Update Instance")
url = "horizon:project:databases:edit_instance" url = "horizon:project:databases:edit_instance"
classes = ("btn-attach-config", "ajax-modal") classes = ("btn-attach-config", "ajax-modal")
policy_rules = (("database", "instance:update"), )
def allowed(self, request, instance=None): def allowed(self, request, instance=None):
return (instance.status in ACTIVE_STATES) return (instance.status in ACTIVE_STATES)