mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 14:05:33 +00:00
[#1304] Checkpoint before regen
This commit is contained in:
@@ -10,17 +10,22 @@
|
||||
|
||||
"http-host": "127.0.0.1",
|
||||
"http-port": 8000,
|
||||
"basic-authentication-realm": "kea-control-agent",
|
||||
|
||||
// In basic HTTP authentication
|
||||
"basic-authentications":
|
||||
[
|
||||
{
|
||||
"comment": "admin is authorized",
|
||||
"user": "admin",
|
||||
"password": "1234"
|
||||
}
|
||||
],
|
||||
// In authentication
|
||||
"authentication":
|
||||
{
|
||||
"comment": "basic HTTP authentication",
|
||||
|
||||
// In basic HTTP authentication clients
|
||||
"clients":
|
||||
[
|
||||
{
|
||||
"comment": "admin is authorized",
|
||||
"user": "admin",
|
||||
"password": "1234"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// In control socket
|
||||
"control-sockets":
|
||||
|
@@ -11,27 +11,35 @@
|
||||
// Another mandatory parameter is the HTTP port.
|
||||
"http-port": 8000,
|
||||
|
||||
// An optional parameter is the basic HTTP authentication realm.
|
||||
// Its default is "kea-control-agent".
|
||||
"basic-authentication-realm": "kea-control-agent",
|
||||
// Optional authentication.
|
||||
"authentication":
|
||||
{
|
||||
// Required authentication type. The only supported value is
|
||||
// basic for the basic HTTP authentication.
|
||||
"type": "basic",
|
||||
|
||||
// This list specifies the user ids and passwords to use for
|
||||
// basic HTTP authentication. If empty or not present any client
|
||||
// is authorized.
|
||||
"basic-authentications":
|
||||
[
|
||||
// This specifies an authorized client.
|
||||
{
|
||||
"comment": "admin is authorized",
|
||||
// An optional parameter is the basic HTTP authentication realm.
|
||||
// Its default is "kea-control-agent".
|
||||
"realm": "kea-control-agent",
|
||||
|
||||
// The user id must not be empty or contain the ':' character.
|
||||
// It is a mandatory parameter.
|
||||
"user": "admin",
|
||||
// This list specifies the user ids and passwords to use for
|
||||
// basic HTTP authentication. If empty or not present any client
|
||||
// is authorized.
|
||||
"clients":
|
||||
[
|
||||
// This specifies an authorized client.
|
||||
{
|
||||
"comment": "admin is authorized",
|
||||
|
||||
// If password is not specified an empty password is used.
|
||||
"password": "1234"
|
||||
}
|
||||
],
|
||||
// The user id must not be empty or contain the ':'
|
||||
// character. It is a mandatory parameter.
|
||||
"user": "admin",
|
||||
|
||||
// If password is not specified an empty password is used.
|
||||
"password": "1234"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// This map specifies where control channel of each server is configured
|
||||
// to listen on. See 'control-socket' object in the respective
|
||||
|
@@ -50,7 +50,15 @@ The following example demonstrates the basic CA configuration.
|
||||
"Control-agent": {
|
||||
"http-host": "10.20.30.40",
|
||||
"http-port": 8000,
|
||||
"basic-authentication-realm": "kea-control-agent",
|
||||
"authentication": {
|
||||
"type": "basic",
|
||||
"realm": "kea-control-agent",
|
||||
"clients": [
|
||||
{
|
||||
"user": "admin",
|
||||
"password": "1234"
|
||||
} ]
|
||||
},
|
||||
|
||||
"control-sockets": {
|
||||
"dhcp4": {
|
||||
@@ -69,12 +77,6 @@ The following example demonstrates the basic CA configuration.
|
||||
},
|
||||
},
|
||||
|
||||
"basic-authentications": [
|
||||
{
|
||||
"user": "admin",
|
||||
"password": "1234"
|
||||
} ],
|
||||
|
||||
"hooks-libraries": [
|
||||
{
|
||||
"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
|
||||
:ref:`agent-secure-connection` provide a stronger security.
|
||||
|
||||
The ``basic-authentication-realm`` is used for error message when
|
||||
the basic HTTP authentication is mandatory but the client is not
|
||||
The authentication is described in the ``authentication`` block
|
||||
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.
|
||||
|
||||
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
|
||||
specifies a user id and a password. The user id is mandatory, must
|
||||
be not empty and must not contain the colon (:) character. The
|
||||
|
@@ -203,9 +203,10 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
||||
\"user-context\" {
|
||||
switch(driver.ctx_) {
|
||||
case ParserContext::AGENT:
|
||||
case ParserContext::AUTHENTICATION;
|
||||
case ParserContext::CLIENTS:
|
||||
case ParserContext::SERVER:
|
||||
case ParserContext::LOGGERS:
|
||||
case ParserContext::CLIENTS:
|
||||
return AgentParser::make_USER_CONTEXT(driver.loc_);
|
||||
default:
|
||||
return AgentParser::make_STRING("user-context", driver.loc_);
|
||||
@@ -215,9 +216,10 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
|
||||
\"comment\" {
|
||||
switch(driver.ctx_) {
|
||||
case ParserContext::AGENT:
|
||||
case ParserContext::AUTHENTICATION;
|
||||
case ParserContext::CLIENTS:
|
||||
case ParserContext::SERVER:
|
||||
case ParserContext::LOGGERS:
|
||||
case ParserContext::CLIENTS:
|
||||
return AgentParser::make_COMMENT(driver.loc_);
|
||||
default:
|
||||
return AgentParser::make_STRING("comment", driver.loc_);
|
||||
|
@@ -1269,7 +1269,7 @@ namespace isc { namespace agent {
|
||||
#line 529 "agent_parser.yy"
|
||||
{
|
||||
// Add unique here
|
||||
ctx.enter(ctx.NO_KEYWORD);
|
||||
ctx.enter(ctx.NO_KEYWORDS);
|
||||
}
|
||||
#line 1275 "agent_parser.cc"
|
||||
break;
|
||||
|
@@ -513,6 +513,9 @@ auth_params: auth_param
|
||||
auth_param: auth_type
|
||||
| realm
|
||||
| clients
|
||||
| comment
|
||||
| user_context
|
||||
| unknown_map_entry
|
||||
;
|
||||
|
||||
auth_type: TYPE {
|
||||
@@ -528,7 +531,7 @@ auth_type_value: BASIC { $$ = ElementPtr(new StringElement("basic", ctx.loc2pos(
|
||||
|
||||
realm: REALM {
|
||||
// Add unique here
|
||||
ctx.enter(ctx.NO_KEYWORD);
|
||||
ctx.enter(ctx.NO_KEYWORDS);
|
||||
} COLON STRING {
|
||||
ElementPtr realm(new StringElement($4, ctx.loc2pos(@4)));
|
||||
ctx.stack_.back()->set("realm", realm);
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include <agent/simple_parser.h>
|
||||
#include <cc/simple_parser.h>
|
||||
#include <cc/command_interpreter.h>
|
||||
#include <http/basic_auth_config.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
using namespace isc::config;
|
||||
@@ -21,15 +22,13 @@ namespace isc {
|
||||
namespace agent {
|
||||
|
||||
CtrlAgentCfgContext::CtrlAgentCfgContext()
|
||||
: http_host_(""), http_port_(0), basic_auth_realm_("") {
|
||||
: http_host_(""), http_port_(0) {
|
||||
}
|
||||
|
||||
CtrlAgentCfgContext::CtrlAgentCfgContext(const CtrlAgentCfgContext& orig)
|
||||
: ConfigBase(), ctrl_sockets_(orig.ctrl_sockets_),
|
||||
http_host_(orig.http_host_), http_port_(orig.http_port_),
|
||||
basic_auth_realm_(orig.basic_auth_realm_),
|
||||
hooks_config_(orig.hooks_config_),
|
||||
basic_auth_config_(orig.basic_auth_config_) {
|
||||
hooks_config_(orig.hooks_config_), auth_config_(orig.auth_config_) {
|
||||
}
|
||||
|
||||
CtrlAgentCfgMgr::CtrlAgentCfgMgr()
|
||||
@@ -53,8 +52,8 @@ CtrlAgentCfgMgr::getConfigSummary(const uint32_t /*selection*/) {
|
||||
s << ctx->getControlSocketInfoSummary();
|
||||
|
||||
// Add something if authentication is required.
|
||||
const isc::http::BasicHttpAuthConfig& auth = ctx->getBasicAuthConfig();
|
||||
if (!auth.getClientList().empty()) {
|
||||
const isc::http::HttpAuthConfigPtr& auth = ctx->getAuthConfig();
|
||||
if (auth && !auth->empty()) {
|
||||
s << ", requires basic HTTP authentication";
|
||||
}
|
||||
|
||||
@@ -160,9 +159,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 basic-authentication-realm
|
||||
ca->set("basic-authentication-realm", Element::create(basic_auth_realm_));
|
||||
// Set hooks-libraries
|
||||
// Set authentication
|
||||
if (auth_config_) {
|
||||
ca->set("authentication", auth_config_->toElement());
|
||||
}
|
||||
ca->set("hooks-libraries", hooks_config_.toElement());
|
||||
// Set control-sockets
|
||||
ElementPtr control_sockets = Element::createMap();
|
||||
@@ -171,11 +171,6 @@ CtrlAgentCfgContext::toElement() const {
|
||||
control_sockets->set(si->first, socket);
|
||||
}
|
||||
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
|
||||
ElementPtr result = Element::createMap();
|
||||
result->set("Control-agent", ca);
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <cc/data.h>
|
||||
#include <hooks/hooks_config.h>
|
||||
#include <http/basic_auth_config.h>
|
||||
#include <http/auth_config.h>
|
||||
#include <process/d_cfg_mgr.h>
|
||||
#include <boost/pointer_cast.hpp>
|
||||
#include <map>
|
||||
@@ -99,18 +99,22 @@ public:
|
||||
return (http_port_);
|
||||
}
|
||||
|
||||
/// @brief Sets basic-authentication-realm parameter
|
||||
/// @brief Sets HTTP authentication configuration.
|
||||
///
|
||||
/// @param real Basic HTTP authentication realm
|
||||
void setBasicAuthRealm(const std::string& realm) {
|
||||
basic_auth_realm_ = realm;
|
||||
/// @note Only the basic HTTP authentication is supported.
|
||||
///
|
||||
/// @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.
|
||||
std::string getBasicAuthRealm() const {
|
||||
return (basic_auth_realm_);
|
||||
/// @note Only the basic HTTP authentication is supported.
|
||||
///
|
||||
/// @return HTTP authentication configuration.
|
||||
const isc::http::HttpAuthConfigPtr& getAuthConfig() const {
|
||||
return (auth_config_);
|
||||
}
|
||||
|
||||
/// @brief Returns non-const reference to configured hooks libraries.
|
||||
@@ -127,22 +131,6 @@ public:
|
||||
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
|
||||
///
|
||||
/// Returns an element which must parse into the same object, i.e.
|
||||
@@ -178,14 +166,11 @@ private:
|
||||
/// TCP port the CA should listen on.
|
||||
uint16_t http_port_;
|
||||
|
||||
/// Basic HTTP authentication realm.
|
||||
std::string basic_auth_realm_;
|
||||
|
||||
/// @brief Configured hooks libraries.
|
||||
isc::hooks::HooksConfig hooks_config_;
|
||||
|
||||
/// @brief Configured basic HTTP authentification clients.
|
||||
isc::http::BasicHttpAuthConfig basic_auth_config_;
|
||||
isc::http::HttpAuthConfigPtr auth_config_;
|
||||
};
|
||||
|
||||
/// @brief Ctrl Agent Configuration Manager.
|
||||
|
@@ -13,7 +13,6 @@
|
||||
#include <agent/ca_response_creator.h>
|
||||
#include <cc/data.h>
|
||||
#include <http/post_request_json.h>
|
||||
#include <http/response_creator_auth.h>
|
||||
#include <http/response_json.h>
|
||||
#include <boost/pointer_cast.hpp>
|
||||
#include <iostream>
|
||||
@@ -81,11 +80,11 @@ createDynamicHttpResponse(const ConstHttpRequestPtr& request) {
|
||||
if (cfgmgr) {
|
||||
ctx = cfgmgr->getCtrlAgentCfgContext();
|
||||
if (ctx) {
|
||||
const BasicHttpAuthConfig& auth = ctx->getBasicAuthConfig();
|
||||
const BasicHttpAuthMap& auth_map = auth.getCredentialMap();
|
||||
// Check authentication.
|
||||
http_response = checkAuth(*this, request, auth_map,
|
||||
ctx->getBasicAuthRealm());
|
||||
const HttpAuthConfigPtr& auth = ctx->getAuthConfig();
|
||||
if (auth) {
|
||||
// Check authentication.
|
||||
http_response = auth->checkAuth(*this, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -96,6 +96,21 @@ ParserContext::loc2pos(isc::agent::location& loc)
|
||||
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
|
||||
ParserContext::enter(const LexerContext& ctx)
|
||||
{
|
||||
|
@@ -147,6 +147,19 @@ public:
|
||||
/// @return Position in format accepted by Element
|
||||
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
|
||||
typedef enum {
|
||||
///< This one is used in pure JSON mode.
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include <cc/dhcp_config_error.h>
|
||||
#include <hooks/hooks_manager.h>
|
||||
#include <hooks/hooks_parser.h>
|
||||
#include <http/basic_auth_config.h>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace isc::data;
|
||||
@@ -36,15 +37,20 @@ namespace agent {
|
||||
///
|
||||
/// These are global Control Agent parameters.
|
||||
const SimpleDefaults AgentSimpleParser::AGENT_DEFAULTS = {
|
||||
{ "http-host", Element::string, "127.0.0.1" },
|
||||
{ "http-port", Element::integer, "8000" },
|
||||
{ "basic-authentication-realm", Element::string, "kea-control-agent" }
|
||||
{ "http-host", Element::string, "127.0.0.1" },
|
||||
{ "http-port", Element::integer, "8000" }
|
||||
};
|
||||
|
||||
/// @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.
|
||||
///
|
||||
const SimpleDefaults AgentSimpleParser::SOCKET_DEFAULTS = {
|
||||
{ "socket-type", Element::string, "unix"}
|
||||
{ "socket-type", Element::string, "unix" }
|
||||
};
|
||||
|
||||
/// @}
|
||||
@@ -59,6 +65,15 @@ size_t AgentSimpleParser::setAllDefaults(const isc::data::ElementPtr& global) {
|
||||
// Set global defaults first.
|
||||
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.
|
||||
ConstElementPtr sockets = global->get("control-sockets");
|
||||
if (sockets) {
|
||||
@@ -89,8 +104,6 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
|
||||
// Let's get the HTTP parameters first.
|
||||
ctx->setHttpHost(SimpleParser::getString(config, "http-host"));
|
||||
ctx->setHttpPort(SimpleParser::getIntType<uint16_t>(config, "http-port"));
|
||||
ctx->setBasicAuthRealm(SimpleParser::getString(config,
|
||||
"basic-authentication-realm"));
|
||||
|
||||
// Control sockets are second.
|
||||
ConstElementPtr ctrl_sockets = config->get("control-sockets");
|
||||
@@ -102,9 +115,13 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
|
||||
}
|
||||
|
||||
// Basic HTTP authentications are third.
|
||||
ConstElementPtr auth_config = config->get("basic-authentications");
|
||||
ctx->getBasicAuthConfig().clear();
|
||||
ctx->getBasicAuthConfig().parse(auth_config);
|
||||
ConstElementPtr auth_config = config->get("authentications");
|
||||
if (auth_config) {
|
||||
using namespace isc::http;
|
||||
BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
|
||||
auth->parse(auth_config);
|
||||
ctx->setAuthConfig(auth);
|
||||
}
|
||||
|
||||
// User context can be done at anytime.
|
||||
ConstElementPtr user_context = config->get("user-context");
|
||||
@@ -113,7 +130,6 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
|
||||
}
|
||||
|
||||
// Finally, let's get the hook libs!
|
||||
|
||||
using namespace isc::hooks;
|
||||
HooksConfig& libraries = ctx->getHooksConfig();
|
||||
ConstElementPtr hooks = config->get("hooks-libraries");
|
||||
|
@@ -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
|
||||
// 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
|
||||
static const isc::data::SimpleDefaults AGENT_DEFAULTS;
|
||||
static const isc::data::SimpleDefaults AUTH_DEFAULTS;
|
||||
static const isc::data::SimpleDefaults SOCKET_DEFAULTS;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -10,7 +10,9 @@
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <process/testutils/d_test_stubs.h>
|
||||
#include <process/d_cfg_mgr.h>
|
||||
#include <http/basic_auth_config.h>
|
||||
#include <agent/tests/test_libraries.h>
|
||||
#include <boost/pointer_cast.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -63,9 +65,6 @@ TEST(CtrlAgentCfgMgr, contextHttpParams) {
|
||||
|
||||
ctx.setHttpHost("alnitak");
|
||||
EXPECT_EQ("alnitak", ctx.getHttpHost());
|
||||
|
||||
ctx.setBasicAuthRealm("foobar");
|
||||
EXPECT_EQ("foobar", ctx.getBasicAuthRealm());
|
||||
}
|
||||
|
||||
// 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.setHttpHost("bellatrix"));
|
||||
EXPECT_NO_THROW(ctx.setBasicAuthRealm("foobar"));
|
||||
|
||||
HooksConfig& libs = ctx.getHooksConfig();
|
||||
string exp_name("testlib1.so");
|
||||
ConstElementPtr exp_param(new StringElement("myparam"));
|
||||
libs.add(exp_name, exp_param);
|
||||
|
||||
BasicHttpAuthConfig& auth = ctx.getBasicAuthConfig();
|
||||
auth.add("foo", "bar");
|
||||
BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
|
||||
auth->setRealm("foobar");
|
||||
auth->add("foo", "bar");
|
||||
EXPECT_NO_THROW(ctx.setAuthConfig(auth));
|
||||
|
||||
// Make a copy.
|
||||
ConfigPtr copy_base(ctx.clone());
|
||||
@@ -144,7 +144,6 @@ TEST(CtrlAgentCfgMgr, contextSocketInfoCopy) {
|
||||
// Now check the values returned
|
||||
EXPECT_EQ(12345, copy->getHttpPort());
|
||||
EXPECT_EQ("bellatrix", copy->getHttpHost());
|
||||
EXPECT_EQ("foobar", copy->getBasicAuthRealm());
|
||||
|
||||
// Check socket info
|
||||
ASSERT_TRUE(copy->getControlSocketInfo("d2"));
|
||||
@@ -161,9 +160,10 @@ TEST(CtrlAgentCfgMgr, contextSocketInfoCopy) {
|
||||
ASSERT_TRUE(libs2[0].second);
|
||||
EXPECT_EQ(exp_param->str(), libs2[0].second->str());
|
||||
|
||||
// Check basic HTTP authentication
|
||||
const BasicHttpAuthConfig& auth2 = copy->getBasicAuthConfig();
|
||||
EXPECT_EQ(auth.toElement()->str(), auth2.toElement()->str());
|
||||
// Check authentication
|
||||
const HttpAuthConfigPtr& auth2 = copy->getAuthConfig();
|
||||
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());
|
||||
}
|
||||
|
||||
// Test if the context can store and retrieve basic HTTP authentication clients.
|
||||
TEST(CtrlAgentCfgMgr, contextBasicAuth) {
|
||||
// Test if the context can store and retrieve basic HTTP authentication
|
||||
// configuration.
|
||||
TEST(CtrlAgentCfgMgr, contextAuthConfig) {
|
||||
CtrlAgentCfgContext ctx;
|
||||
|
||||
// By default there should be no authentication.
|
||||
BasicHttpAuthConfig& auth = ctx.getBasicAuthConfig();
|
||||
EXPECT_TRUE(auth.getClientList().empty());
|
||||
EXPECT_FALSE(ctx.getAuthConfig());
|
||||
BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
|
||||
EXPECT_NO_THROW(ctx.setAuthConfig(auth));
|
||||
|
||||
auth.add("foo", "bar");
|
||||
auth.add("test", "123\xa3");
|
||||
auth->setRealm("foobar");
|
||||
auth->add("foo", "bar");
|
||||
auth->add("test", "123\xa3");
|
||||
|
||||
const BasicHttpAuthConfig& stored_auth = ctx.getBasicAuthConfig();
|
||||
EXPECT_EQ(2, stored_auth.getClientList().size());
|
||||
|
||||
EXPECT_EQ(auth.toElement()->str(), stored_auth.toElement()->str());
|
||||
const HttpAuthConfigPtr& stored_auth = ctx.getAuthConfig();
|
||||
ASSERT_TRUE(stored_auth);
|
||||
EXPECT_FALSE(stored_auth->empty());
|
||||
EXPECT_EQ(auth->toElement()->str(), stored_auth->toElement()->str());
|
||||
}
|
||||
|
||||
/// Control Agent configurations used in tests.
|
||||
@@ -211,15 +214,13 @@ const char* AGENT_CONFIGS[] = {
|
||||
|
||||
// Configuration 1: http parameters only (no control sockets, not hooks)
|
||||
"{ \"http-host\": \"betelgeuse\",\n"
|
||||
" \"http-port\": 8001,\n"
|
||||
" \"basic-authentication-realm\": \"foobar\"\n"
|
||||
" \"http-port\": 8001\n"
|
||||
"}",
|
||||
|
||||
// Configuration 2: http and 1 socket
|
||||
"{\n"
|
||||
" \"http-host\": \"betelgeuse\",\n"
|
||||
" \"http-port\": 8001,\n"
|
||||
" \"basic-authentication-realm\": \"foobar\",\n"
|
||||
" \"control-sockets\": {\n"
|
||||
" \"dhcp4\": {\n"
|
||||
" \"socket-name\": \"/tmp/socket-v4\"\n"
|
||||
@@ -231,7 +232,6 @@ const char* AGENT_CONFIGS[] = {
|
||||
"{\n"
|
||||
" \"http-host\": \"betelgeuse\",\n"
|
||||
" \"http-port\": 8001,\n"
|
||||
" \"basic-authentication-realm\": \"foobar\",\n"
|
||||
" \"control-sockets\": {\n"
|
||||
" \"dhcp4\": {\n"
|
||||
" \"socket-name\": \"/tmp/socket-v4\"\n"
|
||||
@@ -251,7 +251,6 @@ const char* AGENT_CONFIGS[] = {
|
||||
"{\n"
|
||||
" \"http-host\": \"betelgeuse\",\n"
|
||||
" \"http-port\": 8001,\n"
|
||||
" \"basic-authentication-realm\": \"foobar\",\n"
|
||||
" \"control-sockets\": {\n"
|
||||
" \"dhcp4\": {\n"
|
||||
" \"socket-name\": \"/tmp/socket-v4\"\n"
|
||||
@@ -271,7 +270,6 @@ const char* AGENT_CONFIGS[] = {
|
||||
"{\n"
|
||||
" \"http-host\": \"betelgeuse\",\n"
|
||||
" \"http-port\": 8001,\n"
|
||||
" \"basic-authentication-realm\": \"foobar\",\n"
|
||||
" \"control-sockets\": {\n"
|
||||
" \"d2\": {\n"
|
||||
" \"socket-name\": \"/tmp/socket-d2\"\n"
|
||||
@@ -283,7 +281,6 @@ const char* AGENT_CONFIGS[] = {
|
||||
"{\n"
|
||||
" \"http-host\": \"betelgeuse\",\n"
|
||||
" \"http-port\": 8001,\n"
|
||||
" \"basic-authentication-realm\": \"foobar\",\n"
|
||||
" \"control-sockets\": {\n"
|
||||
" \"dhcp6\": {\n"
|
||||
" \"socket-name\": \"/tmp/socket-v6\"\n"
|
||||
@@ -291,25 +288,28 @@ const char* AGENT_CONFIGS[] = {
|
||||
" }\n"
|
||||
"}",
|
||||
|
||||
// Configuration 7: http, 1 socket and basic authentication
|
||||
// Configuration 7: http, 1 socket and authentication
|
||||
"{\n"
|
||||
" \"http-host\": \"betelgeuse\",\n"
|
||||
" \"http-port\": 8001,\n"
|
||||
" \"basic-authentication-realm\": \"foobar\",\n"
|
||||
" \"authentication\": {\n"
|
||||
" \"type\": \"basic\",\n"
|
||||
" \"realm\": \"foobar\",\n"
|
||||
" \"clients\": ["
|
||||
" {"
|
||||
" \"user\": \"foo\",\n"
|
||||
" \"password\": \"bar\"\n"
|
||||
" },{\n"
|
||||
" \"user\": \"test\",\n"
|
||||
" \"password\": \"123\\u00a3\"\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" },\n"
|
||||
" \"control-sockets\": {\n"
|
||||
" \"dhcp4\": {\n"
|
||||
" \"socket-name\": \"/tmp/socket-v4\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"basic-authentications\": ["
|
||||
" {"
|
||||
" \"user\": \"foo\",\n"
|
||||
" \"password\": \"bar\"\n"
|
||||
" },{\n"
|
||||
" \"user\": \"test\",\n"
|
||||
" \"password\": \"123\\u00a3\"\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
"}",
|
||||
|
||||
// Configuration 8: http and 2 sockets with user contexts and comments
|
||||
@@ -317,7 +317,21 @@ const char* AGENT_CONFIGS[] = {
|
||||
" \"user-context\": { \"comment\": \"Indirect comment\" },\n"
|
||||
" \"http-host\": \"betelgeuse\",\n"
|
||||
" \"http-port\": 8001,\n"
|
||||
" \"basic-authentication-realm\": \"foobar\",\n"
|
||||
" \"authentication\": {\n"
|
||||
" \"comment\": \"basic HTTP authentication\",\n"
|
||||
" \"type\": \"basic\",\n"
|
||||
" \"realm\": \"foobar\",\n"
|
||||
" \"clients\": ["
|
||||
" {"
|
||||
" \"comment\": \"foo is authorized\",\n"
|
||||
" \"user\": \"foo\",\n"
|
||||
" \"password\": \"bar\"\n"
|
||||
" },{\n"
|
||||
" \"user\": \"test\",\n"
|
||||
" \"user-context\": { \"no password\": true }\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" },\n"
|
||||
" \"control-sockets\": {\n"
|
||||
" \"dhcp4\": {\n"
|
||||
" \"comment\": \"dhcp4 socket\",\n"
|
||||
@@ -327,17 +341,7 @@ const char* AGENT_CONFIGS[] = {
|
||||
" \"socket-name\": \"/tmp/socket-v6\",\n"
|
||||
" \"user-context\": { \"version\": 1 }\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"basic-authentications\": ["
|
||||
" {"
|
||||
" \"comment\": \"foo is authorized\",\n"
|
||||
" \"user\": \"foo\",\n"
|
||||
" \"password\": \"bar\"\n"
|
||||
" },{\n"
|
||||
" \"user\": \"test\",\n"
|
||||
" \"user-context\": { \"no password\": true }\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
"}"
|
||||
};
|
||||
|
||||
@@ -389,7 +393,6 @@ TEST_F(AgentParserTest, configParseHttpOnly) {
|
||||
ASSERT_TRUE(ctx);
|
||||
EXPECT_EQ("betelgeuse", ctx->getHttpHost());
|
||||
EXPECT_EQ(8001, ctx->getHttpPort());
|
||||
EXPECT_EQ("foobar", ctx->getBasicAuthRealm());
|
||||
}
|
||||
|
||||
// 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
|
||||
// loaded.
|
||||
TEST_F(AgentParserTest, configParseBasicAuth) {
|
||||
TEST_F(AgentParserTest, configParseAuth) {
|
||||
configParse(AGENT_CONFIGS[7], 0);
|
||||
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
|
||||
auto credentials = auth.getCredentialMap();
|
||||
auto credentials = basic_auth->getCredentialMap();
|
||||
EXPECT_EQ(2, credentials.size());
|
||||
std::string user;
|
||||
EXPECT_NO_THROW(user = credentials.at("Zm9vOmJhcg=="));
|
||||
@@ -498,9 +508,10 @@ TEST_F(AgentParserTest, configParseBasicAuth) {
|
||||
|
||||
// Check clients.
|
||||
BasicHttpAuthConfig expected;
|
||||
expected.setRealm("foobar");
|
||||
expected.add("foo", "bar");
|
||||
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.
|
||||
@@ -538,22 +549,33 @@ TEST_F(AgentParserTest, comments) {
|
||||
ASSERT_TRUE(ctx6->get("version"));
|
||||
EXPECT_EQ("1", ctx6->get("version")->str());
|
||||
|
||||
// Check basic HTTP authentication comment.
|
||||
const BasicHttpAuthConfig& auth = agent_ctx->getBasicAuthConfig();
|
||||
auto clients = auth.getClientList();
|
||||
ASSERT_EQ(2, clients.size());
|
||||
ConstElementPtr ctx7 = clients.front().getContext();
|
||||
// Check authentication comment.
|
||||
const HttpAuthConfigPtr& auth = agent_ctx->getAuthConfig();
|
||||
ASSERT_TRUE(auth);
|
||||
ConstElementPtr ctx7 = auth->getContext();
|
||||
ASSERT_TRUE(ctx7);
|
||||
ASSERT_EQ(1, ctx7->size());
|
||||
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.
|
||||
ConstElementPtr ctx8 = clients.back().getContext();
|
||||
// Check basic HTTP authentication client comment.
|
||||
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_EQ(1, ctx8->size());
|
||||
ASSERT_TRUE(ctx8->get("no password"));
|
||||
EXPECT_EQ("true", ctx8->get("no password")->str());
|
||||
ASSERT_TRUE(ctx8->get("comment"));
|
||||
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
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include <agent/ca_command_mgr.h>
|
||||
#include <agent/ca_response_creator.h>
|
||||
#include <cc/command_interpreter.h>
|
||||
#include <http/basic_auth_config.h>
|
||||
#include <http/post_request.h>
|
||||
#include <http/post_request_json.h>
|
||||
#include <http/response_json.h>
|
||||
@@ -250,9 +251,10 @@ TEST_F(CtrlAgentResponseCreatorTest, noAuth) {
|
||||
// Require authentication.
|
||||
CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
|
||||
ASSERT_TRUE(ctx);
|
||||
ctx->setBasicAuthRealm("ISC.ORG");
|
||||
BasicHttpAuthConfig& auth = ctx->getBasicAuthConfig();
|
||||
auth.add("foo", "bar");
|
||||
BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
|
||||
ASSERT_NO_THROW(ctx->setAuthConfig(auth));
|
||||
auth->setRealm("ISC.ORG");
|
||||
auth->add("foo", "bar");
|
||||
|
||||
HttpResponsePtr response;
|
||||
ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
|
||||
@@ -290,8 +292,11 @@ TEST_F(CtrlAgentResponseCreatorTest, basicAuth) {
|
||||
// Require authentication.
|
||||
CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
|
||||
ASSERT_TRUE(ctx);
|
||||
BasicHttpAuthConfig& auth = ctx->getBasicAuthConfig();
|
||||
auth.add("foo", "bar");
|
||||
BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
|
||||
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;
|
||||
ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
|
||||
|
@@ -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_context.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_json.cc response_json.h
|
||||
libkea_http_la_SOURCES += url.cc url.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_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
@@ -91,6 +91,7 @@ endif
|
||||
# Specify the headers for copying into the installation directory tree.
|
||||
libkea_http_includedir = $(pkgincludedir)/http
|
||||
libkea_http_include_HEADERS = \
|
||||
auth_config.h \
|
||||
basic_auth.h \
|
||||
basic_auth_config.h \
|
||||
client.h \
|
||||
@@ -115,7 +116,6 @@ libkea_http_include_HEADERS = \
|
||||
response.h \
|
||||
response_context.h \
|
||||
response_creator.h \
|
||||
response_creator_auth.h \
|
||||
response_creator_factory.h \
|
||||
response_json.h \
|
||||
response_parser.h \
|
||||
|
84
src/lib/http/auth_config.h
Normal file
84
src/lib/http/auth_config.h
Normal 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
|
@@ -7,10 +7,13 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <http/basic_auth_config.h>
|
||||
#include <http/http_log.h>
|
||||
#include <util/strutil.h>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::util;
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
@@ -56,13 +59,30 @@ BasicHttpAuthConfig::clear() {
|
||||
map_.clear();
|
||||
}
|
||||
|
||||
bool
|
||||
BasicHttpAuthConfig::empty() const {
|
||||
return (map_.empty());
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
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_) {
|
||||
result->add(client.toElement());
|
||||
clients->add(client.toElement());
|
||||
}
|
||||
result->set("clients", clients);
|
||||
|
||||
return (result);
|
||||
}
|
||||
@@ -72,21 +92,68 @@ BasicHttpAuthConfig::parse(const ConstElementPtr& config) {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
if (config->getType() != Element::list) {
|
||||
isc_throw(DhcpConfigError, "basic-authentications must be a list ("
|
||||
if (config->getType() != Element::map) {
|
||||
isc_throw(DhcpConfigError, "authentication must be a map ("
|
||||
<< 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) {
|
||||
isc_throw(DhcpConfigError, "basic-authentications items must be "
|
||||
<< "maps (" << client->getPosition() << ")");
|
||||
isc_throw(DhcpConfigError, "clients items must be maps ("
|
||||
<< client->getPosition() << ")");
|
||||
}
|
||||
|
||||
// user
|
||||
ConstElementPtr user_cfg = client->get("user");
|
||||
if (!user_cfg) {
|
||||
isc_throw(DhcpConfigError, "user is required in "
|
||||
<< "basic-authentications items ("
|
||||
isc_throw(DhcpConfigError, "user is required in clients items ("
|
||||
<< client->getPosition() << ")");
|
||||
}
|
||||
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
|
||||
|
@@ -7,10 +7,7 @@
|
||||
#ifndef HTTP_BASIC_AUTH_CONFIG_H
|
||||
#define HTTP_BASIC_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/auth_config.h>
|
||||
#include <http/basic_auth.h>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
@@ -67,9 +64,12 @@ private:
|
||||
typedef std::list<BasicHttpAuthClient> BasicHttpAuthClientList;
|
||||
|
||||
/// @brief Basic HTTP authentication configuration.
|
||||
class BasicHttpAuthConfig : public isc::data::CfgToElement {
|
||||
class BasicHttpAuthConfig : public HttpAuthConfig {
|
||||
public:
|
||||
|
||||
/// @brief Destructor.
|
||||
virtual ~BasicHttpAuthConfig() { }
|
||||
|
||||
/// @brief Add a client configuration.
|
||||
///
|
||||
/// @param user User id
|
||||
@@ -80,8 +80,13 @@ public:
|
||||
const std::string& password,
|
||||
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.
|
||||
void clear();
|
||||
virtual void clear();
|
||||
|
||||
/// @brief Returns the list of client configuration.
|
||||
///
|
||||
@@ -109,6 +114,15 @@ public:
|
||||
/// @return A pointer to unparsed basic HTTP authentication configuration.
|
||||
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:
|
||||
|
||||
/// @brief The list of basic HTTP authentication client configuration.
|
||||
@@ -118,6 +132,9 @@ private:
|
||||
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
|
||||
|
||||
|
@@ -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
|
@@ -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
|
@@ -44,15 +44,26 @@ TEST(BasicHttpAuthConfigTest, basic) {
|
||||
BasicHttpAuthConfig config;
|
||||
|
||||
// Initial configuration is empty.
|
||||
EXPECT_TRUE(config.empty());
|
||||
EXPECT_TRUE(config.getRealm().empty());
|
||||
EXPECT_TRUE(config.getClientList().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 ':'.
|
||||
EXPECT_THROW(config.add("foo:", "bar"), BadValue);
|
||||
|
||||
// Add a client.
|
||||
EXPECT_TRUE(config.empty());
|
||||
ConstElementPtr ctx = Element::fromJSON("{ \"foo\": \"bar\" }");
|
||||
EXPECT_NO_THROW(config.add("foo", "bar", ctx));
|
||||
EXPECT_FALSE(config.empty());
|
||||
|
||||
// Check the client.
|
||||
ASSERT_EQ(1, config.getClientList().size());
|
||||
@@ -68,12 +79,17 @@ TEST(BasicHttpAuthConfigTest, basic) {
|
||||
EXPECT_EQ("foo", user);
|
||||
|
||||
// Check toElement.
|
||||
ElementPtr expected = Element::createList();
|
||||
ElementPtr expected = Element::createMap();
|
||||
ElementPtr clients = Element::createList();
|
||||
ElementPtr elem = Element::createMap();
|
||||
elem->set("user", Element::create(string("foo")));
|
||||
elem->set("password", Element::create(string("bar")));
|
||||
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);
|
||||
|
||||
// Add a second client and test it.
|
||||
@@ -85,7 +101,8 @@ TEST(BasicHttpAuthConfigTest, basic) {
|
||||
|
||||
// Check clear.
|
||||
config.clear();
|
||||
expected = Element::createList();
|
||||
EXPECT_TRUE(config.empty());
|
||||
expected->set("clients", Element::createList());
|
||||
runToElementTest<BasicHttpAuthConfig>(expected, config);
|
||||
|
||||
// Add clients again.
|
||||
@@ -96,8 +113,10 @@ TEST(BasicHttpAuthConfigTest, basic) {
|
||||
ElementPtr elem0 = Element::createMap();
|
||||
elem0->set("user", Element::create(string("test")));
|
||||
elem0->set("password", Element::create(string("123\xa3")));
|
||||
expected->add(elem0);
|
||||
expected->add(elem);
|
||||
clients = Element::createList();
|
||||
clients->add(elem0);
|
||||
clients->add(elem);
|
||||
expected->set("clients", clients);
|
||||
runToElementTest<BasicHttpAuthConfig>(expected, config);
|
||||
}
|
||||
|
||||
@@ -108,35 +127,83 @@ TEST(BasicHttpAuthConfigTest, parse) {
|
||||
|
||||
// No config is accepted.
|
||||
EXPECT_NO_THROW(config.parse(cfg));
|
||||
EXPECT_TRUE(config.empty());
|
||||
EXPECT_TRUE(config.getClientList().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();
|
||||
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.
|
||||
cfg = Element::createList();
|
||||
clients_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,
|
||||
"basic-authentications items must be maps (:0:0)");
|
||||
"clients items must be maps (:0:0)");
|
||||
|
||||
// The user parameter is mandatory in client config.
|
||||
client_cfg = Element::createMap();
|
||||
cfg = Element::createList();
|
||||
cfg->add(client_cfg);
|
||||
clients_cfg = Element::createList();
|
||||
clients_cfg->add(client_cfg);
|
||||
cfg->set("clients", clients_cfg);
|
||||
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.
|
||||
ElementPtr user_cfg = Element::create(1);
|
||||
client_cfg = Element::createMap();
|
||||
client_cfg->set("user", user_cfg);
|
||||
cfg = Element::createList();
|
||||
cfg->add(client_cfg);
|
||||
clients_cfg = Element::createList();
|
||||
clients_cfg->add(client_cfg);
|
||||
cfg->set("clients", clients_cfg);
|
||||
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
|
||||
"user must be a string (:0:0)");
|
||||
|
||||
@@ -144,8 +211,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
|
||||
user_cfg = Element::create(string(""));
|
||||
client_cfg = Element::createMap();
|
||||
client_cfg->set("user", user_cfg);
|
||||
cfg = Element::createList();
|
||||
cfg->add(client_cfg);
|
||||
clients_cfg = Element::createList();
|
||||
clients_cfg->add(client_cfg);
|
||||
cfg->set("clients", clients_cfg);
|
||||
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
|
||||
"user must be not be empty (:0:0)");
|
||||
|
||||
@@ -153,8 +221,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
|
||||
user_cfg = Element::create(string("foo:bar"));
|
||||
client_cfg = Element::createMap();
|
||||
client_cfg->set("user", user_cfg);
|
||||
cfg = Element::createList();
|
||||
cfg->add(client_cfg);
|
||||
clients_cfg = Element::createList();
|
||||
clients_cfg->add(client_cfg);
|
||||
cfg->set("clients", clients_cfg);
|
||||
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
|
||||
"user must not contain a ':': 'foo:bar' (:0:0)");
|
||||
|
||||
@@ -162,8 +231,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
|
||||
user_cfg = Element::create(string("foo"));
|
||||
client_cfg = Element::createMap();
|
||||
client_cfg->set("user", user_cfg);
|
||||
cfg = Element::createList();
|
||||
cfg->add(client_cfg);
|
||||
clients_cfg = Element::createList();
|
||||
clients_cfg->add(client_cfg);
|
||||
cfg->set("clients", clients_cfg);
|
||||
EXPECT_NO_THROW(config.parse(cfg));
|
||||
ASSERT_EQ(1, config.getClientList().size());
|
||||
EXPECT_EQ("", config.getClientList().front().getPassword());
|
||||
@@ -174,8 +244,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
|
||||
client_cfg = Element::createMap();
|
||||
client_cfg->set("user", user_cfg);
|
||||
client_cfg->set("password", password_cfg);
|
||||
cfg = Element::createList();
|
||||
cfg->add(client_cfg);
|
||||
clients_cfg = Element::createList();
|
||||
clients_cfg->add(client_cfg);
|
||||
cfg->set("clients", clients_cfg);
|
||||
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
|
||||
"password must be a string (:0:0)");
|
||||
|
||||
@@ -184,8 +255,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
|
||||
client_cfg = Element::createMap();
|
||||
client_cfg->set("user", user_cfg);
|
||||
client_cfg->set("password", password_cfg);
|
||||
cfg = Element::createList();
|
||||
cfg->add(client_cfg);
|
||||
clients_cfg = Element::createList();
|
||||
clients_cfg->add(client_cfg);
|
||||
cfg->set("clients", clients_cfg);
|
||||
EXPECT_NO_THROW(config.parse(cfg));
|
||||
ASSERT_EQ(1, config.getClientList().size());
|
||||
EXPECT_EQ("", config.getClientList().front().getPassword());
|
||||
@@ -193,13 +265,14 @@ TEST(BasicHttpAuthConfigTest, parse) {
|
||||
|
||||
// User context must be a map.
|
||||
password_cfg = Element::create(string("bar"));
|
||||
ElementPtr ctx = Element::createList();
|
||||
ctx = Element::createList();
|
||||
client_cfg = Element::createMap();
|
||||
client_cfg->set("user", user_cfg);
|
||||
client_cfg->set("password", password_cfg);
|
||||
client_cfg->set("user-context", ctx);
|
||||
cfg = Element::createList();
|
||||
cfg->add(client_cfg);
|
||||
clients_cfg = Element::createList();
|
||||
clients_cfg->add(client_cfg);
|
||||
cfg->set("clients", clients_cfg);
|
||||
EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError,
|
||||
"user-context must be a map (:0:0)");
|
||||
|
||||
@@ -209,8 +282,9 @@ TEST(BasicHttpAuthConfigTest, parse) {
|
||||
client_cfg->set("user", user_cfg);
|
||||
client_cfg->set("password", password_cfg);
|
||||
client_cfg->set("user-context", ctx);
|
||||
cfg = Element::createList();
|
||||
cfg->add(client_cfg);
|
||||
clients_cfg = Element::createList();
|
||||
clients_cfg->add(client_cfg);
|
||||
cfg->set("clients", clients_cfg);
|
||||
EXPECT_NO_THROW(config.parse(cfg));
|
||||
runToElementTest<BasicHttpAuthConfig>(cfg, config);
|
||||
}
|
||||
|
@@ -6,11 +6,11 @@
|
||||
|
||||
#include <config.h>
|
||||
#include <http/basic_auth.h>
|
||||
#include <http/basic_auth_config.h>
|
||||
#include <http/http_types.h>
|
||||
#include <http/request.h>
|
||||
#include <http/response.h>
|
||||
#include <http/response_creator.h>
|
||||
#include <http/response_creator_auth.h>
|
||||
#include <http/response_json.h>
|
||||
#include <http/tests/response_test.h>
|
||||
#include <testutils/log_utils.h>
|
||||
@@ -140,13 +140,14 @@ class HttpResponseCreatorAuthTest : public LogContentTest { };
|
||||
// This test verifies that missing required authentication header gives
|
||||
// unauthorized error.
|
||||
TEST_F(HttpResponseCreatorAuthTest, noAuth) {
|
||||
// Create credentials.
|
||||
BasicHttpAuthPtr basic_auth;
|
||||
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3")));
|
||||
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential());
|
||||
BasicHttpAuthMap credentials;
|
||||
credentials[basic_auth->getCredential()] = "test";
|
||||
string realm = "ISC.ORG";
|
||||
// Create basic HTTP authentication configuration.
|
||||
BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
|
||||
EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
|
||||
const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
|
||||
auto cred = credentials.find("dGVzdDoxMjPCow==");
|
||||
EXPECT_NE(cred, credentials.end());
|
||||
EXPECT_EQ(cred->second, "test");
|
||||
auth_config->setRealm("ISC.ORG");
|
||||
|
||||
// Create request and finalize it.
|
||||
HttpRequestPtr request(new HttpRequest());
|
||||
@@ -158,7 +159,7 @@ TEST_F(HttpResponseCreatorAuthTest, noAuth) {
|
||||
|
||||
HttpResponsePtr response;
|
||||
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);
|
||||
|
||||
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.
|
||||
TEST_F(HttpResponseCreatorAuthTest, authTooShort) {
|
||||
// Create credentials.
|
||||
BasicHttpAuthPtr basic_auth;
|
||||
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3")));
|
||||
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential());
|
||||
BasicHttpAuthMap credentials;
|
||||
credentials[basic_auth->getCredential()] = "test";
|
||||
string realm = "ISC.ORG";
|
||||
// Create basic HTTP authentication configuration.
|
||||
BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
|
||||
EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
|
||||
const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
|
||||
auto cred = credentials.find("dGVzdDoxMjPCow==");
|
||||
EXPECT_NE(cred, credentials.end());
|
||||
EXPECT_EQ(cred->second, "test");
|
||||
auth_config->setRealm("ISC.ORG");
|
||||
|
||||
// Create request and finalize it.
|
||||
HttpRequestPtr request(new HttpRequest());
|
||||
@@ -196,7 +198,7 @@ TEST_F(HttpResponseCreatorAuthTest, authTooShort) {
|
||||
|
||||
HttpResponsePtr response;
|
||||
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);
|
||||
|
||||
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.
|
||||
TEST_F(HttpResponseCreatorAuthTest, badScheme) {
|
||||
// Create credentials.
|
||||
BasicHttpAuthPtr basic_auth;
|
||||
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3")));
|
||||
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential());
|
||||
BasicHttpAuthMap credentials;
|
||||
credentials[basic_auth->getCredential()] = "test";
|
||||
string realm = "ISC.ORG";
|
||||
// Create basic HTTP authentication configuration.
|
||||
BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
|
||||
EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
|
||||
const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
|
||||
auto cred = credentials.find("dGVzdDoxMjPCow==");
|
||||
EXPECT_NE(cred, credentials.end());
|
||||
EXPECT_EQ(cred->second, "test");
|
||||
auth_config->setRealm("ISC.ORG");
|
||||
|
||||
// Create request and finalize it.
|
||||
HttpRequestPtr request(new HttpRequest());
|
||||
@@ -235,7 +238,7 @@ TEST_F(HttpResponseCreatorAuthTest, badScheme) {
|
||||
|
||||
HttpResponsePtr response;
|
||||
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);
|
||||
|
||||
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.
|
||||
TEST_F(HttpResponseCreatorAuthTest, notMatching) {
|
||||
// Create credentials.
|
||||
BasicHttpAuthPtr basic_auth;
|
||||
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3")));
|
||||
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential());
|
||||
BasicHttpAuthMap credentials;
|
||||
credentials[basic_auth->getCredential()] = "test";
|
||||
string realm = "ISC.ORG";
|
||||
// Create basic HTTP authentication configuration.
|
||||
BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
|
||||
EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
|
||||
const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
|
||||
auto cred = credentials.find("dGVzdDoxMjPCow==");
|
||||
EXPECT_NE(cred, credentials.end());
|
||||
EXPECT_EQ(cred->second, "test");
|
||||
auth_config->setRealm("ISC.ORG");
|
||||
|
||||
// Create request and finalize it.
|
||||
HttpRequestPtr request(new HttpRequest());
|
||||
@@ -275,7 +279,7 @@ TEST_F(HttpResponseCreatorAuthTest, notMatching) {
|
||||
|
||||
HttpResponsePtr response;
|
||||
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);
|
||||
|
||||
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.
|
||||
TEST_F(HttpResponseCreatorAuthTest, matching) {
|
||||
// Create credentials.
|
||||
BasicHttpAuthPtr basic_auth;
|
||||
EXPECT_NO_THROW(basic_auth.reset(new BasicHttpAuth("test", "123\xa3")));
|
||||
EXPECT_EQ("dGVzdDoxMjPCow==", basic_auth->getCredential());
|
||||
BasicHttpAuthMap credentials;
|
||||
credentials[basic_auth->getCredential()] = "test";
|
||||
string realm = "ISC.ORG";
|
||||
// Create basic HTTP authentication configuration.
|
||||
BasicHttpAuthConfigPtr auth_config(new BasicHttpAuthConfig());
|
||||
EXPECT_NO_THROW(auth_config->add("test", "123\xa3"));
|
||||
const BasicHttpAuthMap& credentials = auth_config->getCredentialMap();
|
||||
auto cred = credentials.find("dGVzdDoxMjPCow==");
|
||||
EXPECT_NE(cred, credentials.end());
|
||||
EXPECT_EQ(cred->second, "test");
|
||||
auth_config->setRealm("ISC.ORG");
|
||||
|
||||
// Create request and finalize it.
|
||||
HttpRequestPtr request(new HttpRequest());
|
||||
@@ -307,13 +312,13 @@ TEST_F(HttpResponseCreatorAuthTest, matching) {
|
||||
request->context()->http_version_minor_ = 0;
|
||||
request->context()->method_ = "GET";
|
||||
request->context()->uri_ = "/foo";
|
||||
BasicAuthHttpHeaderContext auth(*basic_auth);
|
||||
HttpHeaderContext auth("Authorization", "Basic dGVzdDoxMjPCow==");
|
||||
request->context()->headers_.push_back(auth);
|
||||
ASSERT_NO_THROW(request->finalize());
|
||||
|
||||
HttpResponsePtr response;
|
||||
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);
|
||||
|
||||
addString("HTTP_CLIENT_REQUEST_AUTHORIZED received HTTP request "
|
||||
|
Reference in New Issue
Block a user