2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-03 15:35:17 +00:00

[#1304] Checkpoint before regen

This commit is contained in:
Francis Dupont
2020-09-13 22:03:20 +02:00
parent 940599e958
commit f907a08345
23 changed files with 642 additions and 378 deletions

View File

@@ -10,17 +10,22 @@
"http-host": "127.0.0.1", "http-host": "127.0.0.1",
"http-port": 8000, "http-port": 8000,
"basic-authentication-realm": "kea-control-agent",
// In basic HTTP authentication // In authentication
"basic-authentications": "authentication":
{
"comment": "basic HTTP authentication",
// In basic HTTP authentication clients
"clients":
[ [
{ {
"comment": "admin is authorized", "comment": "admin is authorized",
"user": "admin", "user": "admin",
"password": "1234" "password": "1234"
} }
], ]
},
// In control socket // In control socket
"control-sockets": "control-sockets":

View File

@@ -11,27 +11,35 @@
// Another mandatory parameter is the HTTP port. // Another mandatory parameter is the HTTP port.
"http-port": 8000, "http-port": 8000,
// Optional authentication.
"authentication":
{
// Required authentication type. The only supported value is
// basic for the basic HTTP authentication.
"type": "basic",
// An optional parameter is the basic HTTP authentication realm. // An optional parameter is the basic HTTP authentication realm.
// Its default is "kea-control-agent". // Its default is "kea-control-agent".
"basic-authentication-realm": "kea-control-agent", "realm": "kea-control-agent",
// This list specifies the user ids and passwords to use for // This list specifies the user ids and passwords to use for
// basic HTTP authentication. If empty or not present any client // basic HTTP authentication. If empty or not present any client
// is authorized. // is authorized.
"basic-authentications": "clients":
[ [
// This specifies an authorized client. // This specifies an authorized client.
{ {
"comment": "admin is authorized", "comment": "admin is authorized",
// The user id must not be empty or contain the ':' character. // The user id must not be empty or contain the ':'
// It is a mandatory parameter. // character. It is a mandatory parameter.
"user": "admin", "user": "admin",
// If password is not specified an empty password is used. // If password is not specified an empty password is used.
"password": "1234" "password": "1234"
} }
], ]
},
// This map specifies where control channel of each server is configured // This map specifies where control channel of each server is configured
// to listen on. See 'control-socket' object in the respective // to listen on. See 'control-socket' object in the respective

View File

@@ -50,7 +50,15 @@ The following example demonstrates the basic CA configuration.
"Control-agent": { "Control-agent": {
"http-host": "10.20.30.40", "http-host": "10.20.30.40",
"http-port": 8000, "http-port": 8000,
"basic-authentication-realm": "kea-control-agent", "authentication": {
"type": "basic",
"realm": "kea-control-agent",
"clients": [
{
"user": "admin",
"password": "1234"
} ]
},
"control-sockets": { "control-sockets": {
"dhcp4": { "dhcp4": {
@@ -69,12 +77,6 @@ The following example demonstrates the basic CA configuration.
}, },
}, },
"basic-authentications": [
{
"user": "admin",
"password": "1234"
} ],
"hooks-libraries": [ "hooks-libraries": [
{ {
"library": "/opt/local/control-agent-commands.so", "library": "/opt/local/control-agent-commands.so",
@@ -142,11 +144,15 @@ against not authorized uses of the control agent by local users. For the
protection against remote attackers HTTPS and reverse proxy of protection against remote attackers HTTPS and reverse proxy of
:ref:`agent-secure-connection` provide a stronger security. :ref:`agent-secure-connection` provide a stronger security.
The ``basic-authentication-realm`` is used for error message when The authentication is described in the ``authentication`` block
the basic HTTP authentication is mandatory but the client is not with the mandatory ``type`` parameter which selects the authentication.
Currently only the basic HTTP authentication (type basic) is supported.
The ``realm`` authentication parameter is used for error message when
the basic HTTP authentication is required but the client is not
authorized. authorized.
When the ``basic-authentications`` list is configured and not empty When the ``clients`` authentication list is configured and not empty
the basic HTTP authentication is required. Each element of the list the basic HTTP authentication is required. Each element of the list
specifies a user id and a password. The user id is mandatory, must specifies a user id and a password. The user id is mandatory, must
be not empty and must not contain the colon (:) character. The be not empty and must not contain the colon (:) character. The

View File

@@ -203,9 +203,10 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
\"user-context\" { \"user-context\" {
switch(driver.ctx_) { switch(driver.ctx_) {
case ParserContext::AGENT: case ParserContext::AGENT:
case ParserContext::AUTHENTICATION;
case ParserContext::CLIENTS:
case ParserContext::SERVER: case ParserContext::SERVER:
case ParserContext::LOGGERS: case ParserContext::LOGGERS:
case ParserContext::CLIENTS:
return AgentParser::make_USER_CONTEXT(driver.loc_); return AgentParser::make_USER_CONTEXT(driver.loc_);
default: default:
return AgentParser::make_STRING("user-context", driver.loc_); return AgentParser::make_STRING("user-context", driver.loc_);
@@ -215,9 +216,10 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
\"comment\" { \"comment\" {
switch(driver.ctx_) { switch(driver.ctx_) {
case ParserContext::AGENT: case ParserContext::AGENT:
case ParserContext::AUTHENTICATION;
case ParserContext::CLIENTS:
case ParserContext::SERVER: case ParserContext::SERVER:
case ParserContext::LOGGERS: case ParserContext::LOGGERS:
case ParserContext::CLIENTS:
return AgentParser::make_COMMENT(driver.loc_); return AgentParser::make_COMMENT(driver.loc_);
default: default:
return AgentParser::make_STRING("comment", driver.loc_); return AgentParser::make_STRING("comment", driver.loc_);

View File

@@ -1269,7 +1269,7 @@ namespace isc { namespace agent {
#line 529 "agent_parser.yy" #line 529 "agent_parser.yy"
{ {
// Add unique here // Add unique here
ctx.enter(ctx.NO_KEYWORD); ctx.enter(ctx.NO_KEYWORDS);
} }
#line 1275 "agent_parser.cc" #line 1275 "agent_parser.cc"
break; break;

View File

@@ -513,6 +513,9 @@ auth_params: auth_param
auth_param: auth_type auth_param: auth_type
| realm | realm
| clients | clients
| comment
| user_context
| unknown_map_entry
; ;
auth_type: TYPE { auth_type: TYPE {
@@ -528,7 +531,7 @@ auth_type_value: BASIC { $$ = ElementPtr(new StringElement("basic", ctx.loc2pos(
realm: REALM { realm: REALM {
// Add unique here // Add unique here
ctx.enter(ctx.NO_KEYWORD); ctx.enter(ctx.NO_KEYWORDS);
} COLON STRING { } COLON STRING {
ElementPtr realm(new StringElement($4, ctx.loc2pos(@4))); ElementPtr realm(new StringElement($4, ctx.loc2pos(@4)));
ctx.stack_.back()->set("realm", realm); ctx.stack_.back()->set("realm", realm);

View File

@@ -10,6 +10,7 @@
#include <agent/simple_parser.h> #include <agent/simple_parser.h>
#include <cc/simple_parser.h> #include <cc/simple_parser.h>
#include <cc/command_interpreter.h> #include <cc/command_interpreter.h>
#include <http/basic_auth_config.h>
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>
using namespace isc::config; using namespace isc::config;
@@ -21,15 +22,13 @@ namespace isc {
namespace agent { namespace agent {
CtrlAgentCfgContext::CtrlAgentCfgContext() CtrlAgentCfgContext::CtrlAgentCfgContext()
: http_host_(""), http_port_(0), basic_auth_realm_("") { : http_host_(""), http_port_(0) {
} }
CtrlAgentCfgContext::CtrlAgentCfgContext(const CtrlAgentCfgContext& orig) CtrlAgentCfgContext::CtrlAgentCfgContext(const CtrlAgentCfgContext& orig)
: ConfigBase(), ctrl_sockets_(orig.ctrl_sockets_), : ConfigBase(), ctrl_sockets_(orig.ctrl_sockets_),
http_host_(orig.http_host_), http_port_(orig.http_port_), http_host_(orig.http_host_), http_port_(orig.http_port_),
basic_auth_realm_(orig.basic_auth_realm_), hooks_config_(orig.hooks_config_), auth_config_(orig.auth_config_) {
hooks_config_(orig.hooks_config_),
basic_auth_config_(orig.basic_auth_config_) {
} }
CtrlAgentCfgMgr::CtrlAgentCfgMgr() CtrlAgentCfgMgr::CtrlAgentCfgMgr()
@@ -53,8 +52,8 @@ CtrlAgentCfgMgr::getConfigSummary(const uint32_t /*selection*/) {
s << ctx->getControlSocketInfoSummary(); s << ctx->getControlSocketInfoSummary();
// Add something if authentication is required. // Add something if authentication is required.
const isc::http::BasicHttpAuthConfig& auth = ctx->getBasicAuthConfig(); const isc::http::HttpAuthConfigPtr& auth = ctx->getAuthConfig();
if (!auth.getClientList().empty()) { if (auth && !auth->empty()) {
s << ", requires basic HTTP authentication"; s << ", requires basic HTTP authentication";
} }
@@ -160,9 +159,10 @@ CtrlAgentCfgContext::toElement() const {
ca->set("http-host", Element::create(http_host_)); ca->set("http-host", Element::create(http_host_));
// Set http-port // Set http-port
ca->set("http-port", Element::create(static_cast<int64_t>(http_port_))); ca->set("http-port", Element::create(static_cast<int64_t>(http_port_)));
// Set basic-authentication-realm // Set authentication
ca->set("basic-authentication-realm", Element::create(basic_auth_realm_)); if (auth_config_) {
// Set hooks-libraries ca->set("authentication", auth_config_->toElement());
}
ca->set("hooks-libraries", hooks_config_.toElement()); ca->set("hooks-libraries", hooks_config_.toElement());
// Set control-sockets // Set control-sockets
ElementPtr control_sockets = Element::createMap(); ElementPtr control_sockets = Element::createMap();
@@ -171,11 +171,6 @@ CtrlAgentCfgContext::toElement() const {
control_sockets->set(si->first, socket); control_sockets->set(si->first, socket);
} }
ca->set("control-sockets", control_sockets); ca->set("control-sockets", control_sockets);
// Set basic HTTP authentication
const isc::http::BasicHttpAuthConfig& auth = basic_auth_config_;
if (!basic_auth_config_.getClientList().empty()) {
ca->set("basic-authentications", basic_auth_config_.toElement());
}
// Set Control-agent // Set Control-agent
ElementPtr result = Element::createMap(); ElementPtr result = Element::createMap();
result->set("Control-agent", ca); result->set("Control-agent", ca);

View File

@@ -9,7 +9,7 @@
#include <cc/data.h> #include <cc/data.h>
#include <hooks/hooks_config.h> #include <hooks/hooks_config.h>
#include <http/basic_auth_config.h> #include <http/auth_config.h>
#include <process/d_cfg_mgr.h> #include <process/d_cfg_mgr.h>
#include <boost/pointer_cast.hpp> #include <boost/pointer_cast.hpp>
#include <map> #include <map>
@@ -99,18 +99,22 @@ public:
return (http_port_); return (http_port_);
} }
/// @brief Sets basic-authentication-realm parameter /// @brief Sets HTTP authentication configuration.
/// ///
/// @param real Basic HTTP authentication realm /// @note Only the basic HTTP authentication is supported.
void setBasicAuthRealm(const std::string& realm) { ///
basic_auth_realm_ = realm; /// @param auth_config HTTP authentication configuration.
void setAuthConfig(const isc::http::HttpAuthConfigPtr& auth_config) {
auth_config_ = auth_config;
} }
/// @brief Returns basic-authentication-realm parameter /// @brief Returns HTTP authentication configuration
/// ///
/// @return Basic HTTP authentication realm. /// @note Only the basic HTTP authentication is supported.
std::string getBasicAuthRealm() const { ///
return (basic_auth_realm_); /// @return HTTP authentication configuration.
const isc::http::HttpAuthConfigPtr& getAuthConfig() const {
return (auth_config_);
} }
/// @brief Returns non-const reference to configured hooks libraries. /// @brief Returns non-const reference to configured hooks libraries.
@@ -127,22 +131,6 @@ public:
return (hooks_config_); return (hooks_config_);
} }
/// @brief Returns non-const reference to configured basic HTTP
/// authentification clients.
///
/// @return non-const reference to configured basic auth clients.
isc::http::BasicHttpAuthConfig& getBasicAuthConfig() {
return (basic_auth_config_);
}
/// @brief Returns const reference to configured basic HTTP
/// authentification clients.
///
/// @return const reference to configured basic auth clients.
const isc::http::BasicHttpAuthConfig& getBasicAuthConfig() const {
return (basic_auth_config_);
}
/// @brief Unparse a configuration object /// @brief Unparse a configuration object
/// ///
/// Returns an element which must parse into the same object, i.e. /// Returns an element which must parse into the same object, i.e.
@@ -178,14 +166,11 @@ private:
/// TCP port the CA should listen on. /// TCP port the CA should listen on.
uint16_t http_port_; uint16_t http_port_;
/// Basic HTTP authentication realm.
std::string basic_auth_realm_;
/// @brief Configured hooks libraries. /// @brief Configured hooks libraries.
isc::hooks::HooksConfig hooks_config_; isc::hooks::HooksConfig hooks_config_;
/// @brief Configured basic HTTP authentification clients. /// @brief Configured basic HTTP authentification clients.
isc::http::BasicHttpAuthConfig basic_auth_config_; isc::http::HttpAuthConfigPtr auth_config_;
}; };
/// @brief Ctrl Agent Configuration Manager. /// @brief Ctrl Agent Configuration Manager.

View File

@@ -13,7 +13,6 @@
#include <agent/ca_response_creator.h> #include <agent/ca_response_creator.h>
#include <cc/data.h> #include <cc/data.h>
#include <http/post_request_json.h> #include <http/post_request_json.h>
#include <http/response_creator_auth.h>
#include <http/response_json.h> #include <http/response_json.h>
#include <boost/pointer_cast.hpp> #include <boost/pointer_cast.hpp>
#include <iostream> #include <iostream>
@@ -81,11 +80,11 @@ createDynamicHttpResponse(const ConstHttpRequestPtr& request) {
if (cfgmgr) { if (cfgmgr) {
ctx = cfgmgr->getCtrlAgentCfgContext(); ctx = cfgmgr->getCtrlAgentCfgContext();
if (ctx) { if (ctx) {
const BasicHttpAuthConfig& auth = ctx->getBasicAuthConfig(); const HttpAuthConfigPtr& auth = ctx->getAuthConfig();
const BasicHttpAuthMap& auth_map = auth.getCredentialMap(); if (auth) {
// Check authentication. // Check authentication.
http_response = checkAuth(*this, request, auth_map, http_response = auth->checkAuth(*this, request);
ctx->getBasicAuthRealm()); }
} }
} }
} }

View File

@@ -96,6 +96,21 @@ ParserContext::loc2pos(isc::agent::location& loc)
return (isc::data::Element::Position(file, line, pos)); return (isc::data::Element::Position(file, line, pos));
} }
void
ParserContext::require(const std::string& name,
isc::data::Element::Position open_loc,
isc::data::Element::Position close_loc)
{
ConstElementPtr value = stack_.back()->get(name);
if (!value) {
isc_throw(ParseError,
"missing parameter '" << name << "' ("
<< stack_.back()->getPosition() << ") ["
<< contextName() << " map between "
<< open_loc << " and " << close_loc << "]");
}
}
void void
ParserContext::enter(const LexerContext& ctx) ParserContext::enter(const LexerContext& ctx)
{ {

View File

@@ -147,6 +147,19 @@ public:
/// @return Position in format accepted by Element /// @return Position in format accepted by Element
isc::data::Element::Position loc2pos(isc::agent::location& loc); isc::data::Element::Position loc2pos(isc::agent::location& loc);
/// @brief Check if a required parameter is present
///
/// Check if a required parameter is present in the map at the top
/// of the stack and raise an error when it is not.
///
/// @param name name of the parameter to check
/// @param open_loc location of the opening curly bracket
/// @param close_loc location of the closing curly bracket
/// @throw ParseError
void require(const std::string& name,
isc::data::Element::Position open_loc,
isc::data::Element::Position close_loc);
/// @brief Defines syntactic contexts for lexical tie-ins /// @brief Defines syntactic contexts for lexical tie-ins
typedef enum { typedef enum {
///< This one is used in pure JSON mode. ///< This one is used in pure JSON mode.

View File

@@ -11,6 +11,7 @@
#include <cc/dhcp_config_error.h> #include <cc/dhcp_config_error.h>
#include <hooks/hooks_manager.h> #include <hooks/hooks_manager.h>
#include <hooks/hooks_parser.h> #include <hooks/hooks_parser.h>
#include <http/basic_auth_config.h>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
using namespace isc::data; using namespace isc::data;
@@ -37,8 +38,13 @@ namespace agent {
/// These are global Control Agent parameters. /// These are global Control Agent parameters.
const SimpleDefaults AgentSimpleParser::AGENT_DEFAULTS = { const SimpleDefaults AgentSimpleParser::AGENT_DEFAULTS = {
{ "http-host", Element::string, "127.0.0.1" }, { "http-host", Element::string, "127.0.0.1" },
{ "http-port", Element::integer, "8000" }, { "http-port", Element::integer, "8000" }
{ "basic-authentication-realm", Element::string, "kea-control-agent" } };
/// @brief This table defines default values for authentication.
const SimpleDefaults AgentSimpleParser::AUTH_DEFAULTS = {
{ "type", Element::string, "basic" },
{ "realm", Element::string, "kea-control-agent" }
}; };
/// @brief This table defines default values for control sockets. /// @brief This table defines default values for control sockets.
@@ -59,6 +65,15 @@ size_t AgentSimpleParser::setAllDefaults(const isc::data::ElementPtr& global) {
// Set global defaults first. // Set global defaults first.
cnt = setDefaults(global, AGENT_DEFAULTS); cnt = setDefaults(global, AGENT_DEFAULTS);
// After set the defaults for authentication if it exists.
ConstElementPtr authentication = global->get("authentication");
if (authentication) {
ElementPtr auth = boost::const_pointer_cast<Element>(authentication);
if (auth) {
cnt += SimpleParser::setDefaults(auth, AUTH_DEFAULTS);
}
}
// Now set the defaults for control-sockets, if any. // Now set the defaults for control-sockets, if any.
ConstElementPtr sockets = global->get("control-sockets"); ConstElementPtr sockets = global->get("control-sockets");
if (sockets) { if (sockets) {
@@ -89,8 +104,6 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
// Let's get the HTTP parameters first. // Let's get the HTTP parameters first.
ctx->setHttpHost(SimpleParser::getString(config, "http-host")); ctx->setHttpHost(SimpleParser::getString(config, "http-host"));
ctx->setHttpPort(SimpleParser::getIntType<uint16_t>(config, "http-port")); ctx->setHttpPort(SimpleParser::getIntType<uint16_t>(config, "http-port"));
ctx->setBasicAuthRealm(SimpleParser::getString(config,
"basic-authentication-realm"));
// Control sockets are second. // Control sockets are second.
ConstElementPtr ctrl_sockets = config->get("control-sockets"); ConstElementPtr ctrl_sockets = config->get("control-sockets");
@@ -102,9 +115,13 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
} }
// Basic HTTP authentications are third. // Basic HTTP authentications are third.
ConstElementPtr auth_config = config->get("basic-authentications"); ConstElementPtr auth_config = config->get("authentications");
ctx->getBasicAuthConfig().clear(); if (auth_config) {
ctx->getBasicAuthConfig().parse(auth_config); using namespace isc::http;
BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
auth->parse(auth_config);
ctx->setAuthConfig(auth);
}
// User context can be done at anytime. // User context can be done at anytime.
ConstElementPtr user_context = config->get("user-context"); ConstElementPtr user_context = config->get("user-context");
@@ -113,7 +130,6 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
} }
// Finally, let's get the hook libs! // Finally, let's get the hook libs!
using namespace isc::hooks; using namespace isc::hooks;
HooksConfig& libraries = ctx->getHooksConfig(); HooksConfig& libraries = ctx->getHooksConfig();
ConstElementPtr hooks = config->get("hooks-libraries"); ConstElementPtr hooks = config->get("hooks-libraries");

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") // Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC")
// //
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -43,9 +43,10 @@ public:
// see simple_parser.cc for comments for those parameters // see simple_parser.cc for comments for those parameters
static const isc::data::SimpleDefaults AGENT_DEFAULTS; static const isc::data::SimpleDefaults AGENT_DEFAULTS;
static const isc::data::SimpleDefaults AUTH_DEFAULTS;
static const isc::data::SimpleDefaults SOCKET_DEFAULTS; static const isc::data::SimpleDefaults SOCKET_DEFAULTS;
}; };
}; }
}; }
#endif #endif

View File

@@ -10,7 +10,9 @@
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>
#include <process/testutils/d_test_stubs.h> #include <process/testutils/d_test_stubs.h>
#include <process/d_cfg_mgr.h> #include <process/d_cfg_mgr.h>
#include <http/basic_auth_config.h>
#include <agent/tests/test_libraries.h> #include <agent/tests/test_libraries.h>
#include <boost/pointer_cast.hpp>
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>
@@ -63,9 +65,6 @@ TEST(CtrlAgentCfgMgr, contextHttpParams) {
ctx.setHttpHost("alnitak"); ctx.setHttpHost("alnitak");
EXPECT_EQ("alnitak", ctx.getHttpHost()); EXPECT_EQ("alnitak", ctx.getHttpHost());
ctx.setBasicAuthRealm("foobar");
EXPECT_EQ("foobar", ctx.getBasicAuthRealm());
} }
// Tests if context can store and retrieve control socket information. // Tests if context can store and retrieve control socket information.
@@ -126,15 +125,16 @@ TEST(CtrlAgentCfgMgr, contextSocketInfoCopy) {
EXPECT_NO_THROW(ctx.setHttpPort(12345)); EXPECT_NO_THROW(ctx.setHttpPort(12345));
EXPECT_NO_THROW(ctx.setHttpHost("bellatrix")); EXPECT_NO_THROW(ctx.setHttpHost("bellatrix"));
EXPECT_NO_THROW(ctx.setBasicAuthRealm("foobar"));
HooksConfig& libs = ctx.getHooksConfig(); HooksConfig& libs = ctx.getHooksConfig();
string exp_name("testlib1.so"); string exp_name("testlib1.so");
ConstElementPtr exp_param(new StringElement("myparam")); ConstElementPtr exp_param(new StringElement("myparam"));
libs.add(exp_name, exp_param); libs.add(exp_name, exp_param);
BasicHttpAuthConfig& auth = ctx.getBasicAuthConfig(); BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
auth.add("foo", "bar"); auth->setRealm("foobar");
auth->add("foo", "bar");
EXPECT_NO_THROW(ctx.setAuthConfig(auth));
// Make a copy. // Make a copy.
ConfigPtr copy_base(ctx.clone()); ConfigPtr copy_base(ctx.clone());
@@ -144,7 +144,6 @@ TEST(CtrlAgentCfgMgr, contextSocketInfoCopy) {
// Now check the values returned // Now check the values returned
EXPECT_EQ(12345, copy->getHttpPort()); EXPECT_EQ(12345, copy->getHttpPort());
EXPECT_EQ("bellatrix", copy->getHttpHost()); EXPECT_EQ("bellatrix", copy->getHttpHost());
EXPECT_EQ("foobar", copy->getBasicAuthRealm());
// Check socket info // Check socket info
ASSERT_TRUE(copy->getControlSocketInfo("d2")); ASSERT_TRUE(copy->getControlSocketInfo("d2"));
@@ -161,9 +160,10 @@ TEST(CtrlAgentCfgMgr, contextSocketInfoCopy) {
ASSERT_TRUE(libs2[0].second); ASSERT_TRUE(libs2[0].second);
EXPECT_EQ(exp_param->str(), libs2[0].second->str()); EXPECT_EQ(exp_param->str(), libs2[0].second->str());
// Check basic HTTP authentication // Check authentication
const BasicHttpAuthConfig& auth2 = copy->getBasicAuthConfig(); const HttpAuthConfigPtr& auth2 = copy->getAuthConfig();
EXPECT_EQ(auth.toElement()->str(), auth2.toElement()->str()); ASSERT_TRUE(auth2);
EXPECT_EQ(auth->toElement()->str(), auth2->toElement()->str());
} }
@@ -186,21 +186,24 @@ TEST(CtrlAgentCfgMgr, contextHookParams) {
EXPECT_EQ(libs.get(), stored_libs.get()); EXPECT_EQ(libs.get(), stored_libs.get());
} }
// Test if the context can store and retrieve basic HTTP authentication clients. // Test if the context can store and retrieve basic HTTP authentication
TEST(CtrlAgentCfgMgr, contextBasicAuth) { // configuration.
TEST(CtrlAgentCfgMgr, contextAuthConfig) {
CtrlAgentCfgContext ctx; CtrlAgentCfgContext ctx;
// By default there should be no authentication. // By default there should be no authentication.
BasicHttpAuthConfig& auth = ctx.getBasicAuthConfig(); EXPECT_FALSE(ctx.getAuthConfig());
EXPECT_TRUE(auth.getClientList().empty()); BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
EXPECT_NO_THROW(ctx.setAuthConfig(auth));
auth.add("foo", "bar"); auth->setRealm("foobar");
auth.add("test", "123\xa3"); auth->add("foo", "bar");
auth->add("test", "123\xa3");
const BasicHttpAuthConfig& stored_auth = ctx.getBasicAuthConfig(); const HttpAuthConfigPtr& stored_auth = ctx.getAuthConfig();
EXPECT_EQ(2, stored_auth.getClientList().size()); ASSERT_TRUE(stored_auth);
EXPECT_FALSE(stored_auth->empty());
EXPECT_EQ(auth.toElement()->str(), stored_auth.toElement()->str()); EXPECT_EQ(auth->toElement()->str(), stored_auth->toElement()->str());
} }
/// Control Agent configurations used in tests. /// Control Agent configurations used in tests.
@@ -211,15 +214,13 @@ const char* AGENT_CONFIGS[] = {
// Configuration 1: http parameters only (no control sockets, not hooks) // Configuration 1: http parameters only (no control sockets, not hooks)
"{ \"http-host\": \"betelgeuse\",\n" "{ \"http-host\": \"betelgeuse\",\n"
" \"http-port\": 8001,\n" " \"http-port\": 8001\n"
" \"basic-authentication-realm\": \"foobar\"\n"
"}", "}",
// Configuration 2: http and 1 socket // Configuration 2: http and 1 socket
"{\n" "{\n"
" \"http-host\": \"betelgeuse\",\n" " \"http-host\": \"betelgeuse\",\n"
" \"http-port\": 8001,\n" " \"http-port\": 8001,\n"
" \"basic-authentication-realm\": \"foobar\",\n"
" \"control-sockets\": {\n" " \"control-sockets\": {\n"
" \"dhcp4\": {\n" " \"dhcp4\": {\n"
" \"socket-name\": \"/tmp/socket-v4\"\n" " \"socket-name\": \"/tmp/socket-v4\"\n"
@@ -231,7 +232,6 @@ const char* AGENT_CONFIGS[] = {
"{\n" "{\n"
" \"http-host\": \"betelgeuse\",\n" " \"http-host\": \"betelgeuse\",\n"
" \"http-port\": 8001,\n" " \"http-port\": 8001,\n"
" \"basic-authentication-realm\": \"foobar\",\n"
" \"control-sockets\": {\n" " \"control-sockets\": {\n"
" \"dhcp4\": {\n" " \"dhcp4\": {\n"
" \"socket-name\": \"/tmp/socket-v4\"\n" " \"socket-name\": \"/tmp/socket-v4\"\n"
@@ -251,7 +251,6 @@ const char* AGENT_CONFIGS[] = {
"{\n" "{\n"
" \"http-host\": \"betelgeuse\",\n" " \"http-host\": \"betelgeuse\",\n"
" \"http-port\": 8001,\n" " \"http-port\": 8001,\n"
" \"basic-authentication-realm\": \"foobar\",\n"
" \"control-sockets\": {\n" " \"control-sockets\": {\n"
" \"dhcp4\": {\n" " \"dhcp4\": {\n"
" \"socket-name\": \"/tmp/socket-v4\"\n" " \"socket-name\": \"/tmp/socket-v4\"\n"
@@ -271,7 +270,6 @@ const char* AGENT_CONFIGS[] = {
"{\n" "{\n"
" \"http-host\": \"betelgeuse\",\n" " \"http-host\": \"betelgeuse\",\n"
" \"http-port\": 8001,\n" " \"http-port\": 8001,\n"
" \"basic-authentication-realm\": \"foobar\",\n"
" \"control-sockets\": {\n" " \"control-sockets\": {\n"
" \"d2\": {\n" " \"d2\": {\n"
" \"socket-name\": \"/tmp/socket-d2\"\n" " \"socket-name\": \"/tmp/socket-d2\"\n"
@@ -283,7 +281,6 @@ const char* AGENT_CONFIGS[] = {
"{\n" "{\n"
" \"http-host\": \"betelgeuse\",\n" " \"http-host\": \"betelgeuse\",\n"
" \"http-port\": 8001,\n" " \"http-port\": 8001,\n"
" \"basic-authentication-realm\": \"foobar\",\n"
" \"control-sockets\": {\n" " \"control-sockets\": {\n"
" \"dhcp6\": {\n" " \"dhcp6\": {\n"
" \"socket-name\": \"/tmp/socket-v6\"\n" " \"socket-name\": \"/tmp/socket-v6\"\n"
@@ -291,17 +288,14 @@ const char* AGENT_CONFIGS[] = {
" }\n" " }\n"
"}", "}",
// Configuration 7: http, 1 socket and basic authentication // Configuration 7: http, 1 socket and authentication
"{\n" "{\n"
" \"http-host\": \"betelgeuse\",\n" " \"http-host\": \"betelgeuse\",\n"
" \"http-port\": 8001,\n" " \"http-port\": 8001,\n"
" \"basic-authentication-realm\": \"foobar\",\n" " \"authentication\": {\n"
" \"control-sockets\": {\n" " \"type\": \"basic\",\n"
" \"dhcp4\": {\n" " \"realm\": \"foobar\",\n"
" \"socket-name\": \"/tmp/socket-v4\"\n" " \"clients\": ["
" }\n"
" },\n"
" \"basic-authentications\": ["
" {" " {"
" \"user\": \"foo\",\n" " \"user\": \"foo\",\n"
" \"password\": \"bar\"\n" " \"password\": \"bar\"\n"
@@ -310,6 +304,12 @@ const char* AGENT_CONFIGS[] = {
" \"password\": \"123\\u00a3\"\n" " \"password\": \"123\\u00a3\"\n"
" }\n" " }\n"
" ]\n" " ]\n"
" },\n"
" \"control-sockets\": {\n"
" \"dhcp4\": {\n"
" \"socket-name\": \"/tmp/socket-v4\"\n"
" }\n"
" }\n"
"}", "}",
// Configuration 8: http and 2 sockets with user contexts and comments // Configuration 8: http and 2 sockets with user contexts and comments
@@ -317,18 +317,11 @@ const char* AGENT_CONFIGS[] = {
" \"user-context\": { \"comment\": \"Indirect comment\" },\n" " \"user-context\": { \"comment\": \"Indirect comment\" },\n"
" \"http-host\": \"betelgeuse\",\n" " \"http-host\": \"betelgeuse\",\n"
" \"http-port\": 8001,\n" " \"http-port\": 8001,\n"
" \"basic-authentication-realm\": \"foobar\",\n" " \"authentication\": {\n"
" \"control-sockets\": {\n" " \"comment\": \"basic HTTP authentication\",\n"
" \"dhcp4\": {\n" " \"type\": \"basic\",\n"
" \"comment\": \"dhcp4 socket\",\n" " \"realm\": \"foobar\",\n"
" \"socket-name\": \"/tmp/socket-v4\"\n" " \"clients\": ["
" },\n"
" \"dhcp6\": {\n"
" \"socket-name\": \"/tmp/socket-v6\",\n"
" \"user-context\": { \"version\": 1 }\n"
" }\n"
" },\n"
" \"basic-authentications\": ["
" {" " {"
" \"comment\": \"foo is authorized\",\n" " \"comment\": \"foo is authorized\",\n"
" \"user\": \"foo\",\n" " \"user\": \"foo\",\n"
@@ -338,6 +331,17 @@ const char* AGENT_CONFIGS[] = {
" \"user-context\": { \"no password\": true }\n" " \"user-context\": { \"no password\": true }\n"
" }\n" " }\n"
" ]\n" " ]\n"
" },\n"
" \"control-sockets\": {\n"
" \"dhcp4\": {\n"
" \"comment\": \"dhcp4 socket\",\n"
" \"socket-name\": \"/tmp/socket-v4\"\n"
" },\n"
" \"dhcp6\": {\n"
" \"socket-name\": \"/tmp/socket-v6\",\n"
" \"user-context\": { \"version\": 1 }\n"
" }\n"
" }\n"
"}" "}"
}; };
@@ -389,7 +393,6 @@ TEST_F(AgentParserTest, configParseHttpOnly) {
ASSERT_TRUE(ctx); ASSERT_TRUE(ctx);
EXPECT_EQ("betelgeuse", ctx->getHttpHost()); EXPECT_EQ("betelgeuse", ctx->getHttpHost());
EXPECT_EQ(8001, ctx->getHttpPort()); EXPECT_EQ(8001, ctx->getHttpPort());
EXPECT_EQ("foobar", ctx->getBasicAuthRealm());
} }
// Tests if a single socket can be configured. BTW this test also checks // Tests if a single socket can be configured. BTW this test also checks
@@ -482,13 +485,20 @@ TEST_F(AgentParserTest, configParseHooks) {
// This test checks that the config file with basic HTTP authentication can be // This test checks that the config file with basic HTTP authentication can be
// loaded. // loaded.
TEST_F(AgentParserTest, configParseBasicAuth) { TEST_F(AgentParserTest, configParseAuth) {
configParse(AGENT_CONFIGS[7], 0); configParse(AGENT_CONFIGS[7], 0);
CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext(); CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
const BasicHttpAuthConfig& auth = ctx->getBasicAuthConfig(); const HttpAuthConfigPtr& auth = ctx->getAuthConfig();
ASSERT_TRUE(auth);
const BasicHttpAuthConfigPtr& basic_auth =
boost::dynamic_pointer_cast<BasicHttpAuthConfig>(auth);
ASSERT_TRUE(basic_auth);
// Check realm
EXPECT_EQ("foobar", basic_auth->getRealm());
// Check credentails // Check credentails
auto credentials = auth.getCredentialMap(); auto credentials = basic_auth->getCredentialMap();
EXPECT_EQ(2, credentials.size()); EXPECT_EQ(2, credentials.size());
std::string user; std::string user;
EXPECT_NO_THROW(user = credentials.at("Zm9vOmJhcg==")); EXPECT_NO_THROW(user = credentials.at("Zm9vOmJhcg=="));
@@ -498,9 +508,10 @@ TEST_F(AgentParserTest, configParseBasicAuth) {
// Check clients. // Check clients.
BasicHttpAuthConfig expected; BasicHttpAuthConfig expected;
expected.setRealm("foobar");
expected.add("foo", "bar"); expected.add("foo", "bar");
expected.add("test", "123\xa3"); expected.add("test", "123\xa3");
EXPECT_EQ(expected.toElement()->str(), auth.toElement()->str()); EXPECT_EQ(expected.toElement()->str(), basic_auth->toElement()->str());
} }
// This test checks comments. // This test checks comments.
@@ -538,22 +549,33 @@ TEST_F(AgentParserTest, comments) {
ASSERT_TRUE(ctx6->get("version")); ASSERT_TRUE(ctx6->get("version"));
EXPECT_EQ("1", ctx6->get("version")->str()); EXPECT_EQ("1", ctx6->get("version")->str());
// Check basic HTTP authentication comment. // Check authentication comment.
const BasicHttpAuthConfig& auth = agent_ctx->getBasicAuthConfig(); const HttpAuthConfigPtr& auth = agent_ctx->getAuthConfig();
auto clients = auth.getClientList(); ASSERT_TRUE(auth);
ASSERT_EQ(2, clients.size()); ConstElementPtr ctx7 = auth->getContext();
ConstElementPtr ctx7 = clients.front().getContext();
ASSERT_TRUE(ctx7); ASSERT_TRUE(ctx7);
ASSERT_EQ(1, ctx7->size()); ASSERT_EQ(1, ctx7->size());
ASSERT_TRUE(ctx7->get("comment")); ASSERT_TRUE(ctx7->get("comment"));
EXPECT_EQ("\"foo is authorized\"", ctx7->get("comment")->str()); EXPECT_EQ("\"basic HTTP authentication\"", ctx7->get("comment")->str());
// Check basic HTTP authentication user context. // Check basic HTTP authentication client comment.
ConstElementPtr ctx8 = clients.back().getContext(); const BasicHttpAuthConfigPtr& basic_auth =
boost::dynamic_pointer_cast<BasicHttpAuthConfig>(auth);
ASSERT_TRUE(basic_auth);
auto clients = basic_auth->getClientList();
ASSERT_EQ(2, clients.size());
ConstElementPtr ctx8 = clients.front().getContext();
ASSERT_TRUE(ctx8); ASSERT_TRUE(ctx8);
ASSERT_EQ(1, ctx8->size()); ASSERT_EQ(1, ctx8->size());
ASSERT_TRUE(ctx8->get("no password")); ASSERT_TRUE(ctx8->get("comment"));
EXPECT_EQ("true", ctx8->get("no password")->str()); EXPECT_EQ("\"foo is authorized\"", ctx8->get("comment")->str());
// Check basic HTTP authentication client user context.
ConstElementPtr ctx9 = clients.back().getContext();
ASSERT_TRUE(ctx9);
ASSERT_EQ(1, ctx9->size());
ASSERT_TRUE(ctx9->get("no password"));
EXPECT_EQ("true", ctx9->get("no password")->str());
} }
} // end of anonymous namespace } // end of anonymous namespace

View File

@@ -10,6 +10,7 @@
#include <agent/ca_command_mgr.h> #include <agent/ca_command_mgr.h>
#include <agent/ca_response_creator.h> #include <agent/ca_response_creator.h>
#include <cc/command_interpreter.h> #include <cc/command_interpreter.h>
#include <http/basic_auth_config.h>
#include <http/post_request.h> #include <http/post_request.h>
#include <http/post_request_json.h> #include <http/post_request_json.h>
#include <http/response_json.h> #include <http/response_json.h>
@@ -250,9 +251,10 @@ TEST_F(CtrlAgentResponseCreatorTest, noAuth) {
// Require authentication. // Require authentication.
CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext(); CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
ASSERT_TRUE(ctx); ASSERT_TRUE(ctx);
ctx->setBasicAuthRealm("ISC.ORG"); BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
BasicHttpAuthConfig& auth = ctx->getBasicAuthConfig(); ASSERT_NO_THROW(ctx->setAuthConfig(auth));
auth.add("foo", "bar"); auth->setRealm("ISC.ORG");
auth->add("foo", "bar");
HttpResponsePtr response; HttpResponsePtr response;
ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_)); ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
@@ -290,8 +292,11 @@ TEST_F(CtrlAgentResponseCreatorTest, basicAuth) {
// Require authentication. // Require authentication.
CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext(); CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
ASSERT_TRUE(ctx); ASSERT_TRUE(ctx);
BasicHttpAuthConfig& auth = ctx->getBasicAuthConfig(); BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
auth.add("foo", "bar"); ASSERT_NO_THROW(ctx->setAuthConfig(auth));
// In fact the realm is used only on errors... set it anyway.
auth->setRealm("ISC.ORG");
auth->add("foo", "bar");
HttpResponsePtr response; HttpResponsePtr response;
ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_)); ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));

View File

@@ -35,11 +35,11 @@ libkea_http_la_SOURCES += response.cc response.h
libkea_http_la_SOURCES += response_parser.cc response_parser.h libkea_http_la_SOURCES += response_parser.cc response_parser.h
libkea_http_la_SOURCES += response_context.h libkea_http_la_SOURCES += response_context.h
libkea_http_la_SOURCES += response_creator.cc response_creator.h libkea_http_la_SOURCES += response_creator.cc response_creator.h
libkea_http_la_SOURCES += response_creator_auth.cc response_creator_auth.h
libkea_http_la_SOURCES += response_creator_factory.h libkea_http_la_SOURCES += response_creator_factory.h
libkea_http_la_SOURCES += response_json.cc response_json.h libkea_http_la_SOURCES += response_json.cc response_json.h
libkea_http_la_SOURCES += url.cc url.h libkea_http_la_SOURCES += url.cc url.h
libkea_http_la_SOURCES += basic_auth.cc basic_auth.h libkea_http_la_SOURCES += basic_auth.cc basic_auth.h
libkea_http_la_SOURCES += auth_config.h
libkea_http_la_SOURCES += basic_auth_config.cc basic_auth_config.h libkea_http_la_SOURCES += basic_auth_config.cc basic_auth_config.h
libkea_http_la_CXXFLAGS = $(AM_CXXFLAGS) libkea_http_la_CXXFLAGS = $(AM_CXXFLAGS)
@@ -91,6 +91,7 @@ endif
# Specify the headers for copying into the installation directory tree. # Specify the headers for copying into the installation directory tree.
libkea_http_includedir = $(pkgincludedir)/http libkea_http_includedir = $(pkgincludedir)/http
libkea_http_include_HEADERS = \ libkea_http_include_HEADERS = \
auth_config.h \
basic_auth.h \ basic_auth.h \
basic_auth_config.h \ basic_auth_config.h \
client.h \ client.h \
@@ -115,7 +116,6 @@ libkea_http_include_HEADERS = \
response.h \ response.h \
response_context.h \ response_context.h \
response_creator.h \ response_creator.h \
response_creator_auth.h \
response_creator_factory.h \ response_creator_factory.h \
response_json.h \ response_json.h \
response_parser.h \ response_parser.h \

View File

@@ -0,0 +1,84 @@
// Copyright (C) 2020 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 HTTP_AUTH_CONFIG_H
#define HTTP_AUTH_CONFIG_H
#include <cc/cfg_to_element.h>
#include <cc/data.h>
#include <cc/simple_parser.h>
#include <cc/user_context.h>
#include <http/request.h>
#include <http/response_creator.h>
#include <http/response_json.h>
namespace isc {
namespace http {
/// @brief Base type of HTTP authentication configuration.
class HttpAuthConfig : public isc::data::UserContext,
public isc::data::CfgToElement {
public:
/// @brief Destructor.
virtual ~HttpAuthConfig() { }
/// @brief Set the realm.
///
/// @param realm New realm.
void setRealm(const std::string& realm) {
realm_ = realm;
}
/// @brief Returns the realm.
///
/// @return The basic HTTP authentication realm.
const std::string& getRealm() const {
return (realm_);
}
/// @brief Empty predicate.
/// @return true if the configuration is empty so authentication
/// is not required.
virtual bool empty() const = 0;
/// @brief Clear configuration.
virtual void clear() = 0;
/// @brief Parses HTTP authentication configuration.
///
/// @param config Element holding the basic HTTP authentication
/// configuration to be parsed.
/// @throw DhcpConfigError when the configuration is invalid.
virtual void parse(const isc::data::ConstElementPtr& config) = 0;
/// @brief Unparses HTTP authentication configuration.
///
/// @return A pointer to unparsed HTTP authentication configuration.
virtual isc::data::ElementPtr toElement() const = 0;
/// @brief Validate HTTP request.
///
/// @param creator The HTTP response creator.
/// @param request The HTTP request to validate.
/// @return Error HTTP response if validation failed, null otherwise.
virtual isc::http::HttpResponseJsonPtr
checkAuth(const isc::http::HttpResponseCreator& creator,
const isc::http::ConstHttpRequestPtr& request) const = 0;
private:
/// @brief The realm.
std::string realm_;
};
/// @brief Type of shared pointers to HTTP authentication configuration.
typedef boost::shared_ptr<HttpAuthConfig> HttpAuthConfigPtr;
} // end of namespace isc::http
} // end of namespace isc
#endif // endif HTTP_AUTH_CONFIG_H

View File

@@ -7,10 +7,13 @@
#include <config.h> #include <config.h>
#include <http/basic_auth_config.h> #include <http/basic_auth_config.h>
#include <http/http_log.h>
#include <util/strutil.h>
using namespace isc; using namespace isc;
using namespace isc::data; using namespace isc::data;
using namespace isc::dhcp; using namespace isc::dhcp;
using namespace isc::util;
using namespace std; using namespace std;
namespace isc { namespace isc {
@@ -56,13 +59,30 @@ BasicHttpAuthConfig::clear() {
map_.clear(); map_.clear();
} }
bool
BasicHttpAuthConfig::empty() const {
return (map_.empty());
}
ElementPtr ElementPtr
BasicHttpAuthConfig::toElement() const { BasicHttpAuthConfig::toElement() const {
ElementPtr result = Element::createList(); ElementPtr result = Element::createMap();
// Set user-context
contextToElement(result);
// Set type
result->set("type", Element::create(string("basic")));
// Set realm
result->set("realm", Element::create(getRealm()));
// Set clients
ElementPtr clients = Element::createList();
for (auto client : list_) { for (auto client : list_) {
result->add(client.toElement()); clients->add(client.toElement());
} }
result->set("clients", clients);
return (result); return (result);
} }
@@ -72,21 +92,68 @@ BasicHttpAuthConfig::parse(const ConstElementPtr& config) {
if (!config) { if (!config) {
return; return;
} }
if (config->getType() != Element::list) { if (config->getType() != Element::map) {
isc_throw(DhcpConfigError, "basic-authentications must be a list (" isc_throw(DhcpConfigError, "authentication must be a map ("
<< config->getPosition() << ")"); << config->getPosition() << ")");
} }
for (auto client : config->listValue()) {
// Get and verify the type.
ConstElementPtr type = config->get("type");
if (!type) {
isc_throw(DhcpConfigError, "type is required in authentication ("
<< config->getPosition() << ")");
}
if (type->getType() != Element::string) {
isc_throw(DhcpConfigError, "type is must be a string ("
<< type->getPosition() << ")");
}
if (type->stringValue() != "basic") {
isc_throw(DhcpConfigError, "only basic HTTP authentication is "
<< "supported: type is '" << type->stringValue()
<< "' not 'basic' (" << type->getPosition() << ")");
}
// Get the realm.
ConstElementPtr realm = config->get("realm");
if (realm) {
if (realm->getType() != Element::string) {
isc_throw(DhcpConfigError, "realm is must be a string ("
<< realm->getPosition() << ")");
}
setRealm(realm->stringValue());
}
// Get user context
ConstElementPtr user_context_cfg = config->get("user-context");
if (user_context_cfg) {
if (user_context_cfg->getType() != Element::map) {
isc_throw(DhcpConfigError, "user-context must be a map ("
<< user_context_cfg->getPosition() << ")");
}
setContext(user_context_cfg);
}
// Get clients.
ConstElementPtr clients = config->get("clients");
if (!clients) {
return;
}
if (clients->getType() != Element::list) {
isc_throw(DhcpConfigError, "clients must be a list ("
<< clients->getPosition() << ")");
}
// Iterate on clients.
for (auto client : clients->listValue()) {
if (client->getType() != Element::map) { if (client->getType() != Element::map) {
isc_throw(DhcpConfigError, "basic-authentications items must be " isc_throw(DhcpConfigError, "clients items must be maps ("
<< "maps (" << client->getPosition() << ")"); << client->getPosition() << ")");
} }
// user // user
ConstElementPtr user_cfg = client->get("user"); ConstElementPtr user_cfg = client->get("user");
if (!user_cfg) { if (!user_cfg) {
isc_throw(DhcpConfigError, "user is required in " isc_throw(DhcpConfigError, "user is required in clients items ("
<< "basic-authentications items ("
<< client->getPosition() << ")"); << client->getPosition() << ")");
} }
if (user_cfg->getType() != Element::string) { if (user_cfg->getType() != Element::string) {
@@ -133,5 +200,60 @@ BasicHttpAuthConfig::parse(const ConstElementPtr& config) {
} }
} }
HttpResponseJsonPtr
BasicHttpAuthConfig::checkAuth(const HttpResponseCreator& creator,
const ConstHttpRequestPtr& request) const {
const BasicHttpAuthMap& credentials = getCredentialMap();
bool authentic = false;
if (credentials.empty()) {
authentic = true;
} else try {
string value = request->getHeaderValue("Authorization");
// Trim space characters.
value = str::trim(value);
if (value.size() < 8) {
isc_throw(BadValue, "header content is too short");
}
// Get the authentication scheme which must be "basic".
string scheme = value.substr(0, 5);
str::lowercase(scheme);
if (scheme != "basic") {
isc_throw(BadValue, "not basic authentication");
}
// Skip the authentication scheme name and space characters.
value = value.substr(5);
value = str::trim(value);
// Verify the credential is in the list.
const auto it = credentials.find(value);
if (it != credentials.end()) {
LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC,
HTTP_CLIENT_REQUEST_AUTHORIZED)
.arg(it->second);
authentic = true;
} else {
LOG_INFO(http_logger, HTTP_CLIENT_REQUEST_NOT_AUTHORIZED);
authentic = false;
}
} catch (const HttpMessageNonExistingHeader&) {
LOG_INFO(http_logger, HTTP_CLIENT_REQUEST_NO_AUTH_HEADER);
} catch (const BadValue& ex) {
LOG_INFO(http_logger, HTTP_CLIENT_REQUEST_BAD_AUTH_HEADER)
.arg(ex.what());
}
if (authentic) {
return (HttpResponseJsonPtr());
}
const string& realm = getRealm();
const string& scheme = "Basic";
HttpResponsePtr response =
creator.createStockHttpResponse(request, HttpStatusCode::UNAUTHORIZED);
response->reset();
response->context()->headers_.push_back(
HttpHeaderContext("WWW-Authenticate",
scheme + " realm=\"" + realm + "\""));
response->finalize();
return (boost::dynamic_pointer_cast<HttpResponseJson>(response));
}
} // end of namespace isc::http } // end of namespace isc::http
} // end of namespace isc } // end of namespace isc

View File

@@ -7,10 +7,7 @@
#ifndef HTTP_BASIC_AUTH_CONFIG_H #ifndef HTTP_BASIC_AUTH_CONFIG_H
#define HTTP_BASIC_AUTH_CONFIG_H #define HTTP_BASIC_AUTH_CONFIG_H
#include <cc/cfg_to_element.h> #include <http/auth_config.h>
#include <cc/data.h>
#include <cc/simple_parser.h>
#include <cc/user_context.h>
#include <http/basic_auth.h> #include <http/basic_auth.h>
#include <list> #include <list>
#include <unordered_map> #include <unordered_map>
@@ -67,9 +64,12 @@ private:
typedef std::list<BasicHttpAuthClient> BasicHttpAuthClientList; typedef std::list<BasicHttpAuthClient> BasicHttpAuthClientList;
/// @brief Basic HTTP authentication configuration. /// @brief Basic HTTP authentication configuration.
class BasicHttpAuthConfig : public isc::data::CfgToElement { class BasicHttpAuthConfig : public HttpAuthConfig {
public: public:
/// @brief Destructor.
virtual ~BasicHttpAuthConfig() { }
/// @brief Add a client configuration. /// @brief Add a client configuration.
/// ///
/// @param user User id /// @param user User id
@@ -80,8 +80,13 @@ public:
const std::string& password, const std::string& password,
const isc::data::ConstElementPtr& user_context = isc::data::ConstElementPtr()); const isc::data::ConstElementPtr& user_context = isc::data::ConstElementPtr());
/// @brief Empty predicate.
/// @return true if the configuration is empty so authentication
/// is not required.
virtual bool empty() const;
/// @brief Clear configuration. /// @brief Clear configuration.
void clear(); virtual void clear();
/// @brief Returns the list of client configuration. /// @brief Returns the list of client configuration.
/// ///
@@ -109,6 +114,15 @@ public:
/// @return A pointer to unparsed basic HTTP authentication configuration. /// @return A pointer to unparsed basic HTTP authentication configuration.
virtual isc::data::ElementPtr toElement() const; virtual isc::data::ElementPtr toElement() const;
/// @brief Validate HTTP request.
///
/// @param creator The HTTP response creator.
/// @param request The HTTP request to validate.
/// @return Error HTTP response if validation failed, null otherwise.
virtual isc::http::HttpResponseJsonPtr
checkAuth(const isc::http::HttpResponseCreator& creator,
const isc::http::ConstHttpRequestPtr& request) const;
private: private:
/// @brief The list of basic HTTP authentication client configuration. /// @brief The list of basic HTTP authentication client configuration.
@@ -118,6 +132,9 @@ private:
BasicHttpAuthMap map_; BasicHttpAuthMap map_;
}; };
/// @brief Type of shared pointers to basic HTTP authentication configuration.
typedef boost::shared_ptr<BasicHttpAuthConfig> BasicHttpAuthConfigPtr;
} // end of namespace isc::http } // end of namespace isc::http
} // end of namespace isc } // end of namespace isc

View File

@@ -1,75 +0,0 @@
// Copyright (C) 2020 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/http_log.h>
#include <http/response_creator_auth.h>
#include <util/strutil.h>
using namespace isc;
using namespace isc::util;
using namespace std;
namespace isc {
namespace http {
HttpResponseJsonPtr checkAuth(const HttpResponseCreator& creator,
const ConstHttpRequestPtr& request,
const BasicHttpAuthMap& credentials,
const std::string& realm) {
bool authentic = false;
if (credentials.empty()) {
authentic = true;
} else try {
string value = request->getHeaderValue("Authorization");
// Trim space characters.
value = str::trim(value);
if (value.size() < 8) {
isc_throw(BadValue, "header content is too short");
}
// Get the authentication scheme which must be "basic".
string scheme = value.substr(0, 5);
str::lowercase(scheme);
if (scheme != "basic") {
isc_throw(BadValue, "not basic authentication");
}
// Skip the authentication scheme name and space characters.
value = value.substr(5);
value = str::trim(value);
// Verify the credential is in the list.
const auto it = credentials.find(value);
if (it != credentials.end()) {
LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC,
HTTP_CLIENT_REQUEST_AUTHORIZED)
.arg(it->second);
authentic = true;
} else {
LOG_INFO(http_logger, HTTP_CLIENT_REQUEST_NOT_AUTHORIZED);
authentic = false;
}
} catch (const HttpMessageNonExistingHeader&) {
LOG_INFO(http_logger, HTTP_CLIENT_REQUEST_NO_AUTH_HEADER);
} catch (const BadValue& ex) {
LOG_INFO(http_logger, HTTP_CLIENT_REQUEST_BAD_AUTH_HEADER)
.arg(ex.what());
}
if (authentic) {
return (HttpResponseJsonPtr());
}
string scheme = "Basic";
HttpResponsePtr response =
creator.createStockHttpResponse(request, HttpStatusCode::UNAUTHORIZED);
response->reset();
response->context()->headers_.push_back(
HttpHeaderContext("WWW-Authenticate",
scheme + " realm=\"" + realm + "\""));
response->finalize();
return (boost::dynamic_pointer_cast<HttpResponseJson>(response));
}
} // end of namespace isc::http
} // end of namespace isc

View File

@@ -1,38 +0,0 @@
// Copyright (C) 2020 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 HTTP_RESPONSE_CREATOR_AUTH_H
#define HTTP_RESPONSE_CREATOR_AUTH_H
#include <http/basic_auth_config.h>
#include <http/response_creator.h>
#include <http/response_json.h>
#include <string.h>
#include <unordered_map>
namespace isc {
namespace http {
/// @brief Validate authentication.
///
/// Currently it only validates basic HTTP authentication.
/// Empty credentials map means that basic HTTP authentication is
/// not required i.e. all requests validate.
///
/// @param creator The HTTP response creator.
/// @param request The HTTP request to validate.
/// @param credentials A map of all allowed credentials.
/// @param realm Realm name.
/// @return Error HTTP response if validation failed, null otherwise.
HttpResponseJsonPtr checkAuth(const HttpResponseCreator& creator,
const ConstHttpRequestPtr& request,
const BasicHttpAuthMap& credentials,
const std::string& realm);
} // end of namespace isc::http
} // end of namespace isc
#endif // endif HTTP_RESPONSE_CREATOR_AUTH_H

View File

@@ -44,15 +44,26 @@ TEST(BasicHttpAuthConfigTest, basic) {
BasicHttpAuthConfig config; BasicHttpAuthConfig config;
// Initial configuration is empty. // Initial configuration is empty.
EXPECT_TRUE(config.empty());
EXPECT_TRUE(config.getRealm().empty());
EXPECT_TRUE(config.getClientList().empty()); EXPECT_TRUE(config.getClientList().empty());
EXPECT_TRUE(config.getCredentialMap().empty()); EXPECT_TRUE(config.getCredentialMap().empty());
// Set the realm and user context.
EXPECT_NO_THROW(config.setRealm("my-realm"));
EXPECT_EQ("my-realm", config.getRealm());
ConstElementPtr horse = Element::fromJSON("{ \"value\": \"a horse\" }");
EXPECT_NO_THROW(config.setContext(horse));
EXPECT_TRUE(horse->equals(*config.getContext()));
// Add rejects user id with embedded ':'. // Add rejects user id with embedded ':'.
EXPECT_THROW(config.add("foo:", "bar"), BadValue); EXPECT_THROW(config.add("foo:", "bar"), BadValue);
// Add a client. // Add a client.
EXPECT_TRUE(config.empty());
ConstElementPtr ctx = Element::fromJSON("{ \"foo\": \"bar\" }"); ConstElementPtr ctx = Element::fromJSON("{ \"foo\": \"bar\" }");
EXPECT_NO_THROW(config.add("foo", "bar", ctx)); EXPECT_NO_THROW(config.add("foo", "bar", ctx));
EXPECT_FALSE(config.empty());
// Check the client. // Check the client.
ASSERT_EQ(1, config.getClientList().size()); ASSERT_EQ(1, config.getClientList().size());
@@ -68,12 +79,17 @@ TEST(BasicHttpAuthConfigTest, basic) {
EXPECT_EQ("foo", user); EXPECT_EQ("foo", user);
// Check toElement. // Check toElement.
ElementPtr expected = Element::createList(); ElementPtr expected = Element::createMap();
ElementPtr clients = Element::createList();
ElementPtr elem = Element::createMap(); ElementPtr elem = Element::createMap();
elem->set("user", Element::create(string("foo"))); elem->set("user", Element::create(string("foo")));
elem->set("password", Element::create(string("bar"))); elem->set("password", Element::create(string("bar")));
elem->set("user-context", ctx); elem->set("user-context", ctx);
expected->add(elem); clients->add(elem);
expected->set("type", Element::create(string("basic")));
expected->set("realm", Element::create(string("my-realm")));
expected->set("user-context", horse);
expected->set("clients", clients);
runToElementTest<BasicHttpAuthConfig>(expected, config); runToElementTest<BasicHttpAuthConfig>(expected, config);
// Add a second client and test it. // Add a second client and test it.
@@ -85,7 +101,8 @@ TEST(BasicHttpAuthConfigTest, basic) {
// Check clear. // Check clear.
config.clear(); config.clear();
expected = Element::createList(); EXPECT_TRUE(config.empty());
expected->set("clients", Element::createList());
runToElementTest<BasicHttpAuthConfig>(expected, config); runToElementTest<BasicHttpAuthConfig>(expected, config);
// Add clients again. // Add clients again.
@@ -96,8 +113,10 @@ TEST(BasicHttpAuthConfigTest, basic) {
ElementPtr elem0 = Element::createMap(); ElementPtr elem0 = Element::createMap();
elem0->set("user", Element::create(string("test"))); elem0->set("user", Element::create(string("test")));
elem0->set("password", Element::create(string("123\xa3"))); elem0->set("password", Element::create(string("123\xa3")));
expected->add(elem0); clients = Element::createList();
expected->add(elem); clients->add(elem0);
clients->add(elem);
expected->set("clients", clients);
runToElementTest<BasicHttpAuthConfig>(expected, config); runToElementTest<BasicHttpAuthConfig>(expected, config);
} }
@@ -108,35 +127,83 @@ TEST(BasicHttpAuthConfigTest, parse) {
// No config is accepted. // No config is accepted.
EXPECT_NO_THROW(config.parse(cfg)); EXPECT_NO_THROW(config.parse(cfg));
EXPECT_TRUE(config.empty());
EXPECT_TRUE(config.getClientList().empty()); EXPECT_TRUE(config.getClientList().empty());
EXPECT_TRUE(config.getCredentialMap().empty()); EXPECT_TRUE(config.getCredentialMap().empty());
runToElementTest<BasicHttpAuthConfig>(Element::createList(), config); ElementPtr expected = Element::createMap();
expected->set("type", Element::create(string("basic")));
expected->set("realm", Element::create(string("")));
expected->set("clients", Element::createList());
runToElementTest<BasicHttpAuthConfig>(expected, config);
// The config must be a list. // The config must be a map.
cfg = Element::createList();
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"authentication must be a map (:0:0)");
// The type must be present.
cfg = Element::createMap(); cfg = Element::createMap();
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"basic-authentications must be a list (:0:0)"); "type is required in authentication (:0:0)");
// The type must be a string.
cfg->set("type", Element::create(true));
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"type is must be a string (:0:0)");
// The type must be basic.
cfg->set("type", Element::create(string("foobar")));
string errmsg = "only basic HTTP authentication is supported: type is ";
errmsg += "'foobar' not 'basic' (:0:0)";
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, errmsg);
cfg->set("type", Element::create(string("basic")));
EXPECT_NO_THROW(config.parse(cfg));
// The realm must be a string.
cfg->set("realm", Element::createList());
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"realm is must be a string (:0:0)");
cfg->set("realm", Element::create(string("my-realm")));
EXPECT_NO_THROW(config.parse(cfg));
// The user context must be a map.
ElementPtr ctx = Element::createList();
cfg->set("user-context", ctx);
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"user-context must be a map (:0:0)");
ctx = Element::fromJSON("{ \"value\": \"a horse\" }");
cfg->set("user-context", ctx);
EXPECT_NO_THROW(config.parse(cfg));
// Clients must be a list.
ElementPtr clients_cfg = Element::createMap();
cfg->set("clients", clients_cfg);
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"clients must be a list (:0:0)");
// The client config must be a map. // The client config must be a map.
cfg = Element::createList(); clients_cfg = Element::createList();
ElementPtr client_cfg = Element::createList(); ElementPtr client_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"basic-authentications items must be maps (:0:0)"); "clients items must be maps (:0:0)");
// The user parameter is mandatory in client config. // The user parameter is mandatory in client config.
client_cfg = Element::createMap(); client_cfg = Element::createMap();
cfg = Element::createList(); clients_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"user is required in basic-authentications items (:0:0)"); "user is required in clients items (:0:0)");
// The user parameter must be a string. // The user parameter must be a string.
ElementPtr user_cfg = Element::create(1); ElementPtr user_cfg = Element::create(1);
client_cfg = Element::createMap(); client_cfg = Element::createMap();
client_cfg->set("user", user_cfg); client_cfg->set("user", user_cfg);
cfg = Element::createList(); clients_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"user must be a string (:0:0)"); "user must be a string (:0:0)");
@@ -144,8 +211,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
user_cfg = Element::create(string("")); user_cfg = Element::create(string(""));
client_cfg = Element::createMap(); client_cfg = Element::createMap();
client_cfg->set("user", user_cfg); client_cfg->set("user", user_cfg);
cfg = Element::createList(); clients_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"user must be not be empty (:0:0)"); "user must be not be empty (:0:0)");
@@ -153,8 +221,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
user_cfg = Element::create(string("foo:bar")); user_cfg = Element::create(string("foo:bar"));
client_cfg = Element::createMap(); client_cfg = Element::createMap();
client_cfg->set("user", user_cfg); client_cfg->set("user", user_cfg);
cfg = Element::createList(); clients_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"user must not contain a ':': 'foo:bar' (:0:0)"); "user must not contain a ':': 'foo:bar' (:0:0)");
@@ -162,8 +231,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
user_cfg = Element::create(string("foo")); user_cfg = Element::create(string("foo"));
client_cfg = Element::createMap(); client_cfg = Element::createMap();
client_cfg->set("user", user_cfg); client_cfg->set("user", user_cfg);
cfg = Element::createList(); clients_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_NO_THROW(config.parse(cfg)); EXPECT_NO_THROW(config.parse(cfg));
ASSERT_EQ(1, config.getClientList().size()); ASSERT_EQ(1, config.getClientList().size());
EXPECT_EQ("", config.getClientList().front().getPassword()); EXPECT_EQ("", config.getClientList().front().getPassword());
@@ -174,8 +244,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
client_cfg = Element::createMap(); client_cfg = Element::createMap();
client_cfg->set("user", user_cfg); client_cfg->set("user", user_cfg);
client_cfg->set("password", password_cfg); client_cfg->set("password", password_cfg);
cfg = Element::createList(); clients_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"password must be a string (:0:0)"); "password must be a string (:0:0)");
@@ -184,8 +255,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
client_cfg = Element::createMap(); client_cfg = Element::createMap();
client_cfg->set("user", user_cfg); client_cfg->set("user", user_cfg);
client_cfg->set("password", password_cfg); client_cfg->set("password", password_cfg);
cfg = Element::createList(); clients_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_NO_THROW(config.parse(cfg)); EXPECT_NO_THROW(config.parse(cfg));
ASSERT_EQ(1, config.getClientList().size()); ASSERT_EQ(1, config.getClientList().size());
EXPECT_EQ("", config.getClientList().front().getPassword()); EXPECT_EQ("", config.getClientList().front().getPassword());
@@ -193,13 +265,14 @@ TEST(BasicHttpAuthConfigTest, parse) {
// User context must be a map. // User context must be a map.
password_cfg = Element::create(string("bar")); password_cfg = Element::create(string("bar"));
ElementPtr ctx = Element::createList(); ctx = Element::createList();
client_cfg = Element::createMap(); client_cfg = Element::createMap();
client_cfg->set("user", user_cfg); client_cfg->set("user", user_cfg);
client_cfg->set("password", password_cfg); client_cfg->set("password", password_cfg);
client_cfg->set("user-context", ctx); client_cfg->set("user-context", ctx);
cfg = Element::createList(); clients_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
"user-context must be a map (:0:0)"); "user-context must be a map (:0:0)");
@@ -209,8 +282,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
client_cfg->set("user", user_cfg); client_cfg->set("user", user_cfg);
client_cfg->set("password", password_cfg); client_cfg->set("password", password_cfg);
client_cfg->set("user-context", ctx); client_cfg->set("user-context", ctx);
cfg = Element::createList(); clients_cfg = Element::createList();
cfg->add(client_cfg); clients_cfg->add(client_cfg);
cfg->set("clients", clients_cfg);
EXPECT_NO_THROW(config.parse(cfg)); EXPECT_NO_THROW(config.parse(cfg));
runToElementTest<BasicHttpAuthConfig>(cfg, config); runToElementTest<BasicHttpAuthConfig>(cfg, config);
} }

View File

@@ -6,11 +6,11 @@
#include <config.h> #include <config.h>
#include <http/basic_auth.h> #include <http/basic_auth.h>
#include <http/basic_auth_config.h>
#include <http/http_types.h> #include <http/http_types.h>
#include <http/request.h> #include <http/request.h>
#include <http/response.h> #include <http/response.h>
#include <http/response_creator.h> #include <http/response_creator.h>
#include <http/response_creator_auth.h>
#include <http/response_json.h> #include <http/response_json.h>
#include <http/tests/response_test.h> #include <http/tests/response_test.h>
#include <testutils/log_utils.h> #include <testutils/log_utils.h>
@@ -140,13 +140,14 @@ class HttpResponseCreatorAuthTest : public LogContentTest { };
// This test verifies that missing required authentication header gives // This test verifies that missing required authentication header gives
// unauthorized error. // unauthorized error.
TEST_F(HttpResponseCreatorAuthTest, noAuth) { TEST_F(HttpResponseCreatorAuthTest, noAuth) {
// Create credentials. // Create basic HTTP authentication configuration.
BasicHttpAuthPtr basic_auth; BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3"))); EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential()); const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
BasicHttpAuthMap credentials; auto cred = credentials.find("dGVzdDoxMjPCow==");
credentials[basic_auth->getCredential()] = "test"; EXPECT_NE(cred, credentials.end());
string realm = "ISC.ORG"; EXPECT_EQ(cred->second, "test");
auth_config->setRealm("ISC.ORG");
// Create request and finalize it. // Create request and finalize it.
HttpRequestPtr request(new HttpRequest()); HttpRequestPtr request(new HttpRequest());
@@ -158,7 +159,7 @@ TEST_F(HttpResponseCreatorAuthTest, noAuth) {
HttpResponsePtr response; HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());; TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
ASSERT_NO_THROW(response = checkAuth(*creator, request, credentials, realm)); ASSERT_NO_THROW(response = auth_config->checkAuth(*creator, request));
ASSERT_TRUE(response); ASSERT_TRUE(response);
EXPECT_EQ("HTTP/1.0 401 Unauthorized\r\n" EXPECT_EQ("HTTP/1.0 401 Unauthorized\r\n"
@@ -176,13 +177,14 @@ TEST_F(HttpResponseCreatorAuthTest, noAuth) {
// This test verifies that too short authentication header is rejected. // This test verifies that too short authentication header is rejected.
TEST_F(HttpResponseCreatorAuthTest, authTooShort) { TEST_F(HttpResponseCreatorAuthTest, authTooShort) {
// Create credentials. // Create basic HTTP authentication configuration.
BasicHttpAuthPtr basic_auth; BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3"))); EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential()); const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
BasicHttpAuthMap credentials; auto cred = credentials.find("dGVzdDoxMjPCow==");
credentials[basic_auth->getCredential()] = "test"; EXPECT_NE(cred, credentials.end());
string realm = "ISC.ORG"; EXPECT_EQ(cred->second, "test");
auth_config->setRealm("ISC.ORG");
// Create request and finalize it. // Create request and finalize it.
HttpRequestPtr request(new HttpRequest()); HttpRequestPtr request(new HttpRequest());
@@ -196,7 +198,7 @@ TEST_F(HttpResponseCreatorAuthTest, authTooShort) {
HttpResponsePtr response; HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());; TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
ASSERT_NO_THROW(response = checkAuth(*creator, request, credentials, realm)); ASSERT_NO_THROW(response = auth_config->checkAuth(*creator, request));
ASSERT_TRUE(response); ASSERT_TRUE(response);
EXPECT_EQ("HTTP/1.0 401 Unauthorized\r\n" EXPECT_EQ("HTTP/1.0 401 Unauthorized\r\n"
@@ -215,13 +217,14 @@ TEST_F(HttpResponseCreatorAuthTest, authTooShort) {
// This test verifies that another authentication schema is rejected. // This test verifies that another authentication schema is rejected.
TEST_F(HttpResponseCreatorAuthTest, badScheme) { TEST_F(HttpResponseCreatorAuthTest, badScheme) {
// Create credentials. // Create basic HTTP authentication configuration.
BasicHttpAuthPtr basic_auth; BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3"))); EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential()); const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
BasicHttpAuthMap credentials; auto cred = credentials.find("dGVzdDoxMjPCow==");
credentials[basic_auth->getCredential()] = "test"; EXPECT_NE(cred, credentials.end());
string realm = "ISC.ORG"; EXPECT_EQ(cred->second, "test");
auth_config->setRealm("ISC.ORG");
// Create request and finalize it. // Create request and finalize it.
HttpRequestPtr request(new HttpRequest()); HttpRequestPtr request(new HttpRequest());
@@ -235,7 +238,7 @@ TEST_F(HttpResponseCreatorAuthTest, badScheme) {
HttpResponsePtr response; HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());; TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
ASSERT_NO_THROW(response = checkAuth(*creator, request, credentials, realm)); ASSERT_NO_THROW(response = auth_config->checkAuth(*creator, request));
ASSERT_TRUE(response); ASSERT_TRUE(response);
EXPECT_EQ("HTTP/1.0 401 Unauthorized\r\n" EXPECT_EQ("HTTP/1.0 401 Unauthorized\r\n"
@@ -254,13 +257,14 @@ TEST_F(HttpResponseCreatorAuthTest, badScheme) {
// This test verifies that not matching credential is rejected. // This test verifies that not matching credential is rejected.
TEST_F(HttpResponseCreatorAuthTest, notMatching) { TEST_F(HttpResponseCreatorAuthTest, notMatching) {
// Create credentials. // Create basic HTTP authentication configuration.
BasicHttpAuthPtr basic_auth; BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3"))); EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential()); const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
BasicHttpAuthMap credentials; auto cred = credentials.find("dGVzdDoxMjPCow==");
credentials[basic_auth->getCredential()] = "test"; EXPECT_NE(cred, credentials.end());
string realm = "ISC.ORG"; EXPECT_EQ(cred->second, "test");
auth_config->setRealm("ISC.ORG");
// Create request and finalize it. // Create request and finalize it.
HttpRequestPtr request(new HttpRequest()); HttpRequestPtr request(new HttpRequest());
@@ -275,7 +279,7 @@ TEST_F(HttpResponseCreatorAuthTest, notMatching) {
HttpResponsePtr response; HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());; TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
ASSERT_NO_THROW(response = checkAuth(*creator, request, credentials, realm)); ASSERT_NO_THROW(response = auth_config->checkAuth(*creator, request));
ASSERT_TRUE(response); ASSERT_TRUE(response);
EXPECT_EQ("HTTP/1.0 401 Unauthorized\r\n" EXPECT_EQ("HTTP/1.0 401 Unauthorized\r\n"
@@ -293,13 +297,14 @@ TEST_F(HttpResponseCreatorAuthTest, notMatching) {
// This test verifies that matching credential is accepted. // This test verifies that matching credential is accepted.
TEST_F(HttpResponseCreatorAuthTest, matching) { TEST_F(HttpResponseCreatorAuthTest, matching) {
// Create credentials. // Create basic HTTP authentication configuration.
BasicHttpAuthPtr basic_auth; BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3"))); EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential()); const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
BasicHttpAuthMap credentials; auto cred = credentials.find("dGVzdDoxMjPCow==");
credentials[basic_auth->getCredential()] = "test"; EXPECT_NE(cred, credentials.end());
string realm = "ISC.ORG"; EXPECT_EQ(cred->second, "test");
auth_config->setRealm("ISC.ORG");
// Create request and finalize it. // Create request and finalize it.
HttpRequestPtr request(new HttpRequest()); HttpRequestPtr request(new HttpRequest());
@@ -307,13 +312,13 @@ TEST_F(HttpResponseCreatorAuthTest, matching) {
request->context()->http_version_minor_ = 0; request->context()->http_version_minor_ = 0;
request->context()->method_ = "GET"; request->context()->method_ = "GET";
request->context()->uri_ = "/foo"; request->context()->uri_ = "/foo";
BasicAuthHttpHeaderContext auth(*basic_auth); HttpHeaderContext auth("Authorization", "Basic dGVzdDoxMjPCow==");
request->context()->headers_.push_back(auth); request->context()->headers_.push_back(auth);
ASSERT_NO_THROW(request->finalize()); ASSERT_NO_THROW(request->finalize());
HttpResponsePtr response; HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());; TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
ASSERT_NO_THROW(response = checkAuth(*creator, request, credentials, realm)); ASSERT_NO_THROW(response = auth_config->checkAuth(*creator, request));
EXPECT_FALSE(response); EXPECT_FALSE(response);
addString("HTTP_CLIENT_REQUEST_AUTHORIZED received HTTP request " addString("HTTP_CLIENT_REQUEST_AUTHORIZED received HTTP request "