]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3609] Checkpoint: error UT to add
authorFrancis Dupont <fdupont@isc.org>
Wed, 13 Nov 2024 16:03:07 +0000 (17:03 +0100)
committerFrancis Dupont <fdupont@isc.org>
Fri, 22 Nov 2024 08:55:31 +0000 (09:55 +0100)
src/bin/agent/ca_cfg_mgr.cc
src/bin/agent/ca_cfg_mgr.h
src/bin/agent/simple_parser.cc
src/lib/http/Makefile.am
src/lib/http/cfg_http_header.cc [new file with mode: 0644]
src/lib/http/cfg_http_header.h [new file with mode: 0644]
src/lib/http/tests/Makefile.am
src/lib/http/tests/cfg_http_header_unittests.cc [new file with mode: 0644]

index f1d1047917366e03fb9c2af5a11b97c63b6cb484..4cb9da54801dd39361a4fb849fc6e4a7b6de45c8 100644 (file)
@@ -23,13 +23,14 @@ namespace isc {
 namespace agent {
 
 CtrlAgentCfgContext::CtrlAgentCfgContext()
-    : http_host_(""), http_port_(0),
+    : http_host_(""), http_port_(0), http_headers_(),
       trust_anchor_(""), cert_file_(""), key_file_(""), cert_required_(true) {
 }
 
 CtrlAgentCfgContext::CtrlAgentCfgContext(const CtrlAgentCfgContext& orig)
     : ConfigBase(), ctrl_sockets_(orig.ctrl_sockets_),
       http_host_(orig.http_host_), http_port_(orig.http_port_),
+      http_headers_(orig.http_headers_),
       trust_anchor_(orig.trust_anchor_), cert_file_(orig.cert_file_),
       key_file_(orig.key_file_), cert_required_(orig.cert_required_),
       hooks_config_(orig.hooks_config_), auth_config_(orig.auth_config_) {
@@ -186,6 +187,10 @@ CtrlAgentCfgContext::toElement() const {
     ca->set("http-host", Element::create(http_host_));
     // Set http-port
     ca->set("http-port", Element::create(static_cast<int64_t>(http_port_)));
+    // Set http-headers
+    if (!http_headers_.empty()) {
+        ca->set("http-headers", toElement(http_headers_));
+    }
     // Set TLS setup when enabled
     if (!trust_anchor_.empty()) {
         ca->set("trust-anchor", Element::create(trust_anchor_));
index dbd841e15c02e2a1cf57727d0f1d8c4b5f7db16a..60f01d6699755605ff55681c9172cb433ba9610b 100644 (file)
@@ -10,6 +10,7 @@
 #include <cc/data.h>
 #include <hooks/hooks_config.h>
 #include <http/auth_config.h>
+#include <http/cfg_http_header.h>
 #include <process/d_cfg_mgr.h>
 #include <boost/pointer_cast.hpp>
 #include <map>
@@ -99,6 +100,20 @@ public:
         return (http_port_);
     }
 
+    /// @brief Sets http-headers parameter
+    ///
+    /// @param headers Collection of config HTTP headers.
+    void setHttpHeaders(const isc::http::CfgHttpHeaders& headers) {
+       http_headers_ = headers;
+    }
+
+    /// @brief Returns http-headers parameter
+    ///
+    /// @return Collection of config HTTP headers.
+    const isc::http::CfgHttpHeaders& getHttpHeaders() const {
+       return (http_headers_);
+    }
+
     /// @brief Sets HTTP authentication configuration.
     ///
     /// @note Only the basic HTTP authentication is supported.
@@ -225,6 +240,9 @@ private:
     /// TCP port the CA should listen on.
     uint16_t http_port_;
 
+    /// Config HTTP headers.
+    isc::http::CfgHttpHeaders http_headers_;
+
     /// Trust anchor aka Certificate Authority (can be a file name or
     /// a directory path).
     std::string trust_anchor_;
index 7a6a76372cd9d8e14439ab45e8c2625c02f9610c..e6e86df21ab014b2daa4ce96ff48abcc7497d246 100644 (file)
@@ -162,6 +162,13 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
         ctx->setAuthConfig(auth);
     }
 
+    // HTTP headers are fifth.
+    ConstElementPtr headers_config = config->get("http-headers");
+    if (headers_config) {
+       using namespace isc::http;
+       ctx->setHttpHeaders(parseCfgHttpHeaders(headers_config));
+    }
+
     // User context can be done at anytime.
     ConstElementPtr user_context = config->get("user-context");
     if (user_context) {
index 5935b9e8db6de76cf8dab356a9ac9d5affcb4f26..8650dd70c79cc83215b5341f43c04cba5e726687 100644 (file)
@@ -43,6 +43,7 @@ libkea_http_la_SOURCES += auth_log.cc auth_log.h
 libkea_http_la_SOURCES += auth_messages.cc auth_messages.h
 libkea_http_la_SOURCES += basic_auth_config.cc basic_auth_config.h
 libkea_http_la_SOURCES += basic_auth.cc basic_auth.h
+libkea_http_la_SOURCES += cfg_http_header.h cfg_http_header.cc
 
 libkea_http_la_CXXFLAGS = $(AM_CXXFLAGS)
 libkea_http_la_CPPFLAGS = $(AM_CPPFLAGS)
@@ -103,6 +104,7 @@ libkea_http_include_HEADERS = \
        auth_messages.h \
        basic_auth.h \
        basic_auth_config.h \
+       cfg_http_header.h \
        client.h \
        connection.h \
        connection_pool.h \
diff --git a/src/lib/http/cfg_http_header.cc b/src/lib/http/cfg_http_header.cc
new file mode 100644 (file)
index 0000000..7c7edf3
--- /dev/null
@@ -0,0 +1,95 @@
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <cc/simple_parser.h>
+#include <http/cfg_http_header.h>
+
+using namespace isc;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace std;
+
+namespace isc {
+namespace http {
+
+ElementPtr
+CfgHttpHeader::toElement() const {
+    ElementPtr map = isc::data::Element::createMap();
+    contextToElement(map);
+    map->set("name", Element::create(name_));
+    map->set("value", Element::create(value_));
+    return (map);
+}
+
+ElementPtr
+toElement(const CfgHttpHeaders& headers) {
+    ElementPtr list = Element::createList();
+    for (auto const& header : headers) {
+        list->add(header.toElement());
+    }
+    return (list);
+}
+
+namespace {
+
+const SimpleKeywords HTTP_HEADER_KEYWORDS = {
+    { "name", Element::string },
+    { "value", Element::string },
+    { "user-context", Element::map }
+};
+
+const SimpleRequiredKeywords HTTP_HEADER_REQUIRED = { "name", "value" };
+
+CfgHttpHeader
+parseCfgHttpHeader(const ConstElementPtr& config) {
+    if (!config) {
+        // Should not happen.
+        isc_throw(DhcpConfigError, "null 'http-headers' item");
+    }
+    if (config->getType() != Element::map) {
+        isc_throw(DhcpConfigError, "invalid type specified for 'http-headers' "
+                  "item (" << config->getPosition() << ")");
+    }
+    SimpleParser::checkKeywords(HTTP_HEADER_KEYWORDS, config);
+    SimpleParser::checkRequired(HTTP_HEADER_REQUIRED, config);
+    string name = config->get("name")->stringValue();
+    if (name.empty()) {
+        isc_throw(DhcpConfigError, "empty 'name' ("
+                  << config->get("name")->getPosition() << ")");
+    }
+    string value = config->get("value")->stringValue();
+    if (value.empty()) {
+        isc_throw(DhcpConfigError, "empty 'value' ("
+                  << config->get("value")->getPosition() << ")");
+    }
+    CfgHttpHeader header(name, value);
+    ConstElementPtr user_context = config->get("user-context");
+    if (user_context) {
+        header.setContext(user_context);
+    }
+    return (header);
+}
+
+}
+
+CfgHttpHeaders
+parseCfgHttpHeaders(const ConstElementPtr& config) {
+    CfgHttpHeaders headers;
+    if (!config) {
+        return (headers);
+    }
+    if (config->getType() != Element::list) {
+        isc_throw(DhcpConfigError, "invalid type specified for parameter "
+                  "'http-headers' (" << config->getPosition() << ")");
+    }
+    for (auto const& item : config->listValue()) {
+        headers.push_back(parseCfgHttpHeader(item));
+    }
+    return (headers);
+}
+
+} // namespace http
+} // namespace isc
diff --git a/src/lib/http/cfg_http_header.h b/src/lib/http/cfg_http_header.h
new file mode 100644 (file)
index 0000000..8adb34f
--- /dev/null
@@ -0,0 +1,71 @@
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef CFG_HTTP_HEADER_H
+#define CFG_HTTP_HEADER_H
+
+#include <cc/cfg_to_element.h>
+#include <cc/data.h>
+#include <cc/user_context.h>
+#include <http/request.h>
+#include <http/response.h>
+
+namespace isc {
+namespace http {
+
+/// @brief Config HTTP header.
+class CfgHttpHeader : public isc::data::UserContext, public isc::data::CfgToElement {
+public:
+    std::string name_;
+    std::string value_;
+
+    /// @brief Constructor.
+    ///
+    /// @param name Header name.
+    /// @param value Header value.
+    CfgHttpHeader(const std::string& name, const std::string& value)
+        : name_(name), value_(value) {
+    }
+
+    /// @brief Unparses config HTTP header.
+    ///
+    /// @return A pointer to unparsed header configuration.
+    virtual isc::data::ElementPtr toElement() const;
+};
+
+/// @brief Collection of config HTTP headers.
+typedef std::vector<CfgHttpHeader> CfgHttpHeaders;
+
+/// @brief Copy config HTTP headers to message.
+///
+/// @tparam HTTP_MSG Either HttpRequest or HttpResponse.
+/// @param headers Config HTTP headers.
+/// @param message HTTP_MSG target object.
+template<typename HTTP_MSG>
+void copyHttpHeaders(const CfgHttpHeaders& headers, const HTTP_MSG& message) {
+    for (auto const& header : headers) {
+        message.context()->headers_.
+            push_back(HttpHeaderContext(header.name_, header.value_));
+    }
+}
+
+/// @brief Unparse config HTTP headers.
+///
+/// @param headers Config HTTP headers.
+/// @return A pointer to unparsed headers configuration.
+isc::data::ElementPtr toElement(const CfgHttpHeaders& headers);
+
+/// @brief Parse config HTTP headers.
+///
+/// @param config Element holding the HTTP headers configuration.
+/// @return The HTTP headers.
+/// @throw DhcpConfigError when the configuration is invalid.
+CfgHttpHeaders parseCfgHttpHeaders(const isc::data::ConstElementPtr& config);
+
+} // namespace http
+} // namespace isc
+
+#endif
index b2f629d6717891dbdc3e8c3056ec454e0eb2a782..00b59a939d78170518960c66852d82f5f336f934 100644 (file)
@@ -29,6 +29,7 @@ TESTS += libhttp_unittests
 
 libhttp_unittests_SOURCES  = basic_auth_unittests.cc
 libhttp_unittests_SOURCES += basic_auth_config_unittests.cc
+libhttp_unittests_SOURCES += cfg_http_header_unittests.cc
 libhttp_unittests_SOURCES += connection_pool_unittests.cc
 libhttp_unittests_SOURCES += date_time_unittests.cc
 libhttp_unittests_SOURCES += http_header_unittests.cc
diff --git a/src/lib/http/tests/cfg_http_header_unittests.cc b/src/lib/http/tests/cfg_http_header_unittests.cc
new file mode 100644 (file)
index 0000000..b88cdd0
--- /dev/null
@@ -0,0 +1,79 @@
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <http/cfg_http_header.h>
+#include <gtest/gtest.h>
+
+using namespace isc::data;
+using namespace isc::http;
+using namespace std;
+
+namespace {
+
+// This test verifies copy to response.
+TEST(CfgHttpHeaderTest, copy) {
+    // Create a response.
+    HttpResponse response(HttpVersion(1, 0), HttpStatusCode::OK);
+    // Create a HSTS header.
+    CfgHttpHeader hsts("Strict-Transport-Security", "max-age=31536000");
+    // Create a random header.
+    CfgHttpHeader foobar("Foo", "bar");
+    // Add them to a collection.
+    CfgHttpHeaders headers;
+    headers.push_back(hsts);
+    headers.push_back(foobar);
+    // Copy headers to response.
+    EXPECT_NO_THROW(copyHttpHeaders(headers, response));
+
+    // Verify.
+    auto const& got = response.context()->headers_;
+    ASSERT_EQ(2, got.size());
+    EXPECT_EQ("Strict-Transport-Security", got[0].name_);
+    EXPECT_EQ("max-age=31536000", got[0].value_);
+    EXPECT_EQ("Foo", got[1].name_);
+    EXPECT_EQ("bar", got[1].value_);
+
+    // Unparse.
+    string expected = "[ ";
+    expected += "{ \"name\": \"Strict-Transport-Security\", ";
+    expected += "\"value\": \"max-age=31536000\" }, ";
+    expected += "{ \"name\": \"Foo\", \"value\": \"bar\" } ]";
+    EXPECT_EQ(expected, toElement(headers)->str());
+}
+
+// This test verifies parse and toElement behavior.
+TEST(CfgHttpHeaderTest, parse) {
+    // Config.
+    string config = "[\n"
+       " {\n"
+       " \"name\": \"Strict-Transport-Security\",\n"
+       " \"value\": \"max-age=31536000\",\n"
+       " \"user-context\": { \"comment\": \"HSTS header\" }\n"
+       " },{\n"
+       " \"name\": \"Foo\", \"value\": \"bar\"\n"
+       " }\n"
+       " ]\n";
+    ConstElementPtr json;
+    ASSERT_NO_THROW(json = Element::fromJSON(config));
+    CfgHttpHeaders headers;
+    ASSERT_NO_THROW(headers = parseCfgHttpHeaders(json));
+    ASSERT_EQ(2, headers.size());
+    EXPECT_EQ("Strict-Transport-Security", headers[0].name_);
+    EXPECT_EQ("max-age=31536000", headers[0].value_);
+    ConstElementPtr user_context = headers[0].getContext();
+    ASSERT_TRUE(user_context);
+    EXPECT_EQ("{ \"comment\": \"HSTS header\" }", user_context->str());
+    EXPECT_EQ("Foo", headers[1].name_);
+    EXPECT_EQ("bar", headers[1].value_);
+    EXPECT_FALSE(headers[1].getContext());
+    ConstElementPtr unparsed;
+    ASSERT_NO_THROW(unparsed = toElement(headers));
+    EXPECT_TRUE(json->equals(*unparsed));
+}
+
+}