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.
363 lines
12 KiB
363 lines
12 KiB
#!/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,
|
|
}
|
|
|