Commit 7cc074aa authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge branch 'work/logic'

Conflicts:
	src/lib/acl/Makefile.am
	src/lib/acl/tests/Makefile.am
parents feed2b35 0f1b7a45
......@@ -10,6 +10,7 @@ lib_LTLIBRARIES = libacl.la
libacl_la_SOURCES = acl.h
libacl_la_SOURCES += check.h
libacl_la_SOURCES += ip_check.h ip_check.cc
libacl_la_SOURCES += logic_check.h
libacl_la_SOURCES += loader.h loader.cc
libacl_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
......
......@@ -263,7 +263,7 @@ public:
* \param description The JSON description of the check.
*/
boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
description)
description) const
{
// Get the description as a map
typedef std::map<std::string, data::ConstElementPtr> Map;
......@@ -290,7 +290,7 @@ public:
* \param description The JSON list of ACL.
*/
boost::shared_ptr<ACL<Context, Action> > load(const data::ConstElementPtr&
description)
description) const
{
// We first check it's a list, so we can use the list reference
// (the list may be huge)
......@@ -346,7 +346,7 @@ private:
* the map.
*/
boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
description, Map& map)
description, Map& map) const
{
// Remove the action keyword
map.erase("action");
......
// 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.
#ifndef ACL_LOGIC_CHECK_H
#define ACL_LOGIC_CHECK_H
#include "check.h"
#include "loader.h"
namespace isc {
namespace acl {
/// \brief Constants for the AnyOf implementation
class AnyOfSpec {
public:
static bool start() { return (false); }
static bool terminate(const bool another) {
return (another);
}
};
/// \brief Constants for the AllOf implementation
class AllOfSpec {
public:
static bool start() { return (true); }
static bool terminate(const bool another) {
return (!another);
}
};
/**
* \brief Logic operators
*
* This class implements the AllOf and AnyOf compound checks. As their
* behaviour is almost the same, the same template class is used. Which
* one it is depends on the Mode template parameter. The Mode should be
* one of AnyOfSpec or AllOfSpec, which provide some commands for the
* internal implementation. It would be nice to provide typedefs for
* them, but it is impossible to do so, as we have the Context template
* parameter as well and C++ doesn't like templated typedefs.
*
* The object holds several subexpressions and returns true if all
* of the subexpressions return true (in case of AllOfSpec Mode) or
* at last one of them return true (in case of AnyOfSpec Mode). If
* some subexpression guarantees the result (eg. some returns false
* in case of AllOfSpec), the rest is not tried for performance
* reasons.
*/
template<typename Mode, typename Context>
class LogicOperator : public CompoundCheck<Context> {
public:
/**
* \brief Add another subexpression.
*
* This adds another subexpression to the list of checked expressions.
* This is usually done shortly after the creation, before using the
* check for matches.
*
* Currently there's no way to place the expression into arbitrary place
* or to remove it. It might turn out it would be needed in future to
* optimise or it might even turn out we need shared pointers for it.
*
* \param expr The new expression to put inside.
*/
void addSubexpression(const boost::shared_ptr<Check<Context> >& expr) {
checks_.push_back(expr);
}
/**
* \brief The current list of subexpressions.
*/
virtual typename CompoundCheck<Context>::Checks getSubexpressions() const {
typename CompoundCheck<Context>::Checks result;
for (typename Checks::const_iterator i(checks_.begin());
i != checks_.end(); ++i) {
result.push_back(i->get());
}
return (result);
}
/**
* \brief The match of the check.
*
* Runs the subexpressions, one by one, and then decides based on that
* what to return.
*/
virtual bool matches(const Context& context) const {
/*
* This might look slightly complicated. However, this is just
* generalized version of multi-and or multi-or. The usual
* implementation of multi-and starts with true and if one with
* false is found, it turns to be false forever and false is
* returned. It is exactly the other way around with or.
*
* So, if we ever find one that makes it the other one than start
* (false in case of and, true in case of or), we can just stop and
* return that one right away. If it meets no such expression, we
* get to the end and return the default.
*/
for (typename Checks::const_iterator i(checks_.begin());
i != checks_.end(); ++i) {
if (Mode::terminate((*i)->matches(context))) {
return (!Mode::start());
}
}
return (Mode::start());
}
private:
/// \brief List of subexpressions
typedef typename std::vector<boost::shared_ptr<Check<Context> > > Checks;
Checks checks_;
};
/**
* \brief Creator for the LogicOperator compound check.
*
* This class can load the ANY and ALL operators from JSON. They expect
* a list of subexpressions as a parameter, eg. like this:
*
* \verbatim
* {"ANY": [
* {"ip": "1.2.3.4"},
* {"ip": "5.6.7.8"}
* ]}
* \endverbatim
*
* It uses the loader to load the subexpressions, therefore whatever is
* supported there is supported here as well.
*
* The Mode template parameter has the same meaning as with LogicOperator,
* it is used to know which operators to create.
*/
template<typename Mode, typename Context, typename Action = BasicAction>
class LogicCreator : public Loader<Context, Action>::CheckCreator {
public:
/**
* \brief Constructor.
*
* \param name The name for which the loader will work. In practice,
* it will usually be ANY or ALL (depending on the mode), but
* anything else can be used as well.
*/
LogicCreator(const std::string& name) :
name_(name)
{}
/// \brief Returns vector containing the name.
virtual std::vector<std::string> names() const {
std::vector<std::string> result;
result.push_back(name_);
return (result);
}
/**
* \brief Converts a JSON description into the logic operator.
*
* This is the place where the actual loading happens. It creates
* the logic operator and calls the loader on each of the list
* elements, placing the result into the logic operator.
*
* The first parameter is ignored and is there only to match interface.
*
* \param definition The JSON definition of the subexpressions. This must
* be a list (if it isn't, the LoaderError is thrown) and the elements
* must be loadable by the loader (the exceptions from it are not
* caught).
* \param loader The loader to use for loading of subexpressions.
*/
virtual boost::shared_ptr<Check<Context> > create(const std::string&,
data::ConstElementPtr
definition,
const Loader<Context,
Action>& loader)
{
std::vector<data::ConstElementPtr> subexprs;
try {
subexprs = definition->listValue();
}
catch (const data::TypeError&) {
isc_throw_1(LoaderError, "Logic operator takes list", definition);
}
boost::shared_ptr<LogicOperator<Mode, Context> >
result(new LogicOperator<Mode, Context>);
for (std::vector<data::ConstElementPtr>::const_iterator
i(subexprs.begin());
i != subexprs.end(); ++i) {
result->addSubexpression(loader.loadCheck(*i));
}
return (result);
}
virtual bool allowListAbbreviation() const { return (false); }
private:
const std::string name_;
};
}
}
#endif
......@@ -11,6 +11,7 @@ run_unittests_SOURCES += dns_test.cc
run_unittests_SOURCES += ip_check_unittest.cc
run_unittests_SOURCES += loader_test.cc
run_unittests_SOURCES += logcheck.h
run_unittests_SOURCES += logic_check_test.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
......
// 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.
// This is not a public header, but some code shared between tests
// This one contains various creators to test the loader and other creators
#ifndef CREATORS_H
#define CREATORS_H
#include "logcheck.h"
#include <acl/loader.h>
#include <string>
using isc::data::ConstElementPtr;
using namespace std;
using namespace boost;
namespace {
// Just for convenience, create JSON objects from JSON string
ConstElementPtr el(const string& JSON) {
return (isc::data::Element::fromJSON(JSON));
}
// A check that doesn't check anything but remembers it's own name
// and data
class NamedCheck : public Check<Log> {
public:
NamedCheck(const string& name, ConstElementPtr data) :
name_(name),
data_(data)
{}
virtual bool matches(const Log&) const { return (true); }
const string name_;
const ConstElementPtr data_;
};
// The creator of NamedCheck
class NamedCreator : public Loader<Log>::CheckCreator {
public:
NamedCreator(const string& name, bool abbreviatedList = true) :
abbreviated_list_(abbreviatedList)
{
names_.push_back(name);
}
NamedCreator(const vector<string>& names) :
names_(names),
abbreviated_list_(true)
{}
vector<string> names() const {
return (names_);
}
shared_ptr<Check<Log> > create(const string& name, ConstElementPtr data,
const Loader<Log>&)
{
bool found(false);
for (vector<string>::const_iterator i(names_.begin());
i != names_.end(); ++i) {
if (*i == name) {
found = true;
break;
}
}
EXPECT_TRUE(found) << "Name " << name << " passed to creator which "
"doesn't handle it.";
return (shared_ptr<Check<Log> >(new NamedCheck(name, data)));
}
bool allowListAbbreviation() const {
return (abbreviated_list_);
}
private:
vector<string> names_;
const bool abbreviated_list_;
};
// To be thrown in tests internally
class TestCreatorError {};
// This will throw every time it should create something
class ThrowCreator : public Loader<Log>::CheckCreator {
public:
vector<string> names() const {
vector<string> result;
result.push_back("throw");
return (result);
}
shared_ptr<Check<Log> > create(const string&, ConstElementPtr,
const Loader<Log>&)
{
throw TestCreatorError();
}
};
// This throws whenever the match is called on it
class ThrowCheck : public Check<Log> {
public:
virtual bool matches(const Log&) const {
throw TestCreatorError();
}
};
// And creator for it
class ThrowCheckCreator : public Loader<Log>::CheckCreator {
public:
vector<string> names() const {
vector<string> result;
result.push_back("throwcheck");
return (result);
}
shared_ptr<Check<Log> > create(const string&, ConstElementPtr,
const Loader<Log>&)
{
return (shared_ptr<Check<Log> >(new ThrowCheck()));
}
};
class LogCreator : public Loader<Log>::CheckCreator {
public:
vector<string> names() const {
vector<string> result;
result.push_back("logcheck");
return (result);
}
/*
* For simplicity, we just take two values as a list, first is the
* logging cell used, the second is result of the check. No error checking
* is done, if there's bug in the test, it will throw TypeError for us.
*/
shared_ptr<Check<Log> > create(const string&, ConstElementPtr definition,
const Loader<Log>&)
{
vector<ConstElementPtr> list(definition->listValue());
int logpos(list[0]->intValue());
bool accept(list[1]->boolValue());
return (shared_ptr<ConstCheck>(new ConstCheck(accept, logpos)));
}
// We take a list, so don't interpret it for us
virtual bool allowListAbbreviation() const { return (false); }
};
}
#endif
......@@ -12,22 +12,16 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "logcheck.h"
#include "creators.h"
#include <acl/loader.h>
#include <string>
#include <gtest/gtest.h>
using namespace std;
using namespace boost;
using isc::data::ConstElementPtr;
namespace {
// Just for convenience, create JSON objects from JSON string
ConstElementPtr el(const string& JSON) {
return (isc::data::Element::fromJSON(JSON));
}
// 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.
......@@ -61,122 +55,6 @@ TEST(LoaderHelpers, DefaultActionLoader) {
testActionLoaderException("{}");
}
// A check that doesn't check anything but remembers it's own name
// and data
class NamedCheck : public Check<Log> {
public:
NamedCheck(const string& name, ConstElementPtr data) :
name_(name),
data_(data)
{}
virtual bool matches(const Log&) const { return (true); }
const string name_;
const ConstElementPtr data_;
};
// The creator of NamedCheck
class NamedCreator : public Loader<Log>::CheckCreator {
public:
NamedCreator(const string& name, bool abbreviatedList = true) :
abbreviated_list_(abbreviatedList)
{
names_.push_back(name);
}
NamedCreator(const vector<string>& names) :
names_(names),
abbreviated_list_(true)
{}
vector<string> names() const {
return (names_);
}
shared_ptr<Check<Log> > create(const string& name, ConstElementPtr data,
const Loader<Log>&)
{
bool found(false);
for (vector<string>::const_iterator i(names_.begin());
i != names_.end(); ++i) {
if (*i == name) {
found = true;
break;
}
}
EXPECT_TRUE(found) << "Name " << name << " passed to creator which "
"doesn't handle it.";
return (shared_ptr<Check<Log> >(new NamedCheck(name, data)));
}
bool allowListAbbreviation() const {
return (abbreviated_list_);
}
private:
vector<string> names_;
const bool abbreviated_list_;
};
// To be thrown in tests internally
class TestCreatorError {};
// This will throw every time it should create something
class ThrowCreator : public Loader<Log>::CheckCreator {
public:
vector<string> names() const {
vector<string> result;
result.push_back("throw");
return (result);
}
shared_ptr<Check<Log> > create(const string&, ConstElementPtr,
const Loader<Log>&)
{
throw TestCreatorError();
}
};
// This throws whenever the match is called on it
class ThrowCheck : public Check<Log> {
public:
virtual bool matches(const Log&) const {
throw TestCreatorError();
}
};
// And creator for it
class ThrowCheckCreator : public Loader<Log>::CheckCreator {
public:
vector<string> names() const {
vector<string> result;
result.push_back("throwcheck");
return (result);
}
shared_ptr<Check<Log> > create(const string&, ConstElementPtr,
const Loader<Log>&)
{
return (shared_ptr<Check<Log> >(new ThrowCheck()));
}
};
class LogCreator : public Loader<Log>::CheckCreator {
public:
vector<string> names() const {
vector<string> result;
result.push_back("logcheck");
return (result);
}
/*
* For simplicity, we just take two values as a list, first is the
* logging cell used, the second is result of the check. No error checking
* is done, if there's bug in the test, it will throw TypeError for us.
*/
shared_ptr<Check<Log> > create(const string&, ConstElementPtr definition,
const Loader<Log>&)
{
vector<ConstElementPtr> list(definition->listValue());
int logpos(list[0]->intValue());
bool accept(list[1]->boolValue());
return (shared_ptr<ConstCheck>(new ConstCheck(accept, logpos)));
}
// We take a list, so don't interpret it for us
virtual bool allowListAbbreviation() const { return (false); }
};
class LoaderTest : public ::testing::Test {
public:
LoaderTest() :
......
......@@ -12,6 +12,9 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef LOGCHECK_H
#define LOGCHECK_H
#include <gtest/gtest.h>
#include <acl/acl.h>
#include <cassert>
......@@ -84,3 +87,5 @@ private:
};
}
#endif
// 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 <acl/logic_check.h>
using namespace isc::acl;
namespace {
// Test the defs in AnyOfSpec
TEST(LogicOperators, AnyOfSpec) {
EXPECT_FALSE(AnyOfSpec::start());
EXPECT_FALSE(AnyOfSpec::terminate(false));
EXPECT_TRUE(AnyOfSpec::terminate(true));
}
// Test the defs in AllOfSpec
TEST(LogicOperators, AllOfSpec) {
EXPECT_TRUE(AllOfSpec::start());
EXPECT_TRUE(AllOfSpec::terminate(false));
EXPECT_FALSE(AllOfSpec::terminate(true));
}
// Generic test of one check
template<typename Mode>
void
testCheck(bool emptyResult) {
// It can be created
LogicOperator<Mode, Log> oper;
// It is empty by default
EXPECT_EQ(0, oper.getSubexpressions().size());
// And returns true, as all 0 of the subexpressions return true
Log log;
EXPECT_EQ(emptyResult, oper.matches(log));
log.checkFirst(0);
// Fill it with some subexpressions
typedef shared_ptr<ConstCheck> CheckPtr;
oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 0)));
oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 1)));