mgmt, main: add support for authorized commands

refs: #1227

Change-Id: I907d1fa6e78775470c5376fcdfe898be4c311001
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
+