#!/usr/bin/env python # coding=utf-8 __name__ = "trackme_rest_handler_configuration.py" __author__ = "TrackMe Limited" __copyright__ = "Copyright 2022-2026, TrackMe Limited, U.K." __credits__ = "TrackMe Limited, U.K." __license__ = "TrackMe Limited, all rights reserved" __version__ = "0.1.0" __maintainer__ = "TrackMe Limited, U.K." __email__ = "support@trackme-solutions.com" __status__ = "PRODUCTION" # Built-in libraries import json import os import random import re import sys from collections import OrderedDict # Third-party libraries import requests import urllib.parse # splunk home splunkhome = os.environ["SPLUNK_HOME"] # append current directory sys.path.append(os.path.dirname(os.path.abspath(__file__))) # import libs import import_declare_test # set logging from trackme_libs_logging import setup_logger logger = setup_logger( "trackme.rest.alerting_admin", "trackme_rest_api_alerting_admin.log" ) # Redirect global logging to use the same handler import logging logging.getLogger().handlers = logger.handlers logging.getLogger().setLevel(logger.level) # import rest handler import trackme_rest_handler # import trackme libs from trackme_libs import ( trackme_getloglevel, trackme_audit_event, trackme_reqinfo, trackme_send_to_tcm, ) # import Splunk libs import splunklib.client as client class TrackMeHandlerAlertingWriteOps_v2(trackme_rest_handler.RESTHandler): def __init__(self, command_line, command_arg): super(TrackMeHandlerAlertingWriteOps_v2, self).__init__( command_line, command_arg, logger ) def get_resource_group_desc_alerting_admin(self, request_info, **kwargs): response = { "resource_group_name": "alerting/admin", "resource_group_desc": "These endpoints handle alerting (admin operations)", } return {"payload": response, "status": 200} # Create a new alert for any or our components def post_create_alert(self, request_info, **kwargs): # alert options tenant_id = None alert_name = None alert_search = None alert_properties = None # describe describe = False # Retrieve from data try: resp_dict = json.loads(str(request_info.raw_args["payload"])) except Exception as e: resp_dict = None if resp_dict is not None: try: describe = resp_dict["describe"] if describe in ("true", "True"): describe = True except Exception as e: describe = False if not describe: tenant_id = resp_dict["tenant_id"] alert_name = resp_dict["alert_name"] alert_search = resp_dict["alert_search"] alert_properties = resp_dict["alert_properties"] else: # body is required in this endpoint, if not submitted describe the usage describe = True if describe: response = { "describe": "This endpoint creates a new tracking alert for the component, it requires a POST call with the following information:", "resource_desc": "Create a new TrackMe alert (designed to be used programmatically, spl example not available due to the complexity of the content)", "resource_spl_example": "Not available", "options": [ { "tenant_id": "Tenant identifier", "alert_name": "The alert name", "alert_search": "The alert search SPL statement", "alert_properties": "The JSON alert properties", "update_comment": "OPTIONAL: a comment for the update, comments are added to the audit record, if unset will be defined to: API update", } ], } return {"payload": response, "status": 200} # Update comment is optional and used for audit changes try: update_comment = resp_dict["update_comment"] except Exception as e: update_comment = "API update" # Get splunkd port splunkd_port = request_info.server_rest_port # Define an header for requests authenticated communications with splunkd header = { "Authorization": "Splunk %s" % request_info.system_authtoken, "Content-Type": "application/json", } # Get service service = client.connect( owner="nobody", app="trackme", port=splunkd_port, token=request_info.system_authtoken, timeout=600, ) # set loglevel loglevel = trackme_getloglevel( request_info.system_authtoken, request_info.server_rest_port ) logger.setLevel(loglevel) # get TrackMe conf trackme_conf = trackme_reqinfo( request_info.system_authtoken, request_info.server_rest_uri ) logger.debug(f'trackme_conf="{json.dumps(trackme_conf, indent=2)}"') # TrackMe sharing level trackme_default_sharing = trackme_conf["trackme_conf"]["trackme_general"][ "trackme_default_sharing" ] # # alert check # # in alert_properties, check the value for actions (comma seperated list of actions) # if in actions, we find trackme_stateful_alert, verify that the email_account and email_recipients are set and to non empty values, if not return an error trackme_stateful_alert_base_keys = [ "action.trackme_stateful_alert.param.delivery_target", "action.trackme_stateful_alert.param.orange_as_alerting_state", "action.trackme_stateful_alert.param.drilldown_root_uri", ] trackme_stateful_alert_email_keys = [ "action.trackme_stateful_alert.param.email_account", "action.trackme_stateful_alert.param.email_recipients", "action.trackme_stateful_alert.param.generate_charts", "action.trackme_stateful_alert.param.theme_charts", "action.trackme_stateful_alert.param.timerange_charts", "action.trackme_stateful_alert.param.environment_name", "action.trackme_stateful_alert.param.email_send_update_if_ack_active", "action.trackme_stateful_alert.param.priority_levels_emails", ] trackme_stateful_alert_command_keys = [ "action.trackme_stateful_alert.param.commands_mode", "action.trackme_stateful_alert.param.commands_opened", "action.trackme_stateful_alert.param.commands_updated", "action.trackme_stateful_alert.param.commands_closed", "action.trackme_stateful_alert.param.priority_levels_commands", ] if "trackme_stateful_alert" in alert_properties.get("actions", ""): missing_fields = {} # Check base required fields for key in trackme_stateful_alert_base_keys: value = alert_properties.get(key) if value in ("", None): reason = "missing" if value is None else "empty string" missing_fields[key] = reason # Conditional check for email parameters delivery_target = alert_properties.get( "action.trackme_stateful_alert.param.delivery_target" ) if delivery_target in ( "emails_and_ingest", "emails_only", "emails_commands_and_ingest", "commands_and_emails", ): for key in trackme_stateful_alert_email_keys: value = alert_properties.get(key) if value in ("", None): reason = "missing" if value is None else "empty string" missing_fields[key] = reason elif ( key == "action.trackme_stateful_alert.param.theme_charts" and value not in ("dark", "light") ): missing_fields[key] = ( f"invalid value '{value}', must be 'dark' or 'light'" ) elif key == "action.trackme_stateful_alert.param.timerange_charts": # Pattern: