From 0e0406c4252c3fd69d173f9fae2b1eb546c1ac2c Mon Sep 17 00:00:00 2001 From: Ian Roddis <31021769+iroddis@users.noreply.github.com> Date: Sat, 31 May 2025 10:13:32 -0300 Subject: [PATCH] Adding unit test --- daggyd/tests/unit_jwt_auth.cpp | 117 +++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 daggyd/tests/unit_jwt_auth.cpp diff --git a/daggyd/tests/unit_jwt_auth.cpp b/daggyd/tests/unit_jwt_auth.cpp new file mode 100644 index 0000000..1c00f22 --- /dev/null +++ b/daggyd/tests/unit_jwt_auth.cpp @@ -0,0 +1,117 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rj = rapidjson; + +using namespace daggy; + +TEST_CASE("jwt_authentication", "[server_jwt]") +{ + std::stringstream ss; + daggy::executors::task::NoopTaskExecutor executor; + daggy::loggers::dag_run::OStreamLogger logger(ss); + Pistache::Address listenSpec("localhost", Pistache::Port(0)); + + const size_t nDAGRunners = 10, nWebThreads = 10; + const std::string jwtSecret = "test-secret-key"; + + daggy::daggyd::Server server(listenSpec, logger, executor, nDAGRunners, + "/dev/null"); + server.setJWTSecret(jwtSecret); + server.init(nWebThreads); + server.start(); + + const std::string host = "localhost:"; + const std::string baseURL = host + std::to_string(server.getPort()); + + SECTION("Requests without JWT token should fail") + { + auto response = HTTP_REQUEST(baseURL + "/v1/dagruns"); + REQUIRE(response.code == HTTPCode::Unauthorized); + } + + SECTION("Requests with invalid JWT token should fail") + { + std::map headers = { + {"Authorization", "Bearer invalid-token"}}; + auto response = HTTP_REQUEST_WITH_HEADERS(baseURL + "/v1/dagruns", "", "GET", headers); + REQUIRE(response.code == HTTPCode::Unauthorized); + } + + SECTION("Requests with valid JWT token should succeed") + { + auto token = jwt::create() + .set_issuer("daggy-test") + .set_type("JWT") + .sign(jwt::algorithm::hs256{jwtSecret}); + + std::map headers = { + {"Authorization", "Bearer " + token}}; + auto response = HTTP_REQUEST_WITH_HEADERS(baseURL + "/v1/dagruns", "", "GET", headers); + REQUIRE(response.code == HTTPCode::Ok); + } + + SECTION("DAG submission with valid JWT token should work") + { + auto token = jwt::create() + .set_issuer("daggy-test") + .set_type("JWT") + .sign(jwt::algorithm::hs256{jwtSecret}); + + std::string dagRun = R"({ + "tag": "jwt_test", + "tasks": { + "test_task": { "job": { "command": [ "/bin/echo", "hello" ], "environment": []} } + } + })"; + + std::map headers = { + {"Authorization", "Bearer " + token}}; + auto response = HTTP_REQUEST_WITH_HEADERS(baseURL + "/v1/dagrun/", dagRun, "POST", headers); + REQUIRE(response.code == HTTPCode::Ok); + + rj::Document doc; + daggy::checkRJParse(doc.Parse(response.body.c_str())); + REQUIRE(doc.IsObject()); + REQUIRE(doc.HasMember("runID")); + } + + server.shutdown(); +} + +TEST_CASE("no_jwt_secret_allows_all", "[server_no_jwt]") +{ + std::stringstream ss; + daggy::executors::task::NoopTaskExecutor executor; + daggy::loggers::dag_run::OStreamLogger logger(ss); + Pistache::Address listenSpec("localhost", Pistache::Port(0)); + + const size_t nDAGRunners = 10, nWebThreads = 10; + + daggy::daggyd::Server server(listenSpec, logger, executor, nDAGRunners, + "/dev/null"); + server.init(nWebThreads); + server.start(); + + const std::string host = "localhost:"; + const std::string baseURL = host + std::to_string(server.getPort()); + + SECTION("Requests without JWT secret configured should work") + { + auto response = HTTP_REQUEST(baseURL + "/v1/dagruns"); + REQUIRE(response.code == HTTPCode::Ok); + } + + server.shutdown(); +} \ No newline at end of file