1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "remote/configpackageshandler.hpp"
4 #include "remote/configpackageutility.hpp"
5 #include "remote/httputility.hpp"
6 #include "remote/filterutility.hpp"
7 #include "base/exception.hpp"
8
9 using namespace icinga;
10
11 REGISTER_URLHANDLER("/v1/config/packages", ConfigPackagesHandler);
12
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)13 bool ConfigPackagesHandler::HandleRequest(
14 AsioTlsStream& stream,
15 const ApiUser::Ptr& user,
16 boost::beast::http::request<boost::beast::http::string_body>& request,
17 const Url::Ptr& url,
18 boost::beast::http::response<boost::beast::http::string_body>& response,
19 const Dictionary::Ptr& params,
20 boost::asio::yield_context& yc,
21 HttpServerConnection& server
22 )
23 {
24 namespace http = boost::beast::http;
25
26 if (url->GetPath().size() > 4)
27 return false;
28
29 if (request.method() == http::verb::get)
30 HandleGet(user, request, url, response, params);
31 else if (request.method() == http::verb::post)
32 HandlePost(user, request, url, response, params);
33 else if (request.method() == http::verb::delete_)
34 HandleDelete(user, request, url, response, params);
35 else
36 return false;
37
38 return true;
39 }
40
HandleGet(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)41 void ConfigPackagesHandler::HandleGet(
42 const ApiUser::Ptr& user,
43 boost::beast::http::request<boost::beast::http::string_body>& request,
44 const Url::Ptr& url,
45 boost::beast::http::response<boost::beast::http::string_body>& response,
46 const Dictionary::Ptr& params
47 )
48 {
49 namespace http = boost::beast::http;
50
51 FilterUtility::CheckPermission(user, "config/query");
52
53 std::vector<String> packages;
54
55 try {
56 packages = ConfigPackageUtility::GetPackages();
57 } catch (const std::exception& ex) {
58 HttpUtility::SendJsonError(response, params, 500, "Could not retrieve packages.",
59 DiagnosticInformation(ex));
60 return;
61 }
62
63 ArrayData results;
64
65 {
66 std::unique_lock<std::mutex> lock(ConfigPackageUtility::GetStaticPackageMutex());
67
68 for (const String& package : packages) {
69 String activeStage;
70
71 try {
72 activeStage = ConfigPackageUtility::GetActiveStage(package);
73 } catch (const std::exception&) { } /* Should never happen. */
74
75 results.emplace_back(new Dictionary({
76 { "name", package },
77 { "stages", Array::FromVector(ConfigPackageUtility::GetStages(package)) },
78 { "active-stage", activeStage }
79 }));
80 }
81 }
82
83 Dictionary::Ptr result = new Dictionary({
84 { "results", new Array(std::move(results)) }
85 });
86
87 response.result(http::status::ok);
88 HttpUtility::SendJsonBody(response, params, result);
89 }
90
HandlePost(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)91 void ConfigPackagesHandler::HandlePost(
92 const ApiUser::Ptr& user,
93 boost::beast::http::request<boost::beast::http::string_body>& request,
94 const Url::Ptr& url,
95 boost::beast::http::response<boost::beast::http::string_body>& response,
96 const Dictionary::Ptr& params
97 )
98 {
99 namespace http = boost::beast::http;
100
101 FilterUtility::CheckPermission(user, "config/modify");
102
103 if (url->GetPath().size() >= 4)
104 params->Set("package", url->GetPath()[3]);
105
106 String packageName = HttpUtility::GetLastParameter(params, "package");
107
108 if (!ConfigPackageUtility::ValidatePackageName(packageName)) {
109 HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
110 return;
111 }
112
113 try {
114 std::unique_lock<std::mutex> lock(ConfigPackageUtility::GetStaticPackageMutex());
115
116 ConfigPackageUtility::CreatePackage(packageName);
117 } catch (const std::exception& ex) {
118 HttpUtility::SendJsonError(response, params, 500, "Could not create package '" + packageName + "'.",
119 DiagnosticInformation(ex));
120 return;
121 }
122
123 Dictionary::Ptr result1 = new Dictionary({
124 { "code", 200 },
125 { "package", packageName },
126 { "status", "Created package." }
127 });
128
129 Dictionary::Ptr result = new Dictionary({
130 { "results", new Array({ result1 }) }
131 });
132
133 response.result(http::status::ok);
134 HttpUtility::SendJsonBody(response, params, result);
135 }
136
HandleDelete(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)137 void ConfigPackagesHandler::HandleDelete(
138 const ApiUser::Ptr& user,
139 boost::beast::http::request<boost::beast::http::string_body>& request,
140 const Url::Ptr& url,
141 boost::beast::http::response<boost::beast::http::string_body>& response,
142 const Dictionary::Ptr& params
143 )
144 {
145 namespace http = boost::beast::http;
146
147 FilterUtility::CheckPermission(user, "config/modify");
148
149 if (url->GetPath().size() >= 4)
150 params->Set("package", url->GetPath()[3]);
151
152 String packageName = HttpUtility::GetLastParameter(params, "package");
153
154 if (!ConfigPackageUtility::ValidatePackageName(packageName)) {
155 HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
156 return;
157 }
158
159 try {
160 ConfigPackageUtility::DeletePackage(packageName);
161 } catch (const std::exception& ex) {
162 HttpUtility::SendJsonError(response, params, 500, "Failed to delete package '" + packageName + "'.",
163 DiagnosticInformation(ex));
164 return;
165 }
166
167 Dictionary::Ptr result1 = new Dictionary({
168 { "code", 200 },
169 { "package", packageName },
170 { "status", "Deleted package." }
171 });
172
173 Dictionary::Ptr result = new Dictionary({
174 { "results", new Array({ result1 }) }
175 });
176
177 response.result(http::status::ok);
178 HttpUtility::SendJsonBody(response, params, result);
179 }
180