blob: 3dcae2418778ba6f22ababe2cf0efcf8d10084fc [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
// {
// authorize
// {
// certfile "tests/mgmt/cert1.ndncert"
// privileges
// {
// fib
// stats
// }
// }
// authorize
// {
// certfile "tests/mgmt/cert2.ndncert"
// privileges
// {
// faces
// }
// }
// }
const std::string CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/cert1.ndncert\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/cert2.ndncert\"\n"
" privileges\n"
" {\n"
" faces\n"
" }\n"
" }\n"
"}\n";
const boost::filesystem::path CONFIG_PATH =
boost::filesystem::current_path() /= std::string("unit-test-nfd.conf");
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/cert1.ndncert");
m_tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
m_tester2.saveIdentityToFile("tests/mgmt/cert2.ndncert");
}
~TwoValidatorFixture()
{
boost::system::error_code error;
boost::filesystem::remove("tests/mgmt/cert1.ndncert", error);
boost::filesystem::remove("tests/mgmt/cert2.ndncert", 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");
validator.setConfigFile(config);
config.parse(CONFIG, false, CONFIG_PATH.native());
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 cert2 for fib command (authorized for cert1 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/cert1.ndncert");
CommandValidatorTester tester2;
tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
tester2.saveIdentityToFile("tests/mgmt/cert2.ndncert");
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");
validator.setConfigFile(config);
config.parse(CONFIG, true, CONFIG_PATH.native());
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 cert2 for fib command (authorized for cert1 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;
validator.setConfigFile(config);
BOOST_CHECK_THROW(config.parse(NO_AUTHORIZE_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
}
BOOST_AUTO_TEST_CASE(NoPrivilegesSections)
{
const std::string NO_PRIVILEGES_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/cert1.ndncert\"\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
validator.setConfigFile(config);
BOOST_CHECK_THROW(config.parse(NO_PRIVILEGES_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
}
BOOST_AUTO_TEST_CASE(InvalidCertfile)
{
const std::string INVALID_CERT_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/notacertfile.ndncert\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
validator.setConfigFile(config);
BOOST_CHECK_THROW(config.parse(INVALID_CERT_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
}
BOOST_AUTO_TEST_CASE(NoCertfile)
{
const std::string NO_CERT_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
validator.setConfigFile(config);
BOOST_CHECK_THROW(config.parse(NO_CERT_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
}
BOOST_AUTO_TEST_CASE(MalformedCert)
{
const std::string MALFORMED_CERT_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/malformed.ndncert\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
validator.setConfigFile(config);
BOOST_CHECK_THROW(config.parse(MALFORMED_CERT_CONFIG, false, CONFIG_PATH.native()), 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;
validator.setConfigFile(config);
BOOST_CHECK_EXCEPTION(config.parse(NO_AUTHORIZE_CONFIG, true, CONFIG_PATH.native()),
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"
" certfile \"tests/mgmt/cert1.ndncert\"\n"
" }\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/cert2.ndncert\"\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
validator.setConfigFile(config);
std::stringstream expectedError;
expectedError << "No privileges section found for certificate file tests/mgmt/cert1.ndncert "
<< "(" << m_tester1.getPublicKeyName().toUri() << ")\n"
<< "No privileges section found for certificate file tests/mgmt/cert2.ndncert "
<< "(" << m_tester2.getPublicKeyName().toUri() << ")";
BOOST_CHECK_EXCEPTION(config.parse(NO_PRIVILEGES_CONFIG, true, CONFIG_PATH.native()),
ConfigFile::Error,
bind(&validateErrorMessage, expectedError.str(), _1));
}
BOOST_AUTO_TEST_CASE(InvalidCertfileDryRun)
{
using namespace boost::filesystem;
const std::string INVALID_KEY_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/notacertfile.ndncert\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/stillnotacertfile.ndncert\"\n"
" privileges\n"
" {\n"
" }\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
validator.setConfigFile(config);
std::stringstream error;
error << "Unable to open certificate file "
<< absolute("tests/mgmt/notacertfile.ndncert").native() << "\n"
<< "Unable to open certificate file "
<< absolute("tests/mgmt/stillnotacertfile.ndncert").native();
BOOST_CHECK_EXCEPTION(config.parse(INVALID_KEY_CONFIG, true, CONFIG_PATH.native()),
ConfigFile::Error,
bind(&validateErrorMessage, error.str(), _1));
}
BOOST_AUTO_TEST_CASE(NoCertfileDryRun)
{
const std::string NO_CERT_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;
validator.setConfigFile(config);
BOOST_CHECK_EXCEPTION(config.parse(NO_CERT_CONFIG, true, CONFIG_PATH.native()),
ConfigFile::Error,
bind(&validateErrorMessage,
"No certfile specified\n"
"No certfile specified", _1));
}
BOOST_AUTO_TEST_CASE(MalformedCertDryRun)
{
using namespace boost::filesystem;
const std::string MALFORMED_CERT_CONFIG =
"authorizations\n"
"{\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/malformed.ndncert\"\n"
" privileges\n"
" {\n"
" fib\n"
" stats\n"
" }\n"
" }\n"
" authorize\n"
" {\n"
" certfile \"tests/mgmt/malformed.ndncert\"\n"
" }\n"
"}\n";
ConfigFile config;
CommandValidator validator;
validator.setConfigFile(config);
std::stringstream error;
error << "Malformed certificate file "
<< absolute("tests/mgmt/malformed.ndncert").native() << "\n"
<< "Malformed certificate file "
<< absolute("tests/mgmt/malformed.ndncert").native();
BOOST_CHECK_EXCEPTION(config.parse(MALFORMED_CERT_CONFIG, true, CONFIG_PATH.native()),
ConfigFile::Error,
bind(&validateErrorMessage, error.str(), _1));
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace tests
} // namespace nfd