diff options
Diffstat (limited to 'src/lib/http/tests/basic_auth_config_unittests.cc')
-rw-r--r-- | src/lib/http/tests/basic_auth_config_unittests.cc | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/src/lib/http/tests/basic_auth_config_unittests.cc b/src/lib/http/tests/basic_auth_config_unittests.cc new file mode 100644 index 0000000..5df2170 --- /dev/null +++ b/src/lib/http/tests/basic_auth_config_unittests.cc @@ -0,0 +1,540 @@ +// Copyright (C) 2020-2022 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/basic_auth_config.h> +#include <testutils/gtest_utils.h> +#include <testutils/test_to_element.h> +#include <gtest/gtest.h> + +using namespace isc; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::http; +using namespace isc::test; +using namespace std; + +namespace { + +string data_dir(DATA_DIR); + +// Test that basic auth client works as expected. +TEST(BasicHttpAuthClientTest, basic) { + // Create a client. + ConstElementPtr ctx = Element::fromJSON("{ \"foo\": \"bar\" }"); + BasicHttpAuthClient client("foo", "bar", ctx); + + // Check it. + EXPECT_EQ("foo", client.getUser()); + EXPECT_EQ("", client.getUserFile()); + EXPECT_EQ("bar", client.getPassword()); + EXPECT_EQ("", client.getPasswordFile()); + EXPECT_FALSE(client.getPasswordFileOnly()); + EXPECT_TRUE(ctx->equals(*client.getContext())); + + // Check toElement. + ElementPtr expected = Element::createMap(); + expected->set("user", Element::create(string("foo"))); + expected->set("password", Element::create(string("bar"))); + expected->set("user-context", ctx); + runToElementTest<BasicHttpAuthClient>(expected, client); +} + +// Test that basic auth client with files works as expected. +TEST(BasicHttpAuthClientTest, basicFiles) { + // Create a client. + ConstElementPtr ctx = Element::fromJSON("{ \"foo\": \"bar\" }"); + BasicHttpAuthClient client("", "foo", "", "bar", false, ctx); + + // Check it. + EXPECT_EQ("", client.getUser()); + EXPECT_EQ("foo", client.getUserFile()); + EXPECT_EQ("", client.getPassword()); + EXPECT_EQ("bar", client.getPasswordFile()); + EXPECT_FALSE(client.getPasswordFileOnly()); + EXPECT_TRUE(ctx->equals(*client.getContext())); + + // Check toElement. + ElementPtr expected = Element::createMap(); + expected->set("user-file", Element::create(string("foo"))); + expected->set("password-file", Element::create(string("bar"))); + expected->set("user-context", ctx); + runToElementTest<BasicHttpAuthClient>(expected, client); +} + +// Test that basic auth client with one file works as expected. +TEST(BasicHttpAuthClientTest, basicOneFile) { + // Create a client. + ConstElementPtr ctx = Element::fromJSON("{ \"foo\": \"bar\" }"); + BasicHttpAuthClient client("", "", "", "foobar", true, ctx); + + // Check it. + EXPECT_EQ("", client.getUser()); + EXPECT_EQ("", client.getUserFile()); + EXPECT_EQ("", client.getPassword()); + EXPECT_EQ("foobar", client.getPasswordFile()); + EXPECT_TRUE(client.getPasswordFileOnly()); + EXPECT_TRUE(ctx->equals(*client.getContext())); + + // Check toElement. + ElementPtr expected = Element::createMap(); + expected->set("password-file", Element::create(string("foobar"))); + expected->set("user-context", ctx); + runToElementTest<BasicHttpAuthClient>(expected, client); +} + +// Test that basic auth configuration works as expected. +TEST(BasicHttpAuthConfigTest, basic) { + // Create a configuration. + BasicHttpAuthConfig config; + + // Initial configuration is empty. + EXPECT_TRUE(config.empty()); + EXPECT_TRUE(config.getRealm().empty()); + EXPECT_TRUE(config.getDirectory().empty()); + EXPECT_TRUE(config.getClientList().empty()); + EXPECT_TRUE(config.getCredentialMap().empty()); + + // Set the realm, directory and user context. + EXPECT_NO_THROW(config.setRealm("my-realm")); + EXPECT_EQ("my-realm", config.getRealm()); + EXPECT_NO_THROW(config.setDirectory("/tmp")); + EXPECT_EQ("/tmp", config.getDirectory()); + 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", "", false, ctx)); + EXPECT_FALSE(config.empty()); + + // Check the client. + ASSERT_EQ(1, config.getClientList().size()); + const BasicHttpAuthClient& client = config.getClientList().front(); + EXPECT_EQ("foo", client.getUser()); + EXPECT_EQ("bar", client.getPassword()); + EXPECT_TRUE(ctx->equals(*client.getContext())); + + // Check the credential. + ASSERT_NE(0, config.getCredentialMap().count("Zm9vOmJhcg==")); + string user; + EXPECT_NO_THROW(user = config.getCredentialMap().at("Zm9vOmJhcg==")); + EXPECT_EQ("foo", user); + + // Check toElement. + 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); + clients->add(elem); + expected->set("type", Element::create(string("basic"))); + expected->set("realm", Element::create(string("my-realm"))); + expected->set("directory", Element::create(string("/tmp"))); + expected->set("user-context", horse); + expected->set("clients", clients); + runToElementTest<BasicHttpAuthConfig>(expected, config); + + // Add a second client and test it. + EXPECT_NO_THROW(config.add("test", "", "123\xa3", "")); + ASSERT_EQ(2, config.getClientList().size()); + EXPECT_EQ("foo", config.getClientList().front().getUser()); + EXPECT_EQ("test", config.getClientList().back().getUser()); + ASSERT_NE(0, config.getCredentialMap().count("dGVzdDoxMjPCow==")); + + // Check clear. + config.clear(); + EXPECT_TRUE(config.empty()); + expected->set("clients", Element::createList()); + runToElementTest<BasicHttpAuthConfig>(expected, config); + + // Add clients again. + EXPECT_NO_THROW(config.add("test", "", "123\xa3", "")); + EXPECT_NO_THROW(config.add("foo", "", "bar", "", false, ctx)); + + // Check that toElement keeps add order. + ElementPtr elem0 = Element::createMap(); + elem0->set("user", Element::create(string("test"))); + elem0->set("password", Element::create(string("123\xa3"))); + clients = Element::createList(); + clients->add(elem0); + clients->add(elem); + expected->set("clients", clients); + runToElementTest<BasicHttpAuthConfig>(expected, config); +} + +// Test that basic auth configuration with files works as expected. +TEST(BasicHttpAuthConfigTest, basicFiles) { + // Create a configuration. + BasicHttpAuthConfig config; + + // Set the realm, directory and user context. + EXPECT_NO_THROW(config.setRealm("my-realm")); + EXPECT_EQ("my-realm", config.getRealm()); + EXPECT_NO_THROW(config.setDirectory(data_dir)); + EXPECT_EQ(data_dir, config.getDirectory()); + ConstElementPtr horse = Element::fromJSON("{ \"value\": \"a horse\" }"); + EXPECT_NO_THROW(config.setContext(horse)); + EXPECT_TRUE(horse->equals(*config.getContext())); + + // ':' in user id check is done during parsing + + // Add a client. + EXPECT_TRUE(config.empty()); + ConstElementPtr ctx = Element::fromJSON("{ \"foo\": \"bar\" }"); + EXPECT_NO_THROW(config.add("foo", "", "", "hiddenp", false, ctx)); + EXPECT_FALSE(config.empty()); + + // Check the client. + ASSERT_EQ(1, config.getClientList().size()); + const BasicHttpAuthClient& client = config.getClientList().front(); + EXPECT_EQ("foo", client.getUser()); + EXPECT_EQ("", client.getUserFile()); + EXPECT_EQ("", client.getPassword()); + EXPECT_EQ("hiddenp", client.getPasswordFile()); + EXPECT_FALSE(client.getPasswordFileOnly()); + EXPECT_TRUE(ctx->equals(*client.getContext())); + + // Check toElement. + ElementPtr expected = Element::createMap(); + ElementPtr clients = Element::createList(); + ElementPtr elem = Element::createMap(); + elem->set("user", Element::create(string("foo"))); + elem->set("password-file", Element::create(string("hiddenp"))); + elem->set("user-context", ctx); + clients->add(elem); + expected->set("type", Element::create(string("basic"))); + expected->set("realm", Element::create(string("my-realm"))); + expected->set("directory", Element::create(data_dir)); + expected->set("user-context", horse); + expected->set("clients", clients); + runToElementTest<BasicHttpAuthConfig>(expected, config); + + // Add a second client and test it. + EXPECT_NO_THROW(config.add("", "hiddenu", "", "hiddenp")); + ASSERT_EQ(2, config.getClientList().size()); + EXPECT_EQ("foo", config.getClientList().front().getUser()); + EXPECT_EQ("hiddenu", config.getClientList().back().getUserFile()); + + // Check clear. + config.clear(); + EXPECT_TRUE(config.empty()); + expected->set("clients", Element::createList()); + runToElementTest<BasicHttpAuthConfig>(expected, config); + + // Add clients again. + EXPECT_NO_THROW(config.add("", "hiddenu", "", "hiddenp")); + EXPECT_NO_THROW(config.add("foo", "", "", "hiddenp", false, ctx)); + + // Check that toElement keeps add order. + ElementPtr elem0 = Element::createMap(); + elem0->set("user-file", Element::create(string("hiddenu"))); + elem0->set("password-file", Element::create(string("hiddenp"))); + clients = Element::createList(); + clients->add(elem0); + clients->add(elem); + expected->set("clients", clients); + runToElementTest<BasicHttpAuthConfig>(expected, config); +} + +// Test that basic auth configuration parses. +TEST(BasicHttpAuthConfigTest, parse) { + BasicHttpAuthConfig config; + ElementPtr cfg; + + // No config is accepted. + EXPECT_NO_THROW(config.parse(cfg)); + EXPECT_TRUE(config.empty()); + EXPECT_TRUE(config.getClientList().empty()); + EXPECT_TRUE(config.getCredentialMap().empty()); + ElementPtr expected = Element::createMap(); + expected->set("type", Element::create(string("basic"))); + expected->set("realm", Element::create(string(""))); + expected->set("directory", Element::create(string(""))); + expected->set("clients", Element::createList()); + runToElementTest<BasicHttpAuthConfig>(expected, config); + + // 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, + "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 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 must be a string (:0:0)"); + cfg->set("realm", Element::create(string("my-realm"))); + EXPECT_NO_THROW(config.parse(cfg)); + + // The directory must be a string. + cfg->set("directory", Element::createMap()); + EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, + "directory must be a string (:0:0)"); + cfg->set("directory", Element::create(data_dir)); + 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. + clients_cfg = Element::createList(); + ElementPtr client_cfg = Element::createList(); + clients_cfg->add(client_cfg); + cfg->set("clients", clients_cfg); + EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, + "clients items must be maps (:0:0)"); + + // The user parameter is mandatory in client config + // without a password file. + client_cfg = Element::createMap(); + 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 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); + 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)"); + + // The user parameter must not be empty. + user_cfg = Element::create(string("")); + client_cfg = Element::createMap(); + client_cfg->set("user", user_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 be empty (:0:0)"); + + // The user parameter must not contain ':'. + user_cfg = Element::create(string("foo:bar")); + client_cfg = Element::createMap(); + client_cfg->set("user", user_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)"); + + // The user-file parameter must be a string. + ElementPtr user_file_cfg = Element::create(1); + client_cfg = Element::createMap(); + client_cfg->set("user-file", user_file_cfg); + clients_cfg = Element::createList(); + clients_cfg->add(client_cfg); + cfg->set("clients", clients_cfg); + EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, + "user-file must be a string (:0:0)"); + + // The user and user-file parameters are incompatible. + client_cfg = Element::createMap(); + client_cfg->set("user", user_cfg); + client_cfg->set("user-file", user_file_cfg); + clients_cfg = Element::createList(); + clients_cfg->add(client_cfg); + cfg->set("clients", clients_cfg); + EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, + "user (:0:0) and user-file (:0:0) are " + "mutually exclusive"); + + // The user-file parameter must not be empty. + user_file_cfg = Element::create(string("empty")); + client_cfg = Element::createMap(); + client_cfg->set("user-file", user_file_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 be empty from user-file " + "'empty' (:0:0)"); + + // The user-file parameter must not contain ':'. + user_file_cfg = Element::create(string("hiddens")); + client_cfg = Element::createMap(); + client_cfg->set("user-file", user_file_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 ':' from user-file " + "'hiddens' (:0:0)"); + + // Password is not required. + user_cfg = Element::create(string("foo")); + client_cfg = Element::createMap(); + client_cfg->set("user", user_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()); + config.clear(); + + // The password parameter must be a string. + ElementPtr password_cfg = Element::create(1); + client_cfg = Element::createMap(); + client_cfg->set("password", password_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)"); + + // Empty password is accepted. + password_cfg = Element::create(string("")); + client_cfg = Element::createMap(); + client_cfg->set("user", user_cfg); + client_cfg->set("password", password_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()); + config.clear(); + + // The password-file parameter must be a string. + ElementPtr password_file_cfg = Element::create(1); + client_cfg = Element::createMap(); + // user is not required when password-file is here. + client_cfg->set("password-file", password_file_cfg); + clients_cfg = Element::createList(); + clients_cfg->add(client_cfg); + cfg->set("clients", clients_cfg); + EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, + "password-file must be a string (:0:0)"); + + // The password and password-file parameters are incompatible. + client_cfg = Element::createMap(); + client_cfg->set("user", user_cfg); + client_cfg->set("password", password_cfg); + client_cfg->set("password-file", password_file_cfg); + clients_cfg = Element::createList(); + clients_cfg->add(client_cfg); + cfg->set("clients", clients_cfg); + EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, + "password (:0:0) and password-file (:0:0) are " + "mutually exclusive"); + + // Empty password-file is accepted. + password_file_cfg = Element::create(string("empty")); + client_cfg = Element::createMap(); + client_cfg->set("user", user_cfg); + client_cfg->set("password-file", password_file_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()); + config.clear(); + + // password-file is enough. + password_file_cfg = Element::create(string("hiddens")); + client_cfg = Element::createMap(); + client_cfg->set("password-file", password_file_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("test", config.getClientList().front().getPassword()); + config.clear(); + + // password-file only requires a ':' in the content. + password_file_cfg = Element::create(string("hiddenp")); + client_cfg = Element::createMap(); + client_cfg->set("password-file", password_file_cfg); + clients_cfg = Element::createList(); + clients_cfg->add(client_cfg); + cfg->set("clients", clients_cfg); + EXPECT_THROW_MSG(config.parse(cfg), DhcpConfigError, + "can't find the user id part in password-file " + "'hiddenp' (:0:0)"); + + // User context must be a map. + password_cfg = Element::create(string("bar")); + 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); + 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)"); + + // Check a working not empty config. + ctx = Element::fromJSON("{ \"foo\": \"bar\" }"); + client_cfg = Element::createMap(); + client_cfg->set("user", user_cfg); + client_cfg->set("password", password_cfg); + client_cfg->set("user-context", ctx); + clients_cfg = Element::createList(); + clients_cfg->add(client_cfg); + cfg->set("clients", clients_cfg); + EXPECT_NO_THROW(config.parse(cfg)); + runToElementTest<BasicHttpAuthConfig>(cfg, config); + + // Check a working not empty config with files. + config.clear(); + client_cfg = Element::createMap(); + user_file_cfg = Element::create(string("hiddenu")); + client_cfg->set("user-file", user_file_cfg); + client_cfg->set("password-file", password_file_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); +} + +} // end of anonymous namespace |