loader_test.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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 "logcheck.h"
  15. #include <acl/loader.h>
  16. #include <string>
  17. #include <gtest/gtest.h>
  18. using namespace std;
  19. using namespace boost;
  20. using isc::data::ConstElementPtr;
  21. namespace {
  22. // Just for convenience, create JSON objects from JSON string
  23. ConstElementPtr el(const string& JSON) {
  24. return (isc::data::Element::fromJSON(JSON));
  25. }
  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(el(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(el("\"ACCEPT\"")));
  46. EXPECT_EQ(REJECT, defaultActionLoader(el("\"REJECT\"")));
  47. EXPECT_EQ(DROP, defaultActionLoader(el("\"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. // A check that doesn't check anything but remembers it's own name
  58. // and data
  59. class NamedCheck : public Check<Log> {
  60. public:
  61. NamedCheck(const string& name, ConstElementPtr data) :
  62. name_(name),
  63. data_(data)
  64. {}
  65. virtual bool matches(const Log&) const { return (true); }
  66. const string name_;
  67. const ConstElementPtr data_;
  68. };
  69. // The creator of NamedCheck
  70. class NamedCreator : public Loader<Log>::CheckCreator {
  71. public:
  72. NamedCreator(const string& name, bool abbreviatedList = true) :
  73. abbreviated_list_(abbreviatedList)
  74. {
  75. names_.push_back(name);
  76. }
  77. NamedCreator(const vector<string>& names) :
  78. names_(names),
  79. abbreviated_list_(true)
  80. {}
  81. vector<string> names() const {
  82. return (names_);
  83. }
  84. shared_ptr<Check<Log> > create(const string& name, ConstElementPtr data,
  85. const Loader<Log>&)
  86. {
  87. bool found(false);
  88. for (vector<string>::const_iterator i(names_.begin());
  89. i != names_.end(); ++i) {
  90. if (*i == name) {
  91. found = true;
  92. break;
  93. }
  94. }
  95. EXPECT_TRUE(found) << "Name " << name << " passed to creator which "
  96. "doesn't handle it.";
  97. return (shared_ptr<Check<Log> >(new NamedCheck(name, data)));
  98. }
  99. bool allowListAbbreviation() const {
  100. return (abbreviated_list_);
  101. }
  102. private:
  103. vector<string> names_;
  104. const bool abbreviated_list_;
  105. };
  106. // To be thrown in tests internally
  107. class TestCreatorError {};
  108. // This will throw every time it should create something
  109. class ThrowCreator : public Loader<Log>::CheckCreator {
  110. public:
  111. vector<string> names() const {
  112. vector<string> result;
  113. result.push_back("throw");
  114. return (result);
  115. }
  116. shared_ptr<Check<Log> > create(const string&, ConstElementPtr,
  117. const Loader<Log>&)
  118. {
  119. throw TestCreatorError();
  120. }
  121. };
  122. // This throws whenever the match is called on it
  123. class ThrowCheck : public Check<Log> {
  124. public:
  125. virtual bool matches(const Log&) const {
  126. throw TestCreatorError();
  127. }
  128. };
  129. // And creator for it
  130. class ThrowCheckCreator : public Loader<Log>::CheckCreator {
  131. public:
  132. vector<string> names() const {
  133. vector<string> result;
  134. result.push_back("throwcheck");
  135. return (result);
  136. }
  137. shared_ptr<Check<Log> > create(const string&, ConstElementPtr,
  138. const Loader<Log>&)
  139. {
  140. return (shared_ptr<Check<Log> >(new ThrowCheck()));
  141. }
  142. };
  143. class LogCreator : public Loader<Log>::CheckCreator {
  144. public:
  145. vector<string> names() const {
  146. vector<string> result;
  147. result.push_back("logcheck");
  148. return (result);
  149. }
  150. /*
  151. * For simplicity, we just take two values as a list, first is the
  152. * logging cell used, the second is result of the check. No error checking
  153. * is done, if there's bug in the test, it will throw TypeError for us.
  154. */
  155. shared_ptr<Check<Log> > create(const string&, ConstElementPtr definition,
  156. const Loader<Log>&)
  157. {
  158. vector<ConstElementPtr> list(definition->listValue());
  159. int logpos(list[0]->intValue());
  160. bool accept(list[1]->boolValue());
  161. return (shared_ptr<ConstCheck>(new ConstCheck(accept, logpos)));
  162. }
  163. // We take a list, so don't interpret it for us
  164. virtual bool allowListAbbreviation() const { return (false); }
  165. };
  166. class LoaderTest : public ::testing::Test {
  167. public:
  168. LoaderTest() :
  169. loader_(REJECT)
  170. {}
  171. Loader<Log> loader_;
  172. Log log_;
  173. // Some convenience functions to set up
  174. // Create a NamedCreator, convert to shared pointer
  175. shared_ptr<NamedCreator> namedCreator(const string& name,
  176. bool abbreviatedList = true)
  177. {
  178. return (shared_ptr<NamedCreator>(new NamedCreator(name,
  179. abbreviatedList)));
  180. }
  181. // Create and add a NamedCreator
  182. void addNamed(const string& name, bool abbreviatedList = true) {
  183. EXPECT_NO_THROW(loader_.registerCreator(
  184. namedCreator(name, abbreviatedList)));
  185. }
  186. // Load a check and convert it to named check to examine it
  187. shared_ptr<NamedCheck> loadCheck(const string& definition) {
  188. SCOPED_TRACE("Loading check " + definition);
  189. shared_ptr<Check<Log> > loaded;
  190. EXPECT_NO_THROW(loaded = loader_.loadCheck(el(definition)));
  191. shared_ptr<NamedCheck> result(dynamic_pointer_cast<NamedCheck>(
  192. loaded));
  193. EXPECT_TRUE(result);
  194. return (result);
  195. }
  196. // The loadCheck throws an exception
  197. void checkException(const string& JSON) {
  198. SCOPED_TRACE("Loading check exception: " + JSON);
  199. ConstElementPtr input(el(JSON));
  200. // Not using EXPECT_THROW, we want to examine the exception
  201. try {
  202. loader_.loadCheck(input);
  203. FAIL() << "Should have thrown";
  204. }
  205. catch (const LoaderError& e) {
  206. // It should be identical copy, so checking pointers
  207. EXPECT_EQ(input, e.element());
  208. }
  209. }
  210. // Insert the throw, throwcheck and logcheck checks into the loader
  211. void aclSetup() {
  212. try {
  213. loader_.registerCreator(shared_ptr<ThrowCreator>(new
  214. ThrowCreator()));
  215. loader_.registerCreator(shared_ptr<ThrowCheckCreator>(
  216. new ThrowCheckCreator()));
  217. loader_.registerCreator(shared_ptr<LogCreator>(new LogCreator()));
  218. }
  219. // We ignore this exception here, because it happens when we try to
  220. // insert the creators multiple times. This is harmless.
  221. catch (const LoaderError&) {}
  222. }
  223. // Create an ACL, run it, check it's result and how many first
  224. // log items it marked
  225. //
  226. // Works with preset names throw and logcheck
  227. void aclRun(const string& JSON, BasicAction expectedResult,
  228. size_t logged)
  229. {
  230. SCOPED_TRACE("Running ACL for " + JSON);
  231. aclSetup();
  232. shared_ptr<ACL<Log> > acl;
  233. EXPECT_NO_THROW(acl = loader_.load(el(JSON)));
  234. EXPECT_EQ(expectedResult, acl->execute(log_));
  235. log_.checkFirst(logged);
  236. }
  237. // Check it throws an error when creating the ACL
  238. void aclException(const string& JSON) {
  239. SCOPED_TRACE("Trying to load bad " + JSON);
  240. aclSetup();
  241. EXPECT_THROW(loader_.load(el(JSON)), LoaderError);
  242. }
  243. };
  244. // Test that it does not accept duplicate creator
  245. TEST_F(LoaderTest, CreatorDuplicity) {
  246. addNamed("name");
  247. EXPECT_THROW(loader_.registerCreator(namedCreator("name")), LoaderError);
  248. }
  249. // Test that when it does not accept a duplicate, nothing is inserted
  250. TEST_F(LoaderTest, CreatorDuplicateUnchanged) {
  251. addNamed("name1");
  252. vector<string> names;
  253. names.push_back("name2");
  254. names.push_back("name1");
  255. names.push_back("name3");
  256. EXPECT_THROW(loader_.registerCreator(
  257. shared_ptr<NamedCreator>(new NamedCreator(names))), LoaderError);
  258. // It should now reject both name2 and name3 as not known
  259. checkException("{\"name2\": null}");
  260. checkException("{\"name3\": null}");
  261. }
  262. // Test that we can register a creator and load a check with the name
  263. TEST_F(LoaderTest, SimpleCheckLoad) {
  264. addNamed("name");
  265. shared_ptr<NamedCheck> check(loadCheck("{\"name\": 42}"));
  266. EXPECT_EQ("name", check->name_);
  267. EXPECT_TRUE(check->data_->equals(*el("42")));
  268. }
  269. // As above, but there are multiple creators registered within the loader
  270. TEST_F(LoaderTest, MultiCreatorCheckLoad) {
  271. addNamed("name1");
  272. addNamed("name2");
  273. shared_ptr<NamedCheck> check(loadCheck("{\"name2\": 42}"));
  274. EXPECT_EQ("name2", check->name_);
  275. EXPECT_TRUE(check->data_->equals(*el("42")));
  276. }
  277. // Similar to above, but there's a creator with multiple names
  278. TEST_F(LoaderTest, MultiNameCheckLoad) {
  279. addNamed("name1");
  280. vector<string> names;
  281. names.push_back("name2");
  282. names.push_back("name3");
  283. EXPECT_NO_THROW(loader_.registerCreator(shared_ptr<NamedCreator>(
  284. new NamedCreator(names))));
  285. shared_ptr<NamedCheck> check(loadCheck("{\"name3\": 42}"));
  286. EXPECT_EQ("name3", check->name_);
  287. EXPECT_TRUE(check->data_->equals(*el("42")));
  288. }
  289. // Invalid format is rejected
  290. TEST_F(LoaderTest, InvalidFormatCheck) {
  291. checkException("[]");
  292. checkException("42");
  293. checkException("\"hello\"");
  294. checkException("null");
  295. }
  296. // Empty check is rejected
  297. TEST_F(LoaderTest, EmptyCheck) {
  298. checkException("{}");
  299. }
  300. // The name isn't known
  301. TEST_F(LoaderTest, UnkownName) {
  302. checkException("{\"unknown\": null}");
  303. }
  304. // Exception from the creator is propagated
  305. TEST_F(LoaderTest, CheckPropagate) {
  306. loader_.registerCreator(shared_ptr<ThrowCreator>(new ThrowCreator()));
  307. EXPECT_THROW(loader_.loadCheck(el("{\"throw\": null}")), TestCreatorError);
  308. }
  309. // The abbreviated form is not yet implemented
  310. // (we need the operators to be implemented)
  311. TEST_F(LoaderTest, AndAbbrev) {
  312. addNamed("name1");
  313. addNamed("name2");
  314. EXPECT_THROW(loader_.loadCheck(el("{\"name1\": 1, \"name2\": 2}")),
  315. LoaderError);
  316. }
  317. TEST_F(LoaderTest, OrAbbrev) {
  318. addNamed("name1");
  319. EXPECT_THROW(loader_.loadCheck(el("{\"name1\": [1, 2]}")),
  320. LoaderError);
  321. }
  322. // But this is not abbreviated form, this should be passed directly to the
  323. // creator
  324. TEST_F(LoaderTest, ListCheck) {
  325. addNamed("name1", false);
  326. shared_ptr<NamedCheck> check(loadCheck("{\"name1\": [1, 2]}"));
  327. EXPECT_EQ("name1", check->name_);
  328. EXPECT_TRUE(check->data_->equals(*el("[1, 2]")));
  329. }
  330. // Check the action key is ignored as it should be
  331. TEST_F(LoaderTest, CheckNoAction) {
  332. addNamed("name1");
  333. shared_ptr<NamedCheck> check(loadCheck("{\"name1\": 1, \"action\": 2}"));
  334. EXPECT_EQ("name1", check->name_);
  335. EXPECT_TRUE(check->data_->equals(*el("1")));
  336. }
  337. // The empty ACL can be created and run, providing the default action
  338. TEST_F(LoaderTest, EmptyACL) {
  339. aclRun("[]", REJECT, 0);
  340. }
  341. // We can create a simple ACL, which will return the correct default
  342. // action
  343. TEST_F(LoaderTest, NoMatchACL) {
  344. aclRun("[{\"logcheck\": [0, false], \"action\": \"ACCEPT\"}]",
  345. REJECT, 1);
  346. }
  347. // We can created more complicated ACL, it will match at the second
  348. // check
  349. TEST_F(LoaderTest, MatchACL) {
  350. aclRun("["
  351. " {\"logcheck\": [0, false], \"action\": \"DROP\"},"
  352. " {\"logcheck\": [1, true], \"action\": \"ACCEPT\"}"
  353. "]", ACCEPT, 2);
  354. }
  355. // ACL without a check (matches unconditionally)
  356. // We add another one check after it, to make sure it is really not run
  357. TEST_F(LoaderTest, NoCheckACL) {
  358. aclRun("["
  359. " {\"action\": \"DROP\"},"
  360. " {\"throwcheck\": 1, \"action\": \"ACCEPT\"}"
  361. "]", DROP, 0);
  362. }
  363. // Malformed things are rejected
  364. TEST_F(LoaderTest, InvalidACLFormat) {
  365. // Not a list
  366. aclException("{}");
  367. aclException("42");
  368. aclException("true");
  369. aclException("null");
  370. aclException("\"hello\"");
  371. // Malformed element
  372. aclException("[42]");
  373. aclException("[\"hello\"]");
  374. aclException("[[]]");
  375. aclException("[true]");
  376. aclException("[null]");
  377. }
  378. // If there's no action keyword, it is rejected
  379. TEST_F(LoaderTest, NoAction) {
  380. aclException("[{}]");
  381. aclException("[{\"logcheck\": [0, true]}]");
  382. }
  383. // Exceptions from check creation is propagated
  384. TEST_F(LoaderTest, ACLPropagate) {
  385. aclSetup();
  386. EXPECT_THROW(loader_.load(el("[{\"action\": \"ACCEPT\", \"throw\": 1}]")),
  387. TestCreatorError);
  388. }
  389. }