2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 21:45:37 +00:00

[#3609] Checkpoint: error UT to add

This commit is contained in:
Francis Dupont
2024-11-13 17:03:07 +01:00
parent db1b43918c
commit 2c12db5654
8 changed files with 279 additions and 1 deletions

View 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_));

View 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_;

View 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) {

View 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 \

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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));
}
}