You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
9.6 KiB
258 lines
9.6 KiB
#!/usr/bin/env python
|
|
# coding=utf-8
|
|
|
|
__name__ = "trackme_rest_handler_ack.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 sys
|
|
import time
|
|
|
|
# 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.ack_user", "trackme_rest_api_ack_user.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,
|
|
)
|
|
|
|
from trackme_libs_ack import (
|
|
get_all_ack_records_from_kvcollection,
|
|
convert_epoch_to_datetime,
|
|
)
|
|
|
|
# import Splunk libs
|
|
import splunklib.client as client
|
|
|
|
|
|
class TrackMeHandlerAckReadOps_v2(trackme_rest_handler.RESTHandler):
|
|
def __init__(self, command_line, command_arg):
|
|
super(TrackMeHandlerAckReadOps_v2, self).__init__(command_line, command_arg, logger)
|
|
|
|
def get_resource_group_desc_ack(self, request_info, **kwargs):
|
|
response = {
|
|
"resource_group_name": "ack",
|
|
"resource_group_desc": "Acknowledgments allow silencing an entity alert for a given period of time automatically (read-only operations)",
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
|
|
def post_get_ack_for_object(self, request_info, **kwargs):
|
|
|
|
describe = False
|
|
tenant_id = None
|
|
|
|
# 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 is required
|
|
tenant_id = resp_dict.get("tenant_id", None)
|
|
if tenant_id is None:
|
|
error_msg = f'tenant_id="{tenant_id}", tenant_id is required'
|
|
logger.error(error_msg)
|
|
return {
|
|
"payload": {"action": "failure", "result": error_msg},
|
|
"status": 500,
|
|
}
|
|
|
|
# object_list
|
|
object_list = resp_dict.get("object_list", None)
|
|
object_value_list = []
|
|
|
|
if object_list is None:
|
|
object_list = "*"
|
|
|
|
else:
|
|
# turn as a list
|
|
object_value_list = object_list.split(",")
|
|
|
|
# object_category
|
|
object_category_value = resp_dict["object_category"]
|
|
|
|
else:
|
|
# body is required in this endpoint, if not submitted describe the usage
|
|
describe = True
|
|
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves the Ack record for one or more objects. It requires a POST call with the following information:",
|
|
"resource_desc": "Get acknowledgement for a comma separated list of entities",
|
|
"resource_spl_example": "| trackme url=\"/services/trackme/v2/ack/get_ack_for_object\" mode=\"post\" body=\"{'tenant_id': 'mytenant', 'object_category': 'splk-dsm', 'object_list': 'netscreen:netscreen:firewall'}\"",
|
|
"options": [
|
|
{
|
|
"tenant_id": "The tenant identifier",
|
|
"object_category": "the object category (splk-dsm, splk-dhm, splk-mhm, splk-flx, splk-wlk, splk-fqm)",
|
|
"object_list": "List of entities, in a comma separated format. Use * to retrieve all objects, defaults to * if not specified",
|
|
}
|
|
],
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.session_key,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
collection_name = f"kv_trackme_common_alerts_ack_tenant_{tenant_id}"
|
|
collection = service.kvstore[collection_name]
|
|
|
|
# get the whole collection
|
|
try:
|
|
(
|
|
collection_records_list,
|
|
collection_records_keys,
|
|
collection_records_objects,
|
|
collection_records_objects_dict,
|
|
collection_records_keys_dict,
|
|
) = get_all_ack_records_from_kvcollection(
|
|
collection_name, collection, object_category_value
|
|
)
|
|
|
|
except Exception as e:
|
|
error_msg = f'tenant_id="{tenant_id}", failed to retrieve KVstore collection records using function get_all_records_from_kvcollection, exception="{str(e)}"'
|
|
logger.error(error_msg)
|
|
return {
|
|
"payload": {"action": "failure", "result": error_msg},
|
|
"status": 500,
|
|
}
|
|
|
|
# if action is show and object_list is *, return all records
|
|
filtered_records = []
|
|
|
|
if object_list == "*":
|
|
for record in collection_records_list:
|
|
# convert ack_mtime to ack_mtime_datetime
|
|
ack_mtime_datetime = convert_epoch_to_datetime(record.get("ack_mtime"))
|
|
record["ack_mtime_datetime"] = ack_mtime_datetime
|
|
|
|
# convert ack_expiration to ack_expiration_datetime
|
|
ack_expiration_datetime = convert_epoch_to_datetime(
|
|
record.get("ack_expiration")
|
|
)
|
|
record["ack_expiration_datetime"] = ack_expiration_datetime
|
|
|
|
# create a new field called ack_is_enabled which is a boolean 0/1 depending on if the ack_state is active or inactive
|
|
if record.get("ack_state") == "active":
|
|
record["ack_is_enabled"] = 1
|
|
else:
|
|
record["ack_is_enabled"] = 0
|
|
|
|
# field anomaly_reason is optional, if not set, will be defined to N/A, if set it is a comma seperated string to be turned into a list
|
|
anomaly_reason = record.get("anomaly_reason", None)
|
|
if not anomaly_reason:
|
|
record["anomaly_reason"] = "N/A"
|
|
else:
|
|
if not isinstance(anomaly_reason, list):
|
|
record["anomaly_reason"] = anomaly_reason.split(",")
|
|
|
|
# field ack_source is optional, if not set, will be defined to user_ack
|
|
ack_source = record.get("ack_source", "user_ack")
|
|
record["ack_source"] = ack_source
|
|
|
|
filtered_records.append(record)
|
|
|
|
return {
|
|
"payload": filtered_records,
|
|
"status": 200,
|
|
}
|
|
|
|
else:
|
|
filtered_records = []
|
|
for object_value in object_value_list:
|
|
if object_value in collection_records_objects:
|
|
record = collection_records_objects_dict[object_value]
|
|
|
|
# convert ack_mtime to ack_mtime_datetime
|
|
ack_mtime_datetime = convert_epoch_to_datetime(
|
|
record.get("ack_mtime")
|
|
)
|
|
record["ack_mtime_datetime"] = ack_mtime_datetime
|
|
|
|
# convert ack_expiration to ack_expiration_datetime
|
|
if record.get("ack_expiration") != 0:
|
|
ack_expiration_datetime = convert_epoch_to_datetime(
|
|
record.get("ack_expiration")
|
|
)
|
|
else:
|
|
ack_expiration_datetime = "N/A"
|
|
record["ack_expiration_datetime"] = ack_expiration_datetime
|
|
|
|
# create a new field called ack_is_enabled which is a boolean 0/1 depending on if the ack_state is active or inactive
|
|
if record.get("ack_state") == "active":
|
|
record["ack_is_enabled"] = 1
|
|
else:
|
|
record["ack_is_enabled"] = 0
|
|
|
|
# field anomaly_reason is optional, if not set, will be defined to N/A, if set it is a comma seperated string to be turned into a list
|
|
anomaly_reason = record.get("anomaly_reason", None)
|
|
if not anomaly_reason:
|
|
record["anomaly_reason"] = "N/A"
|
|
else:
|
|
if not isinstance(anomaly_reason, list):
|
|
record["anomaly_reason"] = anomaly_reason.split(",")
|
|
|
|
# field ack_source is optional, if not set, will be defined to user_ack
|
|
ack_source = record.get("ack_source", "user_ack")
|
|
record["ack_source"] = ack_source
|
|
|
|
filtered_records.append(record)
|
|
|
|
return {
|
|
"payload": filtered_records,
|
|
"status": 200,
|
|
}
|