aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/httpclient.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2026-02-17 14:00:53 +0100
committerGitHub Enterprise <[email protected]>2026-02-17 14:00:53 +0100
commit5e1e23e209eec75a396c18f8eee3d93a9e196bfc (patch)
tree31b2b3938468aacdb0621e8b932cb9e9738ee918 /src/zenhttp/httpclient.cpp
parentmisc fixes brought over from sb/proto (#759) (diff)
downloadzen-5e1e23e209eec75a396c18f8eee3d93a9e196bfc.tar.xz
zen-5e1e23e209eec75a396c18f8eee3d93a9e196bfc.zip
add http server root password protection (#757)
- Feature: Added `--security-config-path` option to zenserver to configure security settings - Expects a path to a .json file - Default is an empty path resulting in no extra security settings and legacy behavior - Current support is a top level filter of incoming http requests restricted to the `password` type - `password` type will check the `Authorization` header and match it to the selected authorization strategy - Currently the security settings is very basic and configured to a fixed username+password at startup { "http" { "root": { "filter": { "type": "password", "config": { "password": { "username": "<username>", "password": "<password>" }, "protect-machine-local-requests": false, "unprotected-uris": [ "/health/", "/health/info", "/health/version" ] } } } } }
Diffstat (limited to 'src/zenhttp/httpclient.cpp')
-rw-r--r--src/zenhttp/httpclient.cpp91
1 files changed, 91 insertions, 0 deletions
diff --git a/src/zenhttp/httpclient.cpp b/src/zenhttp/httpclient.cpp
index 16729ce38..d3b59df2b 100644
--- a/src/zenhttp/httpclient.cpp
+++ b/src/zenhttp/httpclient.cpp
@@ -25,6 +25,7 @@
# include <zencore/scopeguard.h>
# include <zencore/testing.h>
# include <zencore/testutils.h>
+# include <zenhttp/security/passwordsecurityfilter.h>
# include "servers/httpasio.h"
# include "servers/httpsys.h"
@@ -662,6 +663,96 @@ TEST_CASE("httpclient.requestfilter")
}
}
+TEST_CASE("httpclient.password")
+{
+ using namespace std::literals;
+
+ struct TestHttpService : public HttpService
+ {
+ TestHttpService() = default;
+
+ virtual const char* BaseUri() const override { return "/test/"; }
+ virtual void HandleRequest(HttpServerRequest& HttpServiceRequest) override
+ {
+ if (HttpServiceRequest.RelativeUri() == "yo")
+ {
+ return HttpServiceRequest.WriteResponse(HttpResponseCode::OK, HttpContentType::kText, "hey family");
+ }
+
+ {
+ CHECK(HttpServiceRequest.RelativeUri() != "should_filter");
+ return HttpServiceRequest.WriteResponse(HttpResponseCode::InternalServerError);
+ }
+
+ {
+ CHECK(HttpServiceRequest.RelativeUri() != "should_forbid");
+ return HttpServiceRequest.WriteResponse(HttpResponseCode::InternalServerError);
+ }
+ }
+ };
+
+ TestHttpService TestService;
+ ScopedTemporaryDirectory TmpDir;
+
+ Ref<HttpServer> AsioServer = CreateHttpAsioServer(AsioConfig{});
+
+ int Port = AsioServer->Initialize(7575, TmpDir.Path());
+ REQUIRE(Port != -1);
+
+ AsioServer->RegisterService(TestService);
+
+ std::thread ServerThread([&]() { AsioServer->Run(false); });
+
+ {
+ auto _ = MakeGuard([&]() {
+ if (ServerThread.joinable())
+ {
+ ServerThread.join();
+ }
+ AsioServer->Close();
+ });
+
+ SUBCASE("usernamepassword")
+ {
+ CbObjectWriter Writer;
+ {
+ Writer.BeginObject("basic");
+ {
+ Writer << "username"sv
+ << "me";
+ Writer << "password"sv
+ << "456123789";
+ }
+ Writer.EndObject();
+ Writer << "protect-machine-local-requests" << true;
+ }
+
+ PasswordHttpFilter::Configuration PasswordFilterOptions = PasswordHttpFilter::ReadConfiguration(Writer.Save());
+
+ PasswordHttpFilter MyFilter(PasswordFilterOptions);
+
+ AsioServer->SetHttpRequestFilter(&MyFilter);
+
+ HttpClient Client(fmt::format("localhost:{}", Port),
+ HttpClientSettings{},
+ /*CheckIfAbortFunction*/ {});
+
+ ZEN_INFO("Request using {}", Client.GetBaseUri());
+
+ HttpClient::Response ForbiddenResponse = Client.Get("/test/yo");
+ CHECK(!ForbiddenResponse.IsSuccess());
+ CHECK_EQ(ForbiddenResponse.StatusCode, HttpResponseCode::Forbidden);
+
+ HttpClient::Response WithBasicResponse =
+ Client.Get("/test/yo",
+ std::pair<std::string, std::string>("Authorization",
+ fmt::format("Basic {}", PasswordFilterOptions.PasswordConfig.Password)));
+ CHECK(WithBasicResponse.IsSuccess());
+ AsioServer->SetHttpRequestFilter(nullptr);
+ }
+ AsioServer->RequestExit();
+ }
+}
void
httpclient_forcelink()
{