#!/usr/bin/env python # coding=utf-8 __name__ = "trackme_rest_handler_notes.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.notes_power", "trackme_rest_api_notes_power.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, ) # import Splunk libs import splunklib.client as client class TrackMeHandlerNotesWrite_v2(trackme_rest_handler.RESTHandler): def __init__(self, command_line, command_arg): super(TrackMeHandlerNotesWrite_v2, self).__init__(command_line, command_arg, logger) def get_resource_group_desc_notes(self, request_info, **kwargs): response = { "resource_group_name": "notes", "resource_group_desc": "Notes allow users to publish notes associated with entities (write operations)", } return {"payload": response, "status": 200} def post_create_note(self, request_info, **kwargs): describe = False tenant_id = None object_id = None note = 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_id is required object_id = resp_dict.get("object_id", None) if object_id is None: error_msg = f'tenant_id="{tenant_id}", object_id="{object_id}", object_id is required' logger.error(error_msg) return { "payload": {"action": "failure", "result": error_msg}, "status": 500, } # note is required note = resp_dict.get("note", None) if note is None or note.strip() == "": error_msg = f'tenant_id="{tenant_id}", object_id="{object_id}", note is required and cannot be empty' logger.error(error_msg) return { "payload": {"action": "failure", "result": error_msg}, "status": 500, } else: # body is required in this endpoint, if not submitted describe the usage describe = True if describe: response = { "describe": "This endpoint creates a new note for a given object_id. It requires a POST call with the following information:", "resource_desc": "Create a note for an entity", "resource_spl_example": "| trackme url=\"/services/trackme/v2/notes/write/create_note\" mode=\"post\" body=\"{'tenant_id': 'mytenant', 'object_id': 'netscreen:netscreen:firewall', 'note': 'This is a note'}\"", "options": [ { "tenant_id": "The tenant identifier", "object_id": "The object_id (entity keyid) to create a note for", "note": "The note content (supports Markdown format)", } ], } return {"payload": response, "status": 200} # Get current user from session created_by = request_info.user # 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_notes_tenant_{tenant_id}" try: collection = service.kvstore[collection_name] # Create note record note_record = { "object_id": object_id, "note": note.strip(), "created_by": created_by, "mtime": time.time(), } # Insert the note result = collection.data.insert(json.dumps(note_record)) # Get the created record with _key note_record["_key"] = result["_key"] # Audit event trackme_audit_event( request_info.system_authtoken, request_info.server_rest_uri, tenant_id, created_by, "success", "create note", str(object_id), "notes", note_record, "Note created successfully", "API update", ) return { "payload": {"action": "success", "result": "Note created successfully", "note": note_record}, "status": 200, } except Exception as e: error_msg = f'tenant_id="{tenant_id}", object_id="{object_id}", failed to create note in KVstore collection, exception="{str(e)}"' logger.error(error_msg) # Audit event for failure try: trackme_audit_event( request_info.system_authtoken, request_info.server_rest_uri, tenant_id, created_by, "failure", "create note", str(object_id), "notes", {"note": note[:100] if note else ""}, # Truncate for audit f"Note creation failed: {str(e)}", "API update", ) except Exception as audit_e: logger.error(f"Failed to create audit event: {str(audit_e)}") return { "payload": {"action": "failure", "result": error_msg}, "status": 500, } def post_delete_note(self, request_info, **kwargs): describe = False tenant_id = None note_key = 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, } # note_key is required note_key = resp_dict.get("note_key", None) if note_key is None: error_msg = f'tenant_id="{tenant_id}", note_key="{note_key}", note_key is required' logger.error(error_msg) return { "payload": {"action": "failure", "result": error_msg}, "status": 500, } else: # body is required in this endpoint, if not submitted describe the usage describe = True if describe: response = { "describe": "This endpoint deletes a note by its _key. It requires a POST call with the following information:", "resource_desc": "Delete a note", "resource_spl_example": "| trackme url=\"/services/trackme/v2/notes/write/delete_note\" mode=\"post\" body=\"{'tenant_id': 'mytenant', 'note_key': 'abc123'}\"", "options": [ { "tenant_id": "The tenant identifier", "note_key": "The _key of the note to delete", } ], } return {"payload": response, "status": 200} # Get current user from session user = request_info.user # 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_notes_tenant_{tenant_id}" try: collection = service.kvstore[collection_name] # First, get the note to retrieve object_id for audit try: note_record = collection.data.query_by_id(note_key) object_id = note_record.get("object_id", "unknown") except Exception as e: object_id = "unknown" logger.warning(f'Could not retrieve note before deletion, note_key="{note_key}", exception="{str(e)}"') # Delete the note - KVstore delete requires JSON format with _key collection.data.delete(json.dumps({"_key": note_key})) # Audit event trackme_audit_event( request_info.system_authtoken, request_info.server_rest_uri, tenant_id, user, "success", "delete note", str(object_id), "notes", {"note_key": note_key}, "Note deleted successfully", "API update", ) return { "payload": {"action": "success", "result": "Note deleted successfully"}, "status": 200, } except Exception as e: error_msg = f'tenant_id="{tenant_id}", note_key="{note_key}", failed to delete note from KVstore collection, exception="{str(e)}"' logger.error(error_msg) # Audit event for failure try: trackme_audit_event( request_info.system_authtoken, request_info.server_rest_uri, tenant_id, user, "failure", "delete note", str(object_id) if 'object_id' in locals() else "unknown", "notes", {"note_key": note_key}, f"Note deletion failed: {str(e)}", "API update", ) except Exception as audit_e: logger.error(f"Failed to create audit event: {str(audit_e)}") return { "payload": {"action": "failure", "result": error_msg}, "status": 500, }