123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #include "creators.h"
- #include <exceptions/exceptions.h>
- #include <acl/loader.h>
- #include <string>
- #include <gtest/gtest.h>
- using namespace std;
- using namespace boost;
- using namespace isc::acl;
- using namespace isc::acl::tests;
- using isc::data::Element;
- using isc::data::ConstElementPtr;
- namespace {
- // We don't use the EXPECT_THROW macro, as it doesn't allow us
- // to examine the exception. We want to check the element is stored
- // there as well.
- void testActionLoaderException(const string& JSON) {
- SCOPED_TRACE("Should throw with input: " + JSON);
- ConstElementPtr elem(Element::fromJSON(JSON));
- try {
- defaultActionLoader(elem);
- FAIL() << "It did not throw";
- }
- catch (const LoaderError& error) {
- // Yes, comparing for pointer equality, that is enough, it
- // should return the exact instance of the JSON object
- EXPECT_EQ(elem, error.element());
- }
- }
- // Test the defaultActionLoader function
- TEST(LoaderHelpers, DefaultActionLoader) {
- // First the three valid inputs
- EXPECT_EQ(ACCEPT, defaultActionLoader(Element::fromJSON("\"ACCEPT\"")));
- EXPECT_EQ(REJECT, defaultActionLoader(Element::fromJSON("\"REJECT\"")));
- EXPECT_EQ(DROP, defaultActionLoader(Element::fromJSON("\"DROP\"")));
- // Now few invalid ones
- // String, but unknown one
- testActionLoaderException("\"UNKNOWN\"");
- testActionLoaderException("42");
- testActionLoaderException("true");
- testActionLoaderException("null");
- testActionLoaderException("[]");
- testActionLoaderException("{}");
- }
- class LoaderTest : public ::testing::Test {
- public:
- LoaderTest() :
- loader_(REJECT)
- {}
- Loader<Log> loader_;
- Log log_;
- // Some convenience functions to set up
- // Create a NamedCreator, convert to shared pointer
- boost::shared_ptr<NamedCreator> namedCreator(const string& name,
- bool abbreviatedList = true)
- {
- return (boost::shared_ptr<NamedCreator>(new NamedCreator(name,
- abbreviatedList)));
- }
- // Create and add a NamedCreator
- void addNamed(const string& name, bool abbreviatedList = true) {
- EXPECT_NO_THROW(loader_.registerCreator(
- namedCreator(name, abbreviatedList)));
- }
- template<class Result> boost::shared_ptr<Result> loadCheckAny(
- const string& definition)
- {
- SCOPED_TRACE("Loading check " + definition);
- boost::shared_ptr<Check<Log> > loaded;
- EXPECT_NO_THROW(loaded = loader_.loadCheck(
- Element::fromJSON(definition)));
- boost::shared_ptr<Result> result(dynamic_pointer_cast<Result>(loaded));
- EXPECT_TRUE(result);
- return (result);
- }
- // Load a check and convert it to named check to examine it
- boost::shared_ptr<NamedCheck> loadCheck(const string& definition) {
- return (loadCheckAny<NamedCheck>(definition));
- }
- // The loadCheck throws an exception
- void checkException(const string& JSON) {
- SCOPED_TRACE("Loading check exception: " + JSON);
- ConstElementPtr input(Element::fromJSON(JSON));
- // Not using EXPECT_THROW, we want to examine the exception
- try {
- loader_.loadCheck(input);
- FAIL() << "Should have thrown";
- }
- catch (const LoaderError& e) {
- // It should be identical copy, so checking pointers
- EXPECT_EQ(input, e.element());
- }
- }
- // Insert the throw, throwcheck and logcheck checks into the loader
- void aclSetup() {
- try {
- loader_.registerCreator(boost::shared_ptr<ThrowCreator>(
- new ThrowCreator()));
- loader_.registerCreator(boost::shared_ptr<ThrowCheckCreator>(
- new ThrowCheckCreator()));
- loader_.registerCreator(boost::shared_ptr<LogCreator>(
- new LogCreator()));
- }
- // We ignore this exception here, because it happens when we try to
- // insert the creators multiple times. This is harmless.
- catch (const LoaderError&) {}
- }
- // Create an ACL, run it, check it's result and how many first
- // log items it marked
- //
- // Works with preset names throw and logcheck
- void aclRun(const string& JSON, BasicAction expectedResult,
- size_t logged)
- {
- SCOPED_TRACE("Running ACL for " + JSON);
- aclSetup();
- boost::shared_ptr<ACL<Log> > acl;
- EXPECT_NO_THROW(acl = loader_.load(Element::fromJSON(JSON)));
- EXPECT_EQ(expectedResult, acl->execute(log_));
- log_.checkFirst(logged);
- }
- // Check it throws an error when creating the ACL
- void aclException(const string& JSON) {
- SCOPED_TRACE("Trying to load bad " + JSON);
- aclSetup();
- EXPECT_THROW(loader_.load(Element::fromJSON(JSON)), LoaderError);
- }
- // Check that the subexpression is NamedCheck with correct data
- void isSubexprNamed(const CompoundCheck<Log>* compound, size_t index,
- const string& name, ConstElementPtr data)
- {
- if (index < compound->getSubexpressions().size()) {
- const NamedCheck*
- check(dynamic_cast<const NamedCheck*>(compound->
- getSubexpressions()
- [index]));
- ASSERT_TRUE(check) << "The subexpression is of different type";
- EXPECT_EQ(name, check->name_);
- EXPECT_TRUE(data->equals(*check->data_));
- }
- }
- };
- // Test that it does not accept duplicate creator
- TEST_F(LoaderTest, CreatorDuplicity) {
- addNamed("name");
- EXPECT_THROW(loader_.registerCreator(namedCreator("name")), LoaderError);
- }
- // Test that when it does not accept a duplicate, nothing is inserted
- TEST_F(LoaderTest, CreatorDuplicateUnchanged) {
- addNamed("name1");
- vector<string> names;
- names.push_back("name2");
- names.push_back("name1");
- names.push_back("name3");
- EXPECT_THROW(loader_.registerCreator(
- boost::shared_ptr<NamedCreator>(new NamedCreator(names))), LoaderError);
- // It should now reject both name2 and name3 as not known
- checkException("{\"name2\": null}");
- checkException("{\"name3\": null}");
- }
- // Test that we can register a creator and load a check with the name
- TEST_F(LoaderTest, SimpleCheckLoad) {
- addNamed("name");
- boost::shared_ptr<NamedCheck> check(loadCheck("{\"name\": 42}"));
- EXPECT_EQ("name", check->name_);
- EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
- }
- // As above, but there are multiple creators registered within the loader
- TEST_F(LoaderTest, MultiCreatorCheckLoad) {
- addNamed("name1");
- addNamed("name2");
- boost::shared_ptr<NamedCheck> check(loadCheck("{\"name2\": 42}"));
- EXPECT_EQ("name2", check->name_);
- EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
- }
- // Similar to above, but there's a creator with multiple names
- TEST_F(LoaderTest, MultiNameCheckLoad) {
- addNamed("name1");
- vector<string> names;
- names.push_back("name2");
- names.push_back("name3");
- EXPECT_NO_THROW(loader_.registerCreator(boost::shared_ptr<NamedCreator>(
- new NamedCreator(names))));
- boost::shared_ptr<NamedCheck> check(loadCheck("{\"name3\": 42}"));
- EXPECT_EQ("name3", check->name_);
- EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
- }
- // Invalid format is rejected
- TEST_F(LoaderTest, InvalidFormatCheck) {
- checkException("[]");
- checkException("42");
- checkException("\"hello\"");
- checkException("null");
- }
- // Empty check is rejected
- TEST_F(LoaderTest, EmptyCheck) {
- checkException("{}");
- }
- // The name isn't known
- TEST_F(LoaderTest, UnkownName) {
- checkException("{\"unknown\": null}");
- }
- // Exception from the creator is propagated
- TEST_F(LoaderTest, CheckPropagate) {
- loader_.registerCreator(boost::shared_ptr<ThrowCreator>(
- new ThrowCreator()));
- EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"throw\": null}")),
- TestCreatorError);
- }
- // The abbreviated form of check
- TEST_F(LoaderTest, AndAbbrev) {
- addNamed("name1");
- addNamed("name2");
- boost::shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
- loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": 2}"));
- // If we don't have anything loaded, the rest would crash. It is already
- // reported from within loadCheckAny if it isn't loaded.
- if (oper) {
- // The subexpressions are correct
- EXPECT_EQ(2, oper->getSubexpressions().size());
- // Note: this test relies on the ordering in which map returns it's
- // elements, which is in the lexicographical order of the strings.
- // This is not required from our interface, but is easier to write
- // the test.
- isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
- isSubexprNamed(&*oper, 1, "name2", Element::fromJSON("2"));
- }
- }
- // The abbreviated form of parameters
- TEST_F(LoaderTest, OrAbbrev) {
- addNamed("name1");
- boost::shared_ptr<LogicOperator<AnyOfSpec, Log> > oper(
- loadCheckAny<LogicOperator<AnyOfSpec, Log> >("{\"name1\": [1, 2]}"));
- // If we don't have anything loaded, the rest would crash. It is already
- // reported from within loadCheckAny if it isn't loaded.
- if (oper) {
- // The subexpressions are correct
- EXPECT_EQ(2, oper->getSubexpressions().size());
- isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
- isSubexprNamed(&*oper, 1, "name1", Element::fromJSON("2"));
- }
- }
- // Combined abbreviated form, both at once
- // The abbreviated form of check
- TEST_F(LoaderTest, BothAbbrev) {
- addNamed("name1");
- addNamed("name2");
- boost::shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
- loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": [3, 4]}"));
- // If we don't have anything loaded, the rest would crash. It is already
- // reported from within loadCheckAny if it isn't loaded.
- if (oper) {
- // The subexpressions are correct
- ASSERT_EQ(2, oper->getSubexpressions().size());
- // Note: this test relies on the ordering in which map returns it's
- // elements, which is in the lexicographical order of the strings.
- // This is not required from our interface, but is easier to write
- // the test.
- isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
- const LogicOperator<AnyOfSpec, Log>*
- orOper(dynamic_cast<const LogicOperator<AnyOfSpec, Log>*>(
- oper->getSubexpressions()[1]));
- ASSERT_TRUE(orOper) << "Different type than AnyOf operator";
- EXPECT_EQ(2, orOper->getSubexpressions().size());
- isSubexprNamed(orOper, 0, "name2", Element::fromJSON("3"));
- isSubexprNamed(orOper, 1, "name2", Element::fromJSON("4"));
- }
- }
- // But this is not abbreviated form, this should be passed directly to the
- // creator
- TEST_F(LoaderTest, ListCheck) {
- addNamed("name1", false);
- boost::shared_ptr<NamedCheck> check(loadCheck("{\"name1\": [1, 2]}"));
- EXPECT_EQ("name1", check->name_);
- EXPECT_TRUE(check->data_->equals(*Element::fromJSON("[1, 2]")));
- }
- // Check the action key is ignored as it should be
- TEST_F(LoaderTest, CheckNoAction) {
- addNamed("name1");
- boost::shared_ptr<NamedCheck> check(loadCheck("{\"name1\": 1, \"action\": 2}"));
- EXPECT_EQ("name1", check->name_);
- EXPECT_TRUE(check->data_->equals(*Element::fromJSON("1")));
- }
- // The empty ACL can be created and run, providing the default action
- TEST_F(LoaderTest, EmptyACL) {
- aclRun("[]", REJECT, 0);
- }
- // We can create a simple ACL, which will return the correct default
- // action
- TEST_F(LoaderTest, NoMatchACL) {
- aclRun("[{\"logcheck\": [0, false], \"action\": \"ACCEPT\"}]",
- REJECT, 1);
- }
- // We can created more complicated ACL, it will match at the second
- // check
- TEST_F(LoaderTest, MatchACL) {
- aclRun("["
- " {\"logcheck\": [0, false], \"action\": \"DROP\"},"
- " {\"logcheck\": [1, true], \"action\": \"ACCEPT\"}"
- "]", ACCEPT, 2);
- }
- // ACL without a check (matches unconditionally)
- // We add another one check after it, to make sure it is really not run
- TEST_F(LoaderTest, NoCheckACL) {
- aclRun("["
- " {\"action\": \"DROP\"},"
- " {\"throwcheck\": 1, \"action\": \"ACCEPT\"}"
- "]", DROP, 0);
- }
- // Malformed things are rejected
- TEST_F(LoaderTest, InvalidACLFormat) {
- // Not a list
- aclException("{}");
- aclException("42");
- aclException("true");
- aclException("null");
- aclException("\"hello\"");
- // Malformed element
- aclException("[42]");
- aclException("[\"hello\"]");
- aclException("[[]]");
- aclException("[true]");
- aclException("[null]");
- }
- // If there's no action keyword, it is rejected
- TEST_F(LoaderTest, NoAction) {
- aclException("[{}]");
- aclException("[{\"logcheck\": [0, true]}]");
- }
- // Exceptions from check creation is propagated
- TEST_F(LoaderTest, ACLPropagate) {
- aclSetup();
- EXPECT_THROW(loader_.load(
- Element::fromJSON(
- "[{\"action\": \"ACCEPT\", \"throw\": 1}]")),
- TestCreatorError);
- }
- TEST_F(LoaderTest, nullDescription) {
- EXPECT_THROW(loader_.load(ConstElementPtr()), isc::InvalidParameter);
- }
- }
|