security: Add general interface to chain validation policies

Change-Id: I1a0d47aeb847455a2d7d6d48185f6edd9024d298
Refs: #3920
diff --git a/src/security/v2/validation-policy.cpp b/src/security/v2/validation-policy.cpp
new file mode 100644
index 0000000..6783f80
--- /dev/null
+++ b/src/security/v2/validation-policy.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "validation-policy.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+void
+ValidationPolicy::setInnerPolicy(unique_ptr<ValidationPolicy> innerPolicy)
+{
+  if (innerPolicy == nullptr) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Inner policy argument cannot be nullptr"));
+  }
+
+  if (m_validator != nullptr) {
+    innerPolicy->setValidator(*m_validator);
+  }
+
+  if (m_innerPolicy == nullptr) {
+    m_innerPolicy = std::move(innerPolicy);
+  }
+  else {
+    m_innerPolicy->setInnerPolicy(std::move(innerPolicy));
+  }
+}
+
+ValidationPolicy&
+ValidationPolicy::getInnerPolicy()
+{
+  return *m_innerPolicy;
+}
+
+void
+ValidationPolicy::setValidator(Validator& validator)
+{
+  m_validator = &validator;
+  if (m_innerPolicy != nullptr) {
+    m_innerPolicy->setValidator(validator);
+  }
+}
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/validation-policy.hpp b/src/security/v2/validation-policy.hpp
index 7d85803..66216c8 100644
--- a/src/security/v2/validation-policy.hpp
+++ b/src/security/v2/validation-policy.hpp
@@ -40,10 +40,41 @@
   using ValidationContinuation = std::function<void(const shared_ptr<CertificateRequest>& certRequest,
                                                     const shared_ptr<ValidationState>& state)>;
 
+  ValidationPolicy()
+    : m_validator(nullptr)
+  {
+  }
+
   virtual
   ~ValidationPolicy() = default;
 
   /**
+   * @brief Set inner policy
+   *
+   * Multiple assignments of the inner policy will create a "chain" of linked policies.
+   * The inner policy from the latest invocation of setInnerPolicy will be at the bottom
+   * of the policy list.
+   *
+   * For example, sequence of `this->setInnerPolicy(policy1)` and
+   * `this->setInnerPolicy(policy2)`, will result in `this->m_innerPolicy == policy1`,
+   * this->m_innerPolicy->m_innerPolicy == policy2', and
+   * `this->m_innerPolicy->m_innerPolicy->m_innerPolicy == nullptr`.
+   *
+   * @throw std::invalid_argument exception, if @p innerPolicy is nullptr.
+   */
+  void
+  setInnerPolicy(unique_ptr<ValidationPolicy> innerPolicy);
+
+  ValidationPolicy&
+  getInnerPolicy();
+
+  /**
+   * @brief Set validator to which the policy is associated
+   */
+  void
+  setValidator(Validator& validator);
+
+  /**
    * @brief Check @p data against the policy
    *
    * Depending on implementation of the policy, this check can be done synchronously or
@@ -101,6 +132,10 @@
   {
     checkPolicy(static_cast<const Data&>(certificate), state, continueValidation);
   }
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PROTECTED:
+  Validator* m_validator;
+  unique_ptr<ValidationPolicy> m_innerPolicy;
 };
 
 } // namespace v2
diff --git a/src/security/v2/validator.cpp b/src/security/v2/validator.cpp
index 6dc1f91..638d12d 100644
--- a/src/security/v2/validator.cpp
+++ b/src/security/v2/validator.cpp
@@ -41,11 +41,24 @@
 {
   BOOST_ASSERT(m_policy != nullptr);
   BOOST_ASSERT(m_certFetcher != nullptr);
+  m_policy->setValidator(*this);
   m_certFetcher->setCertificateStorage(*this);
 }
 
 Validator::~Validator() = default;
 
+ValidationPolicy&
+Validator::getPolicy()
+{
+  return *m_policy;
+}
+
+CertificateFetcher&
+Validator::getFetcher()
+{
+  return *m_certFetcher;
+}
+
 void
 Validator::setMaxDepth(size_t depth)
 {
diff --git a/src/security/v2/validator.hpp b/src/security/v2/validator.hpp
index f24499c..6dfe8a8 100644
--- a/src/security/v2/validator.hpp
+++ b/src/security/v2/validator.hpp
@@ -71,6 +71,12 @@
 
   ~Validator();
 
+  ValidationPolicy&
+  getPolicy();
+
+  CertificateFetcher&
+  getFetcher();
+
   /**
    * @brief Set the maximum depth of the certificate chain
    */
diff --git a/tests/unit-tests/security/v2/validator.t.cpp b/tests/unit-tests/security/v2/validator.t.cpp
index 73bd23d..42bcf2a 100644
--- a/tests/unit-tests/security/v2/validator.t.cpp
+++ b/tests/unit-tests/security/v2/validator.t.cpp
@@ -36,6 +36,19 @@
 BOOST_AUTO_TEST_SUITE(V2)
 BOOST_FIXTURE_TEST_SUITE(TestValidator, HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy>)
 
+BOOST_AUTO_TEST_CASE(ConstructorSetValidator)
+{
+  auto middlePolicy = make_unique<ValidationPolicySimpleHierarchy>();
+  auto innerPolicy = make_unique<ValidationPolicySimpleHierarchy>();
+
+  validator.getPolicy().setInnerPolicy(std::move(middlePolicy));
+  validator.getPolicy().setInnerPolicy(std::move(innerPolicy));
+
+  BOOST_CHECK(validator.getPolicy().m_validator != nullptr);
+  BOOST_CHECK(validator.getPolicy().getInnerPolicy().m_validator != nullptr);
+  BOOST_CHECK(validator.getPolicy().getInnerPolicy().getInnerPolicy().m_validator != nullptr);
+}
+
 BOOST_AUTO_TEST_CASE(Timeouts)
 {
   processInterest = nullptr; // no response for all interests