loader.h 19 KB

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