1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "remote/createobjecthandler.hpp"
4 #include "remote/configobjectutility.hpp"
5 #include "remote/httputility.hpp"
6 #include "remote/jsonrpcconnection.hpp"
7 #include "remote/filterutility.hpp"
8 #include "remote/apiaction.hpp"
9 #include "remote/zone.hpp"
10 #include "base/configtype.hpp"
11 #include <set>
12 
13 using namespace icinga;
14 
15 REGISTER_URLHANDLER("/v1/objects", CreateObjectHandler);
16 
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)17 bool CreateObjectHandler::HandleRequest(
18 	AsioTlsStream& stream,
19 	const ApiUser::Ptr& user,
20 	boost::beast::http::request<boost::beast::http::string_body>& request,
21 	const Url::Ptr& url,
22 	boost::beast::http::response<boost::beast::http::string_body>& response,
23 	const Dictionary::Ptr& params,
24 	boost::asio::yield_context& yc,
25 	HttpServerConnection& server
26 )
27 {
28 	namespace http = boost::beast::http;
29 
30 	if (url->GetPath().size() != 4)
31 		return false;
32 
33 	if (request.method() != http::verb::put)
34 		return false;
35 
36 	Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]);
37 
38 	if (!type) {
39 		HttpUtility::SendJsonError(response, params, 400, "Invalid type specified.");
40 		return true;
41 	}
42 
43 	FilterUtility::CheckPermission(user, "objects/create/" + type->GetName());
44 
45 	String name = url->GetPath()[3];
46 	Array::Ptr templates = params->Get("templates");
47 	Dictionary::Ptr attrs = params->Get("attrs");
48 
49 	/* Put created objects into the local zone if not explicitly defined.
50 	 * This allows additional zone members to sync the
51 	 * configuration at some later point.
52 	 */
53 	Zone::Ptr localZone = Zone::GetLocalZone();
54 	String localZoneName;
55 
56 	if (localZone) {
57 		localZoneName = localZone->GetName();
58 
59 		if (!attrs) {
60 			attrs = new Dictionary({
61 				{ "zone", localZoneName }
62 			});
63 		} else if (!attrs->Contains("zone")) {
64 			attrs->Set("zone", localZoneName);
65 		}
66 	}
67 
68 	/* Sanity checks for unique groups array. */
69 	if (attrs->Contains("groups")) {
70 		Array::Ptr groups = attrs->Get("groups");
71 
72 		if (groups)
73 			attrs->Set("groups", groups->Unique());
74 	}
75 
76 	Dictionary::Ptr result1 = new Dictionary();
77 	String status;
78 	Array::Ptr errors = new Array();
79 	Array::Ptr diagnosticInformation = new Array();
80 
81 	bool ignoreOnError = false;
82 
83 	if (params->Contains("ignore_on_error"))
84 		ignoreOnError = HttpUtility::GetLastParameter(params, "ignore_on_error");
85 
86 	Dictionary::Ptr result = new Dictionary({
87 		{ "results", new Array({ result1 }) }
88 	});
89 
90 	String config;
91 
92 	bool verbose = false;
93 
94 	if (params)
95 		verbose = HttpUtility::GetLastParameter(params, "verbose");
96 
97 	/* Object creation can cause multiple errors and optionally diagnostic information.
98 	 * We can't use SendJsonError() here.
99 	 */
100 	try {
101 		config = ConfigObjectUtility::CreateObjectConfig(type, name, ignoreOnError, templates, attrs);
102 	} catch (const std::exception& ex) {
103 		errors->Add(DiagnosticInformation(ex, false));
104 		diagnosticInformation->Add(DiagnosticInformation(ex));
105 
106 		if (verbose)
107 			result1->Set("diagnostic_information", diagnosticInformation);
108 
109 		result1->Set("errors", errors);
110 		result1->Set("code", 500);
111 		result1->Set("status", "Object could not be created.");
112 
113 		response.result(http::status::internal_server_error);
114 		HttpUtility::SendJsonBody(response, params, result);
115 
116 		return true;
117 	}
118 
119 	if (!ConfigObjectUtility::CreateObject(type, name, config, errors, diagnosticInformation)) {
120 		result1->Set("errors", errors);
121 		result1->Set("code", 500);
122 		result1->Set("status", "Object could not be created.");
123 
124 		if (verbose)
125 			result1->Set("diagnostic_information", diagnosticInformation);
126 
127 		response.result(http::status::internal_server_error);
128 		HttpUtility::SendJsonBody(response, params, result);
129 
130 		return true;
131 	}
132 
133 	auto *ctype = dynamic_cast<ConfigType *>(type.get());
134 	ConfigObject::Ptr obj = ctype->GetObject(name);
135 
136 	result1->Set("code", 200);
137 
138 	if (obj)
139 		result1->Set("status", "Object was created");
140 	else if (!obj && ignoreOnError)
141 		result1->Set("status", "Object was not created but 'ignore_on_error' was set to true");
142 
143 	response.result(http::status::ok);
144 	HttpUtility::SendJsonBody(response, params, result);
145 
146 	return true;
147 }
148