loader.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef ACL_LOADER_H
  15. #define ACL_LOADER_H
  16. #include "acl.h"
  17. #include <cc/data.h>
  18. #include <boost/function.hpp>
  19. #include <boost/shared_ptr.hpp>
  20. #include <map>
  21. namespace isc {
  22. namespace acl {
  23. class AnyOfSpec;
  24. class AllOfSpec;
  25. template<typename Mode, typename Context> class LogicOperator;
  26. /**
  27. * \brief Exception for bad ACL specifications.
  28. *
  29. * This will be thrown by the Loader if the ACL description is malformed
  30. * in some way.
  31. *
  32. * It also can hold optional JSON element where was the error detected, so
  33. * it can be examined.
  34. *
  35. * Checks may subclass this exception for similar errors if they see it fit.
  36. */
  37. class LoaderError : public BadValue {
  38. private:
  39. const data::ConstElementPtr element_;
  40. public:
  41. /**
  42. * \brief Constructor.
  43. *
  44. * Should be used with isc_throw if the fourth argument isn't used.
  45. *
  46. * \param file The file where the throw happened.
  47. * \param line Similar as file, just for the line number.
  48. * \param what Human readable description of what happened.
  49. * \param element This might be passed to hold the JSON element where
  50. * the error was detected.
  51. */
  52. LoaderError(const char* file, size_t line, const char* what,
  53. data::ConstElementPtr element = data::ConstElementPtr()) :
  54. BadValue(file, line, what),
  55. element_(element)
  56. {}
  57. ~ LoaderError() throw() {}
  58. /**
  59. * \brief Get the element.
  60. *
  61. * This returns the element where the error was detected. Note that it
  62. * might be NULL in some situations.
  63. */
  64. const data::ConstElementPtr& element() const {
  65. return (element_);
  66. }
  67. };
  68. /**
  69. * \brief Loader of the default actions of ACLs.
  70. *
  71. * Declared outside the Loader class, as this one does not need to be
  72. * templated. This will throw LoaderError if the parameter isn't string
  73. * or if it doesn't contain one of the accepted values.
  74. *
  75. * \param action The JSON representation of the action. It must be a string
  76. * and contain one of "ACCEPT", "REJECT" or "DENY".
  77. * \note We could define different names or add aliases if needed.
  78. */
  79. BasicAction defaultActionLoader(data::ConstElementPtr action);
  80. /**
  81. * \brief Loader of ACLs.
  82. *
  83. * The goal of this class is to convert JSON description of an ACL to object
  84. * of the ACL class (including the checks inside it).
  85. *
  86. * The class can be used to load the checks only. This is supposed to be used
  87. * by compound checks to create the subexpressions.
  88. *
  89. * To allow any kind of checks to exist in the application, creators are
  90. * registered for the names of the checks.
  91. *
  92. * An ACL definition looks like this:
  93. * \verbatim
  94. * [
  95. * {
  96. * "action": "ACCEPT",
  97. * "match-type": <parameter>
  98. * },
  99. * {
  100. * "action": "REJECT",
  101. * "match-type": <parameter>
  102. * "another-match-type": [<parameter1>, <parameter2>]
  103. * },
  104. * {
  105. * "action": "DROP"
  106. * }
  107. * ]
  108. * \endverbatim
  109. *
  110. * This is a list of elements. Each element must have an "action"
  111. * entry/keyword. That one specifies which action is returned if this
  112. * element matches (the value of the key is passed to the action loader
  113. * (see the constructor). It may be any piece of JSON which the action
  114. * loader expects.
  115. *
  116. * The rest of the element are matches. The left side is the name of the
  117. * match type (for example match for source IP address or match for message
  118. * size). The <parameter> is whatever is needed to describe the match and
  119. * depends on the match type, the loader passes it verbatim to creator
  120. * of that match type.
  121. *
  122. * There may be multiple match types in single element. In such case, all
  123. * of the matches must match for the element to take action (so, in the second
  124. * element, both "match-type" and "another-match-type" must be satisfied).
  125. * If there's no match in the element, the action is taken/returned without
  126. * conditions, every time (makes sense as the last entry, as the ACL will
  127. * never get past it).
  128. *
  129. * The second entry shows another thing - if there's a list as the value
  130. * for some match and the match itself is not expecting a list, it is taken
  131. * as an "or" - a match for at last one of the choices in the list must match.
  132. * So, for the second entry, both "match-type" and "another-match-type" must
  133. * be satisfied, but the another one is satisfied by either parameter1 or
  134. * parameter2.
  135. */
  136. template<typename Context, typename Action = BasicAction> class Loader {
  137. public:
  138. /**
  139. * \brief Constructor.
  140. *
  141. * \param default_action The default action for created ACLs.
  142. * \param actionLoader is the loader which will be used to convert actions
  143. * from their JSON representation. The default value is suitable for
  144. * the BasicAction enum. If you did not specify the second
  145. * template argument, you don't need to specify this loader.
  146. */
  147. Loader(const Action& defaultAction,
  148. const boost::function1<Action, data::ConstElementPtr>
  149. &actionLoader = &defaultActionLoader) :
  150. default_action_(defaultAction),
  151. action_loader_(actionLoader)
  152. {}
  153. /**
  154. * \brief Creator of the checks.
  155. *
  156. * This can be registered within the Loader and will be used to create the
  157. * checks. It is expected multiple creators (for multiple types, one can
  158. * handle even multiple names) will be created and registered to support
  159. * range of things we could check. This allows for customizing/extending
  160. * the loader.
  161. */
  162. class CheckCreator {
  163. public:
  164. /**
  165. * \brief List of names supported by this loader.
  166. *
  167. * List of all names for which this loader is able to create the
  168. * checks. There can be multiple names, to support both aliases
  169. * to the same checks and creators capable of creating multiple
  170. * types of checks.
  171. */
  172. virtual std::vector<std::string> names() const = 0;
  173. /**
  174. * \brief Creates the check.
  175. *
  176. * This function does the actual creation. It is passed all the
  177. * relevant data and is supposed to return shared pointer to the
  178. * check.
  179. *
  180. * It is expected to throw the LoaderError exception when the
  181. * definition is invalid.
  182. *
  183. * \param name The type name of the check. If the creator creates
  184. * only one type of check, it can safely ignore this parameter.
  185. * \param definition The part of JSON describing the parameters of
  186. * check. As there's no way for the loader to know how the
  187. * parameters might look like, they are not checked in any way.
  188. * Therefore it's up to the creator (or the check being created)
  189. * to validate the data and throw if it is bad.
  190. * \param Current loader calling this creator. This can be used
  191. * to load subexpressions in case of compound check.
  192. */
  193. virtual boost::shared_ptr<Check<Context> > create(
  194. const std::string& name, data::ConstElementPtr definition,
  195. const Loader<Context, Action>& loader) = 0;
  196. /**
  197. * \brief Is list or-abbreviation allowed?
  198. *
  199. * If this returns true and the parameter (eg. the value we check
  200. * against, the one that is passed as the second parameter of create)
  201. * is list, the loader will call the create method with each element of
  202. * the list and aggregate all the results in OR compound check. If it
  203. * is false, the parameter is passed verbatim no matter if it is or
  204. * isn't a list. For example, IP check will have this as true (so
  205. * multiple IP addresses can be passed as options), but AND operator
  206. * will return false and handle the list of subexpressions itself.
  207. *
  208. * The rationale behind this is that it is common to specify list of
  209. * something that matches (eg. list of IP addresses).
  210. */
  211. virtual bool allowListAbbreviation() const {
  212. return (true);
  213. }
  214. };
  215. /**
  216. * \brief Register another check creator.
  217. *
  218. * Adds a creator to the list of known ones. The creator's list of names
  219. * must be disjoint with the names already known to the creator or the
  220. * LoaderError exception is thrown. In such case, the creator is not
  221. * registered under any of the names. In case of other exceptions, like
  222. * bad_alloc, only weak exception safety is guaranteed.
  223. *
  224. * \param creator Shared pointer to the creator.
  225. * \note We don't support deregistration yet, but it is expected it will
  226. * be needed in future, when we have some kind of plugins. These
  227. * plugins might want to unload, in which case they would need to
  228. * deregister their creators. It is expected they would pass the same
  229. * pointer to such method as they pass here.
  230. */
  231. void registerCreator(boost::shared_ptr<CheckCreator> creator) {
  232. // First check we can insert all the names
  233. typedef std::vector<std::string> Strings;
  234. const Strings names(creator->names());
  235. for (Strings::const_iterator i(names.begin()); i != names.end();
  236. ++i) {
  237. if (creators_.find(*i) != creators_.end()) {
  238. isc_throw(LoaderError, "The loader already contains creator "
  239. "named " << *i);
  240. }
  241. }
  242. // Now insert them
  243. for (Strings::const_iterator i(names.begin()); i != names.end();
  244. ++i) {
  245. creators_[*i] = creator;
  246. }
  247. }
  248. /**
  249. * \brief Load a check.
  250. *
  251. * This parses a check dict (block, the one element of ACL) and calls a
  252. * creator (or creators, if more than one check is found inside) for it. It
  253. * ignores the "action" key, as it is a reserved keyword used to specify
  254. * actions inside the ACL.
  255. *
  256. * This may throw LoaderError if it is not a dict or if some of the type
  257. * names is not known (there's no creator registered for it). The
  258. * exceptions from creators aren't caught.
  259. *
  260. * \param description The JSON description of the check.
  261. */
  262. boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
  263. description) const
  264. {
  265. // Get the description as a map
  266. typedef std::map<std::string, data::ConstElementPtr> Map;
  267. Map map;
  268. try {
  269. map = description->mapValue();
  270. }
  271. catch (const data::TypeError&) {
  272. isc_throw_1(LoaderError, "Check description is not a map",
  273. description);
  274. }
  275. // Call the internal part with extracted map
  276. return (loadCheck(description, map));
  277. }
  278. /**
  279. * \brief Load an ACL.
  280. *
  281. * This parses an ACL list, creates the checks and actions of each element
  282. * and returns it. It may throw LoaderError if it isn't a list or the
  283. * "action" key is missing in some element. Also, no exceptions from
  284. * loadCheck (therefore from whatever creator is used) and from the
  285. * actionLoader passed to constructor are not caught.
  286. *
  287. * \param description The JSON list of ACL.
  288. */
  289. boost::shared_ptr<ACL<Context, Action> > load(const data::ConstElementPtr&
  290. description) const
  291. {
  292. // We first check it's a list, so we can use the list reference
  293. // (the list may be huge)
  294. if (description->getType() != data::Element::list) {
  295. isc_throw_1(LoaderError, "ACL not a list", description);
  296. }
  297. // First create an empty ACL
  298. const List &list(description->listValue());
  299. boost::shared_ptr<ACL<Context, Action> > result(
  300. new ACL<Context, Action>(default_action_));
  301. // Run trough the list of elements
  302. for (List::const_iterator i(list.begin()); i != list.end(); ++i) {
  303. Map map;
  304. try {
  305. map = (*i)->mapValue();
  306. }
  307. catch (const data::TypeError&) {
  308. isc_throw_1(LoaderError, "ACL element not a map", *i);
  309. }
  310. // Create an action for the element
  311. const Map::const_iterator action(map.find("action"));
  312. if (action == map.end()) {
  313. isc_throw_1(LoaderError, "No action in ACL element", *i);
  314. }
  315. const Action acValue(action_loader_(action->second));
  316. // Now create the check if there's one
  317. if (map.size() >= 2) { // One is the action, another one the check
  318. result->append(loadCheck(*i, map), acValue);
  319. } else {
  320. // In case there's no check, this matches every time. We
  321. // simulate it by our own private "True" check.
  322. result->append(boost::shared_ptr<Check<Context> >(new True()),
  323. acValue);
  324. }
  325. }
  326. return (result);
  327. }
  328. private:
  329. // Some type aliases to save typing
  330. typedef std::map<std::string, boost::shared_ptr<CheckCreator> > Creators;
  331. typedef std::map<std::string, data::ConstElementPtr> Map;
  332. typedef std::vector<data::ConstElementPtr> List;
  333. // Private members
  334. Creators creators_;
  335. const Action default_action_;
  336. const boost::function1<Action, data::ConstElementPtr> action_loader_;
  337. /**
  338. * \brief Internal version of loadCheck.
  339. *
  340. * This is the internal part, shared between load and loadCheck.
  341. * \param description The bit of JSON (used in exceptions).
  342. * \param map The extracted map describing the check. It does change
  343. * the map.
  344. */
  345. boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
  346. description, Map& map) const
  347. {
  348. // Remove the action keyword
  349. map.erase("action");
  350. // Now, do we have any definition? Or is it and abbreviation?
  351. switch (map.size()) {
  352. case 0:
  353. isc_throw_1(LoaderError, "Check description is empty",
  354. description);
  355. case 1: {
  356. // Get the first and only item
  357. const Map::const_iterator checkDesc(map.begin());
  358. const std::string& name(checkDesc->first);
  359. const typename Creators::const_iterator
  360. creatorIt(creators_.find(name));
  361. if (creatorIt == creators_.end()) {
  362. isc_throw_1(LoaderError, "No creator for ACL check " <<
  363. name, description);
  364. }
  365. if (creatorIt->second->allowListAbbreviation() &&
  366. checkDesc->second->getType() == data::Element::list) {
  367. // Or-abbreviated form - create an OR and put everything
  368. // inside.
  369. const std::vector<data::ConstElementPtr>&
  370. params(checkDesc->second->listValue());
  371. boost::shared_ptr<LogicOperator<AnyOfSpec, Context> >
  372. oper(new LogicOperator<AnyOfSpec, Context>);
  373. for (std::vector<data::ConstElementPtr>::const_iterator
  374. i(params.begin());
  375. i != params.end(); ++i) {
  376. oper->addSubexpression(
  377. creatorIt->second->create(name, *i, *this));
  378. }
  379. return (oper);
  380. }
  381. // Create the check and return it
  382. return (creatorIt->second->create(name, checkDesc->second,
  383. *this));
  384. }
  385. default: {
  386. // This is the AND-abbreviated form. We need to create an
  387. // AND (or "ALL") operator, loop trough the whole map and
  388. // fill it in. We do a small trick - we create bunch of
  389. // single-item maps, call this loader recursively (therefore
  390. // it will get into the "case 1" branch, where there is
  391. // the actual loading) and use the results to fill the map.
  392. //
  393. // We keep the description the same, there's nothing we could
  394. // take out (we could create a new one, but that would be
  395. // confusing, as it is used for error messages only).
  396. boost::shared_ptr<LogicOperator<AllOfSpec, Context> >
  397. oper(new LogicOperator<AllOfSpec, Context>);
  398. for (Map::const_iterator i(map.begin()); i != map.end(); ++i) {
  399. Map singleSubexpr;
  400. singleSubexpr.insert(*i);
  401. oper->addSubexpression(loadCheck(description,
  402. singleSubexpr));
  403. }
  404. return (oper);
  405. }
  406. }
  407. }
  408. /**
  409. * \brief Check that always matches.
  410. *
  411. * This one is used internally for ACL elements without condition. We may
  412. * want to make this publicly accesible sometime maybe, but for now,
  413. * there's no need.
  414. */
  415. class True : public Check<Context> {
  416. public:
  417. virtual bool matches(const Context&) const { return (true); };
  418. virtual unsigned cost() const { return (1); }
  419. // We don't write "true" here, as this one was created using empty
  420. // input
  421. virtual std::string toText() const { return ""; }
  422. };
  423. };
  424. }
  425. }
  426. /*
  427. * This include at the end of the file is unusual. But we need to include it,
  428. * we use template classes from there. However, they need to be present only
  429. * at instantiation of our class, which will happen below this header.
  430. *
  431. * The problem is, the header uses us as well, therefore there's a circular
  432. * dependency. If we loaded it at the beginning and someone loaded us first,
  433. * the logic_check header wouldn't have our definitions. This way, no matter
  434. * in which order they are loaded, the definitions from this header will be
  435. * above the ones from logic_check.
  436. */
  437. #include "logic_check.h"
  438. #endif