1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "remote/templatequeryhandler.hpp"
4 #include "remote/httputility.hpp"
5 #include "remote/filterutility.hpp"
6 #include "config/configitem.hpp"
7 #include "base/configtype.hpp"
8 #include "base/scriptglobal.hpp"
9 #include "base/logger.hpp"
10 #include <boost/algorithm/string/case_conv.hpp>
11 #include <set>
12 
13 using namespace icinga;
14 
15 REGISTER_URLHANDLER("/v1/templates", TemplateQueryHandler);
16 
17 class TemplateTargetProvider final : public TargetProvider
18 {
19 public:
20 	DECLARE_PTR_TYPEDEFS(TemplateTargetProvider);
21 
GetTargetForTemplate(const ConfigItem::Ptr & item)22 	static Dictionary::Ptr GetTargetForTemplate(const ConfigItem::Ptr& item)
23 	{
24 		DebugInfo di = item->GetDebugInfo();
25 
26 		return new Dictionary({
27 			{ "name", item->GetName() },
28 			{ "type", item->GetType()->GetName() },
29 			{ "location", new Dictionary({
30 				{ "path", di.Path },
31 				{ "first_line", di.FirstLine },
32 				{ "first_column", di.FirstColumn },
33 				{ "last_line", di.LastLine },
34 				{ "last_column", di.LastColumn }
35 			}) }
36 		});
37 	}
38 
FindTargets(const String & type,const std::function<void (const Value &)> & addTarget) const39 	void FindTargets(const String& type,
40 		const std::function<void (const Value&)>& addTarget) const override
41 	{
42 		Type::Ptr ptype = Type::GetByName(type);
43 
44 		for (const ConfigItem::Ptr& item : ConfigItem::GetItems(ptype)) {
45 			if (item->IsAbstract())
46 				addTarget(GetTargetForTemplate(item));
47 		}
48 	}
49 
GetTargetByName(const String & type,const String & name) const50 	Value GetTargetByName(const String& type, const String& name) const override
51 	{
52 		Type::Ptr ptype = Type::GetByName(type);
53 
54 		ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(ptype, name);
55 
56 		if (!item || !item->IsAbstract())
57 			BOOST_THROW_EXCEPTION(std::invalid_argument("Template does not exist."));
58 
59 		return GetTargetForTemplate(item);
60 	}
61 
IsValidType(const String & type) const62 	bool IsValidType(const String& type) const override
63 	{
64 		Type::Ptr ptype = Type::GetByName(type);
65 
66 		if (!ptype)
67 			return false;
68 
69 		return ConfigObject::TypeInstance->IsAssignableFrom(ptype);
70 	}
71 
GetPluralName(const String & type) const72 	String GetPluralName(const String& type) const override
73 	{
74 		return Type::GetByName(type)->GetPluralName();
75 	}
76 };
77 
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)78 bool TemplateQueryHandler::HandleRequest(
79 	AsioTlsStream& stream,
80 	const ApiUser::Ptr& user,
81 	boost::beast::http::request<boost::beast::http::string_body>& request,
82 	const Url::Ptr& url,
83 	boost::beast::http::response<boost::beast::http::string_body>& response,
84 	const Dictionary::Ptr& params,
85 	boost::asio::yield_context& yc,
86 	HttpServerConnection& server
87 )
88 {
89 	namespace http = boost::beast::http;
90 
91 	if (url->GetPath().size() < 3 || url->GetPath().size() > 4)
92 		return false;
93 
94 	if (request.method() != http::verb::get)
95 		return false;
96 
97 	Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]);
98 
99 	if (!type) {
100 		HttpUtility::SendJsonError(response, params, 400, "Invalid type specified.");
101 		return true;
102 	}
103 
104 	QueryDescription qd;
105 	qd.Types.insert(type->GetName());
106 	qd.Permission = "templates/query/" + type->GetName();
107 	qd.Provider = new TemplateTargetProvider();
108 
109 	params->Set("type", type->GetName());
110 
111 	if (url->GetPath().size() >= 4) {
112 		String attr = type->GetName();
113 		boost::algorithm::to_lower(attr);
114 		params->Set(attr, url->GetPath()[3]);
115 	}
116 
117 	std::vector<Value> objs;
118 
119 	try {
120 		objs = FilterUtility::GetFilterTargets(qd, params, user, "tmpl");
121 	} catch (const std::exception& ex) {
122 		HttpUtility::SendJsonError(response, params, 404,
123 			"No templates found.",
124 			DiagnosticInformation(ex));
125 		return true;
126 	}
127 
128 	Dictionary::Ptr result = new Dictionary({
129 		{ "results", new Array(std::move(objs)) }
130 	});
131 
132 	response.result(http::status::ok);
133 	HttpUtility::SendJsonBody(response, params, result);
134 
135 	return true;
136 }
137