From 823fe34d24f1a1d09e608145e18f0e521b2dca41 Mon Sep 17 00:00:00 2001 From: Andrea Avancini Date: Tue, 16 Jun 2020 14:18:11 +0200 Subject: [PATCH 01/38] Parameter evaluation, macro resolution, update executions, send ExecuteCommand message for ExecuteCommand Endpoint --- lib/icinga/apiactions.cpp | 183 +++++++++++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 1 deletion(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index dd2def790..01f3b7a59 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -555,5 +555,186 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { - return ApiActions::CreateResult(501, "Not implemented"); + Checkable::Ptr checkable = static_pointer_cast(object); + if (! checkable) + return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); + + ObjectLock oLock (checkable); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + String endpoint = "$command_endpoint$"; + if (params->Contains("endpoint")) + endpoint = params->Get("endpoint"); + + /* Resolve endpoint macro */ + MacroProcessor::ResolverList resolvers; + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); + + Dictionary::Ptr resolvedMacros; + bool useResolvedMacros; + + if (params->Contains("macros")) { + resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); + useResolvedMacros = true; + } else { + resolvedMacros = new Dictionary(); + useResolvedMacros = false; + } + + String resolved_endpoint = MacroProcessor::ResolveMacros(endpoint, + resolvers, + checkable->GetLastCheckResult(), + nullptr, + MacroProcessor::EscapeCallback(), + resolvedMacros, + useResolvedMacros); + + /* Check if resolved_endpoint is not empty */ + if (resolved_endpoint.IsEmpty()) + return ApiActions::CreateResult(400, "Endpoint must not be empty."); + + /* Check if endpoint exists */ + if (!Endpoint::GetByName(resolved_endpoint)) + return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); + + /* Get command_type */ + String command_type = "EventCommand"; + if (params->Contains("command_type")) + command_type = HttpUtility::GetLastParameter(params, "command_type"); + + /* Validate command_type */ + if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + + /* Get command */ + String command; + if (!params->Contains("command")) { + if (command_type == "CheckCommand" ) { + command = "$check_command$"; + } else if (command_type == "EventCommand") { + command = "$event_command$"; + } else if (command_type == "NotificationCommand") { + command = "$notification_command$"; + } else { + return ApiActions::CreateResult(404, "Can't infer 'command' from command_type '" + command_type + "'"); + } + } else { + command = HttpUtility::GetLastParameter(params, "command"); + } + + /* Resolve command macro */ + String resolved_command = MacroProcessor::ResolveMacros(command, + resolvers, + checkable->GetLastCheckResult(), + nullptr, + MacroProcessor::EscapeCallback(), + resolvedMacros, + useResolvedMacros); + + /* Check if resolved_command is not empty */ + if (resolved_command.IsEmpty()) + return ApiActions::CreateResult(400, "Command must not be empty."); + + /* Check if resolved_command exists and it is of type command_type */ + if (command_type == "CheckCommand") { + if (!CheckCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else if (command_type == "EventCommand") { + if (!EventCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else if (command_type == "NotificationCommand") { + if (!EventCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + + /* Get TTL param */ + double ttl = 0; /* FIXME default value? */ + if (params->Contains("ttl")) + ttl = HttpUtility::GetLastParameter(params, "ttl"); + + if (ttl <= 0) + return ApiActions::CreateResult(400, "ttl must be greater than 0"); + + /* Get wait param */ + bool wait = false; + if (params->Contains("wait")) + wait = HttpUtility::GetLastParameter(params, "wait"); + + /* This generates a UUID */ + String uuid = Utility::NewUniqueID(); + + /* Create the deadline */ + double deadline = Utility::GetTime() + ttl; + + /* Update executions */ + Dictionary::Ptr pending_execution = new Dictionary(); + pending_execution->Set("pending", true); + pending_execution->Set("deadline", deadline); + Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) + executions = new Dictionary(); + executions->Set(uuid, pending_execution); + checkable->SetExecutions(executions); + + /* Broadcast the update */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return ApiActions::CreateResult(404, "No ApiListener instance available."); + + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", host->GetName()); + if (service) + updateParams->Set("service", service->GetShortName()); + updateParams->Set("executions", executions); + + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", updateParams); + + /* FIXME origin? */ + MessageOrigin::Ptr origin = new MessageOrigin(); + listener->RelayMessage(origin, checkable, updateMessage, true); + + /* Execute command */ + Dictionary::Ptr execMessage = new Dictionary(); + execMessage->Set("jsonrpc", "2.0"); + execMessage->Set("method", "event::ExecuteCommand"); + + /* TODO set the right params */ + Dictionary::Ptr execParams = new Dictionary(); + execMessage->Set("params", execParams); + execParams->Set("command_type", command_type); + execParams->Set("command", resolved_command); + execParams->Set("host", host->GetName()); + if (service) + execParams->Set("service", service->GetShortName()); + + /* + * FIXME? + * If the host/service object specifies the 'check_timeout' attribute, + * forward this to the remote endpoint to limit the command execution time. + */ + if (!checkable->GetCheckTimeout().IsEmpty()) + execParams->Set("check_timeout", checkable->GetCheckTimeout()); + + execParams->Set("macros", resolvedMacros); + execParams->Set("source", uuid); + execParams->Set("deadline", deadline); + + listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); + + /* TODO handle the wait */ + + Dictionary::Ptr result = new Dictionary(); + result->Set(checkable->GetName(), uuid); + return ApiActions::CreateResult(202, "Accepted", result); } + From 159e3d294578057f22c1ba81c8c94074e706ed04 Mon Sep 17 00:00:00 2001 From: Benjamin Groeber Date: Tue, 23 Jun 2020 11:41:15 +0200 Subject: [PATCH 02/38] Remove superfluous else clause --- lib/icinga/apiactions.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 01f3b7a59..493a8db6d 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -621,8 +621,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, command = "$event_command$"; } else if (command_type == "NotificationCommand") { command = "$notification_command$"; - } else { - return ApiActions::CreateResult(404, "Can't infer 'command' from command_type '" + command_type + "'"); } } else { command = HttpUtility::GetLastParameter(params, "command"); From 2bb6f9d38dac35aa8da63e3ec439f85580ccf834 Mon Sep 17 00:00:00 2001 From: Benjamin Groeber Date: Tue, 23 Jun 2020 11:41:49 +0200 Subject: [PATCH 03/38] Fix formatting of long fn --- lib/icinga/apiactions.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 493a8db6d..82ab05b74 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -587,13 +587,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, useResolvedMacros = false; } - String resolved_endpoint = MacroProcessor::ResolveMacros(endpoint, - resolvers, - checkable->GetLastCheckResult(), - nullptr, - MacroProcessor::EscapeCallback(), - resolvedMacros, - useResolvedMacros); + String resolved_endpoint = MacroProcessor::ResolveMacros( + endpoint, resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, + useResolvedMacros + ); /* Check if resolved_endpoint is not empty */ if (resolved_endpoint.IsEmpty()) @@ -627,13 +625,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, } /* Resolve command macro */ - String resolved_command = MacroProcessor::ResolveMacros(command, - resolvers, - checkable->GetLastCheckResult(), - nullptr, - MacroProcessor::EscapeCallback(), - resolvedMacros, - useResolvedMacros); + String resolved_command = MacroProcessor::ResolveMacros( + command, resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, + useResolvedMacros + ); /* Check if resolved_command is not empty */ if (resolved_command.IsEmpty()) @@ -735,4 +731,3 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); } - From e9b6c68fd531d8e53547c5e114adb0e838fc454b Mon Sep 17 00:00:00 2001 From: Benjamin Groeber Date: Tue, 23 Jun 2020 11:44:26 +0200 Subject: [PATCH 04/38] Use tabs instead of spaces --- lib/icinga/apiactions.cpp | 284 +++++++++++++++++++------------------- 1 file changed, 142 insertions(+), 142 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 82ab05b74..153cf7f69 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -555,179 +555,179 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { - Checkable::Ptr checkable = static_pointer_cast(object); - if (! checkable) - return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); + Checkable::Ptr checkable = static_pointer_cast(object); + if (! checkable) + return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); - ObjectLock oLock (checkable); + ObjectLock oLock (checkable); - Host::Ptr host; - Service::Ptr service; - tie(host, service) = GetHostService(checkable); + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); - String endpoint = "$command_endpoint$"; - if (params->Contains("endpoint")) - endpoint = params->Get("endpoint"); + String endpoint = "$command_endpoint$"; + if (params->Contains("endpoint")) + endpoint = params->Get("endpoint"); - /* Resolve endpoint macro */ - MacroProcessor::ResolverList resolvers; - if (service) - resolvers.emplace_back("service", service); - resolvers.emplace_back("host", host); - resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); + /* Resolve endpoint macro */ + MacroProcessor::ResolverList resolvers; + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); - Dictionary::Ptr resolvedMacros; - bool useResolvedMacros; + Dictionary::Ptr resolvedMacros; + bool useResolvedMacros; - if (params->Contains("macros")) { - resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); - useResolvedMacros = true; - } else { - resolvedMacros = new Dictionary(); - useResolvedMacros = false; - } + if (params->Contains("macros")) { + resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); + useResolvedMacros = true; + } else { + resolvedMacros = new Dictionary(); + useResolvedMacros = false; + } - String resolved_endpoint = MacroProcessor::ResolveMacros( - endpoint, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, - useResolvedMacros - ); + String resolved_endpoint = MacroProcessor::ResolveMacros( + endpoint, resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, + useResolvedMacros + ); - /* Check if resolved_endpoint is not empty */ - if (resolved_endpoint.IsEmpty()) - return ApiActions::CreateResult(400, "Endpoint must not be empty."); + /* Check if resolved_endpoint is not empty */ + if (resolved_endpoint.IsEmpty()) + return ApiActions::CreateResult(400, "Endpoint must not be empty."); - /* Check if endpoint exists */ - if (!Endpoint::GetByName(resolved_endpoint)) - return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); + /* Check if endpoint exists */ + if (!Endpoint::GetByName(resolved_endpoint)) + return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); - /* Get command_type */ - String command_type = "EventCommand"; - if (params->Contains("command_type")) - command_type = HttpUtility::GetLastParameter(params, "command_type"); + /* Get command_type */ + String command_type = "EventCommand"; + if (params->Contains("command_type")) + command_type = HttpUtility::GetLastParameter(params, "command_type"); - /* Validate command_type */ - if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") - return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + /* Validate command_type */ + if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); - /* Get command */ - String command; - if (!params->Contains("command")) { - if (command_type == "CheckCommand" ) { - command = "$check_command$"; - } else if (command_type == "EventCommand") { - command = "$event_command$"; - } else if (command_type == "NotificationCommand") { - command = "$notification_command$"; - } - } else { - command = HttpUtility::GetLastParameter(params, "command"); - } + /* Get command */ + String command; + if (!params->Contains("command")) { + if (command_type == "CheckCommand" ) { + command = "$check_command$"; + } else if (command_type == "EventCommand") { + command = "$event_command$"; + } else if (command_type == "NotificationCommand") { + command = "$notification_command$"; + } + } else { + command = HttpUtility::GetLastParameter(params, "command"); + } - /* Resolve command macro */ - String resolved_command = MacroProcessor::ResolveMacros( - command, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, - useResolvedMacros - ); + /* Resolve command macro */ + String resolved_command = MacroProcessor::ResolveMacros( + command, resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, + useResolvedMacros + ); - /* Check if resolved_command is not empty */ - if (resolved_command.IsEmpty()) - return ApiActions::CreateResult(400, "Command must not be empty."); + /* Check if resolved_command is not empty */ + if (resolved_command.IsEmpty()) + return ApiActions::CreateResult(400, "Command must not be empty."); - /* Check if resolved_command exists and it is of type command_type */ - if (command_type == "CheckCommand") { - if (!CheckCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); - } else if (command_type == "EventCommand") { - if (!EventCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); - } else if (command_type == "NotificationCommand") { - if (!EventCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); - } else - return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + /* Check if resolved_command exists and it is of type command_type */ + if (command_type == "CheckCommand") { + if (!CheckCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else if (command_type == "EventCommand") { + if (!EventCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else if (command_type == "NotificationCommand") { + if (!EventCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); - /* Get TTL param */ - double ttl = 0; /* FIXME default value? */ - if (params->Contains("ttl")) - ttl = HttpUtility::GetLastParameter(params, "ttl"); + /* Get TTL param */ + double ttl = 0; /* FIXME default value? */ + if (params->Contains("ttl")) + ttl = HttpUtility::GetLastParameter(params, "ttl"); - if (ttl <= 0) - return ApiActions::CreateResult(400, "ttl must be greater than 0"); + if (ttl <= 0) + return ApiActions::CreateResult(400, "ttl must be greater than 0"); - /* Get wait param */ - bool wait = false; - if (params->Contains("wait")) - wait = HttpUtility::GetLastParameter(params, "wait"); + /* Get wait param */ + bool wait = false; + if (params->Contains("wait")) + wait = HttpUtility::GetLastParameter(params, "wait"); - /* This generates a UUID */ - String uuid = Utility::NewUniqueID(); + /* This generates a UUID */ + String uuid = Utility::NewUniqueID(); - /* Create the deadline */ - double deadline = Utility::GetTime() + ttl; + /* Create the deadline */ + double deadline = Utility::GetTime() + ttl; - /* Update executions */ - Dictionary::Ptr pending_execution = new Dictionary(); - pending_execution->Set("pending", true); - pending_execution->Set("deadline", deadline); - Dictionary::Ptr executions = checkable->GetExecutions(); - if (!executions) - executions = new Dictionary(); - executions->Set(uuid, pending_execution); - checkable->SetExecutions(executions); + /* Update executions */ + Dictionary::Ptr pending_execution = new Dictionary(); + pending_execution->Set("pending", true); + pending_execution->Set("deadline", deadline); + Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) + executions = new Dictionary(); + executions->Set(uuid, pending_execution); + checkable->SetExecutions(executions); - /* Broadcast the update */ - ApiListener::Ptr listener = ApiListener::GetInstance(); - if (!listener) - return ApiActions::CreateResult(404, "No ApiListener instance available."); + /* Broadcast the update */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return ApiActions::CreateResult(404, "No ApiListener instance available."); - Dictionary::Ptr updateParams = new Dictionary(); - updateParams->Set("host", host->GetName()); - if (service) - updateParams->Set("service", service->GetShortName()); - updateParams->Set("executions", executions); + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", host->GetName()); + if (service) + updateParams->Set("service", service->GetShortName()); + updateParams->Set("executions", executions); - Dictionary::Ptr updateMessage = new Dictionary(); - updateMessage->Set("jsonrpc", "2.0"); - updateMessage->Set("method", "event::UpdateExecutions"); - updateMessage->Set("params", updateParams); + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", updateParams); - /* FIXME origin? */ - MessageOrigin::Ptr origin = new MessageOrigin(); - listener->RelayMessage(origin, checkable, updateMessage, true); + /* FIXME origin? */ + MessageOrigin::Ptr origin = new MessageOrigin(); + listener->RelayMessage(origin, checkable, updateMessage, true); - /* Execute command */ - Dictionary::Ptr execMessage = new Dictionary(); - execMessage->Set("jsonrpc", "2.0"); - execMessage->Set("method", "event::ExecuteCommand"); + /* Execute command */ + Dictionary::Ptr execMessage = new Dictionary(); + execMessage->Set("jsonrpc", "2.0"); + execMessage->Set("method", "event::ExecuteCommand"); - /* TODO set the right params */ - Dictionary::Ptr execParams = new Dictionary(); - execMessage->Set("params", execParams); - execParams->Set("command_type", command_type); - execParams->Set("command", resolved_command); - execParams->Set("host", host->GetName()); - if (service) - execParams->Set("service", service->GetShortName()); + /* TODO set the right params */ + Dictionary::Ptr execParams = new Dictionary(); + execMessage->Set("params", execParams); + execParams->Set("command_type", command_type); + execParams->Set("command", resolved_command); + execParams->Set("host", host->GetName()); + if (service) + execParams->Set("service", service->GetShortName()); - /* - * FIXME? - * If the host/service object specifies the 'check_timeout' attribute, - * forward this to the remote endpoint to limit the command execution time. - */ - if (!checkable->GetCheckTimeout().IsEmpty()) - execParams->Set("check_timeout", checkable->GetCheckTimeout()); + /* + * FIXME? + * If the host/service object specifies the 'check_timeout' attribute, + * forward this to the remote endpoint to limit the command execution time. + */ + if (!checkable->GetCheckTimeout().IsEmpty()) + execParams->Set("check_timeout", checkable->GetCheckTimeout()); - execParams->Set("macros", resolvedMacros); - execParams->Set("source", uuid); - execParams->Set("deadline", deadline); + execParams->Set("macros", resolvedMacros); + execParams->Set("source", uuid); + execParams->Set("deadline", deadline); - listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); + listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); - /* TODO handle the wait */ + /* TODO handle the wait */ - Dictionary::Ptr result = new Dictionary(); - result->Set(checkable->GetName(), uuid); + Dictionary::Ptr result = new Dictionary(); + result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); } From cc6fa66ba6e20d3605817dc9340b4c12d613615f Mon Sep 17 00:00:00 2001 From: Benjamin Groeber Date: Tue, 23 Jun 2020 11:52:22 +0200 Subject: [PATCH 05/38] Allow apiactions to return sucecss http codes different from 200 --- lib/remote/actionshandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 7b61cd5b9..e94debb63 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -94,7 +94,7 @@ bool ActionsHandler::HandleRequest( int statusCode = 500; for (const Dictionary::Ptr& res : results) { - if (res->Contains("code") && res->Get("code") == 200) { + if (res->Contains("code") && res->Get("code") >= 200 && res->Get("code") <= 299) { statusCode = 200; break; } From 714e75bbd1102964c05b6a006e0a5e35bb823cd3 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 09:33:23 +0200 Subject: [PATCH 06/38] Remove wait parameter, ttl is required, remove unnecessary checks --- lib/icinga/apiactions.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 153cf7f69..0a396a656 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -579,6 +579,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, Dictionary::Ptr resolvedMacros; bool useResolvedMacros; + if (params->Contains("macros") && !params->Get("macros").IsObjectType()) { + return ApiActions::CreateResult(400, "macros must be a dictionary"); + } + if (params->Contains("macros")) { resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); useResolvedMacros = true; @@ -593,10 +597,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, useResolvedMacros ); - /* Check if resolved_endpoint is not empty */ - if (resolved_endpoint.IsEmpty()) - return ApiActions::CreateResult(400, "Endpoint must not be empty."); - /* Check if endpoint exists */ if (!Endpoint::GetByName(resolved_endpoint)) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); @@ -645,22 +645,16 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, } else if (command_type == "NotificationCommand") { if (!EventCommand::GetByName(resolved_command)) return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); - } else - return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + } /* Get TTL param */ - double ttl = 0; /* FIXME default value? */ - if (params->Contains("ttl")) - ttl = HttpUtility::GetLastParameter(params, "ttl"); + if (!params->Contains("ttl")) + return ApiActions::CreateResult(400, "ttl is required"); + double ttl = HttpUtility::GetLastParameter(params, "ttl"); if (ttl <= 0) return ApiActions::CreateResult(400, "ttl must be greater than 0"); - /* Get wait param */ - bool wait = false; - if (params->Contains("wait")) - wait = HttpUtility::GetLastParameter(params, "wait"); - /* This generates a UUID */ String uuid = Utility::NewUniqueID(); @@ -725,8 +719,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); - /* TODO handle the wait */ - Dictionary::Ptr result = new Dictionary(); result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); From 82444a59e2b97132052ba60fe444e3a5289b144d Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 12:32:12 +0200 Subject: [PATCH 07/38] Fix resolve macros. Check if endpoint is local or remote --- lib/icinga/apiactions.cpp | 115 ++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 0a396a656..199eb68be 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -569,32 +569,23 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (params->Contains("endpoint")) endpoint = params->Get("endpoint"); - /* Resolve endpoint macro */ MacroProcessor::ResolverList resolvers; + if (params->Contains("macros")) { + if (params->Get("macros").IsObjectType()) + resolvers.emplace_back("override",HttpUtility::GetLastParameter(params, "macros")); + else + return ApiActions::CreateResult(400, "macros must be a dictionary"); + } + if (service) resolvers.emplace_back("service", service); resolvers.emplace_back("host", host); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); - Dictionary::Ptr resolvedMacros; - bool useResolvedMacros; - - if (params->Contains("macros") && !params->Get("macros").IsObjectType()) { - return ApiActions::CreateResult(400, "macros must be a dictionary"); - } - - if (params->Contains("macros")) { - resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); - useResolvedMacros = true; - } else { - resolvedMacros = new Dictionary(); - useResolvedMacros = false; - } - String resolved_endpoint = MacroProcessor::ResolveMacros( endpoint, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, - useResolvedMacros + nullptr, MacroProcessor::EscapeCallback(), nullptr, + false ); /* Check if endpoint exists */ @@ -626,9 +617,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Resolve command macro */ String resolved_command = MacroProcessor::ResolveMacros( - command, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, - useResolvedMacros + command, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false ); /* Check if resolved_command is not empty */ @@ -671,55 +661,58 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, executions->Set(uuid, pending_execution); checkable->SetExecutions(executions); - /* Broadcast the update */ - ApiListener::Ptr listener = ApiListener::GetInstance(); - if (!listener) - return ApiActions::CreateResult(404, "No ApiListener instance available."); + Endpoint::Ptr endpointPtr = Endpoint::GetByName(resolved_endpoint); + bool local = !endpointPtr || endpointPtr == Endpoint::GetLocalEndpoint(); + if (local) { + /* TODO */ + } else { + /* Broadcast the update */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return ApiActions::CreateResult(404, "No ApiListener instance available."); - Dictionary::Ptr updateParams = new Dictionary(); - updateParams->Set("host", host->GetName()); - if (service) - updateParams->Set("service", service->GetShortName()); - updateParams->Set("executions", executions); + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", host->GetName()); + if (service) + updateParams->Set("service", service->GetShortName()); + updateParams->Set("executions", executions); - Dictionary::Ptr updateMessage = new Dictionary(); - updateMessage->Set("jsonrpc", "2.0"); - updateMessage->Set("method", "event::UpdateExecutions"); - updateMessage->Set("params", updateParams); + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", updateParams); - /* FIXME origin? */ - MessageOrigin::Ptr origin = new MessageOrigin(); - listener->RelayMessage(origin, checkable, updateMessage, true); + MessageOrigin::Ptr origin = new MessageOrigin(); + listener->SyncSendMessage(endpointPtr, updateMessage); - /* Execute command */ - Dictionary::Ptr execMessage = new Dictionary(); - execMessage->Set("jsonrpc", "2.0"); - execMessage->Set("method", "event::ExecuteCommand"); + /* Execute command */ + Dictionary::Ptr execMessage = new Dictionary(); + execMessage->Set("jsonrpc", "2.0"); + execMessage->Set("method", "event::ExecuteCommand"); - /* TODO set the right params */ - Dictionary::Ptr execParams = new Dictionary(); - execMessage->Set("params", execParams); - execParams->Set("command_type", command_type); - execParams->Set("command", resolved_command); - execParams->Set("host", host->GetName()); - if (service) - execParams->Set("service", service->GetShortName()); + /* TODO set the right params */ + Dictionary::Ptr execParams = new Dictionary(); + execMessage->Set("params", execParams); + execParams->Set("command_type", command_type); + execParams->Set("command", resolved_command); + execParams->Set("host", host->GetName()); + if (service) + execParams->Set("service", service->GetShortName()); - /* - * FIXME? - * If the host/service object specifies the 'check_timeout' attribute, - * forward this to the remote endpoint to limit the command execution time. - */ - if (!checkable->GetCheckTimeout().IsEmpty()) - execParams->Set("check_timeout", checkable->GetCheckTimeout()); + /* + * If the host/service object specifies the 'check_timeout' attribute, + * forward this to the remote endpoint to limit the command execution time. + */ + if (!checkable->GetCheckTimeout().IsEmpty()) + execParams->Set("check_timeout", checkable->GetCheckTimeout()); - execParams->Set("macros", resolvedMacros); - execParams->Set("source", uuid); - execParams->Set("deadline", deadline); + execParams->Set("source", uuid); + execParams->Set("deadline", deadline); - listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); + listener->SyncSendMessage(endpointPtr, execMessage); + } Dictionary::Ptr result = new Dictionary(); result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); -} +} \ No newline at end of file From 35bb329a599da926b66b2ce48b6cc6d6484a68be Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 12:33:44 +0200 Subject: [PATCH 08/38] Implement ClusterEvents::UpdateExecutionsAPIHandler --- lib/icinga/clusterevents.cpp | 46 ++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 29fe71702..e5c709bda 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -949,5 +949,47 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { - return "Not implemented"; -} + Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); + if (!endpoint) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() + << "': Invalid endpoint origin (client not allowed)."; + + return Empty; + } + + Host::Ptr host = Host::GetByName(params->Get("host")); + if (!host) + return Empty; + + Checkable::Ptr checkable; + + if (params->Contains("service")) + checkable = host->GetServiceByShortName(params->Get("service")); + else + checkable = host; + + if (!checkable) + return Empty; + + ObjectLock oLock (checkable); + + if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + return Empty; + } + + if (!params->Contains("executions")) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': missing executions."; + return Empty; + } + + Dictionary::Ptr executions = params->Get("executions"); + checkable->SetExecutions(executions); + + return Empty; +} \ No newline at end of file From 899076c77ba7d9d2c40c3277fca6aaaf7341fff4 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 14:29:03 +0200 Subject: [PATCH 09/38] Code format --- lib/icinga/apiactions.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 199eb68be..72053c093 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -584,8 +584,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, String resolved_endpoint = MacroProcessor::ResolveMacros( endpoint, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), nullptr, - false + nullptr, MacroProcessor::EscapeCallback(), nullptr, false ); /* Check if endpoint exists */ From fd761397ac9f5efa932fee45d4e42300fd4c5e95 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 15:39:42 +0200 Subject: [PATCH 10/38] Unify result status strings --- lib/icinga/apiactions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 72053c093..0f73e4a92 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -574,7 +574,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (params->Get("macros").IsObjectType()) resolvers.emplace_back("override",HttpUtility::GetLastParameter(params, "macros")); else - return ApiActions::CreateResult(400, "macros must be a dictionary"); + return ApiActions::CreateResult(400, "Parameter macros must be a dictionary."); } if (service) @@ -638,11 +638,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Get TTL param */ if (!params->Contains("ttl")) - return ApiActions::CreateResult(400, "ttl is required"); + return ApiActions::CreateResult(400, "Parameter ttl is required."); double ttl = HttpUtility::GetLastParameter(params, "ttl"); if (ttl <= 0) - return ApiActions::CreateResult(400, "ttl must be greater than 0"); + return ApiActions::CreateResult(400, "Parameter ttl must be greater than 0."); /* This generates a UUID */ String uuid = Utility::NewUniqueID(); From 50693d7ea9e3d029a0876924abbb1eaa0cea724f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 16:36:57 +0200 Subject: [PATCH 11/38] Add newline at the end of file --- lib/icinga/apiactions.cpp | 2 +- lib/icinga/clusterevents.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 0f73e4a92..1dca92f3d 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -714,4 +714,4 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, Dictionary::Ptr result = new Dictionary(); result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); -} \ No newline at end of file +} diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index e5c709bda..4dc6f8bc8 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -992,4 +992,4 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin checkable->SetExecutions(executions); return Empty; -} \ No newline at end of file +} From da82c1789163a8486142d7133e4072b62d7a9088 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 16:38:40 +0200 Subject: [PATCH 12/38] Call Endpoint::GetByName only once --- lib/icinga/apiactions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 1dca92f3d..a2002082a 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -588,7 +588,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, ); /* Check if endpoint exists */ - if (!Endpoint::GetByName(resolved_endpoint)) + Endpoint::Ptr endpointPtr = Endpoint::GetByName(resolved_endpoint); + if (!endpointPtr) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); /* Get command_type */ @@ -660,8 +661,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, executions->Set(uuid, pending_execution); checkable->SetExecutions(executions); - Endpoint::Ptr endpointPtr = Endpoint::GetByName(resolved_endpoint); - bool local = !endpointPtr || endpointPtr == Endpoint::GetLocalEndpoint(); + bool local = endpointPtr == Endpoint::GetLocalEndpoint(); if (local) { /* TODO */ } else { From 3f1b51b355e765c04e0187982e2dd4bb1c86c688 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 29 Jun 2020 09:28:02 +0200 Subject: [PATCH 13/38] Use RelayMessage instead of SyncSendMessage --- lib/icinga/apiactions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index a2002082a..4779e8b46 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -682,7 +682,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, updateMessage->Set("params", updateParams); MessageOrigin::Ptr origin = new MessageOrigin(); - listener->SyncSendMessage(endpointPtr, updateMessage); + listener->RelayMessage(origin, checkable, updateMessage, true); /* Execute command */ Dictionary::Ptr execMessage = new Dictionary(); @@ -708,7 +708,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("source", uuid); execParams->Set("deadline", deadline); - listener->SyncSendMessage(endpointPtr, execMessage); + listener->RelayMessage(origin, checkable, execMessage, true); } Dictionary::Ptr result = new Dictionary(); From 7077605880a16514d6ad63a4173b65aede14c322 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 29 Jun 2020 13:55:20 +0200 Subject: [PATCH 14/38] Use the right method to get the notification command --- lib/icinga/apiactions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 4779e8b46..1aa356dc0 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -633,7 +633,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (!EventCommand::GetByName(resolved_command)) return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); } else if (command_type == "NotificationCommand") { - if (!EventCommand::GetByName(resolved_command)) + if (!NotificationCommand::GetByName(resolved_command)) return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); } From 22dc2f23930ffd41d069177fa0967e76c7a1ccc2 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 13:43:00 +0200 Subject: [PATCH 15/38] Use SyncSendMessage for remote endpoint and call ExecuteCommandAPIHandler for local endpoint --- lib/icinga/apiactions.cpp | 45 ++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 1aa356dc0..677c00809 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -8,6 +8,7 @@ #include "icinga/checkcommand.hpp" #include "icinga/eventcommand.hpp" #include "icinga/notificationcommand.hpp" +#include "icinga/clusterevents.hpp" #include "remote/apiaction.hpp" #include "remote/apilistener.hpp" #include "remote/pkiutility.hpp" @@ -661,9 +662,28 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, executions->Set(uuid, pending_execution); checkable->SetExecutions(executions); + /* Create execution parameters */ + Dictionary::Ptr execParams = new Dictionary(); + execParams->Set("command_type", command_type); + execParams->Set("command", resolved_command); + execParams->Set("host", host->GetName()); + if (service) + execParams->Set("service", service->GetShortName()); + + /* + * If the host/service object specifies the 'check_timeout' attribute, + * forward this to the remote endpoint to limit the command execution time. + */ + if (!checkable->GetCheckTimeout().IsEmpty()) + execParams->Set("check_timeout", checkable->GetCheckTimeout()); + + execParams->Set("source", uuid); + execParams->Set("deadline", deadline); + bool local = endpointPtr == Endpoint::GetLocalEndpoint(); if (local) { - /* TODO */ + MessageOrigin::Ptr origin = new MessageOrigin(); + ClusterEvents::ExecuteCommandAPIHandler(origin, execParams); } else { /* Broadcast the update */ ApiListener::Ptr listener = ApiListener::GetInstance(); @@ -681,34 +701,15 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, updateMessage->Set("method", "event::UpdateExecutions"); updateMessage->Set("params", updateParams); - MessageOrigin::Ptr origin = new MessageOrigin(); - listener->RelayMessage(origin, checkable, updateMessage, true); + listener->SyncSendMessage(endpointPtr, updateMessage); /* Execute command */ Dictionary::Ptr execMessage = new Dictionary(); execMessage->Set("jsonrpc", "2.0"); execMessage->Set("method", "event::ExecuteCommand"); - - /* TODO set the right params */ - Dictionary::Ptr execParams = new Dictionary(); execMessage->Set("params", execParams); - execParams->Set("command_type", command_type); - execParams->Set("command", resolved_command); - execParams->Set("host", host->GetName()); - if (service) - execParams->Set("service", service->GetShortName()); - /* - * If the host/service object specifies the 'check_timeout' attribute, - * forward this to the remote endpoint to limit the command execution time. - */ - if (!checkable->GetCheckTimeout().IsEmpty()) - execParams->Set("check_timeout", checkable->GetCheckTimeout()); - - execParams->Set("source", uuid); - execParams->Set("deadline", deadline); - - listener->RelayMessage(origin, checkable, execMessage, true); + listener->SyncSendMessage(endpointPtr, execMessage); } Dictionary::Ptr result = new Dictionary(); From e4e0115c9d635be169b2eb95a790af727be59112 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 13:47:13 +0200 Subject: [PATCH 16/38] Get local endpoint if FromClient is not set and origin is local --- lib/icinga/clusterevents-check.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 96b3de146..515003661 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -77,7 +77,12 @@ void ClusterEvents::EnqueueCheck(const MessageOrigin::Ptr& origin, const Diction void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { - Endpoint::Ptr sourceEndpoint = origin->FromClient->GetEndpoint(); + Endpoint::Ptr sourceEndpoint; + if (origin->FromClient) { + sourceEndpoint = origin->FromClient->GetEndpoint(); + } else if (origin->IsLocal()){ + sourceEndpoint = Endpoint::GetLocalEndpoint(); + } if (!sourceEndpoint || (origin->FromZone && !Zone::GetLocalZone()->IsChildOf(origin->FromZone))) { Log(LogNotice, "ClusterEvents") From 94be948ff6c7cc7d6fcfebdf6f44581de28f4a9f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 13:51:42 +0200 Subject: [PATCH 17/38] Unify how to take macros parameter --- lib/icinga/apiactions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 677c00809..8c20a1259 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -572,8 +572,9 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::ResolverList resolvers; if (params->Contains("macros")) { - if (params->Get("macros").IsObjectType()) - resolvers.emplace_back("override",HttpUtility::GetLastParameter(params, "macros")); + Value macros = HttpUtility::GetLastParameter(params, "macros"); + if (macros.IsObjectType()) + resolvers.emplace_back("override", macros); else return ApiActions::CreateResult(400, "Parameter macros must be a dictionary."); } From 5c06256ac56bc8535e9c34a15ffd768618db385f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 15:41:47 +0200 Subject: [PATCH 18/38] Use dynamic_pointer_cast --- lib/icinga/apiactions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 8c20a1259..31ba6e184 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -556,8 +556,8 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { - Checkable::Ptr checkable = static_pointer_cast(object); - if (! checkable) + Checkable::Ptr checkable = dynamic_pointer_cast(object); + if (!checkable) return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); ObjectLock oLock (checkable); From 8f3617d0743b6470654a4dad4f20cf5e53cba8b5 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 15:48:29 +0200 Subject: [PATCH 19/38] Fix response strings --- lib/icinga/apiactions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 31ba6e184..6df22d82b 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -558,7 +558,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, { Checkable::Ptr checkable = dynamic_pointer_cast(object); if (!checkable) - return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); + return ApiActions::CreateResult(404, "Can't start a command execution for a non-existent object."); ObjectLock oLock (checkable); @@ -630,13 +630,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Check if resolved_command exists and it is of type command_type */ if (command_type == "CheckCommand") { if (!CheckCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } else if (command_type == "EventCommand") { if (!EventCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } else if (command_type == "NotificationCommand") { if (!NotificationCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } /* Get TTL param */ From 15dac855834cd8c9b6c38f77f6b9ab6a2a1b1b5a Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 15:51:15 +0200 Subject: [PATCH 20/38] Use HttpUtility::GetLastParameter to get endpoint --- lib/icinga/apiactions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 6df22d82b..f94289af5 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -568,7 +568,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, String endpoint = "$command_endpoint$"; if (params->Contains("endpoint")) - endpoint = params->Get("endpoint"); + endpoint = HttpUtility::GetLastParameter(params, "endpoint"); MacroProcessor::ResolverList resolvers; if (params->Contains("macros")) { From 626080f6103689d4c9fc3c3ce14effeb97b08f07 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 15:53:25 +0200 Subject: [PATCH 21/38] Remove unnecessary check --- lib/icinga/apiactions.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index f94289af5..b341def92 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -623,10 +623,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - /* Check if resolved_command is not empty */ - if (resolved_command.IsEmpty()) - return ApiActions::CreateResult(400, "Command must not be empty."); - /* Check if resolved_command exists and it is of type command_type */ if (command_type == "CheckCommand") { if (!CheckCommand::GetByName(resolved_command)) From cb32786880b16d2cfc82c363d65afccd134b70ec Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 17:50:08 +0200 Subject: [PATCH 22/38] Use RelayMessage to broadcast the updated executions --- lib/icinga/apiactions.cpp | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index b341def92..67ae389cd 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -659,6 +659,25 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, executions->Set(uuid, pending_execution); checkable->SetExecutions(executions); + /* Broadcast the update */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return ApiActions::CreateResult(404, "No ApiListener instance available."); + + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", host->GetName()); + if (service) + updateParams->Set("service", service->GetShortName()); + updateParams->Set("executions", executions); + + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", updateParams); + + MessageOrigin::Ptr origin = new MessageOrigin(); + listener->RelayMessage(origin, checkable, updateMessage, true); + /* Create execution parameters */ Dictionary::Ptr execParams = new Dictionary(); execParams->Set("command_type", command_type); @@ -677,30 +696,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("source", uuid); execParams->Set("deadline", deadline); + /* Execute command */ bool local = endpointPtr == Endpoint::GetLocalEndpoint(); if (local) { - MessageOrigin::Ptr origin = new MessageOrigin(); ClusterEvents::ExecuteCommandAPIHandler(origin, execParams); } else { - /* Broadcast the update */ - ApiListener::Ptr listener = ApiListener::GetInstance(); - if (!listener) - return ApiActions::CreateResult(404, "No ApiListener instance available."); - - Dictionary::Ptr updateParams = new Dictionary(); - updateParams->Set("host", host->GetName()); - if (service) - updateParams->Set("service", service->GetShortName()); - updateParams->Set("executions", executions); - - Dictionary::Ptr updateMessage = new Dictionary(); - updateMessage->Set("jsonrpc", "2.0"); - updateMessage->Set("method", "event::UpdateExecutions"); - updateMessage->Set("params", updateParams); - - listener->SyncSendMessage(endpointPtr, updateMessage); - - /* Execute command */ Dictionary::Ptr execMessage = new Dictionary(); execMessage->Set("jsonrpc", "2.0"); execMessage->Set("method", "event::ExecuteCommand"); From edbd8688fbca469663ba8c4b10d0bfc3ab790a36 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 1 Jul 2020 08:19:35 +0200 Subject: [PATCH 23/38] Update status code --- lib/icinga/apiactions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 67ae389cd..432fae212 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -626,13 +626,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Check if resolved_command exists and it is of type command_type */ if (command_type == "CheckCommand") { if (!CheckCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } else if (command_type == "EventCommand") { if (!EventCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } else if (command_type == "NotificationCommand") { if (!NotificationCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } /* Get TTL param */ From ee2f7bafdddbabcdbe999ef7b8332f1e47a3fa01 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 1 Jul 2020 14:27:00 +0200 Subject: [PATCH 24/38] Move some checks. Add macros parameters to ExecuteCommand message --- lib/icinga/apiactions.cpp | 57 ++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 432fae212..4f0a6f704 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -556,10 +556,31 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured.")); + + /* Get command_type */ + String command_type = "EventCommand"; + if (params->Contains("command_type")) + command_type = HttpUtility::GetLastParameter(params, "command_type"); + + /* Validate command_type */ + if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + Checkable::Ptr checkable = dynamic_pointer_cast(object); if (!checkable) return ApiActions::CreateResult(404, "Can't start a command execution for a non-existent object."); + /* Get TTL param */ + if (!params->Contains("ttl")) + return ApiActions::CreateResult(400, "Parameter ttl is required."); + + double ttl = HttpUtility::GetLastParameter(params, "ttl"); + if (ttl <= 0) + return ApiActions::CreateResult(400, "Parameter ttl must be greater than 0."); + ObjectLock oLock (checkable); Host::Ptr host; @@ -571,10 +592,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, endpoint = HttpUtility::GetLastParameter(params, "endpoint"); MacroProcessor::ResolverList resolvers; + Dictionary::Ptr macros; if (params->Contains("macros")) { - Value macros = HttpUtility::GetLastParameter(params, "macros"); - if (macros.IsObjectType()) - resolvers.emplace_back("override", macros); + Value vmacros = HttpUtility::GetLastParameter(params, "macros"); + if (vmacros.IsObjectType()) { + resolvers.emplace_back("override", vmacros); + macros = vmacros; + } else return ApiActions::CreateResult(400, "Parameter macros must be a dictionary."); } @@ -594,15 +618,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (!endpointPtr) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); - /* Get command_type */ - String command_type = "EventCommand"; - if (params->Contains("command_type")) - command_type = HttpUtility::GetLastParameter(params, "command_type"); - - /* Validate command_type */ - if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") - return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); - /* Get command */ String command; if (!params->Contains("command")) { @@ -635,14 +650,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } - /* Get TTL param */ - if (!params->Contains("ttl")) - return ApiActions::CreateResult(400, "Parameter ttl is required."); - - double ttl = HttpUtility::GetLastParameter(params, "ttl"); - if (ttl <= 0) - return ApiActions::CreateResult(400, "Parameter ttl must be greater than 0."); - /* This generates a UUID */ String uuid = Utility::NewUniqueID(); @@ -660,15 +667,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, checkable->SetExecutions(executions); /* Broadcast the update */ - ApiListener::Ptr listener = ApiListener::GetInstance(); - if (!listener) - return ApiActions::CreateResult(404, "No ApiListener instance available."); - + Dictionary::Ptr executionsToBroadcast = new Dictionary(); + executionsToBroadcast->Set(uuid, pending_execution); Dictionary::Ptr updateParams = new Dictionary(); updateParams->Set("host", host->GetName()); if (service) updateParams->Set("service", service->GetShortName()); - updateParams->Set("executions", executions); + updateParams->Set("executions", executionsToBroadcast); Dictionary::Ptr updateMessage = new Dictionary(); updateMessage->Set("jsonrpc", "2.0"); @@ -695,6 +700,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("source", uuid); execParams->Set("deadline", deadline); + if (macros) + execParams->Set("macros", macros); /* Execute command */ bool local = endpointPtr == Endpoint::GetLocalEndpoint(); From f2e9aee158ffa274e007b80410ded3c7c4b4252a Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 2 Jul 2020 10:31:24 +0200 Subject: [PATCH 25/38] Update execMacros --- lib/icinga/apiactions.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 4f0a6f704..72e612f68 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -592,12 +592,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, endpoint = HttpUtility::GetLastParameter(params, "endpoint"); MacroProcessor::ResolverList resolvers; - Dictionary::Ptr macros; if (params->Contains("macros")) { - Value vmacros = HttpUtility::GetLastParameter(params, "macros"); - if (vmacros.IsObjectType()) { - resolvers.emplace_back("override", vmacros); - macros = vmacros; + Value macros = HttpUtility::GetLastParameter(params, "macros"); + if (macros.IsObjectType()) { + resolvers.emplace_back("override", macros); } else return ApiActions::CreateResult(400, "Parameter macros must be a dictionary."); @@ -683,6 +681,25 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MessageOrigin::Ptr origin = new MessageOrigin(); listener->RelayMessage(origin, checkable, updateMessage, true); + double scheduled_start = checkable->GetNextCheck(); + double before_check = Utility::GetTime(); + + CheckResult::Ptr cr = new CheckResult(); + cr->SetScheduleStart(scheduled_start); + cr->SetExecutionStart(before_check); + + Dictionary::Ptr execMacros = new Dictionary(); + if (command_type == "CheckCommand") { + CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); + cmd->Execute(checkable, cr, execMacros, false); + } else if (command_type == "EventCommand") { + EventCommand::Ptr cmd = EventCommand::GetByName(resolved_command); + cmd->Execute(checkable, execMacros, false); + } else if (command_type == "NotificationCommand") { + /* TODO */ + return ApiActions::CreateResult(501, "Not implementd."); + } + /* Create execution parameters */ Dictionary::Ptr execParams = new Dictionary(); execParams->Set("command_type", command_type); @@ -700,8 +717,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("source", uuid); execParams->Set("deadline", deadline); - if (macros) - execParams->Set("macros", macros); + execParams->Set("macros", execMacros); /* Execute command */ bool local = endpointPtr == Endpoint::GetLocalEndpoint(); From 6102cf0625541de5cd19b5a9137def1127bdf194 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 2 Jul 2020 12:20:16 +0200 Subject: [PATCH 26/38] Call notificationCommand->Execute --- lib/icinga/apiactions.cpp | 78 ++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 72e612f68..5f2534fd2 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -636,16 +636,69 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); + double scheduled_start = checkable->GetNextCheck(); + double before_check = Utility::GetTime(); + + CheckResult::Ptr cr = new CheckResult(); + cr->SetScheduleStart(scheduled_start); + cr->SetExecutionStart(before_check); + /* Check if resolved_command exists and it is of type command_type */ + Dictionary::Ptr execMacros = new Dictionary(); if (command_type == "CheckCommand") { - if (!CheckCommand::GetByName(resolved_command)) + CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); + if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + else + cmd->Execute(checkable, cr, execMacros, false); } else if (command_type == "EventCommand") { - if (!EventCommand::GetByName(resolved_command)) + EventCommand::Ptr cmd = EventCommand::GetByName(resolved_command); + if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + else + cmd->Execute(checkable, execMacros, false); } else if (command_type == "NotificationCommand") { - if (!NotificationCommand::GetByName(resolved_command)) + NotificationCommand::Ptr cmd = NotificationCommand::GetByName(resolved_command); + if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + else { + /* Get user */ + String user_string = ""; + if (params->Contains("user")) + user_string = HttpUtility::GetLastParameter(params, "user"); + + /* Resolve user macro */ + String resolved_user = MacroProcessor::ResolveMacros( + user_string, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false + ); + + User::Ptr user = User::GetByName(resolved_user); + if (!user) + return ApiActions::CreateResult(404, "Can't find a valid user for '" + resolved_user + "'."); + + /* Get notification */ + String notification_string = ""; + if (params->Contains("notification")) + notification_string = HttpUtility::GetLastParameter(params, "notification"); + + /* Resolve notification macro */ + String resolved_notification = MacroProcessor::ResolveMacros( + notification_string, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false + ); + + Notification::Ptr notification = Notification::GetByName(resolved_notification); + if (!user) + return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); + + /* Get author */ + String author = ""; + if (params->Contains("author")) + author = HttpUtility::GetLastParameter(params, "author"); + + cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, author, "", execMacros, false); + } } /* This generates a UUID */ @@ -681,25 +734,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MessageOrigin::Ptr origin = new MessageOrigin(); listener->RelayMessage(origin, checkable, updateMessage, true); - double scheduled_start = checkable->GetNextCheck(); - double before_check = Utility::GetTime(); - - CheckResult::Ptr cr = new CheckResult(); - cr->SetScheduleStart(scheduled_start); - cr->SetExecutionStart(before_check); - - Dictionary::Ptr execMacros = new Dictionary(); - if (command_type == "CheckCommand") { - CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); - cmd->Execute(checkable, cr, execMacros, false); - } else if (command_type == "EventCommand") { - EventCommand::Ptr cmd = EventCommand::GetByName(resolved_command); - cmd->Execute(checkable, execMacros, false); - } else if (command_type == "NotificationCommand") { - /* TODO */ - return ApiActions::CreateResult(501, "Not implementd."); - } - /* Create execution parameters */ Dictionary::Ptr execParams = new Dictionary(); execParams->Set("command_type", command_type); From 41b379523e3a12badd12acdcfcd985d5b7debb91 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 08:38:39 +0200 Subject: [PATCH 27/38] Fix indentation --- lib/icinga/apiactions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 5f2534fd2..1698133f9 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -669,8 +669,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Resolve user macro */ String resolved_user = MacroProcessor::ResolveMacros( - user_string, resolvers, checkable->GetLastCheckResult(), nullptr, - MacroProcessor::EscapeCallback(), nullptr, false + user_string, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false ); User::Ptr user = User::GetByName(resolved_user); @@ -684,8 +684,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Resolve notification macro */ String resolved_notification = MacroProcessor::ResolveMacros( - notification_string, resolvers, checkable->GetLastCheckResult(), nullptr, - MacroProcessor::EscapeCallback(), nullptr, false + notification_string, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false ); Notification::Ptr notification = Notification::GetByName(resolved_notification); From 670835fd9a21272596128b12853722ab571c5849 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 08:43:50 +0200 Subject: [PATCH 28/38] Get last CheckResult --- lib/icinga/apiactions.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 1698133f9..97f93ca5a 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -636,12 +636,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - double scheduled_start = checkable->GetNextCheck(); - double before_check = Utility::GetTime(); - - CheckResult::Ptr cr = new CheckResult(); - cr->SetScheduleStart(scheduled_start); - cr->SetExecutionStart(before_check); + CheckResult::Ptr cr = checkable->GetLastCheckResult(); /* Check if resolved_command exists and it is of type command_type */ Dictionary::Ptr execMacros = new Dictionary(); From 27a6fd6b40afe0e1c5d496e37d0dd419f7585924 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 10:16:23 +0200 Subject: [PATCH 29/38] Get API user from ActionsHandler --- lib/icinga/apiactions.cpp | 8 +++++--- lib/remote/actionshandler.cpp | 8 ++++++++ lib/remote/actionshandler.hpp | 2 ++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 97f93ca5a..f1661bfc3 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -15,6 +15,7 @@ #include "remote/httputility.hpp" #include "base/utility.hpp" #include "base/convert.hpp" +#include "remote/actionshandler.hpp" #include using namespace icinga; @@ -688,9 +689,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); /* Get author */ - String author = ""; - if (params->Contains("author")) - author = HttpUtility::GetLastParameter(params, "author"); + if (!authenticatedApiUser) + return ApiActions::CreateResult(401, "Can't find API user"); + + String author = authenticatedApiUser->GetName(); cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, author, "", execMacros, false); } diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index e94debb63..8668b8ea6 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -4,12 +4,15 @@ #include "remote/httputility.hpp" #include "remote/filterutility.hpp" #include "remote/apiaction.hpp" +#include "base/defer.hpp" #include "base/exception.hpp" #include "base/logger.hpp" #include using namespace icinga; +thread_local ApiUser::Ptr authenticatedApiUser; + REGISTER_URLHANDLER("/v1/actions", ActionsHandler); bool ActionsHandler::HandleRequest( @@ -71,6 +74,11 @@ bool ActionsHandler::HandleRequest( bool verbose = false; + authenticatedApiUser = user; + Defer a ([&]() { + authenticatedApiUser = nullptr; + }); + if (params) verbose = HttpUtility::GetLastParameter(params, "verbose"); diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index c2465cf7e..a7b70ff0d 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -5,6 +5,8 @@ #include "remote/httphandler.hpp" +extern thread_local icinga::ApiUser::Ptr authenticatedApiUser; + namespace icinga { From 08c6e9bc42833824510740a2732e080c42317b9f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 11:00:40 +0200 Subject: [PATCH 30/38] Make authenticatedApiUser a static property of ActionsHandler --- lib/icinga/apiactions.cpp | 9 ++++----- lib/remote/actionshandler.cpp | 6 +++--- lib/remote/actionshandler.hpp | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index f1661bfc3..20306a74a 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -689,12 +689,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); /* Get author */ - if (!authenticatedApiUser) - return ApiActions::CreateResult(401, "Can't find API user"); + if (!ActionsHandler::authenticatedApiUser) + BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); - String author = authenticatedApiUser->GetName(); - - cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, author, "", execMacros, false); + cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, + ActionsHandler::authenticatedApiUser->GetName(), "", execMacros, false); } } diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 8668b8ea6..175e38a3a 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -11,7 +11,7 @@ using namespace icinga; -thread_local ApiUser::Ptr authenticatedApiUser; +thread_local ApiUser::Ptr ActionsHandler::authenticatedApiUser; REGISTER_URLHANDLER("/v1/actions", ActionsHandler); @@ -74,9 +74,9 @@ bool ActionsHandler::HandleRequest( bool verbose = false; - authenticatedApiUser = user; + ActionsHandler::authenticatedApiUser = user; Defer a ([&]() { - authenticatedApiUser = nullptr; + ActionsHandler::authenticatedApiUser = nullptr; }); if (params) diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index a7b70ff0d..e99b40cd9 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -5,8 +5,6 @@ #include "remote/httphandler.hpp" -extern thread_local icinga::ApiUser::Ptr authenticatedApiUser; - namespace icinga { @@ -15,6 +13,8 @@ class ActionsHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ActionsHandler); + static thread_local ApiUser::Ptr authenticatedApiUser; + bool HandleRequest( AsioTlsStream& stream, const ApiUser::Ptr& user, From b07471d803fbdf46253c6239fcb3067041431ce3 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 11:17:36 +0200 Subject: [PATCH 31/38] AuthenticatedApiUser name refactor --- lib/icinga/apiactions.cpp | 4 ++-- lib/remote/actionshandler.cpp | 8 ++++---- lib/remote/actionshandler.hpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 20306a74a..ff8b68a32 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -689,11 +689,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); /* Get author */ - if (!ActionsHandler::authenticatedApiUser) + if (!ActionsHandler::AuthenticatedApiUser) BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, - ActionsHandler::authenticatedApiUser->GetName(), "", execMacros, false); + ActionsHandler::AuthenticatedApiUser->GetName(), "", execMacros, false); } } diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 175e38a3a..f8a823632 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -11,7 +11,7 @@ using namespace icinga; -thread_local ApiUser::Ptr ActionsHandler::authenticatedApiUser; +thread_local ApiUser::Ptr ActionsHandler::AuthenticatedApiUser; REGISTER_URLHANDLER("/v1/actions", ActionsHandler); @@ -74,9 +74,9 @@ bool ActionsHandler::HandleRequest( bool verbose = false; - ActionsHandler::authenticatedApiUser = user; - Defer a ([&]() { - ActionsHandler::authenticatedApiUser = nullptr; + ActionsHandler::AuthenticatedApiUser = user; + Defer a ([]() { + ActionsHandler::AuthenticatedApiUser = nullptr; }); if (params) diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index e99b40cd9..ca662caba 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -13,7 +13,7 @@ class ActionsHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ActionsHandler); - static thread_local ApiUser::Ptr authenticatedApiUser; + static thread_local ApiUser::Ptr AuthenticatedApiUser; bool HandleRequest( AsioTlsStream& stream, From be2701bc8acf75f8210b7ae885704337090d723a Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 15:13:51 +0200 Subject: [PATCH 32/38] Add overrideMacros property to MacroResolver --- lib/icinga/apiactions.cpp | 7 +++++++ lib/icinga/macroprocessor.cpp | 2 ++ lib/icinga/macroresolver.hpp | 2 ++ lib/methods/pluginchecktask.cpp | 3 +++ 4 files changed, 14 insertions(+) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index ff8b68a32..ef3690847 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -15,6 +15,7 @@ #include "remote/httputility.hpp" #include "base/utility.hpp" #include "base/convert.hpp" +#include "base/defer.hpp" #include "remote/actionshandler.hpp" #include @@ -641,6 +642,12 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Check if resolved_command exists and it is of type command_type */ Dictionary::Ptr execMacros = new Dictionary(); + + MacroResolver::overrideMacros = execMacros; + Defer o ([]() { + MacroResolver::overrideMacros = nullptr; + }); + if (command_type == "CheckCommand") { CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); if (!cmd) diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp index c92ab19d4..8fb014efc 100644 --- a/lib/icinga/macroprocessor.cpp +++ b/lib/icinga/macroprocessor.cpp @@ -15,6 +15,8 @@ using namespace icinga; +thread_local Dictionary::Ptr MacroResolver::overrideMacros; + Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolvers, const CheckResult::Ptr& cr, String *missingMacro, const MacroProcessor::EscapeCallback& escapeFn, const Dictionary::Ptr& resolvedMacros, diff --git a/lib/icinga/macroresolver.hpp b/lib/icinga/macroresolver.hpp index ea29ce1e4..65dc69679 100644 --- a/lib/icinga/macroresolver.hpp +++ b/lib/icinga/macroresolver.hpp @@ -21,6 +21,8 @@ class MacroResolver public: DECLARE_PTR_TYPEDEFS(MacroResolver); + static thread_local Dictionary::Ptr overrideMacros; + virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const = 0; }; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 212d7447c..68458d3d9 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -29,6 +29,9 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; + if (MacroResolver::overrideMacros) + resolvers.emplace_back("override", MacroResolver::overrideMacros); + if (service) resolvers.emplace_back("service", service); resolvers.emplace_back("host", host); From a33560548c3506a47caa3fa6b13477eade4dcb63 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 15:53:51 +0200 Subject: [PATCH 33/38] Refactor MacroResolver::OverrideMacros --- lib/icinga/apiactions.cpp | 4 ++-- lib/icinga/macroprocessor.cpp | 2 +- lib/icinga/macroresolver.hpp | 2 +- lib/methods/pluginchecktask.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index ef3690847..4cd3173e8 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -643,9 +643,9 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Check if resolved_command exists and it is of type command_type */ Dictionary::Ptr execMacros = new Dictionary(); - MacroResolver::overrideMacros = execMacros; + MacroResolver::OverrideMacros = execMacros; Defer o ([]() { - MacroResolver::overrideMacros = nullptr; + MacroResolver::OverrideMacros = nullptr; }); if (command_type == "CheckCommand") { diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp index 8fb014efc..67e7b7e78 100644 --- a/lib/icinga/macroprocessor.cpp +++ b/lib/icinga/macroprocessor.cpp @@ -15,7 +15,7 @@ using namespace icinga; -thread_local Dictionary::Ptr MacroResolver::overrideMacros; +thread_local Dictionary::Ptr MacroResolver::OverrideMacros; Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolvers, const CheckResult::Ptr& cr, String *missingMacro, diff --git a/lib/icinga/macroresolver.hpp b/lib/icinga/macroresolver.hpp index 65dc69679..62cd41d3c 100644 --- a/lib/icinga/macroresolver.hpp +++ b/lib/icinga/macroresolver.hpp @@ -21,7 +21,7 @@ class MacroResolver public: DECLARE_PTR_TYPEDEFS(MacroResolver); - static thread_local Dictionary::Ptr overrideMacros; + static thread_local Dictionary::Ptr OverrideMacros; virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const = 0; }; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 68458d3d9..70523b49f 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -29,8 +29,8 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; - if (MacroResolver::overrideMacros) - resolvers.emplace_back("override", MacroResolver::overrideMacros); + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); if (service) resolvers.emplace_back("service", service); From 6a5deaf7d4d4e53e6f9e5af7e11dc5d2180cfe55 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 16:36:58 +0200 Subject: [PATCH 34/38] Fix update executions. Fix code indentation --- lib/icinga/clusterevents.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 4dc6f8bc8..5c65d5720 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -952,8 +952,8 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); if (!endpoint) { Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() - << "': Invalid endpoint origin (client not allowed)."; + << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() + << "': Invalid endpoint origin (client not allowed)."; return Empty; } @@ -976,19 +976,14 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; return Empty; } - if (!params->Contains("executions")) { - Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': missing executions."; - return Empty; - } - - Dictionary::Ptr executions = params->Get("executions"); + Dictionary::Ptr executions = checkable->GetExecutions(); + Dictionary::Ptr newExecutions = params->Get("executions"); + newExecutions->CopyTo(executions); checkable->SetExecutions(executions); return Empty; From 33926ba983a8897e3187cd194085df8da7ebeb5b Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 6 Jul 2020 16:21:05 +0200 Subject: [PATCH 35/38] Check if executions are not null --- lib/icinga/clusterevents.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 5c65d5720..4146d2852 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -982,6 +982,8 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin } Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) + executions = new Dictionary(); Dictionary::Ptr newExecutions = params->Get("executions"); newExecutions->CopyTo(executions); checkable->SetExecutions(executions); From 704aa0f685e6cd8a4644ae9a62289656d987cf8e Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 6 Jul 2020 17:30:18 +0200 Subject: [PATCH 36/38] Change result --- lib/icinga/apiactions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 4cd3173e8..f3269d6f9 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -770,6 +770,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, } Dictionary::Ptr result = new Dictionary(); - result->Set(checkable->GetName(), uuid); + result->Set("checkable", checkable->GetName()); + result->Set("execution", uuid); return ApiActions::CreateResult(202, "Accepted", result); } From c304b59990b44cf0500721ef523228e3f44027cd Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 7 Jul 2020 14:23:36 +0200 Subject: [PATCH 37/38] Add GetSingleObjectByNameUsingPermissions function --- lib/icinga/apiactions.cpp | 48 +++++++++++++++++++++++++++++---------- lib/icinga/apiactions.hpp | 2 ++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index f3269d6f9..9f65186f2 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -11,6 +11,7 @@ #include "icinga/clusterevents.hpp" #include "remote/apiaction.hpp" #include "remote/apilistener.hpp" +#include "remote/filterutility.hpp" #include "remote/pkiutility.hpp" #include "remote/httputility.hpp" #include "base/utility.hpp" @@ -555,6 +556,30 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, + cn + "'.", additional); } +Value ApiActions::GetSingleObjectByNameUsingPermissions(String type, String value, ApiUser::Ptr user) +{ + Dictionary::Ptr queryParams = new Dictionary(); + queryParams->Set("type", type); + queryParams->Set(type.ToLower(), value); + + QueryDescription qd; + qd.Types.insert(type); + qd.Permission = "objects/query/" + type; + + std::vector objs; + try { + objs = FilterUtility::GetFilterTargets(qd, queryParams, ActionsHandler::AuthenticatedApiUser); + } catch (const std::exception& ex) { + Log(LogCritical, "ApiActions") << DiagnosticInformation(ex); + return nullptr; + } + + if (objs.empty()) + return nullptr; + + return objs.at(0); +}; + Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { @@ -613,8 +638,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, nullptr, MacroProcessor::EscapeCallback(), nullptr, false ); - /* Check if endpoint exists */ - Endpoint::Ptr endpointPtr = Endpoint::GetByName(resolved_endpoint); + if (!ActionsHandler::AuthenticatedApiUser) + BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); + + /* Get endpoint */ + Endpoint::Ptr endpointPtr = ApiActions::GetSingleObjectByNameUsingPermissions(Endpoint::GetTypeName(), resolved_endpoint, ActionsHandler::AuthenticatedApiUser); if (!endpointPtr) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); @@ -649,19 +677,19 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, }); if (command_type == "CheckCommand") { - CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); + CheckCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, cr, execMacros, false); } else if (command_type == "EventCommand") { - EventCommand::Ptr cmd = EventCommand::GetByName(resolved_command); + EventCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, execMacros, false); } else if (command_type == "NotificationCommand") { - NotificationCommand::Ptr cmd = NotificationCommand::GetByName(resolved_command); + NotificationCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else { @@ -676,7 +704,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - User::Ptr user = User::GetByName(resolved_user); + User::Ptr user = ApiActions::GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!user) return ApiActions::CreateResult(404, "Can't find a valid user for '" + resolved_user + "'."); @@ -691,14 +719,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - Notification::Ptr notification = Notification::GetByName(resolved_notification); - if (!user) + Notification::Ptr notification = ApiActions::GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + if (!notification) return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); - /* Get author */ - if (!ActionsHandler::AuthenticatedApiUser) - BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); - cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, ActionsHandler::AuthenticatedApiUser->GetName(), "", execMacros, false); } diff --git a/lib/icinga/apiactions.hpp b/lib/icinga/apiactions.hpp index 46e03c02f..c588c7ddc 100644 --- a/lib/icinga/apiactions.hpp +++ b/lib/icinga/apiactions.hpp @@ -6,6 +6,7 @@ #include "icinga/i2-icinga.hpp" #include "base/configobject.hpp" #include "base/dictionary.hpp" +#include "remote/apiuser.hpp" namespace icinga { @@ -33,6 +34,7 @@ public: private: static Dictionary::Ptr CreateResult(int code, const String& status, const Dictionary::Ptr& additional = nullptr); + static Value GetSingleObjectByNameUsingPermissions(String type, String value, ApiUser::Ptr user); }; } From e8c8a7d9ddc631ede9dce18e83514238339e3475 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 7 Jul 2020 15:45:58 +0200 Subject: [PATCH 38/38] Refactor GetSingleObjectByNameUsingPermissions --- lib/icinga/apiactions.cpp | 20 ++++++++++---------- lib/icinga/apiactions.hpp | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 9f65186f2..e1d25cefd 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -556,11 +556,11 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, + cn + "'.", additional); } -Value ApiActions::GetSingleObjectByNameUsingPermissions(String type, String value, ApiUser::Ptr user) +Value ApiActions::GetSingleObjectByNameUsingPermissions(const String& type, const String& objectName, const ApiUser::Ptr& user) { Dictionary::Ptr queryParams = new Dictionary(); queryParams->Set("type", type); - queryParams->Set(type.ToLower(), value); + queryParams->Set(type.ToLower(), objectName); QueryDescription qd; qd.Types.insert(type); @@ -568,9 +568,9 @@ Value ApiActions::GetSingleObjectByNameUsingPermissions(String type, String valu std::vector objs; try { - objs = FilterUtility::GetFilterTargets(qd, queryParams, ActionsHandler::AuthenticatedApiUser); + objs = FilterUtility::GetFilterTargets(qd, queryParams, user); } catch (const std::exception& ex) { - Log(LogCritical, "ApiActions") << DiagnosticInformation(ex); + Log(LogWarning, "ApiActions") << DiagnosticInformation(ex); return nullptr; } @@ -642,7 +642,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); /* Get endpoint */ - Endpoint::Ptr endpointPtr = ApiActions::GetSingleObjectByNameUsingPermissions(Endpoint::GetTypeName(), resolved_endpoint, ActionsHandler::AuthenticatedApiUser); + Endpoint::Ptr endpointPtr = GetSingleObjectByNameUsingPermissions(Endpoint::GetTypeName(), resolved_endpoint, ActionsHandler::AuthenticatedApiUser); if (!endpointPtr) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); @@ -677,19 +677,19 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, }); if (command_type == "CheckCommand") { - CheckCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + CheckCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, cr, execMacros, false); } else if (command_type == "EventCommand") { - EventCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + EventCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, execMacros, false); } else if (command_type == "NotificationCommand") { - NotificationCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + NotificationCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else { @@ -704,7 +704,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - User::Ptr user = ApiActions::GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + User::Ptr user = GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_user, ActionsHandler::AuthenticatedApiUser); if (!user) return ApiActions::CreateResult(404, "Can't find a valid user for '" + resolved_user + "'."); @@ -719,7 +719,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - Notification::Ptr notification = ApiActions::GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + Notification::Ptr notification = GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_notification, ActionsHandler::AuthenticatedApiUser); if (!notification) return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); diff --git a/lib/icinga/apiactions.hpp b/lib/icinga/apiactions.hpp index c588c7ddc..b6ba83500 100644 --- a/lib/icinga/apiactions.hpp +++ b/lib/icinga/apiactions.hpp @@ -34,7 +34,7 @@ public: private: static Dictionary::Ptr CreateResult(int code, const String& status, const Dictionary::Ptr& additional = nullptr); - static Value GetSingleObjectByNameUsingPermissions(String type, String value, ApiUser::Ptr user); + static Value GetSingleObjectByNameUsingPermissions(const String& type, const String& value, const ApiUser::Ptr& user); }; }