summaryrefslogtreecommitdiffstats
path: root/lib/remote/actionshandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/remote/actionshandler.cpp')
-rw-r--r--lib/remote/actionshandler.cpp145
1 files changed, 145 insertions, 0 deletions
diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp
new file mode 100644
index 0000000..016c76d
--- /dev/null
+++ b/lib/remote/actionshandler.cpp
@@ -0,0 +1,145 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "remote/actionshandler.hpp"
+#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 <set>
+
+using namespace icinga;
+
+thread_local ApiUser::Ptr ActionsHandler::AuthenticatedApiUser;
+
+REGISTER_URLHANDLER("/v1/actions", ActionsHandler);
+
+bool ActionsHandler::HandleRequest(
+ AsioTlsStream& stream,
+ const ApiUser::Ptr& user,
+ boost::beast::http::request<boost::beast::http::string_body>& request,
+ const Url::Ptr& url,
+ boost::beast::http::response<boost::beast::http::string_body>& response,
+ const Dictionary::Ptr& params,
+ boost::asio::yield_context& yc,
+ HttpServerConnection& server
+)
+{
+ namespace http = boost::beast::http;
+
+ if (url->GetPath().size() != 3)
+ return false;
+
+ if (request.method() != http::verb::post)
+ return false;
+
+ String actionName = url->GetPath()[2];
+
+ ApiAction::Ptr action = ApiAction::GetByName(actionName);
+
+ if (!action) {
+ HttpUtility::SendJsonError(response, params, 404, "Action '" + actionName + "' does not exist.");
+ return true;
+ }
+
+ QueryDescription qd;
+
+ const std::vector<String>& types = action->GetTypes();
+ std::vector<Value> objs;
+
+ String permission = "actions/" + actionName;
+
+ if (!types.empty()) {
+ qd.Types = std::set<String>(types.begin(), types.end());
+ qd.Permission = permission;
+
+ try {
+ objs = FilterUtility::GetFilterTargets(qd, params, user);
+ } catch (const std::exception& ex) {
+ HttpUtility::SendJsonError(response, params, 404,
+ "No objects found.",
+ DiagnosticInformation(ex));
+ return true;
+ }
+
+ if (objs.empty()) {
+ HttpUtility::SendJsonError(response, params, 404,
+ "No objects found.");
+ return true;
+ }
+ } else {
+ FilterUtility::CheckPermission(user, permission);
+ objs.emplace_back(nullptr);
+ }
+
+ ArrayData results;
+
+ Log(LogNotice, "ApiActionHandler")
+ << "Running action " << actionName;
+
+ bool verbose = false;
+
+ ActionsHandler::AuthenticatedApiUser = user;
+ Defer a ([]() {
+ ActionsHandler::AuthenticatedApiUser = nullptr;
+ });
+
+ if (params)
+ verbose = HttpUtility::GetLastParameter(params, "verbose");
+
+ for (const ConfigObject::Ptr& obj : objs) {
+ try {
+ results.emplace_back(action->Invoke(obj, params));
+ } catch (const std::exception& ex) {
+ Dictionary::Ptr fail = new Dictionary({
+ { "code", 500 },
+ { "status", "Action execution failed: '" + DiagnosticInformation(ex, false) + "'." }
+ });
+
+ /* Exception for actions. Normally we would handle this inside SendJsonError(). */
+ if (verbose)
+ fail->Set("diagnostic_information", DiagnosticInformation(ex));
+
+ results.emplace_back(std::move(fail));
+ }
+ }
+
+ int statusCode = 500;
+ std::set<int> okStatusCodes, nonOkStatusCodes;
+
+ for (const Dictionary::Ptr& res : results) {
+ if (!res->Contains("code")) {
+ continue;
+ }
+
+ auto code = res->Get("code");
+
+ if (code >= 200 && code <= 299) {
+ okStatusCodes.insert(code);
+ } else {
+ nonOkStatusCodes.insert(code);
+ }
+ }
+
+ size_t okSize = okStatusCodes.size();
+ size_t nonOkSize = nonOkStatusCodes.size();
+
+ if (okSize == 1u && nonOkSize == 0u) {
+ statusCode = *okStatusCodes.begin();
+ } else if (nonOkSize == 1u) {
+ statusCode = *nonOkStatusCodes.begin();
+ } else if (okSize >= 2u && nonOkSize == 0u) {
+ statusCode = 200;
+ }
+
+ response.result(statusCode);
+
+ Dictionary::Ptr result = new Dictionary({
+ { "results", new Array(std::move(results)) }
+ });
+
+ HttpUtility::SendJsonBody(response, params, result);
+
+ return true;
+}