Commit e45768f4 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge branch 'work/acl'

parents 356ffa38 9652c9fd
SUBDIRS = tests
EXTRA_DIST = check.h
EXTRA_DIST = check.h acl.h
# TODO: Once we have some cc file we are able to compile, create the library.
# For now, we have only header files, not creating empty library.
// 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_ACL_H
#define ACL_ACL_H
#include "check.h"
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
namespace isc {
namespace acl {
/**
* \brief Default actions an ACL could perform.
*
* This is the default for the ACL class. It is possible to specify any other
* data type, as the ACL class does nothing about them, but these look
* reasonable, so they are provided for convenience. It is not specified what
* exactly these mean and it's up to whoever uses them.
*/
enum BasicAction {
ACCEPT,
REJECT,
DROP
};
/**
* \brief The ACL itself.
*
* It holds bunch of ordered entries, each one consisting of a check (
* of any kind, it might be even compound) and an action that is returned
* whenever the action matches. They are tested in the order and first
* match counts.
*
* This is non-copyable. It seems that there's no need to copy them (even
* when it would be technically possible), so we forbid it just to prevent
* copying it by accident. If there really is legitimate use, this restriction
* can be removed.
*
* The class is template. It is possible to specify on which context the checks
* match and which actions it returns. The actions must be copyable
* for this to work and it is expected to be something small, usually an enum
* (but other objects are also possible).
*
* \note There are protected functions. In fact, you should consider them
* private, they are protected so tests can get inside. This class
* is not expected to be subclassed in real applications.
*/
template<typename Context, typename Action = BasicAction> class ACL :
public boost::noncopyable {
public:
/**
* \brief Constructor.
*
* \param default_action It is the action that is returned when the checked
* things "falls off" the end of the list (when no rule matched).
*/
ACL(const Action& default_action) : default_action_(default_action)
{}
/**
* \brief Pointer to the check.
*
* We use the shared pointer, because we are not able to copy the checks.
* However, we might need to copy the entries (when we concatenate ACLs
* together in future).
*/
typedef boost::shared_ptr<const Check<Context> > ConstCheckPtr;
/**
* \brief The actual main function that decides.
*
* This is the function that takes the entries one by one, checks
* the context against conditions and if it matches, returns the
* action that belongs to the first matched entry or default action
* if nothing matches.
* \param context The thing that should be checked. It is directly
* passed to the checks.
*/
const Action& execute(const Context& context) const {
const typename Entries::const_iterator end(entries_.end());
for (typename Entries::const_iterator i(entries_.begin()); i != end;
++i) {
if (i->first->matches(context)) {
return (i->second);
}
}
return (default_action_);
}
/**
* \brief Add new entry at the end of the list.
*
* \note We currently allow only adding at the end. This is enough for now,
* but we may need more when we start implementing some kind optimisations,
* including replacements, reorderings and removals.
*
* \param check The check to test if the thing matches.
* \param action The action to return when the thing matches this check.
*/
void append(ConstCheckPtr check, const Action& action) {
entries_.push_back(Entry(check, action));
}
private:
// Just type abbreviations.
typedef std::pair<ConstCheckPtr, Action> Entry;
typedef std::vector<Entry> Entries;
/// \brief The default action, when nothing mathes.
const Action default_action_;
/// \brief The entries we have.
Entries entries_;
protected:
/**
* \brief Get the default action.
*
* This is for testing purposes only.
*/
const Action& getDefaultAction() const {
return (default_action_);
}
};
}
}
#endif
......@@ -4,7 +4,7 @@ TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += check_test.cc
run_unittests_SOURCES += check_test.cc acl_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.
#include <gtest/gtest.h>
#include <acl/acl.h>
#include <cassert>
using namespace isc::acl;
using boost::shared_ptr;
namespace {
// This is arbitrary guess of size for the log. If it's too small for your
// test, just make it bigger.
const size_t LOG_SIZE = 10;
// This will remember which checks did run already.
struct Log {
// The actual log cells, if i-th check did run
mutable bool run[LOG_SIZE];
Log() {
// Nothing run yet
for (size_t i(0); i < LOG_SIZE; ++i) {
run[i] = false;
}
}
// Checks that the first amount of checks did run and the rest didn't.
void checkFirst(size_t amount) const {
ASSERT_LE(amount, LOG_SIZE) << "Wrong test: amount bigger than size "
"of log";
{
SCOPED_TRACE("Checking that the first amount of checks did run");
for (size_t i(0); i < amount; ++i) {
EXPECT_TRUE(run[i]) << "Check #" << i << " did not run.";
}
}
{
SCOPED_TRACE("Checking that the rest did not run");
for (size_t i(amount); i < LOG_SIZE; ++i) {
EXPECT_FALSE(run[i]) << "Check #" << i << "did run.";
}
}
}
};
// This returns true or false every time, no matter what is passed to it.
// But it logs that it did run.
class ConstCheck : public Check<Log> {
public:
ConstCheck(bool accepts, size_t log_num) :
log_num_(log_num),
accepts_(accepts)
{
assert(log_num < LOG_SIZE); // If this fails, the LOG_SIZE is too small
}
/*
* This use of mutable log context is abuse for testing purposes.
* It is expected that the context will not be modified in the real
* applications of ACLs, but we want to know which checks were called
* and this is an easy way.
*/
virtual bool matches(const Log& log) const {
log.run[log_num_] = true;
return (accepts_);
}
private:
size_t log_num_;
bool accepts_;
};
// Test version of the ACL class. It adds few methods to examine the protected
// data, but does not change the implementation.
class TestACL : public ACL<Log> {
public:
TestACL() :
ACL(DROP)
{}
// Check the stored default action there
void checkDefaultAction(BasicAction ac) {
EXPECT_EQ(getDefaultAction(), ac);
}
};
// The test fixture. Contains some members so they don't need to be manually
// created each time and some convenience functions.
class ACLTest : public ::testing::Test {
public:
ACLTest() :
next_check_(0)
{}
TestACL acl_;
Log log_;
size_t next_check_;
shared_ptr<Check<Log> > getCheck(bool accepts) {
return (shared_ptr<Check<Log> >(new ConstCheck(accepts,
next_check_++)));
}
};
/*
* This tests the default action and that nothing is run if nothing is
* inserted (it's hard to imagine otherwise though).
*
* We use the default ACL unchanged from the test class.
*/
TEST_F(ACLTest, emptyRule) {
acl_.checkDefaultAction(DROP);
EXPECT_EQ(DROP, acl_.execute(log_));
// No test was run
log_.checkFirst(0);
}
/*
* This tests the default action in case no check matches.
*/
TEST_F(ACLTest, noMatch) {
acl_.append(getCheck(false), ACCEPT);
acl_.append(getCheck(false), REJECT);
EXPECT_EQ(DROP, acl_.execute(log_));
// The first two checks were actually run (and didn't match)
log_.checkFirst(2);
}
/*
* Checks that it takes the first matching check and returns the
* value. Also checks that the others aren't run at all.
*/
TEST_F(ACLTest, firstMatch) {
acl_.append(getCheck(false), ACCEPT);
acl_.append(getCheck(true), REJECT);
acl_.append(getCheck(true), ACCEPT);
EXPECT_EQ(REJECT, acl_.execute(log_));
log_.checkFirst(2);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment