From 8a1f4972657c6d6da6897071c639680d4d589ca8 Mon Sep 17 00:00:00 2001 From: wh1te909 <7434746+wh1te909@users.noreply.github.com> Date: Mon, 22 Jul 2024 19:19:58 +0000 Subject: [PATCH] fix alert actions not honoring 'run only on' settings and fix availability webhook invalid escape --- api/tacticalrmm/agents/models.py | 4 +-- api/tacticalrmm/alerts/models.py | 43 ++++++++++++++------------ api/tacticalrmm/autotasks/models.py | 4 +-- api/tacticalrmm/checks/models.py | 4 +-- api/tacticalrmm/core/utils.py | 2 ++ api/tacticalrmm/logs/models.py | 2 +- api/tacticalrmm/tacticalrmm/helpers.py | 30 +++++++++++++++--- 7 files changed, 58 insertions(+), 31 deletions(-) diff --git a/api/tacticalrmm/agents/models.py b/api/tacticalrmm/agents/models.py index ee2973cb..101795fc 100644 --- a/api/tacticalrmm/agents/models.py +++ b/api/tacticalrmm/agents/models.py @@ -969,8 +969,8 @@ class Agent(BaseAuditModel): return bool( has_agent_notification or has_alert_template_notification - or has_webhook(alert_template) - or has_script_actions(alert_template) + or has_webhook(alert_template, "agent") + or has_script_actions(alert_template, "agent") ) def send_outage_email(self) -> None: diff --git a/api/tacticalrmm/alerts/models.py b/api/tacticalrmm/alerts/models.py index c28b6d75..e5c4e6d0 100644 --- a/api/tacticalrmm/alerts/models.py +++ b/api/tacticalrmm/alerts/models.py @@ -133,7 +133,7 @@ class Alert(models.Model): agent=agent, alert_type=AlertType.AVAILABILITY, severity=AlertSeverity.ERROR, - message=f"{agent.hostname} in {agent.client.name}\\{agent.site.name} is overdue.", + message=f"{agent.hostname} in {agent.client.name}, {agent.site.name} is overdue.", hidden=True, ), ) @@ -306,7 +306,7 @@ class Alert(models.Model): alert_interval = None email_task = None text_task = None - run_script_action = None + should_run_script_or_webhook = False # check what the instance passed is if isinstance(instance, Agent): @@ -332,7 +332,7 @@ class Alert(models.Model): always_email = alert_template.agent_always_email always_text = alert_template.agent_always_text alert_interval = alert_template.agent_periodic_alert_days - run_script_action = alert_template.agent_script_actions + should_run_script_or_webhook = alert_template.agent_script_actions elif isinstance(instance, CheckResult): from checks.tasks import ( @@ -383,7 +383,7 @@ class Alert(models.Model): always_email = alert_template.check_always_email always_text = alert_template.check_always_text alert_interval = alert_template.check_periodic_alert_days - run_script_action = alert_template.check_script_actions + should_run_script_or_webhook = alert_template.check_script_actions elif isinstance(instance, TaskResult): from autotasks.tasks import handle_task_email_alert, handle_task_sms_alert @@ -417,7 +417,7 @@ class Alert(models.Model): always_email = alert_template.task_always_email always_text = alert_template.task_always_text alert_interval = alert_template.task_periodic_alert_days - run_script_action = alert_template.task_script_actions + should_run_script_or_webhook = alert_template.task_script_actions else: return @@ -490,11 +490,10 @@ class Alert(models.Model): text_task.delay(pk=alert.pk, alert_interval=alert_interval) # check if any scripts/webhooks should be run - if alert_template and not alert.action_run: + if alert_template and not alert.action_run and should_run_script_or_webhook: if ( alert_template.action_type == AlertTemplateActionType.SCRIPT and alert_template.action - and run_script_action ): hist = AgentHistory.objects.create( agent=agent, @@ -516,7 +515,6 @@ class Alert(models.Model): elif ( alert_template.action_type == AlertTemplateActionType.SERVER and alert_template.action - and run_script_action ): stdout, stderr, execution_time, retcode = run_server_script( body=alert_template.action.script_body, @@ -595,7 +593,7 @@ class Alert(models.Model): text_on_resolved = False resolved_email_task = None resolved_text_task = None - run_script_action = None + should_run_script_or_webhook = False # check what the instance passed is if isinstance(instance, Agent): @@ -611,7 +609,7 @@ class Alert(models.Model): if alert_template: email_on_resolved = alert_template.agent_email_on_resolved text_on_resolved = alert_template.agent_text_on_resolved - run_script_action = alert_template.agent_script_actions + should_run_script_or_webhook = alert_template.agent_script_actions email_severities = [AlertSeverity.ERROR] text_severities = [AlertSeverity.ERROR] @@ -636,7 +634,7 @@ class Alert(models.Model): if alert_template: email_on_resolved = alert_template.check_email_on_resolved text_on_resolved = alert_template.check_text_on_resolved - run_script_action = alert_template.check_script_actions + should_run_script_or_webhook = alert_template.check_script_actions email_severities = alert_template.check_email_alert_severity or [ AlertSeverity.ERROR, AlertSeverity.WARNING, @@ -662,7 +660,7 @@ class Alert(models.Model): if alert_template: email_on_resolved = alert_template.task_email_on_resolved text_on_resolved = alert_template.task_text_on_resolved - run_script_action = alert_template.task_script_actions + should_run_script_or_webhook = alert_template.task_script_actions email_severities = alert_template.task_email_alert_severity or [ AlertSeverity.ERROR, AlertSeverity.WARNING, @@ -714,11 +712,14 @@ class Alert(models.Model): resolved_text_task.delay(pk=alert.pk) # check if resolved script/webhook should be run - if alert_template and not alert.resolved_action_run: + if ( + alert_template + and not alert.resolved_action_run + and should_run_script_or_webhook + ): if ( alert_template.resolved_action_type == AlertTemplateActionType.SCRIPT and alert_template.resolved_action - and run_script_action ): hist = AgentHistory.objects.create( agent=agent, @@ -740,7 +741,6 @@ class Alert(models.Model): elif ( alert_template.resolved_action_type == AlertTemplateActionType.SERVER and alert_template.resolved_action - and run_script_action ): stdout, stderr, execution_time, retcode = run_server_script( body=alert_template.resolved_action.script_body, @@ -863,7 +863,9 @@ class AlertTemplate(BaseAuditModel): ) action_timeout = models.PositiveIntegerField(default=15) resolved_action_type = models.CharField( - max_length=10, choices=AlertTemplateActionType.choices, default="script" + max_length=10, + choices=AlertTemplateActionType.choices, + default=AlertTemplateActionType.SCRIPT, ) resolved_action = models.ForeignKey( "scripts.Script", @@ -917,7 +919,8 @@ class AlertTemplate(BaseAuditModel): agent_always_text = BooleanField(null=True, blank=True, default=None) agent_always_alert = BooleanField(null=True, blank=True, default=None) agent_periodic_alert_days = PositiveIntegerField(blank=True, null=True, default=0) - agent_script_actions = BooleanField(null=True, blank=True, default=True) + # fmt: off + agent_script_actions = BooleanField(null=True, blank=True, default=True) # should be renamed because also deals with webhooks # check alert settings check_email_alert_severity = ArrayField( @@ -941,7 +944,8 @@ class AlertTemplate(BaseAuditModel): check_always_text = BooleanField(null=True, blank=True, default=None) check_always_alert = BooleanField(null=True, blank=True, default=None) check_periodic_alert_days = PositiveIntegerField(blank=True, null=True, default=0) - check_script_actions = BooleanField(null=True, blank=True, default=True) + # fmt: off + check_script_actions = BooleanField(null=True, blank=True, default=True) # should be renamed because also deals with webhooks # task alert settings task_email_alert_severity = ArrayField( @@ -965,7 +969,8 @@ class AlertTemplate(BaseAuditModel): task_always_text = BooleanField(null=True, blank=True, default=None) task_always_alert = BooleanField(null=True, blank=True, default=None) task_periodic_alert_days = PositiveIntegerField(blank=True, null=True, default=0) - task_script_actions = BooleanField(null=True, blank=True, default=True) + # fmt: off + task_script_actions = BooleanField(null=True, blank=True, default=True) # should be renamed because also deals with webhooks # exclusion settings exclude_workstations = BooleanField(null=True, blank=True, default=False) diff --git a/api/tacticalrmm/autotasks/models.py b/api/tacticalrmm/autotasks/models.py index 46a99063..21bb40f6 100644 --- a/api/tacticalrmm/autotasks/models.py +++ b/api/tacticalrmm/autotasks/models.py @@ -458,8 +458,8 @@ class AutomatedTask(BaseAuditModel): return ( has_autotask_notification or has_alert_template_notification - or has_webhook(alert_template) - or has_script_actions(alert_template) + or has_webhook(alert_template, "task") + or has_script_actions(alert_template, "task") ) diff --git a/api/tacticalrmm/checks/models.py b/api/tacticalrmm/checks/models.py index 27cacfba..e6fe3bd6 100644 --- a/api/tacticalrmm/checks/models.py +++ b/api/tacticalrmm/checks/models.py @@ -242,8 +242,8 @@ class Check(BaseAuditModel): return ( has_check_notifications or has_alert_template_notification - or has_webhook(alert_template) - or has_script_actions(alert_template) + or has_webhook(alert_template, "check") + or has_script_actions(alert_template, "check") ) def add_check_history( diff --git a/api/tacticalrmm/core/utils.py b/api/tacticalrmm/core/utils.py index 7ce323d9..b4428991 100644 --- a/api/tacticalrmm/core/utils.py +++ b/api/tacticalrmm/core/utils.py @@ -24,6 +24,7 @@ from tacticalrmm.constants import ( AgentPlat, MeshAgentIdent, ) +from tacticalrmm.logger import logger if TYPE_CHECKING: from core.models import CoreSettings @@ -264,6 +265,7 @@ def run_url_rest_action(*, action_id: int, instance=None) -> tuple[str, int]: url=url, method=method, body=body, headers=headers, instance=instance ) except Exception as e: + logger.error(str(e)) return (str(e), 500) return (response.text, response.status_code) diff --git a/api/tacticalrmm/logs/models.py b/api/tacticalrmm/logs/models.py index 1fced9b6..14ecc6bb 100644 --- a/api/tacticalrmm/logs/models.py +++ b/api/tacticalrmm/logs/models.py @@ -297,7 +297,7 @@ class AuditLog(models.Model): target = f"on all agents within client: {client.name}" elif affected["target"] == "site": site = Site.objects.get(pk=affected["site"]) - target = f"on all agents within site: {site.client.name}\\{site.name}" + target = f"on all agents within site: {site.client.name} - {site.name}" elif affected["target"] == "agents": agents = Agent.objects.filter(agent_id__in=affected["agents"]).values_list( "hostname", flat=True diff --git a/api/tacticalrmm/tacticalrmm/helpers.py b/api/tacticalrmm/tacticalrmm/helpers.py index 50816c6d..fedb96b1 100644 --- a/api/tacticalrmm/tacticalrmm/helpers.py +++ b/api/tacticalrmm/tacticalrmm/helpers.py @@ -1,9 +1,11 @@ +from __future__ import annotations + import os import random import secrets import string from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal from urllib.parse import urlparse from zoneinfo import ZoneInfo @@ -140,11 +142,29 @@ def days_until_cert_expires() -> int: return delta.days -def has_webhook(alert_templ: "AlertTemplate") -> bool: +def has_webhook( + alert_templ: AlertTemplate | None, instance: Literal["agent", "check", "task"] +) -> bool: return bool( - alert_templ and (alert_templ.action_rest or alert_templ.resolved_action_rest) + alert_templ + and (alert_templ.action_rest or alert_templ.resolved_action_rest) + and ( + (instance == "agent" and alert_templ.agent_script_actions) + or (instance == "check" and alert_templ.check_script_actions) + or (instance == "task" and alert_templ.task_script_actions) + ) ) -def has_script_actions(alert_templ: "AlertTemplate") -> bool: - return bool(alert_templ and (alert_templ.action or alert_templ.resolved_action)) +def has_script_actions( + alert_templ: AlertTemplate | None, instance: Literal["agent", "check", "task"] +) -> bool: + return bool( + alert_templ + and (alert_templ.action or alert_templ.resolved_action) + and ( + (instance == "agent" and alert_templ.agent_script_actions) + or (instance == "check" and alert_templ.check_script_actions) + or (instance == "task" and alert_templ.task_script_actions) + ) + )