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.
1168 lines
42 KiB
1168 lines
42 KiB
#!/usr/bin/env python
|
|
# coding=utf-8
|
|
|
|
__name__ = "trackme_rest_handler_vtenant.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"
|
|
|
|
import os, sys
|
|
import json
|
|
from collections import OrderedDict
|
|
import time
|
|
import requests
|
|
|
|
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.vtenants_user", "trackme_rest_api_vtenants_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_reqinfo, trackme_vtenant_account, trackme_get_version
|
|
|
|
# import trackme load libs
|
|
from trackme_libs_load import trackmeload
|
|
|
|
# import trackme libs schema
|
|
from trackme_libs_schema import trackme_schema_format_version
|
|
|
|
# import trackme decision maker
|
|
from trackme_libs_decisionmaker import convert_epoch_to_datetime
|
|
|
|
# import the collections dict
|
|
from collections_data import vtenant_account_default
|
|
|
|
# import Splunk SDK client
|
|
import splunklib.client as client
|
|
import splunklib.results as results
|
|
|
|
|
|
class TrackMeHandlerVtenantsRead_v2(trackme_rest_handler.RESTHandler):
|
|
def __init__(self, command_line, command_arg):
|
|
super(TrackMeHandlerVtenantsRead_v2, self).__init__(
|
|
command_line, command_arg, logger
|
|
)
|
|
|
|
def get_resource_group_desc_vtenants(self, request_info, **kwargs):
|
|
response = {
|
|
"resource_group_name": "vtenants",
|
|
"resource_group_desc": "Endpoints related to the management of TrackMe Virtual Tenants (read only operations)",
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
#
|
|
# Get tenants
|
|
#
|
|
|
|
def get_show_tenants(self, request_info, **kwargs):
|
|
"""
|
|
| trackme url="/services/trackme/v2/vtenants/show_tenants" mode="get"
|
|
"""
|
|
|
|
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
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves the tenants KVStore collection returned as a JSON object, it requires a GET call with no data required",
|
|
"resource_desc": "Retrieve the virtual tenants collection",
|
|
"resource_spl_example": '| trackme url="/services/trackme/v2/vtenants/show_tenants" mode="get"',
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
try:
|
|
collection_name = "kv_trackme_virtual_tenants"
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.session_key,
|
|
timeout=600,
|
|
)
|
|
collection = service.kvstore[collection_name]
|
|
|
|
# TrackMe version
|
|
trackme_version = trackme_get_version(service)
|
|
|
|
# Get schema_version_required
|
|
schema_version_required = trackme_schema_format_version(trackme_version)
|
|
|
|
records = collection.data.query()
|
|
results_records = []
|
|
|
|
for record in records:
|
|
# Add the schema_version_required to the record
|
|
record["schema_version_required"] = schema_version_required
|
|
# Set the status of tenant_updated_status, if schema_version in the record is equal to schema_version_required,
|
|
# the status is "updated", otherwise it is "pending"
|
|
# If schema_version_required is 0 (version retrieval failed), treat all tenants as "updated"
|
|
# to align with graceful degradation when DB Connect causes permission issues
|
|
if schema_version_required == 0:
|
|
record["tenant_updated_status"] = "updated"
|
|
else:
|
|
# Handle case where schema_version is missing from the record (e.g., tenant created when version retrieval failed)
|
|
schema_version = record.get("schema_version")
|
|
if schema_version is None:
|
|
# If schema_version is missing, use "undetermined" to indicate we cannot determine the status
|
|
# This is different from "pending" which implies an upgrade is in progress
|
|
# "undetermined" should not block underlying logic like hybrid trackers
|
|
record["tenant_updated_status"] = "undetermined"
|
|
elif int(schema_version) == schema_version_required:
|
|
record["tenant_updated_status"] = "updated"
|
|
else:
|
|
record["tenant_updated_status"] = "pending"
|
|
|
|
schema_version_mtime = record.get("schema_version_mtime")
|
|
if schema_version_mtime:
|
|
schema_version_mtime = convert_epoch_to_datetime(
|
|
schema_version_mtime
|
|
)
|
|
else:
|
|
schema_version_mtime = "N/A"
|
|
record["schema_version_mtime"] = schema_version_mtime
|
|
|
|
# Add to our final list
|
|
results_records.append(record)
|
|
|
|
# Render
|
|
return {"payload": results_records, "status": 200}
|
|
|
|
except Exception as e:
|
|
response = {
|
|
"action": "failure",
|
|
"response": f'an exception was encountered, exception="{str(e)}"',
|
|
}
|
|
logger.error(json.dumps(response))
|
|
return {"payload": response, "status": 500}
|
|
|
|
#
|
|
# trackmeload
|
|
#
|
|
|
|
def post_trackmeload(self, request_info, **kwargs):
|
|
describe = False
|
|
start = time.time()
|
|
|
|
# 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
|
|
mode = resp_dict.get("mode", "full")
|
|
if not mode in ("full", "expanded"):
|
|
response = {
|
|
"action": "failure",
|
|
"response": f"Invalid value for mode",
|
|
}
|
|
logger.error(json.dumps(response))
|
|
return {"payload": response, "status": 500}
|
|
|
|
else:
|
|
describe = True
|
|
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves the unified Virtual Tenants view, it requires a POST call with the following data:",
|
|
"resource_desc": "Retrieve the unifed Virtual Tenants view",
|
|
"resource_spl_example": '| trackme url="/services/trackme/v2/vtenants/trackmeload" mode="post" body="{\'mode\': \'full\'}"',
|
|
"options": [
|
|
{
|
|
"mode": "The mode, valid options: <full|expanded>",
|
|
}
|
|
],
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# set service_system
|
|
service_system = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# Define an header for requests authenticated communications with splunkd
|
|
header = {
|
|
"Authorization": "Splunk %s" % request_info.session_key,
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
# TrackMe reqinfo
|
|
reqinfo = trackme_reqinfo(
|
|
request_info.system_authtoken, request_info.server_rest_uri
|
|
)
|
|
trackmeconf = reqinfo["trackme_conf"]
|
|
|
|
# get current user
|
|
username = request_info.user
|
|
|
|
# get users
|
|
users = service_system.users
|
|
|
|
# get roles
|
|
roles = service_system.roles
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# the final response
|
|
final_response = {}
|
|
|
|
# add the username
|
|
final_response["username"] = username
|
|
|
|
# get the number of tenants and add to the response
|
|
collection_name = "kv_trackme_virtual_tenants"
|
|
collection = service_system.kvstore[collection_name]
|
|
vtenants_records = collection.data.query()
|
|
final_response["vtenants_count"] = len(vtenants_records)
|
|
|
|
# get the response from trackmeload
|
|
try:
|
|
trackmeload_response = trackmeload(
|
|
request_info.session_key,
|
|
request_info.server_rest_uri,
|
|
service_system,
|
|
users,
|
|
roles,
|
|
username,
|
|
mode,
|
|
)
|
|
trackmeload_response_tenants = trackmeload_response.get("tenants", {})
|
|
|
|
# Get the list
|
|
trackmeload_response_tenants_list = trackmeload_response.get("tenants", [])
|
|
|
|
# loop and add the account
|
|
virtual_tenants_list = []
|
|
for virtual_tenant in trackmeload_response_tenants_list:
|
|
|
|
# Get Virtual Tenant account
|
|
try:
|
|
vtenant_account = trackme_vtenant_account(
|
|
request_info.session_key,
|
|
request_info.server_rest_uri,
|
|
virtual_tenant.get("tenant_id"),
|
|
)
|
|
except Exception as e:
|
|
vtenant_account = {}
|
|
|
|
# add
|
|
virtual_tenant["vtenant_account"] = vtenant_account
|
|
virtual_tenants_list.append(virtual_tenant)
|
|
|
|
# update the final response
|
|
final_response["tenants_json"] = {"tenants": virtual_tenants_list}
|
|
|
|
except Exception as e:
|
|
# log error and return 500
|
|
response = {
|
|
"action": "failure",
|
|
"response": f"An exception was encountered while retrieving the tenants from the central KVStore, collection={collection_name}, exception={str(e)}",
|
|
}
|
|
logger.error(json.dumps(response))
|
|
return {"payload": response, "status": 500}
|
|
|
|
# active count
|
|
vtenants_count_active = 0
|
|
for vtenant_record in trackmeload_response_tenants:
|
|
logger.debug(f'vtenant_record="{json.dumps(vtenant_record, indent=2)}"')
|
|
if vtenant_record.get("tenant_status") == "enabled":
|
|
vtenants_count_active += 1
|
|
final_response["vtenants_count_active"] = vtenants_count_active
|
|
|
|
#
|
|
# Privilege level
|
|
#
|
|
|
|
record_url = (
|
|
"%s/services/trackme/v2/configuration/trackme_check_privileges_level"
|
|
% (request_info.server_rest_uri)
|
|
)
|
|
|
|
user_level = None
|
|
|
|
# retrieve and add to the response
|
|
try:
|
|
response = requests.get(
|
|
record_url,
|
|
headers=header,
|
|
verify=False,
|
|
timeout=600,
|
|
)
|
|
if response.status_code == 200:
|
|
user_level = response.json().get("user_level")
|
|
final_response["user_level"] = user_level
|
|
except Exception as e:
|
|
return {
|
|
"payload": {
|
|
"response": f"An exception was encountered while retrieving the privileges level, exception={str(e)}",
|
|
"exception": str(e),
|
|
},
|
|
"status": 500,
|
|
}
|
|
|
|
# theme & user prefs are parts of the check_privileges_level endpoint answer
|
|
user_theme_prefs = response.json().get("user_prefs")
|
|
for key in user_theme_prefs:
|
|
final_response[key] = user_theme_prefs.get(key)
|
|
|
|
# log perf only in debug
|
|
logger.debug(
|
|
f"function post_trackmeload has terminated, run_time={round(time.time() - start, 3)}"
|
|
)
|
|
|
|
# add trackmeconf to the response
|
|
final_response["trackmeconf"] = trackmeconf
|
|
|
|
# Return
|
|
return {
|
|
"payload": final_response,
|
|
"status": 200,
|
|
}
|
|
|
|
#
|
|
# trackmeload legacy: this endpoint uses a search driven approach and was the previous way to retrieve the TrackMe Virtual Tenants json data
|
|
# very view users seems to have had issues with the new faster method, and can switch to this mode in case of an issue with the new method
|
|
#
|
|
|
|
def post_trackmeload_legacy(self, request_info, **kwargs):
|
|
describe = False
|
|
start = time.time()
|
|
|
|
# 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
|
|
mode = resp_dict.get("mode", "full")
|
|
if not mode in ("full", "expanded"):
|
|
response = {
|
|
"action": "failure",
|
|
"response": f"Invalid value for mode",
|
|
}
|
|
logger.error(json.dumps(response))
|
|
return {"payload": response, "status": 500}
|
|
|
|
else:
|
|
describe = True
|
|
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves the unified Virtual Tenants view (legacy search driven method), it requires a POST call with the following data:",
|
|
"resource_desc": "Retrieve the unifed Virtual Tenants view",
|
|
"resource_spl_example": '| trackme url="/services/trackme/v2/vtenants/trackmeload_legacy" mode="post" body="{\'mode\': \'full\'}"',
|
|
"options": [
|
|
{
|
|
"mode": "The mode, valid options: <full|expanded>",
|
|
}
|
|
],
|
|
}
|
|
|
|
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,
|
|
)
|
|
|
|
# Define an header for requests authenticated communications with splunkd
|
|
header = {
|
|
"Authorization": "Splunk %s" % request_info.session_key,
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
# TrackMe reqinfo
|
|
reqinfo = trackme_reqinfo(
|
|
request_info.system_authtoken, request_info.server_rest_uri
|
|
)
|
|
trackmeconf = reqinfo["trackme_conf"]
|
|
|
|
# get current user
|
|
username = request_info.user
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# the final response
|
|
final_response = {}
|
|
|
|
# add the username
|
|
final_response["username"] = username
|
|
|
|
# get the number of tenants and add to the response
|
|
collection_name = "kv_trackme_virtual_tenants"
|
|
collection = service.kvstore[collection_name]
|
|
vtenants_records = collection.data.query()
|
|
final_response["vtenants_count"] = len(vtenants_records)
|
|
|
|
#
|
|
# trackmeload custom command
|
|
#
|
|
|
|
# run trackmeload custom command and add to the response
|
|
search = f"| trackmeload mode={mode}"
|
|
kwargs_oneshot = {
|
|
"earliest_time": "-5m",
|
|
"latest_time": "now",
|
|
"output_mode": "json",
|
|
"count": 0,
|
|
}
|
|
|
|
try:
|
|
oneshotsearch_results = service.jobs.oneshot(search, **kwargs_oneshot)
|
|
reader = results.JSONResultsReader(oneshotsearch_results)
|
|
|
|
for item in reader:
|
|
if isinstance(item, dict):
|
|
final_response["tenants_json"] = json.loads(item.get("_raw"))
|
|
|
|
except Exception as e:
|
|
logger.error(f'execution failed with exception="{str(e)}"')
|
|
|
|
#
|
|
# Privilege level
|
|
#
|
|
|
|
record_url = (
|
|
"%s/services/trackme/v2/configuration/trackme_check_privileges_level"
|
|
% (request_info.server_rest_uri)
|
|
)
|
|
|
|
user_level = None
|
|
|
|
# retrieve and add to the response
|
|
try:
|
|
response = requests.get(
|
|
record_url,
|
|
headers=header,
|
|
verify=False,
|
|
timeout=600,
|
|
)
|
|
if response.status_code == 200:
|
|
user_level = response.json().get("user_level")
|
|
final_response["user_level"] = user_level
|
|
except Exception as e:
|
|
return {
|
|
"payload": {
|
|
"response": "An exception was encountered",
|
|
"exception": str(e),
|
|
},
|
|
"status": 500,
|
|
}
|
|
|
|
# theme & user prefs are parts of the check_privileges_level endpoint answer
|
|
user_theme_prefs = response.json().get("user_prefs")
|
|
for key in user_theme_prefs:
|
|
final_response[key] = user_theme_prefs.get(key)
|
|
|
|
# active count
|
|
trackmeload_response_tenants = final_response["tenants_json"].get("tenants", {})
|
|
|
|
vtenants_count_active = 0
|
|
virtual_tenants_list = []
|
|
|
|
for vtenant_record in trackmeload_response_tenants:
|
|
logger.debug(f'vtenant_record="{json.dumps(vtenant_record, indent=2)}"')
|
|
if vtenant_record.get("tenant_status") == "enabled":
|
|
vtenants_count_active += 1
|
|
|
|
# Get Virtual Tenant account
|
|
try:
|
|
vtenant_account = trackme_vtenant_account(
|
|
request_info.session_key,
|
|
request_info.server_rest_uri,
|
|
vtenant_record.get("tenant_id"),
|
|
)
|
|
except Exception as e:
|
|
vtenant_account = {}
|
|
|
|
# add
|
|
vtenant_record["vtenant_account"] = vtenant_account
|
|
|
|
# add to our final list
|
|
virtual_tenants_list.append(vtenant_record)
|
|
|
|
final_response["vtenants_count_active"] = vtenants_count_active
|
|
|
|
# update the final response
|
|
final_response["tenants_json"] = {"tenants": virtual_tenants_list}
|
|
|
|
# add trackmeconf to the response
|
|
final_response["trackmeconf"] = trackmeconf
|
|
|
|
# log perf only in debug
|
|
logger.debug(
|
|
f"function post_trackmeload_legacy has terminated, run_time={round(time.time() - start, 3)}"
|
|
)
|
|
|
|
# Return
|
|
return {
|
|
"payload": final_response,
|
|
"status": 200,
|
|
}
|
|
|
|
#
|
|
# Get a single tenant
|
|
#
|
|
|
|
def post_show_tenant(self, request_info, **kwargs):
|
|
"""
|
|
| trackme url="/services/trackme/v2/vtenants/show_tenant" mode="post" body="{'tenant_id': 'mytenant'}"
|
|
"""
|
|
|
|
tenant_id = None
|
|
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"]
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves a single tenant record in the KVStore collection and returns it as a JSON object, it requires a POST call with the following data:",
|
|
"resource_desc": "Retrieve a single virtual tenant",
|
|
"resource_spl_example": '| trackme url="/services/trackme/v2/vtenants/show_tenant" mode="post" body="{\'tenant_id\': \'mytenant\'}"',
|
|
"options": [
|
|
{
|
|
"tenant_id": "The tenant identifier",
|
|
}
|
|
],
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# Get Virtual Tenant account
|
|
try:
|
|
vtenant_account = trackme_vtenant_account(
|
|
request_info.session_key,
|
|
request_info.server_rest_uri,
|
|
tenant_id,
|
|
)
|
|
except Exception as e:
|
|
vtenant_account = None
|
|
|
|
try:
|
|
collection_name = "kv_trackme_virtual_tenants"
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.session_key,
|
|
timeout=600,
|
|
)
|
|
collection = service.kvstore[collection_name]
|
|
|
|
# Define the KV query search string
|
|
query_string = {
|
|
"tenant_id": tenant_id,
|
|
}
|
|
|
|
try:
|
|
vtenant_record = collection.data.query(query=json.dumps(query_string))[
|
|
0
|
|
]
|
|
key = vtenant_record.get("_key")
|
|
|
|
except Exception as e:
|
|
key = None
|
|
|
|
if key:
|
|
|
|
# Add the vtenant_account to the vtenant_record
|
|
if vtenant_account:
|
|
vtenant_record["vtenant_account"] = vtenant_account
|
|
|
|
# Render
|
|
return {"payload": vtenant_record, "status": 200}
|
|
else:
|
|
return {
|
|
"payload": {"response": "this tenant could be not found"},
|
|
"status": 404,
|
|
}
|
|
|
|
except Exception as e:
|
|
response = {
|
|
"action": "failure",
|
|
"response": f'an exception was encountered, exception="{str(e)}"',
|
|
}
|
|
logger.error(json.dumps(response))
|
|
return {"payload": response, "status": 500}
|
|
|
|
#
|
|
# Get indexes configuration for a given tenant
|
|
#
|
|
|
|
def post_tenant_idx_settings(self, request_info, **kwargs):
|
|
"""
|
|
| trackme url="/services/trackme/v2/vtenants/tenant_idx_settings" mode="post" body="{'tenant_id': 'mytenant'}"
|
|
"""
|
|
|
|
tenant_id = None
|
|
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"]
|
|
|
|
# Optionally filter on a given index stanza name
|
|
try:
|
|
idx_stanza = resp_dict["idx_stanza"]
|
|
except Exception as e:
|
|
idx_stanza = "all"
|
|
if not idx_stanza in (
|
|
"all",
|
|
"trackme_summary_idx",
|
|
"trackme_audit_idx",
|
|
"trackme_metric_idx",
|
|
"trackme_notable_idx",
|
|
):
|
|
idx_stanza = "all"
|
|
|
|
else:
|
|
# body is required in this endpoint, if not submitted describe the usage
|
|
describe = True
|
|
|
|
if describe or not resp_dict:
|
|
response = {
|
|
"describe": "This endpoint retrieves indexes settings for a given tenant returned as a JSON object, it requires a POST call with the following data:",
|
|
"resource_desc": "Get indexes configuration for a virtual tenant",
|
|
"resource_spl_example": '| trackme url="/services/trackme/v2/vtenants/tenant_idx_settings" mode="post" body="{\'tenant_id\': \'mytenant\'}"',
|
|
"options": [
|
|
{
|
|
"tenant_id": "The tenant identifier",
|
|
"idx_stanza": "Optional, the index stanza, defaults to: all, valid additional options are: trackme_summary_idx | trackme_audit_idx | trackme_metric_idx | trackme_notable_idx",
|
|
}
|
|
],
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# TrackMe reqinfo
|
|
reqinfo = trackme_reqinfo(
|
|
request_info.system_authtoken, request_info.server_rest_uri
|
|
)
|
|
trackmeconf = reqinfo["trackme_conf"]["index_settings"]
|
|
|
|
# set global indexes
|
|
global_trackme_summary_idx = trackmeconf["trackme_summary_idx"]
|
|
global_trackme_audit_idx = trackmeconf["trackme_audit_idx"]
|
|
global_trackme_metric_idx = trackmeconf["trackme_metric_idx"]
|
|
global_trackme_notable_idx = trackmeconf["trackme_notable_idx"]
|
|
|
|
# small function to verify the local idx in the next block
|
|
def get_index_value(
|
|
local_tenant_idx_dict, key, global_value, tenant_idx_settings
|
|
):
|
|
try:
|
|
return local_tenant_idx_dict[key]
|
|
except Exception as e:
|
|
return global_value
|
|
|
|
try:
|
|
collection_name = "kv_trackme_virtual_tenants"
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.session_key,
|
|
timeout=600,
|
|
)
|
|
collection = service.kvstore[collection_name]
|
|
|
|
# Define the KV query search string
|
|
query_string = {
|
|
"tenant_id": tenant_id,
|
|
}
|
|
|
|
try:
|
|
vtenant_record = collection.data.query(query=json.dumps(query_string))[
|
|
0
|
|
]
|
|
key = vtenant_record.get("_key")
|
|
tenant_idx_settings = vtenant_record.get("tenant_idx_settings")
|
|
|
|
except Exception as e:
|
|
key = None
|
|
|
|
if key:
|
|
# If the tenant_idx_settings record does not equal to global, attempt to the JSON config
|
|
if tenant_idx_settings != "global":
|
|
try:
|
|
local_tenant_idx_dict = json.loads(tenant_idx_settings)
|
|
trackme_summary_idx = get_index_value(
|
|
local_tenant_idx_dict,
|
|
"trackme_summary_idx",
|
|
global_trackme_summary_idx,
|
|
tenant_idx_settings,
|
|
)
|
|
trackme_audit_idx = get_index_value(
|
|
local_tenant_idx_dict,
|
|
"trackme_audit_idx",
|
|
global_trackme_audit_idx,
|
|
tenant_idx_settings,
|
|
)
|
|
trackme_metric_idx = get_index_value(
|
|
local_tenant_idx_dict,
|
|
"trackme_metric_idx",
|
|
global_trackme_metric_idx,
|
|
tenant_idx_settings,
|
|
)
|
|
trackme_notable_idx = get_index_value(
|
|
local_tenant_idx_dict,
|
|
"trackme_notable_idx",
|
|
global_trackme_notable_idx,
|
|
tenant_idx_settings,
|
|
)
|
|
|
|
# final dict
|
|
tenant_idx_dict = {
|
|
"trackme_summary_idx": trackme_summary_idx,
|
|
"trackme_audit_idx": trackme_audit_idx,
|
|
"trackme_metric_idx": trackme_metric_idx,
|
|
"trackme_notable_idx": trackme_notable_idx,
|
|
}
|
|
|
|
except Exception as e:
|
|
# use global if load failed
|
|
tenant_idx_dict = {
|
|
"trackme_summary_idx": global_trackme_summary_idx,
|
|
"trackme_audit_idx": global_trackme_audit_idx,
|
|
"trackme_metric_idx": global_trackme_metric_idx,
|
|
"trackme_notable_idx": global_trackme_notable_idx,
|
|
}
|
|
|
|
# use global otherwise
|
|
else:
|
|
tenant_idx_dict = {
|
|
"trackme_summary_idx": global_trackme_summary_idx,
|
|
"trackme_audit_idx": global_trackme_audit_idx,
|
|
"trackme_metric_idx": global_trackme_metric_idx,
|
|
"trackme_notable_idx": global_trackme_notable_idx,
|
|
}
|
|
|
|
# Render
|
|
if idx_stanza == "all":
|
|
return {"payload": tenant_idx_dict, "status": 200}
|
|
else:
|
|
return {
|
|
"payload": {idx_stanza: tenant_idx_dict[idx_stanza]},
|
|
"status": 200,
|
|
}
|
|
|
|
else: # return default
|
|
|
|
if idx_stanza == "all":
|
|
return {
|
|
"payload": {
|
|
"trackme_summary_idx": trackmeconf["trackme_summary_idx"],
|
|
"trackme_audit_idx": trackmeconf["trackme_audit_idx"],
|
|
"trackme_metric_idx": trackmeconf["trackme_metric_idx"],
|
|
"trackme_notable_idx": trackmeconf["trackme_notable_idx"],
|
|
},
|
|
"status": 200,
|
|
}
|
|
else:
|
|
return {
|
|
"payload": {idx_stanza: trackmeconf[idx_stanza]},
|
|
"status": 200,
|
|
}
|
|
|
|
except Exception as e:
|
|
response = {
|
|
"action": "failure",
|
|
"response": f'an exception was encountered, exception="{str(e)}"',
|
|
}
|
|
logger.error(json.dumps(response))
|
|
return {"payload": response, "status": 500}
|
|
|
|
#
|
|
# Get vtenants account configuration
|
|
#
|
|
|
|
def post_vtenants_accounts(self, request_info, **kwargs):
|
|
tenant_id = None
|
|
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
|
|
# tenant_id is optional
|
|
tenant_id = resp_dict.get("tenant_id", None)
|
|
else:
|
|
describe = False
|
|
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves all vtenants accounts and return these as a JSON object, it requires a POST call with the following data:",
|
|
"resource_desc": "Get virtual tenants accounts",
|
|
"resource_spl_example": '| trackme url="/services/trackme/v2/vtenants/vtenants_accounts" mode="post" body="{\'tenant_id\': \'mytenant\'}"',
|
|
"options": [
|
|
{
|
|
"tenant_id": "Optional, The tenant identifier",
|
|
}
|
|
],
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# set service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.session_key,
|
|
timeout=600,
|
|
)
|
|
|
|
# conf
|
|
conf_file = "trackme_settings"
|
|
confs = service.confs[str(conf_file)]
|
|
|
|
# get vtenant account
|
|
conf_file = "trackme_vtenants"
|
|
|
|
# if there are no account, raise an exception, otherwise what we would do here?
|
|
try:
|
|
confs = service.confs[str(conf_file)]
|
|
except Exception as e:
|
|
confs = None
|
|
|
|
# init
|
|
trackme_vtenant_conf = {}
|
|
|
|
# get accounts
|
|
if confs:
|
|
for stanza in confs:
|
|
# Store key-value pairs from the stanza content in the corresponding sub-dictionary
|
|
if not tenant_id or stanza.name == str(tenant_id):
|
|
trackme_vtenant_conf[stanza.name] = {}
|
|
for stanzakey, stanzavalue in stanza.content.items():
|
|
logger.debug(
|
|
f'get virtual tenant account, Processing stanzakey="{stanzakey}", stanzavalue="{stanzavalue}"'
|
|
)
|
|
trackme_vtenant_conf[stanza.name][stanzakey] = stanzavalue
|
|
|
|
# check that we have a field alias defined in trackme_vtenant_conf[stanza.name], otherwise add it equal to stanza.name
|
|
if not trackme_vtenant_conf[stanza.name].get("alias"):
|
|
trackme_vtenant_conf[stanza.name]["alias"] = stanza.name
|
|
|
|
#
|
|
# mloutliers:
|
|
# - loop troough each component in mloutliers_allowlist,
|
|
# for each define a new key as mloutliers_<component> which gets 0 if mloutliers is disabled, 0 if enabled and not in the list, 1 if enabled and in the list
|
|
mloutliers = int(
|
|
trackme_vtenant_conf[stanza.name].get("mloutliers", 0)
|
|
)
|
|
|
|
# outliers_allowlist
|
|
outliers_allowlist = trackme_vtenant_conf[stanza.name].get(
|
|
"mloutliers_allowlist", "dsm,dhm,flx,wlk,fqm"
|
|
)
|
|
|
|
# Define the components
|
|
outliers_components = ["dsm", "dhm", "flx", "wlk", "fqm"]
|
|
|
|
# Convert the allowlist to a set for faster lookups
|
|
mloutliers_set = set(outliers_allowlist.split(","))
|
|
|
|
# Create a dictionary dynamically
|
|
mloutliers_dict = {
|
|
f"mloutliers_{comp}": (
|
|
1 if comp in mloutliers_set and mloutliers == 1 else 0
|
|
)
|
|
for comp in outliers_components
|
|
}
|
|
|
|
# If you need separate variables, you can unpack the dictionary
|
|
trackme_vtenant_conf[stanza.name]["mloutliers_dsm"] = (
|
|
mloutliers_dict["mloutliers_dsm"]
|
|
)
|
|
trackme_vtenant_conf[stanza.name]["mloutliers_dhm"] = (
|
|
mloutliers_dict["mloutliers_dhm"]
|
|
)
|
|
trackme_vtenant_conf[stanza.name]["mloutliers_flx"] = (
|
|
mloutliers_dict["mloutliers_flx"]
|
|
)
|
|
trackme_vtenant_conf[stanza.name]["mloutliers_fqm"] = (
|
|
mloutliers_dict["mloutliers_fqm"]
|
|
)
|
|
trackme_vtenant_conf[stanza.name]["mloutliers_wlk"] = (
|
|
mloutliers_dict["mloutliers_wlk"]
|
|
)
|
|
|
|
# response
|
|
try:
|
|
if tenant_id:
|
|
return {"payload": trackme_vtenant_conf[tenant_id], "status": 200}
|
|
else:
|
|
return {"payload": trackme_vtenant_conf, "status": 200}
|
|
except Exception as e:
|
|
msg = f"an exception occurred, either you have requested a wrong tenant_id, either this installation is corrupted? exception={str(e)}"
|
|
logger.error(msg)
|
|
return {"payload": msg, "status": 500}
|
|
|
|
else:
|
|
return {"payload": trackme_vtenant_conf, "status": 200}
|
|
|
|
#
|
|
# Get tenant score configuration
|
|
#
|
|
|
|
def get_tenant_score_config(self, request_info, **kwargs):
|
|
"""
|
|
| trackme url=/services/trackme/v2/vtenants/tenant_score_config mode=get params="{'tenant_id': 'mytenant'}"
|
|
"""
|
|
|
|
# Declare main variables
|
|
tenant_id = None
|
|
describe = False
|
|
|
|
# Retrieve from query parameters
|
|
try:
|
|
params_dict = request_info.raw_args["query_parameters"]
|
|
except Exception as e:
|
|
params_dict = None
|
|
|
|
if params_dict is not None:
|
|
try:
|
|
tenant_id = params_dict.get("tenant_id")
|
|
except Exception as e:
|
|
tenant_id = None
|
|
|
|
# Check if describe is requested
|
|
try:
|
|
describe_param = params_dict.get("describe")
|
|
if describe_param in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
else:
|
|
describe = True
|
|
|
|
if describe or tenant_id is None:
|
|
response = {
|
|
"describe": "This endpoint retrieves the impact score configuration for a virtual tenant, it requires a GET call with the following query parameters:",
|
|
"resource_desc": "Get virtual tenant impact score configuration",
|
|
"resource_spl_example": "| trackme url=/services/trackme/v2/vtenants/tenant_score_config mode=get params=\"{'tenant_id': 'mytenant'}\"",
|
|
"options": [
|
|
{
|
|
"tenant_id": "The tenant identifier",
|
|
"describe": "OPTIONAL: set to true to get endpoint description",
|
|
}
|
|
],
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# Get vtenant account configuration via REST call
|
|
url = f"{request_info.server_rest_uri}/servicesNS/nobody/trackme/trackme_vtenants/{tenant_id}"
|
|
vtenant_data = {}
|
|
vtenant_account_found = False
|
|
|
|
try:
|
|
# Get current vtenant account configuration
|
|
response = requests.get(
|
|
url,
|
|
headers={"Authorization": f"Splunk {request_info.system_authtoken}"},
|
|
verify=False,
|
|
params={"output_mode": "json"},
|
|
timeout=600,
|
|
)
|
|
if response.status_code in (200, 201, 204):
|
|
logger.info(f"successfully retrieved vtenant configuration")
|
|
vtenant_data_json = response.json()
|
|
vtenant_data_current = vtenant_data_json["entry"][0]["content"]
|
|
vtenant_account_found = True
|
|
vtenant_data = dict(vtenant_data_current)
|
|
else:
|
|
error_msg = f"failed to retrieve vtenant configuration, status_code={response.status_code}"
|
|
logger.error(error_msg)
|
|
|
|
except Exception as e:
|
|
error_msg = f"failed to retrieve vtenant configuration, exception={str(e)}"
|
|
logger.error(error_msg)
|
|
|
|
if not vtenant_account_found:
|
|
error_msg = f'tenant_id="{tenant_id}" cannot be found'
|
|
logger.error(error_msg)
|
|
return {
|
|
"payload": {
|
|
"response": error_msg,
|
|
},
|
|
"status": 404,
|
|
}
|
|
|
|
# Extract all impact_score_* fields from the tenant account configuration
|
|
# If a field is not present, use the default from vtenant_account_default
|
|
score_config = {}
|
|
for field_name in vtenant_account_default.keys():
|
|
if field_name.startswith("impact_score_"):
|
|
score_config[field_name] = vtenant_data.get(
|
|
field_name, vtenant_account_default[field_name]
|
|
)
|
|
|
|
response = {
|
|
"tenant_id": tenant_id,
|
|
"score_config": score_config,
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|