loader_test.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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. #include "creators.h"
  15. #include <exceptions/exceptions.h>
  16. #include <acl/loader.h>
  17. #include <string>
  18. #include <gtest/gtest.h>
  19. using namespace std;
  20. using namespace boost;
  21. using namespace isc::acl;
  22. using namespace isc::acl::tests;
  23. using isc::data::Element;
  24. using isc::data::ConstElementPtr;
  25. namespace {
  26. // We don't use the EXPECT_THROW macro, as it doesn't allow us
  27. // to examine the exception. We want to check the element is stored
  28. // there as well.
  29. void testActionLoaderException(const string& JSON) {
  30. SCOPED_TRACE("Should throw with input: " + JSON);
  31. ConstElementPtr elem(Element::fromJSON(JSON));
  32. try {
  33. defaultActionLoader(elem);
  34. FAIL() << "It did not throw";
  35. }
  36. catch (const LoaderError& error) {
  37. // Yes, comparing for pointer equality, that is enough, it
  38. // should return the exact instance of the JSON object
  39. EXPECT_EQ(elem, error.element());
  40. }
  41. }
  42. // Test the defaultActionLoader function
  43. TEST(LoaderHelpers, DefaultActionLoader) {
  44. // First the three valid inputs
  45. EXPECT_EQ(ACCEPT, defaultActionLoader(Element::fromJSON("\"ACCEPT\"")));
  46. EXPECT_EQ(REJECT, defaultActionLoader(Element::fromJSON("\"REJECT\"")));
  47. EXPECT_EQ(DROP, defaultActionLoader(Element::fromJSON("\"DROP\"")));
  48. // Now few invalid ones
  49. // String, but unknown one
  50. testActionLoaderException("\"UNKNOWN\"");
  51. testActionLoaderException("42");
  52. testActionLoaderException("true");
  53. testActionLoaderException("null");
  54. testActionLoaderException("[]");
  55. testActionLoaderException("{}");
  56. }
  57. class LoaderTest : public ::testing::Test {
  58. public:
  59. LoaderTest() :
  60. loader_(REJECT)
  61. {}
  62. Loader<Log> loader_;
  63. Log log_;
  64. // Some convenience functions to set up
  65. // Create a NamedCreator, convert to shared pointer
  66. boost::shared_ptr<NamedCreator> namedCreator(const string& name,
  67. bool abbreviatedList = true)
  68. {
  69. return (boost::shared_ptr<NamedCreator>(new NamedCreator(name,
  70. abbreviatedList)));
  71. }
  72. // Create and add a NamedCreator
  73. void addNamed(const string& name, bool abbreviatedList = true) {
  74. EXPECT_NO_THROW(loader_.registerCreator(
  75. namedCreator(name, abbreviatedList)));
  76. }
  77. template<class Result> boost::shared_ptr<Result> loadCheckAny(
  78. const string& definition)
  79. {
  80. SCOPED_TRACE("Loading check " + definition);
  81. boost::shared_ptr<Check<Log> > loaded;
  82. EXPECT_NO_THROW(loaded = loader_.loadCheck(
  83. Element::fromJSON(definition)));
  84. boost::shared_ptr<Result> result(dynamic_pointer_cast<Result>(loaded));
  85. EXPECT_TRUE(result);
  86. return (result);
  87. }
  88. // Load a check and convert it to named check to examine it
  89. boost::shared_ptr<NamedCheck> loadCheck(const string& definition) {
  90. return (loadCheckAny<NamedCheck>(definition));
  91. }
  92. // The loadCheck throws an exception
  93. void checkException(const string& JSON) {
  94. SCOPED_TRACE("Loading check exception: " + JSON);
  95. ConstElementPtr input(Element::fromJSON(JSON));
  96. // Not using EXPECT_THROW, we want to examine the exception
  97. try {
  98. loader_.loadCheck(input);
  99. FAIL() << "Should have thrown";
  100. }
  101. catch (const LoaderError& e) {
  102. // It should be identical copy, so checking pointers
  103. EXPECT_EQ(input, e.element());
  104. }
  105. }
  106. // Insert the throw, throwcheck and logcheck checks into the loader
  107. void aclSetup() {
  108. try {
  109. loader_.registerCreator(boost::shared_ptr<ThrowCreator>(
  110. new ThrowCreator()));
  111. loader_.registerCreator(boost::shared_ptr<ThrowCheckCreator>(
  112. new ThrowCheckCreator()));
  113. loader_.registerCreator(boost::shared_ptr<LogCreator>(
  114. new LogCreator()));
  115. }
  116. // We ignore this exception here, because it happens when we try to
  117. // insert the creators multiple times. This is harmless.
  118. catch (const LoaderError&) {}
  119. }
  120. // Create an ACL, run it, check it's result and how many first
  121. // log items it marked
  122. //
  123. // Works with preset names throw and logcheck
  124. void aclRun(const string& JSON, BasicAction expectedResult,
  125. size_t logged)
  126. {
  127. SCOPED_TRACE("Running ACL for " + JSON);
  128. aclSetup();
  129. boost::shared_ptr<ACL<Log> > acl;
  130. EXPECT_NO_THROW(acl = loader_.load(Element::fromJSON(JSON)));
  131. EXPECT_EQ(expectedResult, acl->execute(log_));
  132. log_.checkFirst(logged);
  133. }
  134. // Check it throws an error when creating the ACL
  135. void aclException(const string& JSON) {
  136. SCOPED_TRACE("Trying to load bad " + JSON);
  137. aclSetup();
  138. EXPECT_THROW(loader_.load(Element::fromJSON(JSON)), LoaderError);
  139. }
  140. // Check that the subexpression is NamedCheck with correct data
  141. void isSubexprNamed(const CompoundCheck<Log>* compound, size_t index,
  142. const string& name, ConstElementPtr data)
  143. {
  144. if (index < compound->getSubexpressions().size()) {
  145. const NamedCheck*
  146. check(dynamic_cast<const NamedCheck*>(compound->
  147. getSubexpressions()
  148. [index]));
  149. ASSERT_TRUE(check) << "The subexpression is of different type";
  150. EXPECT_EQ(name, check->name_);
  151. EXPECT_TRUE(data->equals(*check->data_));
  152. }
  153. }
  154. };
  155. // Test that it does not accept duplicate creator
  156. TEST_F(LoaderTest, CreatorDuplicity) {
  157. addNamed("name");
  158. EXPECT_THROW(loader_.registerCreator(namedCreator("name")), LoaderError);
  159. }
  160. // Test that when it does not accept a duplicate, nothing is inserted
  161. TEST_F(LoaderTest, CreatorDuplicateUnchanged) {
  162. addNamed("name1");
  163. vector<string> names;
  164. names.push_back("name2");
  165. names.push_back("name1");
  166. names.push_back("name3");
  167. EXPECT_THROW(loader_.registerCreator(
  168. boost::shared_ptr<NamedCreator>(new NamedCreator(names))), LoaderError);
  169. // It should now reject both name2 and name3 as not known
  170. checkException("{\"name2\": null}");
  171. checkException("{\"name3\": null}");
  172. }
  173. // Test that we can register a creator and load a check with the name
  174. TEST_F(LoaderTest, SimpleCheckLoad) {
  175. addNamed("name");
  176. boost::shared_ptr<NamedCheck> check(loadCheck("{\"name\": 42}"));
  177. EXPECT_EQ("name", check->name_);
  178. EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
  179. }
  180. // As above, but there are multiple creators registered within the loader
  181. TEST_F(LoaderTest, MultiCreatorCheckLoad) {
  182. addNamed("name1");
  183. addNamed("name2");
  184. boost::shared_ptr<NamedCheck> check(loadCheck("{\"name2\": 42}"));
  185. EXPECT_EQ("name2", check->name_);
  186. EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
  187. }
  188. // Similar to above, but there's a creator with multiple names
  189. TEST_F(LoaderTest, MultiNameCheckLoad) {
  190. addNamed("name1");
  191. vector<string> names;
  192. names.push_back("name2");
  193. names.push_back("name3");
  194. EXPECT_NO_THROW(loader_.registerCreator(boost::shared_ptr<NamedCreator>(
  195. new NamedCreator(names))));
  196. boost::shared_ptr<NamedCheck> check(loadCheck("{\"name3\": 42}"));
  197. EXPECT_EQ("name3", check->name_);
  198. EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
  199. }
  200. // Invalid format is rejected
  201. TEST_F(LoaderTest, InvalidFormatCheck) {
  202. checkException("[]");
  203. checkException("42");
  204. checkException("\"hello\"");
  205. checkException("null");
  206. }
  207. // Empty check is rejected
  208. TEST_F(LoaderTest, EmptyCheck) {
  209. checkException("{}");
  210. }
  211. // The name isn't known
  212. TEST_F(LoaderTest, UnkownName) {
  213. checkException("{\"unknown\": null}");
  214. }
  215. // Exception from the creator is propagated
  216. TEST_F(LoaderTest, CheckPropagate) {
  217. loader_.registerCreator(boost::shared_ptr<ThrowCreator>(
  218. new ThrowCreator()));
  219. EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"throw\": null}")),
  220. TestCreatorError);
  221. }
  222. // The abbreviated form of check
  223. TEST_F(LoaderTest, AndAbbrev) {
  224. addNamed("name1");
  225. addNamed("name2");
  226. boost::shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
  227. loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": 2}"));
  228. // If we don't have anything loaded, the rest would crash. It is already
  229. // reported from within loadCheckAny if it isn't loaded.
  230. if (oper) {
  231. // The subexpressions are correct
  232. EXPECT_EQ(2, oper->getSubexpressions().size());
  233. // Note: this test relies on the ordering in which map returns it's
  234. // elements, which is in the lexicographical order of the strings.
  235. // This is not required from our interface, but is easier to write
  236. // the test.
  237. isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
  238. isSubexprNamed(&*oper, 1, "name2", Element::fromJSON("2"));
  239. }
  240. }
  241. // The abbreviated form of parameters
  242. TEST_F(LoaderTest, OrAbbrev) {
  243. addNamed("name1");
  244. boost::shared_ptr<LogicOperator<AnyOfSpec, Log> > oper(
  245. loadCheckAny<LogicOperator<AnyOfSpec, Log> >("{\"name1\": [1, 2]}"));
  246. // If we don't have anything loaded, the rest would crash. It is already
  247. // reported from within loadCheckAny if it isn't loaded.
  248. if (oper) {
  249. // The subexpressions are correct
  250. EXPECT_EQ(2, oper->getSubexpressions().size());
  251. isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
  252. isSubexprNamed(&*oper, 1, "name1", Element::fromJSON("2"));
  253. }
  254. }
  255. // Combined abbreviated form, both at once
  256. // The abbreviated form of check
  257. TEST_F(LoaderTest, BothAbbrev) {
  258. addNamed("name1");
  259. addNamed("name2");
  260. boost::shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
  261. loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": [3, 4]}"));
  262. // If we don't have anything loaded, the rest would crash. It is already
  263. // reported from within loadCheckAny if it isn't loaded.
  264. if (oper) {
  265. // The subexpressions are correct
  266. ASSERT_EQ(2, oper->getSubexpressions().size());
  267. // Note: this test relies on the ordering in which map returns it's
  268. // elements, which is in the lexicographical order of the strings.
  269. // This is not required from our interface, but is easier to write
  270. // the test.
  271. isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
  272. const LogicOperator<AnyOfSpec, Log>*
  273. orOper(dynamic_cast<const LogicOperator<AnyOfSpec, Log>*>(
  274. oper->getSubexpressions()[1]));
  275. ASSERT_TRUE(orOper) << "Different type than AnyOf operator";
  276. EXPECT_EQ(2, orOper->getSubexpressions().size());
  277. isSubexprNamed(orOper, 0, "name2", Element::fromJSON("3"));
  278. isSubexprNamed(orOper, 1, "name2", Element::fromJSON("4"));
  279. }
  280. }
  281. // But this is not abbreviated form, this should be passed directly to the
  282. // creator
  283. TEST_F(LoaderTest, ListCheck) {
  284. addNamed("name1", false);
  285. boost::shared_ptr<NamedCheck> check(loadCheck("{\"name1\": [1, 2]}"));
  286. EXPECT_EQ("name1", check->name_);
  287. EXPECT_TRUE(check->data_->equals(*Element::fromJSON("[1, 2]")));
  288. }
  289. // Check the action key is ignored as it should be
  290. TEST_F(LoaderTest, CheckNoAction) {
  291. addNamed("name1");
  292. boost::shared_ptr<NamedCheck> check(loadCheck("{\"name1\": 1, \"action\": 2}"));
  293. EXPECT_EQ("name1", check->name_);
  294. EXPECT_TRUE(check->data_->equals(*Element::fromJSON("1")));
  295. }
  296. // The empty ACL can be created and run, providing the default action
  297. TEST_F(LoaderTest, EmptyACL) {
  298. aclRun("[]", REJECT, 0);
  299. }
  300. // We can create a simple ACL, which will return the correct default
  301. // action
  302. TEST_F(LoaderTest, NoMatchACL) {
  303. aclRun("[{\"logcheck\": [0, false], \"action\": \"ACCEPT\"}]",
  304. REJECT, 1);
  305. }
  306. // We can created more complicated ACL, it will match at the second
  307. // check
  308. TEST_F(LoaderTest, MatchACL) {
  309. aclRun("["
  310. " {\"logcheck\": [0, false], \"action\": \"DROP\"},"
  311. " {\"logcheck\": [1, true], \"action\": \"ACCEPT\"}"
  312. "]", ACCEPT, 2);
  313. }
  314. // ACL without a check (matches unconditionally)
  315. // We add another one check after it, to make sure it is really not run
  316. TEST_F(LoaderTest, NoCheckACL) {
  317. aclRun("["
  318. " {\"action\": \"DROP\"},"
  319. " {\"throwcheck\": 1, \"action\": \"ACCEPT\"}"
  320. "]", DROP, 0);
  321. }
  322. // Malformed things are rejected
  323. TEST_F(LoaderTest, InvalidACLFormat) {
  324. // Not a list
  325. aclException("{}");
  326. aclException("42");
  327. aclException("true");
  328. aclException("null");
  329. aclException("\"hello\"");
  330. // Malformed element
  331. aclException("[42]");
  332. aclException("[\"hello\"]");
  333. aclException("[[]]");
  334. aclException("[true]");
  335. aclException("[null]");
  336. }
  337. // If there's no action keyword, it is rejected
  338. TEST_F(LoaderTest, NoAction) {
  339. aclException("[{}]");
  340. aclException("[{\"logcheck\": [0, true]}]");
  341. }
  342. // Exceptions from check creation is propagated
  343. TEST_F(LoaderTest, ACLPropagate) {
  344. aclSetup();
  345. EXPECT_THROW(loader_.load(
  346. Element::fromJSON(
  347. "[{\"action\": \"ACCEPT\", \"throw\": 1}]")),
  348. TestCreatorError);
  349. }
  350. TEST_F(LoaderTest, nullDescription) {
  351. EXPECT_THROW(loader_.load(ConstElementPtr()), isc::InvalidParameter);
  352. }
  353. }