-// Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-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
ADD_FAILURE() << "CtrlAgentResponseCreator::createNewHttpRequest"
" returns NULL!";
}
+ HttpRequest::recordBasicAuth = true;
// Initialize process and cfgmgr.
try {
initProcess();
///
/// Removes registered commands from the command manager.
virtual ~CtrlAgentResponseCreatorTest() {
+ HttpRequest::recordBasicAuth = false;
CtrlAgentCommandMgr::instance().deregisterAll();
HooksManager::prepareUnloadLibraries();
static_cast<void>(HooksManager::unloadLibraries());
HttpResponsePtr response;
ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ EXPECT_TRUE(request_->getBasicAuth().empty());
ASSERT_TRUE(response);
// Response must be convertible to HttpResponseJsonPtr.
HttpResponsePtr response;
ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ EXPECT_EQ("foo", request_->getBasicAuth());
ASSERT_TRUE(response);
// Response must be convertible to HttpResponseJsonPtr.
HttpResponsePtr response;
ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ EXPECT_TRUE(request_->getBasicAuth().empty());
ASSERT_TRUE(response);
// Request should have no extra.
HttpResponsePtr response;
ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ EXPECT_EQ("foo", request_->getBasicAuth());
ASSERT_TRUE(response);
// Request should have no extra.
if (it != credentials.end()) {
LOG_INFO(auth_logger, HTTP_CLIENT_REQUEST_AUTHORIZED)
.arg(it->second);
+ if (HttpRequest::recordBasicAuth) {
+ request->setBasicAuth(it->second);
+ }
authentic = true;
} else {
LOG_INFO(auth_logger, HTTP_CLIENT_REQUEST_NOT_AUTHORIZED);
-// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-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
close();
}
+void
+HttpConnection::recordParameters(const HttpRequestPtr& request) const {
+ if (!request) {
+ // Should never happen.
+ return;
+ }
+
+ // Record the remote address.
+ request->setRemote(getRemoteEndpointAddressAsText());
+
+ // Record TLS parameters.
+ if (!tls_socket_) {
+ return;
+ }
+
+ // The connection uses HTTPS aka HTTP over TLS.
+ request->setTls(true);
+
+ // Record the first commonName of the subjectName of the client
+ // certificate when wanted.
+ if (HttpRequest::recordSubject) {
+ request->setSubject(tls_socket_->getTlsStream().getSubject());
+ }
+
+ // Record the first commonName of the issuerName of the client
+ // certificate when wanted.
+ if (HttpRequest::recordIssuer) {
+ request->setIssuer(tls_socket_->getTlsStream().getIssuer());
+ }
+}
+
void
HttpConnection::shutdownCallback(const boost::system::error_code&) {
tls_socket_->close();
// new request.
if (!transaction) {
transaction = Transaction::create(response_creator_);
+ recordParameters(transaction->getRequest());
}
// Create instance of the callback. It is safe to pass the local instance
-// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-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
/// @brief Closes the socket.
void close();
- /// @brief Asynchronously performs TLS handshake.
+ /// @brief Records connection parameters into the HTTP request.
///
+ /// @param request Pointer to the HTTP request.
+ void recordParameters(const HttpRequestPtr& request) const;
+ /// @brief Asynchronously performs TLS handshake.
+ ///
/// When the handshake is performed successfully or skipped because TLS
/// was not enabled, the asynchronous read from the socket is started.
void doHandshake();
-// Copyright (C) 2016-2020 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-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
namespace isc {
namespace http {
+bool HttpRequest::recordSubject = false;
+
+bool HttpRequest::recordIssuer = false;
+
+bool HttpRequest::recordBasicAuth = false;
+
HttpRequest::HttpRequest()
: HttpMessage(INBOUND), required_methods_(),
method_(Method::HTTP_METHOD_UNKNOWN),
- context_(new HttpRequestContext()) {
+ context_(new HttpRequestContext()),
+ remote_(""), tls_(false), subject_(""), issuer_(""),
+ basic_auth_(""), custom_("") {
}
HttpRequest::HttpRequest(const Method& method,
const BasicHttpAuthPtr& basic_auth)
: HttpMessage(OUTBOUND), required_methods_(),
method_(Method::HTTP_METHOD_UNKNOWN),
- context_(new HttpRequestContext()) {
+ context_(new HttpRequestContext()),
+ remote_(""), tls_(false), subject_(""), issuer_(""),
+ basic_auth_(""), custom_("") {
context()->method_ = methodToString(method);
context()->uri_ = uri;
context()->http_version_major_ = version.major_;
-// Copyright (C) 2016-2020 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-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
/// includes body holding a JSON structure and provides methods to parse the
/// JSON body.
///
+/// Objects of this class is used to record some parameters for access control:
+/// - the remote address
+/// - the use of TLS
+/// - the first commonName of the SubjectName of the client certificate
+/// - the first commonName of the IssuerName of the client certificate
+/// - the user ID of the basic HTTP authentication
+/// - a custom value
+///
/// Callouts are associated to the request.
class HttpRequest : public HttpMessage, public hooks::CalloutHandleAssociate {
public:
/// otherwise.
bool isPersistent() const;
+ /// Access control parameters: get/set methods.
+
+ /// @brief Returns recorded remote address.
+ ///
+ /// @return recorded remote address.
+ std::string getRemote() const {
+ return (remote_);
+ }
+
+ /// @brief Set (record) remote address.
+ ///
+ /// @param remote Remote end-point address in textual form.
+ void setRemote(const std::string& remote) {
+ remote_ = remote;
+ }
+
+ /// @brief Returns recorded TLS usage.
+ ///
+ /// @return recorded TLS usage.
+ bool getTls() const {
+ return (tls_);
+ }
+
+ /// @brief Set (record) TLS usage.
+ ///
+ /// @param tls TLS usage.
+ void setTls(bool tls) {
+ tls_ = tls;
+ }
+
+ /// @brief Returns recorded subject name.
+ ///
+ /// @return recorded subject name.
+ std::string getSubject() const {
+ return (subject_);
+ }
+
+ /// @brief Set (record) subjet name.
+ ///
+ /// @param subjet Subject name.
+ void setSubject(const std::string& subject) {
+ subject_ = subject;
+ }
+
+ /// @brief Returns recorded issuer name.
+ ///
+ /// @return recorded issuer name.
+ std::string getIssuer() const {
+ return (issuer_);
+ }
+
+ /// @brief Set (record) issuer name.
+ ///
+ /// @param issuer Issuer name.
+ void setIssuer(const std::string& issuer) {
+ issuer_ = issuer;
+ }
+
+ /// @brief Returns recorded basic auth.
+ ///
+ /// @return recorded basic auth.
+ std::string getBasicAuth() const {
+ return (basic_auth_);
+ }
+
+ /// @brief Set (record) basic auth.
+ ///
+ /// @param basic_auth Basic auth.
+ void setBasicAuth(const std::string& basic_auth) {
+ basic_auth_ = basic_auth;
+ }
+
+ /// @brief Returns recorded custom name.
+ ///
+ /// @return recorded custom name.
+ std::string getCustom() const {
+ return (custom_);
+ }
+
+ /// @brief Set (record) custom name.
+ ///
+ /// @param custom Custom name.
+ void setCustom(const std::string& custom) {
+ custom_ = custom;
+ }
+
+ /// Access control parameters: want to record flags.
+ /// Remote address and TLS usage are always recorded.
+
+ /// @brief Record subjet name.
+ static bool recordSubject;
+
+ /// @brief Record issuer name.
+ static bool recordIssuer;
+
+ /// @brief Record basic auth.
+ static bool recordBasicAuth;
+
protected:
/// @brief Converts HTTP method specified in textual format to @ref Method.
/// @brief Pointer to the @ref HttpRequestContext holding parsed
/// data.
HttpRequestContextPtr context_;
+
+ /// @brief Remote address.
+ std::string remote_;
+
+ /// @brief TLS usage.
+ bool tls_;
+
+ /// @brief Subject name.
+ std::string subject_;
+
+ /// @brief Issuer name.
+ std::string issuer_;
+
+ /// @brief Basic auth.
+ std::string basic_auth_;
+
+ /// @brief Custom name.
+ std::string custom_;
};
}
-// Copyright (C) 2016-2020 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-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
EXPECT_EQ(value, "Basic " + basic_auth->getCredential());
}
+/// This test verifies that access parameters are handled as expected.
+TEST_F(HttpRequestTest, parameters) {
+ setContextBasics("GET", "/isc/org", HttpVersion(1, 1));
+ ASSERT_NO_THROW(request_->create());
+
+ EXPECT_TRUE(request_->getRemote().empty());
+ EXPECT_FALSE(request_->getTls());
+ EXPECT_TRUE(request_->getSubject().empty());
+ EXPECT_TRUE(request_->getIssuer().empty());
+ EXPECT_TRUE(request_->getBasicAuth().empty());
+ EXPECT_TRUE(request_->getCustom().empty());
+
+ request_->setRemote("my-remote");
+ request_->setTls(true);
+ request_->setSubject("my-subject");
+ request_->setIssuer("my-issuer");
+ request_->setBasicAuth("foo");
+ request_->setCustom("bar");
+
+ EXPECT_EQ("my-remote", request_->getRemote());
+ EXPECT_TRUE(request_->getTls());
+ EXPECT_EQ("my-subject", request_->getSubject());
+ EXPECT_EQ("my-issuer", request_->getIssuer());
+ EXPECT_EQ("foo", request_->getBasicAuth());
+ EXPECT_EQ("bar", request_->getCustom());
+}
+
}
request->context()->method_ = "GET";
request->context()->uri_ = "/foo";
ASSERT_NO_THROW(request->finalize());
+ HttpRequest::recordBasicAuth = true;
HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
"{ \"result\": 401, \"text\": \"Unauthorized\" }",
response->toString());
+ EXPECT_TRUE(request->getBasicAuth().empty());
addString("HTTP_CLIENT_REQUEST_NO_AUTH_HEADER received HTTP request "
"without required authentication header");
EXPECT_TRUE(checkFile());
HttpHeaderContext auth("Authorization", "Basic =");
request->context()->headers_.push_back(auth);
ASSERT_NO_THROW(request->finalize());
+ HttpRequest::recordBasicAuth = true;
HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
"{ \"result\": 401, \"text\": \"Unauthorized\" }",
response->toString());
+ EXPECT_TRUE(request->getBasicAuth().empty());
addString("HTTP_CLIENT_REQUEST_BAD_AUTH_HEADER received HTTP request "
"with malformed authentication header: "
"header content is too short");
HttpHeaderContext auth("Authorization", "Basis dGVzdDoxMjPCow==");
request->context()->headers_.push_back(auth);
ASSERT_NO_THROW(request->finalize());
+ HttpRequest::recordBasicAuth = true;
HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
"{ \"result\": 401, \"text\": \"Unauthorized\" }",
response->toString());
+ EXPECT_TRUE(request->getBasicAuth().empty());
addString("HTTP_CLIENT_REQUEST_BAD_AUTH_HEADER received HTTP request "
"with malformed authentication header: "
"not basic authentication");
HttpHeaderContext auth("Authorization", "Basic dGvZdDoxMjPcOw==");
request->context()->headers_.push_back(auth);
ASSERT_NO_THROW(request->finalize());
+ HttpRequest::recordBasicAuth = true;
HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
"{ \"result\": 401, \"text\": \"Unauthorized\" }",
response->toString());
+ EXPECT_TRUE(request->getBasicAuth().empty());
addString("HTTP_CLIENT_REQUEST_NOT_AUTHORIZED received HTTP request "
"with not matching authentication header");
EXPECT_TRUE(checkFile());
HttpHeaderContext auth("Authorization", "Basic dGVzdDoxMjPCow==");
request->context()->headers_.push_back(auth);
ASSERT_NO_THROW(request->finalize());
+ HttpRequest::recordBasicAuth = true;
HttpResponsePtr response;
TestHttpResponseCreatorPtr creator(new TestHttpResponseCreator());;
ASSERT_NO_THROW(response = auth_config->checkAuth(*creator, request));
EXPECT_FALSE(response);
+ EXPECT_EQ("test", request->getBasicAuth());
addString("HTTP_CLIENT_REQUEST_AUTHORIZED received HTTP request "
"authorized for 'test'");
EXPECT_TRUE(checkFile());
+ HttpRequest::recordBasicAuth = false;
}
}
-// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-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
/// @return Pointer to the generated HTTP OK response with no content.
virtual HttpResponsePtr
createDynamicHttpResponse(HttpRequestPtr request) {
+ // Check access parameters.
+ if (HttpRequest::recordSubject) {
+ EXPECT_TRUE(request->getTls());
+ EXPECT_EQ("kea-client", request->getSubject());
+ }
+ if (HttpRequest::recordIssuer) {
+ EXPECT_TRUE(request->getTls());
+ EXPECT_EQ("kea-ca", request->getIssuer());
+ }
// Request must always be JSON.
PostHttpRequestJsonPtr request_json =
boost::dynamic_pointer_cast<PostHttpRequestJson>(request);
listener3_->stop();
io_service_.poll();
MultiThreadingMgr::instance().setMode(false);
+ HttpRequest::recordSubject = false;
+ HttpRequest::recordIssuer = false;
}
/// @brief Creates HTTP request with JSON body.
}
}));
+ // Record subjet and issuer: they will be check during response creation.
+ HttpRequest::recordSubject = true;
+ HttpRequest::recordIssuer = true;
+
// Actually trigger the requests.
ASSERT_NO_THROW(runIOService());