]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1304] Checkpoint before regen
authorFrancis Dupont <fdupont@isc.org>
Sun, 13 Sep 2020 20:03:20 +0000 (22:03 +0200)
committerFrancis Dupont <fdupont@isc.org>
Sun, 13 Sep 2020 20:03:20 +0000 (22:03 +0200)
23 files changed:
doc/examples/agent/comments.json
doc/examples/agent/simple.json
doc/sphinx/arm/agent.rst
src/bin/agent/agent_lexer.ll
src/bin/agent/agent_parser.cc
src/bin/agent/agent_parser.yy
src/bin/agent/ca_cfg_mgr.cc
src/bin/agent/ca_cfg_mgr.h
src/bin/agent/ca_response_creator.cc
src/bin/agent/parser_context.cc
src/bin/agent/parser_context.h
src/bin/agent/simple_parser.cc
src/bin/agent/simple_parser.h
src/bin/agent/tests/ca_cfg_mgr_unittests.cc
src/bin/agent/tests/ca_response_creator_unittests.cc
src/lib/http/Makefile.am
src/lib/http/auth_config.h [new file with mode: 0644]
src/lib/http/basic_auth_config.cc
src/lib/http/basic_auth_config.h
src/lib/http/response_creator_auth.cc [deleted file]
src/lib/http/response_creator_auth.h [deleted file]
src/lib/http/tests/basic_auth_config_unittests.cc
src/lib/http/tests/response_creator_unittests.cc

index afa706e27ccc513c2952202eda3d7190bd9a247d..01eb15bb2573e4b59e73b27722068a49ccd9265e 100644 (file)
 
         "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":
index db372485682d231aa40c9bd9499c220e10f23753..7aa741d33d8d50d253329b449544bf23d3938915 100644 (file)
         // 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
index a65d2edaa3b83b98dd153b3a46db7ead567b2f5c..dda3268a955590c17191a95b23b50e2c7b8eaad1 100644 (file)
@@ -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
index 962abf0c1c3f032b59f0b2d5e7d6fc405bf8be18..37d3df9fda3617973795326e53fa97f7bcc4f8bf 100644 (file)
@@ -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_);
index afc20206897feedca3cc9fd8c075c50f700e3bbd..ad0cdd029b09037d222ab7b40d92cebc64d375e5 100644 (file)
@@ -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;
index 36c686288b8a6cf5a52e9f514fc4c234f00c14e2..c6294e269120059ea34aab0105daff30f62a8af2 100644 (file)
@@ -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);
index 6cf058357b640dd653a2ca22ca95636d6e7febb0..507c3409f409b8a00e4a25556a64c3371b954ee3 100644 (file)
@@ -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);
index ae39e178604bc129fd9f6dc4fab2be2e3c461533..7c09ce397ab3438ad9569ccf35dd3c2a34e2f731 100644 (file)
@@ -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
+    ///
+    /// @note Only the basic HTTP authentication is supported.
     ///
-    /// @return Basic HTTP authentication realm.
-    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.
@@ -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.
index 75fb93b6cd1b65d67593ae03a7d647631bdbda77..702d031688608934f54ed4e3a463517a1f0f4dc4 100644 (file)
@@ -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);
+                    }
                 }
             }
         }
index c0a1a4aa5e1f8df98cda22c56f8b1dfc3d942411..ff517b56d4762dd6aaac3a54106c7fd97d90c8d1 100644 (file)
@@ -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)
 {
index ffcd8e3530d3aaa68f1fdd6b860f6ed850e8758c..a8fdea861e6fc52aa891ea704dc3db681fa6a254 100644 (file)
@@ -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.
index f347ea40f9e2ec3ac814d9b2914fac64c62ccbda..1890608d337802cac6a66c9a11e6b0d7bb06d37f 100644 (file)
@@ -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");
index f01aacca306d1f979c7d0e1e9b3d498be8d13dd0..6d29fbf20587cc877ce808f7ea4df1bfb420a79f 100644 (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
 // 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
index a77b0012a37f14dbae8f2bf81b03e6c892aa1a32..480e8c2234c191be59727b7734c13d5f32457313 100644 (file)
@@ -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());
-
-    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());
+    EXPECT_FALSE(ctx.getAuthConfig());
+    BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
+    EXPECT_NO_THROW(ctx.setAuthConfig(auth));
+
+    auth->setRealm("foobar");
+    auth->add("foo", "bar");
+    auth->add("test", "123\xa3");
+
+    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
index 35028cdb4a25ac45b651e037c0217556f7f5f50f..58bce6800e937de7b2f2d4d70cc526424791d661 100644 (file)
@@ -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_));
index 34e5f500ecaf86fef7b38aeeacec696326d4275b..08d3484af95af56ab93c44284f9dc7d6e5168076 100644 (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_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 \
diff --git a/src/lib/http/auth_config.h b/src/lib/http/auth_config.h
new file mode 100644 (file)
index 0000000..a2450b5
--- /dev/null
@@ -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
index d6213f5465e571b5015d070094b85c9ea3e6ee31..b43cb15a3927126baa20b3a7dddd06a342261d7e 100644 (file)
@@ -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
index c459ba3c5713c1321ddb2fd2d22b0614ccce8311..78d541061b9c572db7b7adaeb2ab2dc4118a6b99 100644 (file)
@@ -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
 
diff --git a/src/lib/http/response_creator_auth.cc b/src/lib/http/response_creator_auth.cc
deleted file mode 100644 (file)
index 4a19c14..0000000
+++ /dev/null
@@ -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
diff --git a/src/lib/http/response_creator_auth.h b/src/lib/http/response_creator_auth.h
deleted file mode 100644 (file)
index 26e1c1a..0000000
+++ /dev/null
@@ -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
index 5b7cd8605f7211a9c416d699e2cf601e9fca9a58..5a3f121d3bd814ae1691a320e6170fdf77c1ce07 100644 (file)
@@ -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);
 }
index 35836c22eb7cde8ae7c6bcf79beb14bf31aa6aa9..1b2f9fdfb87f4be85cabf3001022f61f0265b52d 100644 (file)
@@ -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 "