blob: 7a6d39d1202b3d8ed2e95af86f0ef051262f943d [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (C) 2014 Named Data Networking Project
* See COPYING for copyright and distribution information.
*/
#include "mgmt/command-validator.hpp"
#include "mgmt/config-file.hpp"
#include "tests/test-common.hpp"
#include <boost/test/unit_test.hpp>
#include <ndn-cpp-dev/util/command-interest-generator.hpp>
#include <ndn-cpp-dev/util/io.hpp>
#include <boost/filesystem.hpp>
namespace nfd {
namespace tests {
NFD_LOG_INIT("CommandValidatorTest");
BOOST_FIXTURE_TEST_SUITE(MgmtCommandValidator, BaseFixture)
// authorizations
// {
// ; an authorize section grants privileges to a key
// authorize
// {
// keyfile "tests/mgmt/key1.pub" ; public key file
// privileges ; set of privileges granted to this public key
// {
// fib
// stats
// }
// }
// authorize
// {
// keyfile "tests/mgmt/key2.pub" ; public key file
// privileges ; set of privileges granted to this public key
// {
// faces
// }
// }
// }
const std::string CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/key1.pub\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/key2.pub\"\n"
" privileges\n"
" {\n"
" faces\n"
" }\n"
" }\n"
"}\n";
class CommandValidatorTester
{
public:
CommandValidatorTester()
: m_validated(false),
m_validationFailed(false)
{
}
void
generateIdentity(const Name& prefix)
{
m_identityName = prefix;
m_identityName.append(boost::lexical_cast<std::string>(ndn::time::now()));
const Name certName = m_keys.createIdentity(m_identityName);
m_certificate = m_keys.getCertificate(certName);
}
void
saveIdentityToFile(const char* filename)
{
std::ofstream out;
out.open(filename);
BOOST_REQUIRE(out.is_open());
BOOST_REQUIRE(static_cast<bool>(m_certificate));
ndn::io::save<ndn::IdentityCertificate>(*m_certificate, out);
out.close();
}
const Name&
getIdentityName() const
{
BOOST_REQUIRE_NE(m_identityName, Name());
return m_identityName;
}
const Name&
getPublicKeyName() const
{
BOOST_REQUIRE(static_cast<bool>(m_certificate));
return m_certificate->getPublicKeyName();
}
void
onValidated(const shared_ptr<const Interest>& interest)
{
// NFD_LOG_DEBUG("validated command");
m_validated = true;
}
void
onValidationFailed(const shared_ptr<const Interest>& interest, const std::string& info)
{
NFD_LOG_DEBUG("validation failed: " << info);
m_validationFailed = true;
}
bool
commandValidated() const
{
return m_validated;
}
bool
commandValidationFailed() const
{
return m_validationFailed;
}
void
resetValidation()
{
m_validated = false;
m_validationFailed = false;
}
~CommandValidatorTester()
{
m_keys.deleteIdentity(m_identityName);
}
private:
bool m_validated;
bool m_validationFailed;
ndn::KeyChain m_keys;
Name m_identityName;
shared_ptr<ndn::IdentityCertificate> m_certificate;
};
class TwoValidatorFixture : public BaseFixture
{
public:
TwoValidatorFixture()
{
m_tester1.generateIdentity("/test/CommandValidator/TwoKeys/id1");
m_tester1.saveIdentityToFile("tests/mgmt/key1.pub");
m_tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
m_tester2.saveIdentityToFile("tests/mgmt/key2.pub");
}
~TwoValidatorFixture()
{
boost::system::error_code error;
boost::filesystem::remove("tests/mgmt/key1.pub", error);
boost::filesystem::remove("tests/mgmt/key2.pub", error);
}
protected:
CommandValidatorTester m_tester1;
CommandValidatorTester m_tester2;
};
BOOST_FIXTURE_TEST_CASE(TwoKeys, TwoValidatorFixture)
{
shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
ndn::CommandInterestGenerator generator;
generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
generator.generateWithIdentity(*facesCommand, m_tester2.getIdentityName());
ConfigFile config;
CommandValidator validator;
validator.addSupportedPrivilege("faces");
validator.addSupportedPrivilege("fib");
validator.addSupportedPrivilege("stats");
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
config.parse(CONFIG, false);
validator.validate(*fibCommand,
bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
BOOST_REQUIRE(m_tester1.commandValidated());
m_tester1.resetValidation();
validator.validate(*statsCommand,
bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
BOOST_REQUIRE(m_tester1.commandValidated());
validator.validate(*facesCommand,
bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
BOOST_REQUIRE(m_tester2.commandValidated());
m_tester2.resetValidation();
// use key2 for fib command (authorized for key1 only)
shared_ptr<Interest> unauthorizedFibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
generator.generateWithIdentity(*unauthorizedFibCommand, m_tester2.getIdentityName());
validator.validate(*unauthorizedFibCommand,
bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
BOOST_REQUIRE(m_tester2.commandValidationFailed());
}
BOOST_FIXTURE_TEST_CASE(TwoKeysDryRun, TwoValidatorFixture)
{
CommandValidatorTester tester1;
tester1.generateIdentity("/test/CommandValidator/TwoKeys/id1");
tester1.saveIdentityToFile("tests/mgmt/key1.pub");
CommandValidatorTester tester2;
tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
tester2.saveIdentityToFile("tests/mgmt/key2.pub");
shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
ndn::CommandInterestGenerator generator;
generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
generator.generateWithIdentity(*facesCommand, m_tester2.getIdentityName());
ConfigFile config;
CommandValidator validator;
validator.addSupportedPrivilege("faces");
validator.addSupportedPrivilege("fib");
validator.addSupportedPrivilege("stats");
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
config.parse(CONFIG, true);
validator.validate(*fibCommand,
bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
BOOST_REQUIRE(m_tester1.commandValidationFailed());
m_tester1.resetValidation();
validator.validate(*statsCommand,
bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
BOOST_REQUIRE(m_tester1.commandValidationFailed());
validator.validate(*facesCommand,
bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
BOOST_REQUIRE(m_tester2.commandValidationFailed());
m_tester2.resetValidation();
// use key2 for fib command (authorized for key1 only)
shared_ptr<Interest> unauthorizedFibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
generator.generateWithIdentity(*unauthorizedFibCommand, m_tester2.getIdentityName());
validator.validate(*unauthorizedFibCommand,
bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
BOOST_REQUIRE(m_tester2.commandValidationFailed());
}
BOOST_AUTO_TEST_CASE(NoAuthorizeSections)
{
const std::string NO_AUTHORIZE_CONFIG =
"authorizations\n"
"{\n"
"}\n";
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
BOOST_CHECK_THROW(config.parse(NO_AUTHORIZE_CONFIG, false), ConfigFile::Error);
}
BOOST_AUTO_TEST_CASE(NoPrivilegesSections)
{
const std::string NO_PRIVILEGES_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/key1.pub\"\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
BOOST_CHECK_THROW(config.parse(NO_PRIVILEGES_CONFIG, false), ConfigFile::Error);
}
BOOST_AUTO_TEST_CASE(InvalidKeyFile)
{
const std::string INVALID_KEY_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/notakeyfile.pub\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
BOOST_CHECK_THROW(config.parse(INVALID_KEY_CONFIG, false), ConfigFile::Error);
}
BOOST_AUTO_TEST_CASE(NoKeyFile)
{
const std::string NO_KEY_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
BOOST_CHECK_THROW(config.parse(NO_KEY_CONFIG, false), ConfigFile::Error);
}
BOOST_AUTO_TEST_CASE(MalformedKey)
{
const std::string MALFORMED_KEY_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/malformedkey.pub\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
BOOST_CHECK_THROW(config.parse(MALFORMED_KEY_CONFIG, false), ConfigFile::Error);
}
bool
validateErrorMessage(const std::string& expectedMessage, const ConfigFile::Error& error)
{
bool gotExpected = error.what() == expectedMessage;
if (!gotExpected)
{
NFD_LOG_WARN("\ncaught exception: " << error.what()
<< "\n\nexpected exception: " << expectedMessage);
}
return gotExpected;
}
BOOST_AUTO_TEST_CASE(NoAuthorizeSectionsDryRun)
{
const std::string NO_AUTHORIZE_CONFIG =
"authorizations\n"
"{\n"
"}\n";
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
BOOST_CHECK_EXCEPTION(config.parse(NO_AUTHORIZE_CONFIG, true),
ConfigFile::Error,
bind(&validateErrorMessage,
"No authorize sections found", _1));
}
BOOST_FIXTURE_TEST_CASE(NoPrivilegesSectionsDryRun, TwoValidatorFixture)
{
const std::string NO_PRIVILEGES_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/key1.pub\"\n"
" }\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/key2.pub\"\n"
" }\n"
"}\n";
// CommandValidatorTester tester1;
// tester1.generateIdentity("/tests/CommandValidator/TwoKeys/id1");
// tester1.saveIdentityToFile("tests/mgmt/key1.pub");
// CommandValidatorTester tester2;
// tester2.generateIdentity("/tests/CommandValidator/TwoKeys/id2");
// tester2.saveIdentityToFile("tests/mgmt/key2.pub");
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
std::stringstream expectedError;
expectedError << "No privileges section found for key file tests/mgmt/key1.pub "
<< "(" << m_tester1.getPublicKeyName().toUri() << ")\n"
<< "No privileges section found for key file tests/mgmt/key2.pub "
<< "(" << m_tester2.getPublicKeyName().toUri() << ")";
BOOST_CHECK_EXCEPTION(config.parse(NO_PRIVILEGES_CONFIG, true),
ConfigFile::Error,
bind(&validateErrorMessage, expectedError.str(), _1));
}
BOOST_AUTO_TEST_CASE(InvalidKeyFileDryRun)
{
const std::string INVALID_KEY_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/notakeyfile.pub\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/stillnotakeyfile.pub\"\n"
" privileges\n"
" {\n"
" }\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
BOOST_CHECK_EXCEPTION(config.parse(INVALID_KEY_CONFIG, true),
ConfigFile::Error,
bind(&validateErrorMessage,
"Unable to open key file tests/mgmt/notakeyfile.pub\n"
"Unable to open key file tests/mgmt/stillnotakeyfile.pub", _1));
}
BOOST_AUTO_TEST_CASE(NoKeyFileDryRun)
{
const std::string NO_KEY_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
" authorize\n"
" {\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
BOOST_CHECK_EXCEPTION(config.parse(NO_KEY_CONFIG, true),
ConfigFile::Error,
bind(&validateErrorMessage,
"No keyfile specified\n"
"No keyfile specified", _1));
}
BOOST_AUTO_TEST_CASE(MalformedKeyDryRun)
{
const std::string MALFORMED_KEY_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/malformedkey.pub\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
" authorize\n"
" {\n"
" keyfile \"tests/mgmt/malformedkey.pub\"\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
config.addSectionHandler("authorizations",
bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
BOOST_CHECK_EXCEPTION(config.parse(MALFORMED_KEY_CONFIG, true),
ConfigFile::Error,
bind(&validateErrorMessage,
"Malformed key file tests/mgmt/malformedkey.pub\n"
"Malformed key file tests/mgmt/malformedkey.pub", _1));
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace tests
} // namespace nfd