mgmt, main: add support for authorized commands

refs: #1227

Change-Id: I907d1fa6e78775470c5376fcdfe898be4c311001
diff --git a/daemon/main.cpp b/daemon/main.cpp
index 541d8ff..e4ead64 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -12,6 +12,7 @@
 #include "mgmt/face-manager.hpp"
 #include "mgmt/local-control-header-manager.hpp"
 #include "mgmt/strategy-choice-manager.hpp"
+#include "mgmt/config-file.hpp"
 #include "face/tcp-factory.hpp"
 
 #ifdef HAVE_UNIX_SOCKETS
@@ -39,6 +40,7 @@
   std::pair<std::string, std::string> m_tcpListen;
   std::vector<TcpOutgoing> m_tcpOutgoings;
   std::string m_unixListen;
+  std::string m_config;
 };
 
 static ProgramOptions g_options;
@@ -67,7 +69,8 @@
        "[--unix-listen \"/var/run/nfd.sock\"] "
 #endif
        "[--tcp-connect \"192.0.2.1:6363\" "
-            "[--prefix </example>]]\n"
+            "[--prefix </example>]] "
+       "[--config /path/to/nfd.conf]\n"
       "\trun forwarding daemon\n"
       "\t--tcp-listen <ip:port>: listen on IP and port\n"
 #ifdef HAVE_UNIX_SOCKETS
@@ -76,6 +79,7 @@
       "\t--tcp-connect <ip:port>: connect to IP and port (can occur multiple times)\n"
       "\t--prefix <NDN name>: add this face as nexthop to FIB entry "
         "(must appear after --tcp-connect, can occur multiple times)\n"
+       "\t--config <configuration file>]: path to configuration file\n"
     "\n",
     programName, programName
   );
@@ -99,6 +103,7 @@
   g_options.m_tcpListen = std::make_pair("0.0.0.0", "6363");
   g_options.m_unixListen = "/var/run/nfd.sock";
   g_options.m_tcpOutgoings.clear();
+  g_options.m_config = DEFAULT_CONFIG_FILE;
 
   while (1) {
     int option_index = 0;
@@ -108,6 +113,7 @@
       { "tcp-connect"   , required_argument, 0, 0 },
       { "prefix"        , required_argument, 0, 0 },
       { "unix-listen"   , required_argument, 0, 0 },
+      { "config"        , required_argument, 0, 0 },
       { 0               , 0                , 0, 0 }
     };
     int c = getopt_long_only(argc, argv, "", long_options, &option_index);
@@ -134,10 +140,14 @@
           case 4://unix-listen
             g_options.m_unixListen = ::optarg;
             break;
+          case 5://config
+            g_options.m_config = ::optarg;
+            break;
         }
         break;
     }
   }
+
   return true;
 }
 
@@ -206,9 +216,14 @@
 void
 initializeMgmt()
 {
+  ConfigFile config;
+
   g_internalFace = make_shared<InternalFace>();
   g_forwarder->addFace(g_internalFace);
 
+  g_internalFace->getValidator().setConfigFile(config);
+
+
   g_fibManager = new FibManager(g_forwarder->getFib(),
                                 bind(&Forwarder::getFace, g_forwarder, _1),
                                 g_internalFace);
@@ -222,6 +237,11 @@
   g_strategyChoiceManager = new StrategyChoiceManager(g_forwarder->getStrategyChoice(),
                                                       g_internalFace);
 
+  /// \todo add face manager section handler
+
+  /// \todo add parsing back when there is an official default config file
+  // config.parse(g_options.m_config);
+
   shared_ptr<fib::Entry> entry = g_forwarder->getFib().insert("/localhost/nfd").first;
   entry->addNextHop(g_internalFace, 0);
 }
diff --git a/daemon/mgmt/command-validator.cpp b/daemon/mgmt/command-validator.cpp
new file mode 100644
index 0000000..9028ba2
--- /dev/null
+++ b/daemon/mgmt/command-validator.cpp
@@ -0,0 +1,172 @@
+/* -*- 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 "command-validator.hpp"
+#include <ndn-cpp-dev/util/io.hpp>
+#include <ndn-cpp-dev/security/identity-certificate.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("CommandValidator");
+
+CommandValidator::CommandValidator()
+{
+
+}
+
+CommandValidator::~CommandValidator()
+{
+
+}
+
+void
+CommandValidator::setConfigFile(ConfigFile& configFile)
+{
+  configFile.addSectionHandler("authorizations",
+                               bind(&CommandValidator::onConfig, this, _1, _2));
+}
+
+static inline void
+aggregateErrors(std::stringstream& ss, const std::string& msg)
+{
+  if (!ss.str().empty())
+    {
+      ss << "\n";
+    }
+  ss << msg;
+}
+
+void
+CommandValidator::onConfig(const ConfigSection& section,
+                           bool isDryRun)
+{
+  const ConfigSection EMPTY_SECTION;
+
+  if (section.begin() == section.end())
+    {
+      throw ConfigFile::Error("No authorize sections found");
+    }
+
+  std::stringstream dryRunErrors;
+  ConfigSection::const_iterator authIt;
+  for (authIt = section.begin(); authIt != section.end(); authIt++)
+    {
+      std::string keyfile;
+      try
+        {
+          keyfile = authIt->second.get<std::string>("keyfile");
+        }
+      catch (const std::runtime_error& e)
+        {
+          std::string msg = "No keyfile specified";
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      std::ifstream in;
+      in.open(keyfile.c_str());
+      if (!in.is_open())
+        {
+          std::string msg = "Unable to open key file " + keyfile;
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      shared_ptr<ndn::IdentityCertificate> id;
+      try
+        {
+          id = ndn::io::load<ndn::IdentityCertificate>(in);
+        }
+      catch(const std::runtime_error& error)
+        {
+          std::string msg = "Malformed key file " + keyfile;
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      in.close();
+
+      const ConfigSection* privileges = 0;
+
+      try
+        {
+          privileges = &authIt->second.get_child("privileges");
+        }
+      catch (const std::runtime_error& error)
+        {
+          std::string msg = "No privileges section found for key file " +
+            keyfile + " (" + id->getPublicKeyName().toUri() + ")";
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      if (privileges->begin() == privileges->end())
+        {
+          NFD_LOG_WARN("No privileges specified for key file " << keyfile + " (" << id->getPublicKeyName().toUri() << ")");
+        }
+
+      ConfigSection::const_iterator privIt;
+      for (privIt = privileges->begin(); privIt != privileges->end(); privIt++)
+        {
+          const std::string& privilegeName = privIt->first;
+          if (m_supportedPrivileges.find(privilegeName) != m_supportedPrivileges.end())
+            {
+              NFD_LOG_INFO("Giving privilege \"" << privilegeName
+                           << "\" to key " << id->getPublicKeyName());
+              if (!isDryRun)
+                {
+                  const std::string regex = "^<localhost><nfd><" + privilegeName + ">";
+                  m_validator.addInterestRule(regex, *id);
+                }
+            }
+          else
+            {
+              // Invalid configuration
+              std::string msg = "Invalid privilege \"" + privilegeName + "\" for key file " +
+                keyfile + " (" + id->getPublicKeyName().toUri() + ")";
+              if (!isDryRun)
+                {
+                  throw ConfigFile::Error(msg);
+                }
+              aggregateErrors(dryRunErrors, msg);
+            }
+        }
+    }
+
+  if (!dryRunErrors.str().empty())
+    {
+      throw ConfigFile::Error(dryRunErrors.str());
+    }
+}
+
+void
+CommandValidator::addSupportedPrivilege(const std::string& privilege)
+{
+  if (m_supportedPrivileges.find(privilege) != m_supportedPrivileges.end())
+    {
+      throw CommandValidator::Error("Duplicated privivilege: " + privilege);
+    }
+  m_supportedPrivileges.insert(privilege);
+}
+
+} // namespace nfd
+
diff --git a/daemon/mgmt/command-validator.hpp b/daemon/mgmt/command-validator.hpp
new file mode 100644
index 0000000..466bb70
--- /dev/null
+++ b/daemon/mgmt/command-validator.hpp
@@ -0,0 +1,95 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_MGMT_COMMAND_VALIDATOR_HPP
+#define NFD_MGMT_COMMAND_VALIDATOR_HPP
+
+#include "config-file.hpp"
+#include <ndn-cpp-dev/util/command-interest-validator.hpp>
+
+namespace nfd {
+
+class CommandValidator
+{
+public:
+
+  class Error : public std::runtime_error
+  {
+  public:
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+
+    }
+  };
+
+  CommandValidator();
+
+  ~CommandValidator();
+
+  void
+  setConfigFile(ConfigFile& configFile);
+
+  /**
+   * \param section "authorizations" section to parse
+   * \param isDryRun true if performing a dry run of configuration, false otherwise
+   * \throws ConfigFile::Error on parse error
+   */
+  void
+  onConfig(const ConfigSection& section, bool isDryRun);
+
+  /**
+   * \param privilege name of privilege to add
+   * \throws CommandValidator::Error on duplicated privilege
+   */
+  void
+  addSupportedPrivilege(const std::string& privilege);
+
+  void
+  addInterestRule(const std::string& regex,
+                  const ndn::IdentityCertificate& certificate);
+
+  void
+  addInterestRule(const std::string& regex,
+                  const Name& keyName,
+                  const ndn::PublicKey& publicKey);
+
+  void
+  validate(const Interest& interest,
+           const ndn::OnInterestValidated& onValidated,
+           const ndn::OnInterestValidationFailed& onValidationFailed);
+
+private:
+  ndn::CommandInterestValidator m_validator;
+  std::set<std::string> m_supportedPrivileges;
+};
+
+inline void
+CommandValidator::addInterestRule(const std::string& regex,
+                                  const ndn::IdentityCertificate& certificate)
+{
+  m_validator.addInterestRule(regex, certificate);
+}
+
+inline void
+CommandValidator::addInterestRule(const std::string& regex,
+                                  const Name& keyName,
+                                  const ndn::PublicKey& publicKey)
+{
+  m_validator.addInterestRule(regex, keyName, publicKey);
+}
+
+inline void
+CommandValidator::validate(const Interest& interest,
+                           const ndn::OnInterestValidated& onValidated,
+                           const ndn::OnInterestValidationFailed& onValidationFailed)
+{
+  m_validator.validate(interest, onValidated, onValidationFailed);
+}
+
+} // namespace nfd
+
+#endif // NFD_MGMT_COMMAND_VALIDATOR_HPP
diff --git a/daemon/mgmt/config-file.cpp b/daemon/mgmt/config-file.cpp
index a55650e..8018310 100644
--- a/daemon/mgmt/config-file.cpp
+++ b/daemon/mgmt/config-file.cpp
@@ -34,7 +34,7 @@
     {
       std::string msg = "Failed to read configuration file: ";
       msg += filename;
-      throw Error(filename);
+      throw Error(msg);
     }
   parse(inputFile, isDryRun, filename);
   inputFile.close();
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 30e3abd..9874176 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -34,7 +34,7 @@
 
 const size_t FaceManager::COMMAND_SIGNED_NCOMPS =
   FaceManager::COMMAND_UNSIGNED_NCOMPS +
-  0; // No signed Interest support in mock, otherwise 4 (timestamp, nonce, signed info tlv, signature tlv)
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
 
 const FaceManager::VerbAndProcessor FaceManager::COMMAND_VERBS[] =
   {
@@ -52,8 +52,8 @@
 
 
 FaceManager::FaceManager(FaceTable& faceTable,
-                         shared_ptr<AppFace> face)
-  : ManagerBase(face)
+                         shared_ptr<InternalFace> face)
+  : ManagerBase(face, FACE_MANAGER_PRIVILEGE)
   , m_faceTable(faceTable)
   , m_verbDispatch(COMMAND_VERBS,
                    COMMAND_VERBS +
@@ -411,7 +411,9 @@
       return;
     }
 
-  onValidatedFaceRequest(request.shared_from_this());
+  validate(request,
+           bind(&FaceManager::onValidatedFaceRequest, this, _1),
+           bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
 }
 
 void
@@ -430,14 +432,6 @@
           return;
         }
 
-      /// \todo authorize command
-      if (false)
-        {
-          NFD_LOG_INFO("command result: unauthorized verb: " << command);
-          sendResponse(command, 403, "Unauthorized command");
-          return;
-        }
-
       NFD_LOG_INFO("command result: processing verb: " << verb);
       (verbProcessor->second)(this, command, options);
     }
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index 87574cd..316af2a 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -35,7 +35,7 @@
    */
 
   FaceManager(FaceTable& faceTable,
-              shared_ptr<AppFace> face);
+              shared_ptr<InternalFace> face);
 
   /** \brief Subscribe to a face management section(s) for the config file
    */
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
index 8e27114..dee765b 100644
--- a/daemon/mgmt/fib-manager.cpp
+++ b/daemon/mgmt/fib-manager.cpp
@@ -17,18 +17,18 @@
 
 NFD_LOG_INIT("FibManager");
 
-const Name FibManager::FIB_MANAGER_COMMAND_PREFIX = "/localhost/nfd/fib";
+const Name FibManager::COMMAND_PREFIX = "/localhost/nfd/fib";
 
-const size_t FibManager::FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS =
-  FibManager::FIB_MANAGER_COMMAND_PREFIX.size() +
+const size_t FibManager::COMMAND_UNSIGNED_NCOMPS =
+  FibManager::COMMAND_PREFIX.size() +
   1 + // verb
   1;  // verb options
 
-const size_t FibManager::FIB_MANAGER_COMMAND_SIGNED_NCOMPS =
-  FibManager::FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS +
-  0; // No signed Interest support in mock, otherwise 3 (timestamp, signed info tlv, signature tlv)
+const size_t FibManager::COMMAND_SIGNED_NCOMPS =
+  FibManager::COMMAND_UNSIGNED_NCOMPS +
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
 
-const FibManager::VerbAndProcessor FibManager::FIB_MANAGER_COMMAND_VERBS[] =
+const FibManager::VerbAndProcessor FibManager::COMMAND_VERBS[] =
   {
     VerbAndProcessor(
                      Name::Component("insert"),
@@ -45,8 +45,6 @@
                      &FibManager::addNextHop
                      ),
 
-
-
     VerbAndProcessor(
                      Name::Component("remove-nexthop"),
                      &FibManager::removeNextHop
@@ -56,13 +54,13 @@
 
 FibManager::FibManager(Fib& fib,
                        function<shared_ptr<Face>(FaceId)> getFace,
-                       shared_ptr<AppFace> face)
-  : ManagerBase(face),
+                       shared_ptr<InternalFace> face)
+  : ManagerBase(face, FIB_PRIVILEGE),
     m_managedFib(fib),
     m_getFace(getFace),
-    m_verbDispatch(FIB_MANAGER_COMMAND_VERBS,
-                   FIB_MANAGER_COMMAND_VERBS +
-                   (sizeof(FIB_MANAGER_COMMAND_VERBS) / sizeof(VerbAndProcessor)))
+    m_verbDispatch(COMMAND_VERBS,
+                   COMMAND_VERBS +
+                   (sizeof(COMMAND_VERBS) / sizeof(VerbAndProcessor)))
 {
   face->setInterestFilter("/localhost/nfd/fib",
                           bind(&FibManager::onFibRequest, this, _2));
@@ -74,56 +72,56 @@
   const Name& command = request.getName();
   const size_t commandNComps = command.size();
 
-
-
-  if (FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
-      commandNComps < FIB_MANAGER_COMMAND_SIGNED_NCOMPS)
+  if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+      commandNComps < COMMAND_SIGNED_NCOMPS)
     {
       NFD_LOG_INFO("command result: unsigned verb: " << command);
       sendResponse(command, 401, "Signature required");
 
       return;
     }
-  else if (commandNComps < FIB_MANAGER_COMMAND_SIGNED_NCOMPS ||
-      !FIB_MANAGER_COMMAND_PREFIX.isPrefixOf(command))
+  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
+      !COMMAND_PREFIX.isPrefixOf(command))
     {
       NFD_LOG_INFO("command result: malformed");
       sendResponse(command, 400, "Malformed command");
       return;
     }
 
-  const Name::Component& verb = command.get(FIB_MANAGER_COMMAND_PREFIX.size());
+  validate(request,
+           bind(&FibManager::onValidatedFibRequest,
+                this, _1),
+           bind(&ManagerBase::onCommandValidationFailed,
+                this, _1, _2));
+}
+
+void
+FibManager::onValidatedFibRequest(const shared_ptr<const Interest>& request)
+{
+  const Name& command = request->getName();
+  const Name::Component& verb = command.get(COMMAND_PREFIX.size());
 
   VerbDispatchTable::const_iterator verbProcessor = m_verbDispatch.find (verb);
   if (verbProcessor != m_verbDispatch.end())
     {
       FibManagementOptions options;
-      if (!extractOptions(request, options))
+      if (!extractOptions(*request, options))
         {
+          NFD_LOG_INFO("command result: malformed verb: " << verb);
           sendResponse(command, 400, "Malformed command");
           return;
         }
 
-      /// \todo authorize command
-      if (false)
-        {
-          NFD_LOG_INFO("command result: unauthorized verb: " << command);
-          sendResponse(request.getName(), 403, "Unauthorized command");
-          return;
-        }
-
       NFD_LOG_INFO("command result: processing verb: " << verb);
       ControlResponse response;
       (verbProcessor->second)(this, options, response);
-
       sendResponse(command, response);
     }
   else
     {
       NFD_LOG_INFO("command result: unsupported verb: " << verb);
-      sendResponse(request.getName(), 501, "Unsupported command");
+      sendResponse(command, 501, "Unsupported command");
     }
-
 }
 
 bool
@@ -131,8 +129,7 @@
                            FibManagementOptions& extractedOptions)
 {
   const Name& command = request.getName();
-  const size_t optionCompIndex =
-    FIB_MANAGER_COMMAND_PREFIX.size() + 1;
+  const size_t optionCompIndex = COMMAND_PREFIX.size() + 1;
 
   try
     {
@@ -220,7 +217,7 @@
 
 void
 FibManager::removeNextHop(const FibManagementOptions& options,
-                          ControlResponse &response)
+                          ControlResponse& response)
 {
   NFD_LOG_DEBUG("remove-nexthop prefix: " << options.getName()
                 << " faceid: " << options.getFaceId());
diff --git a/daemon/mgmt/fib-manager.hpp b/daemon/mgmt/fib-manager.hpp
index c7b7793..43b410e 100644
--- a/daemon/mgmt/fib-manager.hpp
+++ b/daemon/mgmt/fib-manager.hpp
@@ -22,13 +22,15 @@
 class Forwarder;
 class Fib;
 
+const std::string FIB_PRIVILEGE = "fib"; // config file privilege name
+
 class FibManager : public ManagerBase
 {
 public:
 
   FibManager(Fib& fib,
              function<shared_ptr<Face>(FaceId)> getFace,
-             shared_ptr<AppFace> face);
+             shared_ptr<InternalFace> face);
 
   void
   onFibRequest(const Interest& request);
@@ -36,9 +38,13 @@
 private:
 
   void
+  onValidatedFibRequest(const shared_ptr<const Interest>& request);
+
+  void
   insertEntry(const FibManagementOptions& options,
               ControlResponse& response);
 
+
   void
   deleteEntry(const FibManagementOptions& options,
               ControlResponse& response);
@@ -55,9 +61,6 @@
   extractOptions(const Interest& request,
                  FibManagementOptions& extractedOptions);
 
-  // void
-  // onConfig(ConfigFile::Node section, bool isDryRun);
-
 private:
 
   Fib& m_managedFib;
@@ -75,17 +78,17 @@
 
   const VerbDispatchTable m_verbDispatch;
 
-  static const Name FIB_MANAGER_COMMAND_PREFIX; // /localhost/nfd/fib
+  static const Name COMMAND_PREFIX; // /localhost/nfd/fib
 
   // number of components in an invalid, but not malformed, unsigned command.
   // (/localhost/nfd/fib + verb + options) = 5
-  static const size_t FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS;
+  static const size_t COMMAND_UNSIGNED_NCOMPS;
 
   // number of components in a valid signed Interest.
-  // 5 in mock (see UNSIGNED_NCOMPS), 8 with signed Interest support.
-  static const size_t FIB_MANAGER_COMMAND_SIGNED_NCOMPS;
+  // UNSIGNED_NCOMPS + 4 command Interest components = 9
+  static const size_t COMMAND_SIGNED_NCOMPS;
 
-  static const VerbAndProcessor FIB_MANAGER_COMMAND_VERBS[];
+  static const VerbAndProcessor COMMAND_VERBS[];
 
 };
 
diff --git a/daemon/mgmt/internal-face.hpp b/daemon/mgmt/internal-face.hpp
index 8d5805c..3ce32e3 100644
--- a/daemon/mgmt/internal-face.hpp
+++ b/daemon/mgmt/internal-face.hpp
@@ -10,6 +10,8 @@
 #include "face/face.hpp"
 #include "app-face.hpp"
 
+#include "command-validator.hpp"
+
 namespace nfd {
 
 class InternalFace : public Face, public AppFace
@@ -25,6 +27,12 @@
 
   InternalFace();
 
+  CommandValidator&
+  getValidator();
+
+  virtual
+  ~InternalFace();
+
   // Overridden Face methods for forwarder
 
   virtual void
@@ -45,17 +53,19 @@
   virtual void
   put(const Data& data);
 
-  virtual
-  ~InternalFace();
-
 private:
 
-  // void
-  // onConfig(ConfigFile::Node section, bool isDryRun);
-
   std::map<Name, OnInterest> m_interestFilters;
+  CommandValidator m_validator;
 };
 
+inline CommandValidator&
+InternalFace::getValidator()
+{
+  return m_validator;
+}
+
+
 } // namespace nfd
 
 #endif //NFD_MGMT_INTERNAL_FACE_HPP
diff --git a/daemon/mgmt/local-control-header-manager.cpp b/daemon/mgmt/local-control-header-manager.cpp
index 5717f54..aae1d0c 100644
--- a/daemon/mgmt/local-control-header-manager.cpp
+++ b/daemon/mgmt/local-control-header-manager.cpp
@@ -6,6 +6,7 @@
 
 #include "local-control-header-manager.hpp"
 #include "face/local-face.hpp"
+#include "mgmt/internal-face.hpp"
 
 namespace nfd {
 
@@ -20,26 +21,23 @@
 
 const size_t LocalControlHeaderManager::COMMAND_SIGNED_NCOMPS =
   LocalControlHeaderManager::COMMAND_UNSIGNED_NCOMPS +
-  0; // No signed Interest support in mock
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
 
 
 LocalControlHeaderManager::LocalControlHeaderManager(function<shared_ptr<Face>(FaceId)> getFace,
-                                                     shared_ptr<AppFace> face)
-  : ManagerBase(face),
+                                                     shared_ptr<InternalFace> face)
+  : ManagerBase(face, CONTROL_HEADER_PRIVILEGE),
     m_getFace(getFace)
 {
   face->setInterestFilter("/localhost/nfd/control-header",
                           bind(&LocalControlHeaderManager::onLocalControlHeaderRequest, this, _2));
 }
 
+
+
 void
 LocalControlHeaderManager::onLocalControlHeaderRequest(const Interest& request)
 {
-  static const Name::Component MODULE_IN_FACEID("in-faceid");
-  static const Name::Component MODULE_NEXTHOP_FACEID("nexthop-faceid");
-  static const Name::Component VERB_ENABLE("enable");
-  static const Name::Component VERB_DISABLE("disable");
-
   const Name& command = request.getName();
   const size_t commandNComps = command.size();
 
@@ -59,35 +57,53 @@
       return;
     }
 
+  validate(request,
+             bind(&LocalControlHeaderManager::onCommandValidated,
+                    this, _1),
+             bind(&ManagerBase::onCommandValidationFailed,
+                    this, _1, _2));
+
+
+}
+
+void
+LocalControlHeaderManager::onCommandValidated(const shared_ptr<const Interest>& command)
+{
+  static const Name::Component MODULE_IN_FACEID("in-faceid");
+  static const Name::Component MODULE_NEXTHOP_FACEID("nexthop-faceid");
+  static const Name::Component VERB_ENABLE("enable");
+  static const Name::Component VERB_DISABLE("disable");
+
   shared_ptr<LocalFace> face =
-    dynamic_pointer_cast<LocalFace>(m_getFace(request.getIncomingFaceId()));
+    dynamic_pointer_cast<LocalFace>(m_getFace(command->getIncomingFaceId()));
 
   if (!static_cast<bool>(face))
     {
-      NFD_LOG_INFO("command result: request to enable control header on non-local face");
-      sendResponse(command, 400, "Command not supported on the requested face");
+      NFD_LOG_INFO("command result: command to enable control header on non-local face");
+      sendResponse(command->getName(), 400, "Command not supported on the requested face");
       return;
     }
 
-  const Name::Component& module = command.get(COMMAND_PREFIX.size());
-  const Name::Component& verb = command.get(COMMAND_PREFIX.size() + 1);
+  const Name& commandName = command->getName();
+  const Name::Component& module = commandName[COMMAND_PREFIX.size()];
+  const Name::Component& verb = commandName[COMMAND_PREFIX.size() + 1];
 
   if (module == MODULE_IN_FACEID)
     {
       if (verb == VERB_ENABLE)
         {
           face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID, true);
-          sendResponse(command, 200, "Success");
+          sendResponse(commandName, 200, "Success");
         }
       else if (verb == VERB_DISABLE)
         {
           face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID, false);
-          sendResponse(command, 200, "Success");
+          sendResponse(commandName, 200, "Success");
         }
       else
         {
           NFD_LOG_INFO("command result: unsupported verb: " << verb);
-          sendResponse(command, 501, "Unsupported");
+          sendResponse(commandName, 501, "Unsupported");
         }
     }
   else if (module == MODULE_NEXTHOP_FACEID)
@@ -95,23 +111,23 @@
       if (verb == VERB_ENABLE)
         {
           face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID, true);
-          sendResponse(command, 200, "Success");
+          sendResponse(commandName, 200, "Success");
         }
       else if (verb == VERB_DISABLE)
         {
           face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID, false);
-          sendResponse(command, 200, "Success");
+          sendResponse(commandName, 200, "Success");
         }
       else
         {
           NFD_LOG_INFO("command result: unsupported verb: " << verb);
-          sendResponse(command, 501, "Unsupported");
+          sendResponse(commandName, 501, "Unsupported");
         }
     }
   else
     {
       NFD_LOG_INFO("command result: unsupported module: " << module);
-      sendResponse(command, 501, "Unsupported");
+      sendResponse(commandName, 501, "Unsupported");
     }
 }
 
diff --git a/daemon/mgmt/local-control-header-manager.hpp b/daemon/mgmt/local-control-header-manager.hpp
index 8c03e4e..0aa5d63 100644
--- a/daemon/mgmt/local-control-header-manager.hpp
+++ b/daemon/mgmt/local-control-header-manager.hpp
@@ -14,26 +14,31 @@
 
 namespace nfd {
 
+const std::string CONTROL_HEADER_PRIVILEGE = "control-header"; // config file privilege name
+
 class LocalControlHeaderManager : public ManagerBase
 {
 public:
   LocalControlHeaderManager(function<shared_ptr<Face>(FaceId)> getFace,
-                            shared_ptr<AppFace> face);
+                            shared_ptr<InternalFace> face);
 
   void
   onLocalControlHeaderRequest(const Interest& request);
 
+  void
+  onCommandValidated(const shared_ptr<const Interest>& command);
+
 private:
   function<shared_ptr<Face>(FaceId)> m_getFace;
 
   static const Name COMMAND_PREFIX; // /localhost/nfd/control-header
 
   // number of components in an invalid, but not malformed, unsigned command.
-  // (/localhost/nfd/control-headeer + control-module + verb) = 5
+  // (/localhost/nfd/control-header + control-module + verb) = 5
   static const size_t COMMAND_UNSIGNED_NCOMPS;
 
   // number of components in a valid signed Interest.
-  // 5 in mock (see UNSIGNED_NCOMPS)
+  // UNSIGNED_NCOMPS + 4 command Interest components = 9
   static const size_t COMMAND_SIGNED_NCOMPS;
 };
 
diff --git a/daemon/mgmt/manager-base.cpp b/daemon/mgmt/manager-base.cpp
index 1994416..bd94b18 100644
--- a/daemon/mgmt/manager-base.cpp
+++ b/daemon/mgmt/manager-base.cpp
@@ -5,16 +5,15 @@
  */
 
 #include "manager-base.hpp"
-#include "mgmt/app-face.hpp"
 
 namespace nfd {
 
 NFD_LOG_INIT("ManagerBase");
 
-ManagerBase::ManagerBase(shared_ptr<AppFace> face)
+ManagerBase::ManagerBase(shared_ptr<InternalFace> face, const std::string& privilege)
   : m_face(face)
 {
-
+  face->getValidator().addSupportedPrivilege(privilege);
 }
 
 ManagerBase::~ManagerBase()
@@ -49,5 +48,13 @@
   m_face->put(*responseData);
 }
 
+void
+ManagerBase::onCommandValidationFailed(const shared_ptr<const Interest>& command,
+                                       const std::string& error)
+{
+  NFD_LOG_INFO("command result: unauthorized verb: " << command);
+  sendResponse(command->getName(), 403, "Unauthorized command");
+}
+
 
 } // namespace nfd
diff --git a/daemon/mgmt/manager-base.hpp b/daemon/mgmt/manager-base.hpp
index 78c6efe..9522812 100644
--- a/daemon/mgmt/manager-base.hpp
+++ b/daemon/mgmt/manager-base.hpp
@@ -10,25 +10,34 @@
 #include "common.hpp"
 #include <ndn-cpp-dev/management/nfd-control-response.hpp>
 
+#include "mgmt/command-validator.hpp"
+#include "mgmt/internal-face.hpp"
+
+
 namespace nfd {
 
 using ndn::nfd::ControlResponse;
 
-class AppFace;
+class InternalFace;
 
 class ManagerBase
 {
 public:
+
   struct Error : public std::runtime_error
   {
     Error(const std::string& what) : std::runtime_error(what) {}
   };
 
-  ManagerBase(shared_ptr<AppFace> face);
+  ManagerBase(shared_ptr<InternalFace> face, const std::string& privilege);
 
   virtual
   ~ManagerBase();
 
+  void
+  onCommandValidationFailed(const shared_ptr<const Interest>& command,
+                            const std::string& error);
+
 protected:
 
   void
@@ -50,8 +59,23 @@
                uint32_t code,
                const std::string& text);
 
+PUBLIC_WITH_TESTS_ELSE_PROTECTED:
+  void
+  addInterestRule(const std::string& regex,
+                  const ndn::IdentityCertificate& certificate);
+
+  void
+  addInterestRule(const std::string& regex,
+                  const Name& keyName,
+                  const ndn::PublicKey& publicKey);
+
+  void
+  validate(const Interest& interest,
+           const ndn::OnInterestValidated& onValidated,
+           const ndn::OnInterestValidationFailed& onValidationFailed);
+
 protected:
-  shared_ptr<AppFace> m_face;
+  shared_ptr<InternalFace> m_face;
 };
 
 inline void
@@ -73,6 +97,29 @@
   response.setBody(body);
 }
 
+inline void
+ManagerBase::addInterestRule(const std::string& regex,
+                             const ndn::IdentityCertificate& certificate)
+{
+  m_face->getValidator().addInterestRule(regex, certificate);
+}
+
+inline void
+ManagerBase::addInterestRule(const std::string& regex,
+                             const Name& keyName,
+                             const ndn::PublicKey& publicKey)
+{
+  m_face->getValidator().addInterestRule(regex, keyName, publicKey);
+}
+
+inline void
+ManagerBase::validate(const Interest& interest,
+                      const ndn::OnInterestValidated& onValidated,
+                      const ndn::OnInterestValidationFailed& onValidationFailed)
+{
+  m_face->getValidator().validate(interest, onValidated, onValidationFailed);
+}
+
 
 } // namespace nfd
 
diff --git a/daemon/mgmt/strategy-choice-manager.cpp b/daemon/mgmt/strategy-choice-manager.cpp
index 00761dd..665f4a8 100644
--- a/daemon/mgmt/strategy-choice-manager.cpp
+++ b/daemon/mgmt/strategy-choice-manager.cpp
@@ -21,11 +21,11 @@
 
 const size_t StrategyChoiceManager::COMMAND_SIGNED_NCOMPS =
   StrategyChoiceManager::COMMAND_UNSIGNED_NCOMPS +
-  0; // No signed Interest support in mock, otherwise 4 (timestamp, nonce, signed info tlv, signature tlv)
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
 
 StrategyChoiceManager::StrategyChoiceManager(StrategyChoice& strategyChoice,
-                                             shared_ptr<AppFace> face)
-  : ManagerBase(face)
+                                             shared_ptr<InternalFace> face)
+  : ManagerBase(face, STRATEGY_CHOICE_PRIVILEGE)
   , m_strategyChoice(strategyChoice)
 {
   face->setInterestFilter("/localhost/nfd/strategy-choice",
@@ -59,7 +59,9 @@
       return;
     }
 
-  onValidatedStrategyChoiceRequest(request.shared_from_this());
+  validate(request,
+           bind(&StrategyChoiceManager::onValidatedStrategyChoiceRequest, this, _1),
+           bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
 }
 
 void
diff --git a/daemon/mgmt/strategy-choice-manager.hpp b/daemon/mgmt/strategy-choice-manager.hpp
index 34046ac..6313fbb 100644
--- a/daemon/mgmt/strategy-choice-manager.hpp
+++ b/daemon/mgmt/strategy-choice-manager.hpp
@@ -21,7 +21,7 @@
 {
 public:
   StrategyChoiceManager(StrategyChoice& strategyChoice,
-                        shared_ptr<AppFace> face);
+                        shared_ptr<InternalFace> face);
 
   virtual
   ~StrategyChoiceManager();
diff --git a/tests/mgmt/command-validator.cpp b/tests/mgmt/command-validator.cpp
new file mode 100644
index 0000000..7a6d39d
--- /dev/null
+++ b/tests/mgmt/command-validator.cpp
@@ -0,0 +1,585 @@
+/* -*- 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
+
diff --git a/tests/mgmt/face-manager.cpp b/tests/mgmt/face-manager.cpp
index 92dc680..9937632 100644
--- a/tests/mgmt/face-manager.cpp
+++ b/tests/mgmt/face-manager.cpp
@@ -13,6 +13,7 @@
 
 #include "common.hpp"
 #include "tests/test-common.hpp"
+#include "validation-common.hpp"
 
 namespace nfd {
 namespace tests {
@@ -274,6 +275,13 @@
     return m_manager;
   }
 
+  void
+  addInterestRule(const std::string& regex,
+                  ndn::IdentityCertificate& certificate)
+  {
+    m_manager.addInterestRule(regex, certificate);
+  }
+
   bool
   didFaceTableAddFire() const
   {
@@ -669,9 +677,85 @@
   BOOST_REQUIRE(didCallbackFire());
 }
 
-/// \todo add tests for unsigned and unauthorized commands
+BOOST_AUTO_TEST_CASE(UnsignedCommand)
+{
+  ndn::nfd::FaceManagementOptions options;
+  options.setUri("tcp://127.0.0.1");
 
-BOOST_AUTO_TEST_CASE(UnsupportedVerb)
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedOptions);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData +=
+    bind(&FaceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 401, "Signature required");
+
+  getManager().onFaceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FaceManagerFixture>)
+{
+  ndn::nfd::FaceManagementOptions options;
+  options.setUri("tcp://127.0.0.1");
+
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedOptions);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData +=
+    bind(&FaceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 403, "Unauthorized command");
+
+  getManager().onFaceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+template <typename T> class AuthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+  AuthorizedCommandFixture()
+  {
+    const std::string regex = "^<localhost><nfd><faces>";
+    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+  }
+
+  virtual
+  ~AuthorizedCommandFixture()
+  {
+
+  }
+};
+
+// template <> class AuthorizedCommandFixture<FaceManagerFixture> :
+//     public CommandFixture<FaceManagerFixture>
+// {
+// public:
+//   AuthorizedCommandFixture()
+//   {
+//     const std::string regex = "^<localhost><nfd><faces>";
+//     FaceManagerFixture::ManagerBase::addInterestRule(regex, *CommandFixture<FaceManagerFixture>::m_certificate);
+//   }
+
+//   virtual
+//   ~AuthorizedCommandFixture()
+//   {
+
+//   }
+// };
+
+BOOST_FIXTURE_TEST_CASE(UnsupportedCommand, AuthorizedCommandFixture<FaceManagerFixture>)
 {
   ndn::nfd::FaceManagementOptions options;
 
@@ -682,6 +766,7 @@
   commandName.append(encodedOptions);
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
   getFace()->onReceiveData +=
     bind(&FaceManagerFixture::validateControlResponse, this, _1,
@@ -743,13 +828,15 @@
   bool m_destroyFaceFired;
 };
 
-BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestBadOptionParse, ValidatedFaceRequestFixture)
+BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestBadOptionParse,
+                        AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
 {
   Name commandName("/localhost/nfd/faces");
   commandName.append("create");
   commandName.append("NotReallyOptions");
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
   getFace()->onReceiveData +=
     bind(&ValidatedFaceRequestFixture::validateControlResponse, this, _1,
@@ -760,7 +847,8 @@
   BOOST_REQUIRE(didCallbackFire());
 }
 
-BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestCreateFace, ValidatedFaceRequestFixture)
+BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestCreateFace,
+                        AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
 {
   ndn::nfd::FaceManagementOptions options;
   options.setUri("tcp://127.0.0.1");
@@ -772,12 +860,14 @@
   commandName.append(encodedOptions);
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
   onValidatedFaceRequest(command);
   BOOST_CHECK(didCreateFaceFire());
 }
 
-BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestDestroyFace, ValidatedFaceRequestFixture)
+BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestDestroyFace,
+                        AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
 {
   ndn::nfd::FaceManagementOptions options;
   options.setUri("tcp://127.0.0.1");
@@ -789,6 +879,7 @@
   commandName.append(encodedOptions);
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
   onValidatedFaceRequest(command);
   BOOST_CHECK(didDestroyFaceFire());
@@ -812,7 +903,7 @@
   }
 };
 
-BOOST_FIXTURE_TEST_CASE(CreateFaceBadUri, FaceFixture)
+BOOST_FIXTURE_TEST_CASE(CreateFaceBadUri, AuthorizedCommandFixture<FaceFixture>)
 {
   ndn::nfd::FaceManagementOptions options;
   options.setUri("tcp:/127.0.0.1");
@@ -823,16 +914,19 @@
   commandName.append("create");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   getFace()->onReceiveData +=
     bind(&FaceFixture::validateControlResponse, this, _1,
-         commandName, 400, "Malformed command");
+         command->getName(), 400, "Malformed command");
 
-  createFace(commandName, options);
+  createFace(command->getName(), options);
 
   BOOST_REQUIRE(didCallbackFire());
 }
 
-BOOST_FIXTURE_TEST_CASE(CreateFaceUnknownScheme, FaceFixture)
+BOOST_FIXTURE_TEST_CASE(CreateFaceUnknownScheme, AuthorizedCommandFixture<FaceFixture>)
 {
   ndn::nfd::FaceManagementOptions options;
   // this will be an unsupported protocol because no factories have been
@@ -845,16 +939,19 @@
   commandName.append("create");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   getFace()->onReceiveData +=
     bind(&FaceFixture::validateControlResponse, this, _1,
-         commandName, 501, "Unsupported protocol");
+         command->getName(), 501, "Unsupported protocol");
 
-  createFace(commandName, options);
+  createFace(command->getName(), options);
 
   BOOST_REQUIRE(didCallbackFire());
 }
 
-BOOST_FIXTURE_TEST_CASE(OnCreated, FaceFixture)
+BOOST_FIXTURE_TEST_CASE(OnCreated, AuthorizedCommandFixture<FaceFixture>)
 {
   ndn::nfd::FaceManagementOptions options;
   options.setUri("tcp://127.0.0.1");
@@ -865,6 +962,9 @@
   commandName.append("create");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   ndn::nfd::FaceManagementOptions resultOptions;
   resultOptions.setUri("tcp://127.0.0.1");
   resultOptions.setFaceId(-1);
@@ -873,17 +973,17 @@
 
   getFace()->onReceiveData +=
     bind(&FaceFixture::validateControlResponse, this, _1,
-         commandName, 200, "Success", encodedResultOptions);
+         command->getName(), 200, "Success", encodedResultOptions);
 
-  onCreated(commandName, options, make_shared<DummyFace>());
+  onCreated(command->getName(), options, make_shared<DummyFace>());
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_CHECK(TestFaceTableFixture::m_faceTable.didAddFire());
 }
 
-BOOST_FIXTURE_TEST_CASE(OnConnectFailed, FaceFixture)
+BOOST_FIXTURE_TEST_CASE(OnConnectFailed, AuthorizedCommandFixture<FaceFixture>)
 {
-    ndn::nfd::FaceManagementOptions options;
+  ndn::nfd::FaceManagementOptions options;
   options.setUri("tcp://127.0.0.1");
 
   Block encodedOptions(options.wireEncode());
@@ -892,18 +992,21 @@
   commandName.append("create");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   getFace()->onReceiveData +=
     bind(&FaceFixture::validateControlResponse, this, _1,
-         commandName, 400, "Failed to create face");
+         command->getName(), 400, "Failed to create face");
 
-  onConnectFailed(commandName, "unit-test-reason");
+  onConnectFailed(command->getName(), "unit-test-reason");
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_CHECK_EQUAL(TestFaceTableFixture::m_faceTable.didAddFire(), false);
 }
 
 
-BOOST_FIXTURE_TEST_CASE(DestroyFace, FaceFixture)
+BOOST_FIXTURE_TEST_CASE(DestroyFace, AuthorizedCommandFixture<FaceFixture>)
 {
   ndn::nfd::FaceManagementOptions options;
   options.setUri("tcp://127.0.0.1");
@@ -914,11 +1017,14 @@
   commandName.append("destroy");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   getFace()->onReceiveData +=
     bind(&FaceFixture::validateControlResponse, this, _1,
-         commandName, 200, "Success");
+         command->getName(), 200, "Success");
 
-  destroyFace(commandName, options);
+  destroyFace(command->getName(), options);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_CHECK(TestFaceTableFixture::m_faceTable.didRemoveFire());
diff --git a/tests/mgmt/fib-manager.cpp b/tests/mgmt/fib-manager.cpp
index 9905f8a..e6748be 100644
--- a/tests/mgmt/fib-manager.cpp
+++ b/tests/mgmt/fib-manager.cpp
@@ -11,6 +11,7 @@
 #include "mgmt/internal-face.hpp"
 #include "tests/face/dummy-face.hpp"
 
+#include "validation-common.hpp"
 #include "tests/test-common.hpp"
 
 namespace nfd {
@@ -22,10 +23,9 @@
 {
 public:
 
-  FibManagerFixture()
-    : m_callbackFired(false)
+  virtual
+  ~FibManagerFixture()
   {
-
   }
 
   shared_ptr<Face>
@@ -57,10 +57,10 @@
 
     control.wireDecode(controlRaw);
 
-    NFD_LOG_DEBUG("received control response"
-                  << " Name: " << response.getName()
-                  << " code: " << control.getCode()
-                  << " text: " << control.getText());
+    // NFD_LOG_DEBUG("received control response"
+    //               << " Name: " << response.getName()
+    //               << " code: " << control.getCode()
+    //               << " text: " << control.getText());
 
     BOOST_CHECK_EQUAL(response.getName(), expectedName);
     BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
@@ -114,12 +114,70 @@
     m_callbackFired = false;
   }
 
+  shared_ptr<InternalFace>
+  getInternalFace()
+  {
+    return m_face;
+  }
+
+  FibManager&
+  getFibManager()
+  {
+    return m_manager;
+  }
+
+  Fib&
+  getFib()
+  {
+    return m_fib;
+  }
+
+  void
+  addInterestRule(const std::string& regex,
+                  ndn::IdentityCertificate& certificate)
+  {
+    m_manager.addInterestRule(regex, certificate);
+  }
+
+protected:
+    FibManagerFixture()
+    : m_face(make_shared<InternalFace>())
+    , m_nameTree(1024)
+    , m_fib(m_nameTree)
+    , m_manager(boost::ref(m_fib),
+                bind(&FibManagerFixture::getFace, this, _1),
+                m_face)
+    , m_callbackFired(false)
+  {
+  }
+
 private:
+  shared_ptr<InternalFace> m_face;
+  NameTree m_nameTree;
+  Fib m_fib;
+  FibManager m_manager;
+
   std::vector<shared_ptr<Face> > m_faces;
   bool m_callbackFired;
 };
 
-BOOST_FIXTURE_TEST_SUITE(MgmtFibManager, FibManagerFixture)
+template <typename T> class AuthorizedCommandFixture:
+    public CommandFixture<T>
+{
+public:
+  AuthorizedCommandFixture()
+  {
+    const std::string regex = "^<localhost><nfd><fib>";
+    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+  }
+
+  virtual
+  ~AuthorizedCommandFixture()
+  {
+  }
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFibManager, AuthorizedCommandFixture<FibManagerFixture>)
 
 bool
 foundNextHop(FaceId id, uint32_t cost, const fib::NextHop& next)
@@ -165,12 +223,7 @@
 
 BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
 {
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                     face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   Interest command("/localhost/nfd/fib");
 
@@ -185,12 +238,7 @@
 
 BOOST_AUTO_TEST_CASE(MalformedCommmand)
 {
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                          face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   BOOST_REQUIRE(didCallbackFire() == false);
 
@@ -200,19 +248,14 @@
     bind(&FibManagerFixture::validateControlResponse, this, _1,
          command.getName(), 400, "Malformed command");
 
-  manager.onFibRequest(command);
+  getFibManager().onFibRequest(command);
 
   BOOST_REQUIRE(didCallbackFire());
 }
 
 BOOST_AUTO_TEST_CASE(UnsupportedVerb)
 {
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                          face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   FibManagementOptions options;
   options.setName("/hello");
@@ -225,12 +268,14 @@
   commandName.append("unsupported");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   face->onReceiveData +=
     bind(&FibManagerFixture::validateControlResponse, this, _1,
-         commandName, 501, "Unsupported command");
+         command->getName(), 501, "Unsupported command");
 
-  Interest command(commandName);
-  manager.onFibRequest(command);
+  getFibManager().onFibRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
 }
@@ -239,12 +284,7 @@
 {
   addFace(make_shared<DummyFace>());
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                     face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   FibManagementOptions options;
   options.setName("/hello");
@@ -257,29 +297,24 @@
   commandName.append("add-nexthop");
   commandName.append(encodedOptions);
 
-  face->onReceiveData +=
-    bind(&FibManagerFixture::validateControlResponse, this, _1,
-         commandName, 404, "Prefix not found");
-  /// \todo enable once sig checking implemented
-    // bind(&FibManagerFixture::validateControlResponse, this, _1, 401, "Signature required");
-
   Interest command(commandName);
-  manager.onFibRequest(command);
+
+  face->onReceiveData +=
+    bind(&FibManagerFixture::validateControlResponse,
+         this, _1, command.getName(), 401, "Signature required");
+
+
+  getFibManager().onFibRequest(command);
 
   BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!addedNextHopWithCost(fib, "/hello", 0, 101));
+  BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
 }
 
-BOOST_AUTO_TEST_CASE(UnauthorizedCommand)
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FibManagerFixture>)
 {
   addFace(make_shared<DummyFace>());
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                     face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   FibManagementOptions options;
   options.setName("/hello");
@@ -292,40 +327,37 @@
   commandName.append("add-nexthop");
   commandName.append(encodedOptions);
 
-  face->onReceiveData +=
-    bind(&FibManagerFixture::validateControlResponse, this, _1,
-         commandName, 404, "Prefix not found");
-  /// \todo enable once sig checking implemented
-    // bind(&FibManagerFixture::validateControlResponse, this, _1, 403, "Unauthorized command");
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
-  Interest command(commandName);
-  manager.onFibRequest(command);
+  face->onReceiveData +=
+    bind(&FibManagerFixture::validateControlResponse,
+         this, _1, command->getName(), 403, "Unauthorized command");
+
+  getFibManager().onFibRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!addedNextHopWithCost(fib, "/hello", 0, 101));
+  BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
 }
 
 BOOST_AUTO_TEST_CASE(BadOptionParse)
 {
   addFace(make_shared<DummyFace>());
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                     face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   Name commandName("/localhost/nfd/fib");
   commandName.append("add-nexthop");
   commandName.append("NotReallyOptions");
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   face->onReceiveData +=
     bind(&FibManagerFixture::validateControlResponse, this, _1,
-         commandName, 400, "Malformed command");
+         command->getName(), 400, "Malformed command");
 
-  Interest command(commandName);
-  manager.onFibRequest(command);
+  getFibManager().onFibRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
 }
@@ -334,12 +366,7 @@
 {
   addFace(make_shared<DummyFace>());
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                     face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   FibManagementOptions options;
   options.setName("/hello");
@@ -352,27 +379,24 @@
   commandName.append("add-nexthop");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   face->onReceiveData +=
     bind(&FibManagerFixture::validateControlResponse, this, _1,
-         commandName, 404, "Face not found");
+         command->getName(), 404, "Face not found");
 
-  Interest command(commandName);
-  manager.onFibRequest(command);
+  getFibManager().onFibRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(addedNextHopWithCost(fib, "/hello", 0, 101) == false);
+  BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101) == false);
 }
 
 BOOST_AUTO_TEST_CASE(TestImplicitFaceId)
 {
   addFace(make_shared<DummyFace>());
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                          face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   FibManagementOptions options;
   options.setName("/hello");
@@ -392,30 +416,27 @@
 
   Block encodedExpectedOptions(expectedOptions.wireEncode());
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  command->setIncomingFaceId(1);
+  generateCommand(*command);
+
   face->onReceiveData +=
     bind(&FibManagerFixture::validateControlResponse, this, _1,
-         commandName, 200, "Success", encodedExpectedOptions);
+         command->getName(), 200, "Success", encodedExpectedOptions);
 
-  fib.insert("/hello");
+  getFib().insert("/hello");
 
-  Interest command(commandName);
-  command.setIncomingFaceId(1);
-  manager.onFibRequest(command);
+  getFibManager().onFibRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(addedNextHopWithFace(fib, "/hello", 0, 101, getFace(1)));
+  BOOST_REQUIRE(addedNextHopWithFace(getFib(), "/hello", 0, 101, getFace(1)));
 }
 
 BOOST_AUTO_TEST_CASE(AddNextHopVerbInitialAdd)
 {
   addFace(make_shared<DummyFace>());
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                          face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   FibManagementOptions options;
   options.setName("/hello");
@@ -428,31 +449,27 @@
   commandName.append("add-nexthop");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   face->onReceiveData +=
     bind(&FibManagerFixture::validateControlResponse, this, _1,
-         commandName, 200, "Success", encodedOptions);
+         command->getName(), 200, "Success", encodedOptions);
 
-  fib.insert("/hello");
+  getFib().insert("/hello");
 
-  Interest command(commandName);
-  manager.onFibRequest(command);
+  getFibManager().onFibRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(addedNextHopWithCost(fib, "/hello", 0, 101));
+  BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101));
 }
 
 BOOST_AUTO_TEST_CASE(AddNextHopVerbAddToExisting)
 {
   addFace(make_shared<DummyFace>());
+  shared_ptr<InternalFace> face = getInternalFace();
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                          face);
-
-  fib.insert("/hello");
+  getFib().insert("/hello");
 
   for (int i = 1; i <= 2; i++)
     {
@@ -468,16 +485,18 @@
       commandName.append("add-nexthop");
       commandName.append(encodedOptions);
 
+      shared_ptr<Interest> command(make_shared<Interest>(commandName));
+      generateCommand(*command);
+
       face->onReceiveData +=
         bind(&FibManagerFixture::validateControlResponse, this, _1,
-             commandName, 200, "Success", encodedOptions);
+             command->getName(), 200, "Success", encodedOptions);
 
-      Interest command(commandName);
-      manager.onFibRequest(command);
+      getFibManager().onFibRequest(*command);
       BOOST_REQUIRE(didCallbackFire());
       resetCallbackFired();
 
-      shared_ptr<fib::Entry> entry = fib.findExactMatch("/hello");
+      shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
 
       if (static_cast<bool>(entry))
         {
@@ -499,16 +518,9 @@
 BOOST_AUTO_TEST_CASE(AddNextHopVerbUpdateFaceCost)
 {
   addFace(make_shared<DummyFace>());
+  shared_ptr<InternalFace> face = getInternalFace();
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace,
-                          this, _1),
-                          face);
-
-  fib.insert("/hello");
+  getFib().insert("/hello");
 
   FibManagementOptions options;
   options.setName("/hello");
@@ -523,12 +535,14 @@
     commandName.append("add-nexthop");
     commandName.append(encodedOptions);
 
+    shared_ptr<Interest> command(make_shared<Interest>(commandName));
+    generateCommand(*command);
+
     face->onReceiveData +=
       bind(&FibManagerFixture::validateControlResponse, this, _1,
-           commandName, 200, "Success", encodedOptions);
+           command->getName(), 200, "Success", encodedOptions);
 
-    Interest command(commandName);
-    manager.onFibRequest(command);
+    getFibManager().onFibRequest(*command);
 
     BOOST_REQUIRE(didCallbackFire());
   }
@@ -545,17 +559,19 @@
     commandName.append("add-nexthop");
     commandName.append(encodedOptions);
 
+    shared_ptr<Interest> command(make_shared<Interest>(commandName));
+    generateCommand(*command);
+
     face->onReceiveData +=
       bind(&FibManagerFixture::validateControlResponse, this, _1,
-           commandName, 200, "Success", encodedOptions);
+           command->getName(), 200, "Success", encodedOptions);
 
-    Interest command(commandName);
-    manager.onFibRequest(command);
+    getFibManager().onFibRequest(*command);
 
     BOOST_REQUIRE(didCallbackFire());
   }
 
-  shared_ptr<fib::Entry> entry = fib.findExactMatch("/hello");
+  shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
 
   // Add faces with cost == FaceID for the name /hello
   // This test assumes:
@@ -576,12 +592,7 @@
 
 BOOST_AUTO_TEST_CASE(Insert)
 {
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                     face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   {
     FibManagementOptions options;
@@ -593,17 +604,19 @@
     commandName.append("insert");
     commandName.append(encodedOptions);
 
+    shared_ptr<Interest> command(make_shared<Interest>(commandName));
+    generateCommand(*command);
+
     face->onReceiveData +=
       bind(&FibManagerFixture::validateControlResponse, this, _1,
-           commandName, 200, "Success", encodedOptions);
+           command->getName(), 200, "Success", encodedOptions);
 
-    Interest command(commandName);
-    manager.onFibRequest(command);
+    getFibManager().onFibRequest(*command);
   }
 
   BOOST_REQUIRE(didCallbackFire());
 
-  shared_ptr<fib::Entry> entry = fib.findExactMatch("/hello");
+  shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
   if (static_cast<bool>(entry))
     {
       const fib::NextHopList& hops = entry->getNextHops();
@@ -611,6 +624,7 @@
     }
 
   resetCallbackFired();
+  face->onReceiveData.clear();
 
   {
     FibManagementOptions options;
@@ -622,17 +636,19 @@
     commandName.append("insert");
     commandName.append(encodedOptions);
 
+    shared_ptr<Interest> command(make_shared<Interest>(commandName));
+    generateCommand(*command);
+
     face->onReceiveData +=
       bind(&FibManagerFixture::validateControlResponse, this, _1,
-           commandName, 200, "Success", encodedOptions);
+           command->getName(), 200, "Success", encodedOptions);
 
-    Interest command(commandName);
-    manager.onFibRequest(command);
+    getFibManager().onFibRequest(*command);
   }
 
   BOOST_REQUIRE(didCallbackFire());
 
-  entry = fib.findExactMatch("/hello");
+  entry = getFib().findExactMatch("/hello");
   if (static_cast<bool>(entry))
     {
       const fib::NextHopList& hops = entry->getNextHops();
@@ -642,7 +658,7 @@
 }
 
 void
-testRemove(FibManagerFixture* fixture,
+testRemove(CommandFixture<FibManagerFixture>* fixture,
            FibManager& manager,
            Fib& fib,
            shared_ptr<Face> face,
@@ -657,12 +673,14 @@
   commandName.append("delete");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  fixture->generateCommand(*command);
+
   face->onReceiveData +=
     bind(&FibManagerFixture::validateControlResponse, fixture, _1,
-         commandName, 200, "Success", encodedOptions);
+         command->getName(), 200, "Success", encodedOptions);
 
-  Interest command(commandName);
-  manager.onFibRequest(command);
+  manager.onFibRequest(*command);
 
   BOOST_REQUIRE(fixture->didCallbackFire());
 
@@ -675,12 +693,9 @@
 
 BOOST_AUTO_TEST_CASE(Delete)
 {
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                     face);
+  shared_ptr<InternalFace> face = getInternalFace();
+  FibManager& manager = getFibManager();
+  Fib& fib = getFib();
 
   fib.insert("/a");
   fib.insert("/a/b");
@@ -730,7 +745,7 @@
 }
 
 void
-testRemoveNextHop(FibManagerFixture* fixture,
+testRemoveNextHop(CommandFixture<FibManagerFixture>* fixture,
                   FibManager& manager,
                   Fib& fib,
                   shared_ptr<Face> face,
@@ -747,12 +762,14 @@
   commandName.append("remove-nexthop");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  fixture->generateCommand(*command);
+
   face->onReceiveData +=
     bind(&FibManagerFixture::validateControlResponse, fixture, _1,
-         commandName, 200, "Success", encodedOptions);
+         command->getName(), 200, "Success", encodedOptions);
 
-  Interest command(commandName);
-  manager.onFibRequest(command);
+  manager.onFibRequest(*command);
 
   BOOST_REQUIRE(fixture->didCallbackFire());
 
@@ -770,12 +787,9 @@
   addFace(face2);
   addFace(face3);
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                          face);
+  shared_ptr<InternalFace> face = getInternalFace();
+  FibManager& manager = getFibManager();
+  Fib& fib = getFib();
 
   shared_ptr<fib::Entry> entry = fib.insert("/hello").first;
 
@@ -792,7 +806,7 @@
   testRemoveNextHop(this, manager, fib, face, "/hello", 1);
   BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 1, 101));
 
-  if (!static_cast<bool>(fib.findExactMatch("/hello")))
+  if (!static_cast<bool>(getFib().findExactMatch("/hello")))
     {
       BOOST_FAIL("removed entry after removing all next hops");
     }
@@ -801,12 +815,7 @@
 
 BOOST_AUTO_TEST_CASE(RemoveNoFace)
 {
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                          face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   FibManagementOptions options;
   options.setName("/hello");
@@ -818,12 +827,14 @@
   commandName.append("remove-nexthop");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   face->onReceiveData +=
     bind(&FibManagerFixture::validateControlResponse, this, _1,
-         commandName, 404, "Face not found");
+         command->getName(), 404, "Face not found");
 
-  Interest command(commandName);
-  manager.onFibRequest(command);
+  getFibManager().onFibRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
 }
@@ -832,12 +843,7 @@
 {
   addFace(make_shared<DummyFace>());
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-  NameTree nameTree(1024);
-  Fib fib(nameTree);
-  FibManager manager(fib,
-                     bind(&FibManagerFixture::getFace, this, _1),
-                     face);
+  shared_ptr<InternalFace> face = getInternalFace();
 
   FibManagementOptions options;
   options.setName("/hello");
@@ -849,12 +855,14 @@
   commandName.append("remove-nexthop");
   commandName.append(encodedOptions);
 
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
   face->onReceiveData +=
     bind(&FibManagerFixture::validateControlResponse, this, _1,
-         commandName, 404, "Prefix not found");
+         command->getName(), 404, "Prefix not found");
 
-  Interest command(commandName);
-  manager.onFibRequest(command);
+  getFibManager().onFibRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
 }
diff --git a/tests/mgmt/local-control-header-manager.cpp b/tests/mgmt/local-control-header-manager.cpp
index 43a649d..168f43f 100644
--- a/tests/mgmt/local-control-header-manager.cpp
+++ b/tests/mgmt/local-control-header-manager.cpp
@@ -9,6 +9,7 @@
 #include "tests/face/dummy-face.hpp"
 
 #include "tests/test-common.hpp"
+#include "validation-common.hpp"
 
 namespace nfd {
 namespace tests {
@@ -19,12 +20,6 @@
 {
 public:
 
-  LocalControlHeaderManagerFixture()
-    : m_callbackFired(false)
-  {
-
-  }
-
   shared_ptr<Face>
   getFace(FaceId id)
   {
@@ -42,6 +37,25 @@
     m_faces.push_back(face);
   }
 
+  shared_ptr<InternalFace>
+  getInternalFace()
+  {
+    return m_face;
+  }
+
+  LocalControlHeaderManager&
+  getManager()
+  {
+    return m_manager;
+  }
+
+  void
+  addInterestRule(const std::string& regex,
+                  ndn::IdentityCertificate& certificate)
+  {
+    m_manager.addInterestRule(regex, certificate);
+  }
+
   void
   validateControlResponse(const Data& response,
                           const Name& expectedName,
@@ -82,49 +96,76 @@
     m_callbackFired = false;
   }
 
+protected:
+  LocalControlHeaderManagerFixture()
+    : m_face(make_shared<InternalFace>()),
+      m_manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
+                m_face),
+      m_callbackFired(false)
+  {
+  }
+
 private:
+  shared_ptr<InternalFace> m_face;
+  LocalControlHeaderManager m_manager;
   std::vector<shared_ptr<Face> > m_faces;
   bool m_callbackFired;
 };
 
-BOOST_FIXTURE_TEST_SUITE(MgmtLocalControlHeaderManager, LocalControlHeaderManagerFixture)
+template <typename T> class AuthorizedCommandFixture:
+    public CommandFixture<T>
+{
+public:
+  AuthorizedCommandFixture()
+  {
+    const std::string regex = "^<localhost><nfd><control-header>";
+    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+  }
+
+  virtual
+  ~AuthorizedCommandFixture()
+  {
+  }
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtLocalControlHeaderManager,
+                         AuthorizedCommandFixture<LocalControlHeaderManagerFixture>)
 
 BOOST_AUTO_TEST_CASE(InFaceId)
 {
   shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
   addFace(dummy);
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-
-  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
-                                        face);
-
   Name enable("/localhost/nfd/control-header/in-faceid/enable");
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(1);
 
-  face->onReceiveData +=
+  generateCommand(*enableCommand);
+
+  getInternalFace()->onReceiveData +=
     bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
-         enable, 200, "Success");
+         enableCommand->getName(), 200, "Success");
 
-  Interest enableCommand(enable);
-  enableCommand.setIncomingFaceId(1);
-  manager.onLocalControlHeaderRequest(enableCommand);
+  getManager().onLocalControlHeaderRequest(*enableCommand);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
 
-  face->onReceiveData.clear();
+  getInternalFace()->onReceiveData.clear();
   resetCallbackFired();
 
   Name disable("/localhost/nfd/control-header/in-faceid/disable");
+  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+  disableCommand->setIncomingFaceId(1);
 
-  face->onReceiveData +=
+  generateCommand(*disableCommand);
+
+  getInternalFace()->onReceiveData +=
     bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
-         disable, 200, "Success");
+         disableCommand->getName(), 200, "Success");
 
-  Interest disableCommand(disable);
-  disableCommand.setIncomingFaceId(1);
-  manager.onLocalControlHeaderRequest(disableCommand);
+  getManager().onLocalControlHeaderRequest(*disableCommand);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
@@ -136,38 +177,36 @@
   shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
   addFace(dummy);
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-
-  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
-                                        face);
-
   Name enable("/localhost/nfd/control-header/nexthop-faceid/enable");
 
-  face->onReceiveData +=
-    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
-         enable, 200, "Success");
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(1);
+  generateCommand(*enableCommand);
 
-  Interest enableCommand(enable);
-  enableCommand.setIncomingFaceId(1);
-  manager.onLocalControlHeaderRequest(enableCommand);
+  getInternalFace()->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         enableCommand->getName(), 200, "Success");
+
+  getManager().onLocalControlHeaderRequest(*enableCommand);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
 
-
-  face->onReceiveData.clear();
+  getInternalFace()->onReceiveData.clear();
   resetCallbackFired();
 
   Name disable("/localhost/nfd/control-header/nexthop-faceid/disable");
+  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+  disableCommand->setIncomingFaceId(1);
 
-  face->onReceiveData +=
+  generateCommand(*disableCommand);
+
+  getInternalFace()->onReceiveData +=
     bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
-         disable, 200, "Success");
+         disableCommand->getName(), 200, "Success");
 
-  Interest disableCommand(disable);
-  disableCommand.setIncomingFaceId(1);
-  manager.onLocalControlHeaderRequest(disableCommand);
+  getManager().onLocalControlHeaderRequest(*disableCommand);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
@@ -179,20 +218,15 @@
   shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
   addFace(dummy);
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-
-  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
-                                        face);
-
   Name commandName("/localhost/nfd/control-header");
+  Interest command(commandName);
+  command.setIncomingFaceId(1);
 
-  face->onReceiveData +=
+  getInternalFace()->onReceiveData +=
     bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
          commandName, 400, "Malformed command");
 
-  Interest command(commandName);
-  command.setIncomingFaceId(1);
-  manager.onLocalControlHeaderRequest(command);
+  getManager().onLocalControlHeaderRequest(command);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
@@ -204,20 +238,15 @@
   shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
   addFace(dummy);
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-
-  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
-                                        face);
-
   Name commandName("/localhost/nfd/control-header/in-faceid");
 
-  face->onReceiveData +=
+  getInternalFace()->onReceiveData +=
     bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
          commandName, 400, "Malformed command");
 
   Interest command(commandName);
   command.setIncomingFaceId(1);
-  manager.onLocalControlHeaderRequest(command);
+  getManager().onLocalControlHeaderRequest(command);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
@@ -229,20 +258,17 @@
   shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
   addFace(dummy);
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-
-  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
-                                        face);
-
   Name commandName("/localhost/nfd/control-header/madeup/moremadeup");
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  command->setIncomingFaceId(1);
 
-  face->onReceiveData +=
+  generateCommand(*command);
+
+  getInternalFace()->onReceiveData +=
     bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
-         commandName, 501, "Unsupported");
+         command->getName(), 501, "Unsupported");
 
-  Interest command(commandName);
-  command.setIncomingFaceId(1);
-  manager.onLocalControlHeaderRequest(command);
+  getManager().onLocalControlHeaderRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
@@ -254,20 +280,18 @@
   shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
   addFace(dummy);
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-
-  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
-                                        face);
-
   Name commandName("/localhost/nfd/control-header/in-faceid/madeup");
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  command->setIncomingFaceId(1);
 
-  face->onReceiveData +=
+
+  generateCommand(*command);
+
+  getInternalFace()->onReceiveData +=
     bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
-         commandName, 501, "Unsupported");
+         command->getName(), 501, "Unsupported");
 
-  Interest command(commandName);
-  command.setIncomingFaceId(1);
-  manager.onLocalControlHeaderRequest(command);
+  getManager().onLocalControlHeaderRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
@@ -279,26 +303,45 @@
   shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
   addFace(dummy);
 
-  shared_ptr<InternalFace> face(make_shared<InternalFace>());
-
-  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
-                                        face);
-
   Name commandName("/localhost/nfd/control-header/nexthop-faceid/madeup");
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  command->setIncomingFaceId(1);
 
-  face->onReceiveData +=
+  generateCommand(*command);
+
+  getInternalFace()->onReceiveData +=
     bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
-         commandName, 501, "Unsupported");
+         command->getName(), 501, "Unsupported");
 
-  Interest command(commandName);
-  command.setIncomingFaceId(1);
-  manager.onLocalControlHeaderRequest(command);
+  getManager().onLocalControlHeaderRequest(*command);
 
   BOOST_REQUIRE(didCallbackFire());
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
 }
 
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand,
+                        UnauthorizedCommandFixture<LocalControlHeaderManagerFixture>)
+{
+  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+  addFace(dummy);
+
+  Name enable("/localhost/nfd/control-header/in-faceid/enable");
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(1);
+
+  generateCommand(*enableCommand);
+
+  getInternalFace()->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         enableCommand->getName(), 403, "Unauthorized command");
+
+  getManager().onLocalControlHeaderRequest(*enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/mgmt/malformedkey.pub b/tests/mgmt/malformedkey.pub
new file mode 100644
index 0000000..38b2fbb
--- /dev/null
+++ b/tests/mgmt/malformedkey.pub
@@ -0,0 +1 @@
+definitely not a key
\ No newline at end of file
diff --git a/tests/mgmt/manager-base.cpp b/tests/mgmt/manager-base.cpp
index f6ddcb7..fb10973 100644
--- a/tests/mgmt/manager-base.cpp
+++ b/tests/mgmt/manager-base.cpp
@@ -20,7 +20,7 @@
 public:
 
   ManagerBaseTest()
-    : ManagerBase(make_shared<InternalFace>()),
+    : ManagerBase(make_shared<InternalFace>(), "TEST-PRIVILEGE"),
       m_callbackFired(false)
   {
 
diff --git a/tests/mgmt/strategy-choice-manager.cpp b/tests/mgmt/strategy-choice-manager.cpp
index e2285c2..dd3291b 100644
--- a/tests/mgmt/strategy-choice-manager.cpp
+++ b/tests/mgmt/strategy-choice-manager.cpp
@@ -15,6 +15,7 @@
 
 
 #include "tests/test-common.hpp"
+#include "validation-common.hpp"
 
 namespace nfd {
 namespace tests {
@@ -90,6 +91,12 @@
 
   }
 
+  virtual
+  ~StrategyChoiceManagerFixture()
+  {
+
+  }
+
   void
   validateControlResponseCommon(const Data& response,
                                 const Name& expectedName,
@@ -177,6 +184,13 @@
     return m_strategyChoice;
   }
 
+  void
+  addInterestRule(const std::string& regex,
+                  ndn::IdentityCertificate& certificate)
+  {
+    m_manager.addInterestRule(regex, certificate);
+  }
+
 protected:
   Forwarder m_forwarder;
   NameTree m_nameTree;
@@ -203,9 +217,26 @@
   }
 };
 
-BOOST_FIXTURE_TEST_SUITE(MgmtStrategyChoiceManager, AllStrategiesFixture)
+template <typename T> class AuthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+  AuthorizedCommandFixture()
+  {
+    const std::string regex = "^<localhost><nfd><strategy-choice>";
+    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+  }
 
-BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
+  virtual
+  ~AuthorizedCommandFixture()
+  {
+
+  }
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtStrategyChoiceManager,
+                         AuthorizedCommandFixture<AllStrategiesFixture>)
+
+BOOST_FIXTURE_TEST_CASE(TestFireInterestFilter, AllStrategiesFixture)
 {
   shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
 
@@ -218,7 +249,7 @@
   BOOST_REQUIRE(didCallbackFire());
 }
 
-BOOST_AUTO_TEST_CASE(MalformedCommmand)
+BOOST_FIXTURE_TEST_CASE(MalformedCommmand, AllStrategiesFixture)
 {
   shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
 
@@ -231,6 +262,54 @@
   BOOST_REQUIRE(didCallbackFire());
 }
 
+BOOST_FIXTURE_TEST_CASE(UnsignedCommand, AllStrategiesFixture)
+{
+  ndn::nfd::FibManagementOptions options;
+  options.setName("/test");
+  options.setStrategy("/localhost/nfd/strategy/best-route");
+
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedOptions);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 401, "Signature required");
+
+  getManager().onStrategyChoiceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand,
+                        UnauthorizedCommandFixture<StrategyChoiceManagerFixture>)
+{
+  ndn::nfd::FibManagementOptions options;
+  options.setName("/test");
+  options.setStrategy("/localhost/nfd/strategy/best-route");
+
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedOptions);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 403, "Unauthorized command");
+
+  getManager().onStrategyChoiceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
 BOOST_AUTO_TEST_CASE(UnsupportedVerb)
 {
   ndn::nfd::FibManagementOptions options;
@@ -243,6 +322,7 @@
   commandName.append(encodedOptions);
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
   getFace()->onReceiveData +=
     bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
@@ -260,6 +340,7 @@
   commandName.append("NotReallyOptions");
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
   getFace()->onReceiveData +=
     bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
@@ -311,6 +392,7 @@
   commandName.append(encodedOptions);
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
   getFace()->onReceiveData +=
     bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
@@ -355,6 +437,7 @@
 //   commandName.append(encodedOptions);
 
 //   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+//   generateCommand(*command);
 
 //   getFace()->onReceiveData +=
 //     bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
@@ -383,6 +466,7 @@
   commandName.append(encodedOptions);
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
   getFace()->onReceiveData +=
     bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
@@ -408,6 +492,7 @@
   commandName.append(encodedOptions);
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
 
   getFace()->onReceiveData +=
     bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
diff --git a/tests/mgmt/validation-common.hpp b/tests/mgmt/validation-common.hpp
new file mode 100644
index 0000000..8e16103
--- /dev/null
+++ b/tests/mgmt/validation-common.hpp
@@ -0,0 +1,88 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef VALIDATION_COMMON_HPP
+#define VALIDATION_COMMON_HPP
+
+#include <ndn-cpp-dev/util/command-interest-generator.hpp>
+
+namespace nfd {
+namespace tests {
+
+// class ValidatedManagementFixture
+// {
+// public:
+//   ValidatedManagementFixture()
+//     : m_validator(make_shared<ndn::CommandInterestValidator>())
+//   {
+//   }
+
+//   virtual
+//   ~ValidatedManagementFixture()
+//   {
+//   }
+
+// protected:
+//   shared_ptr<ndn::CommandInterestValidator> m_validator;
+// };
+
+
+template<typename T>
+class CommandFixture : public T
+{
+public:
+  virtual
+  ~CommandFixture()
+  {
+    m_keys.deleteIdentity(m_identityName);
+
+  }
+
+  void
+  generateCommand(Interest& interest)
+  {
+    m_generator.generateWithIdentity(interest, m_identityName);
+  }
+
+  const Name&
+  getIdentityName() const
+  {
+    return m_identityName;
+  }
+
+protected:
+  CommandFixture()
+    : m_identityName("/unit-test/CommandFixture/id"),
+      m_certificate(m_keys.getCertificate(m_keys.createIdentity(m_identityName)))
+  {
+
+  }
+
+protected:
+  ndn::KeyChain m_keys;
+  const Name m_identityName;
+  shared_ptr<ndn::IdentityCertificate> m_certificate;
+  ndn::CommandInterestGenerator m_generator;
+};
+
+template <typename T>
+class UnauthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+  UnauthorizedCommandFixture()
+  {
+  }
+
+  virtual
+  ~UnauthorizedCommandFixture()
+  {
+  }
+};
+
+} //namespace tests
+} // namespace nfd
+
+#endif // VALIDATION_COMMON_HPP
diff --git a/wscript b/wscript
index 7d654ab..dbdff3e 100644
--- a/wscript
+++ b/wscript
@@ -5,7 +5,7 @@
 import os
 
 def options(opt):
-    opt.load('compiler_cxx')
+    opt.load('compiler_cxx gnu_dirs')
     opt.load('boost doxygen coverage unix-socket', tooldir=['.waf-tools'])
 
     nfdopt = opt.add_option_group('NFD Options')
@@ -15,7 +15,7 @@
                       help='''Use NDN-CPP library from the specified path''')
     
 def configure(conf):
-    conf.load("compiler_cxx boost")
+    conf.load("compiler_cxx boost gnu_dirs")
     try:
         conf.load("doxygen")
     except:
@@ -67,6 +67,8 @@
 
     conf.load('coverage')
 
+    conf.define('DEFAULT_CONFIG_FILE', '%s/nfd/nfd.conf' % conf.env['SYSCONFDIR'])
+
     conf.write_config_header('daemon/config.hpp')
 
 def build(bld):