- parse the Basic auth-scheme case-insensitively (RFC 7235), matching Bearer - register the http_server test only when pip_http_server and pip_http_client targets exist, so TESTS=ON without the HTTP modules does not fail to link - add a Basic_LowercaseScheme test
514 lines
19 KiB
C++
514 lines
19 KiB
C++
#include "pihttpclient.h"
|
|
#include "pihttpservermodule.h"
|
|
#include "piliterals_string.h"
|
|
#include "piliterals_time.h"
|
|
#include "pisemaphore.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
//! \~english Test server that allows access only with the "Bearer secret" authorization header.
|
|
//! \~russian Тестовый сервер, разрешающий доступ только с заголовком авторизации "Bearer secret".
|
|
class TokenServer: public PIHTTPServerProtected {
|
|
public:
|
|
PIHTTP::AuthInfo authenticate(const PIHTTP::MessageConst & request) override {
|
|
return request.headers().value(PIHTTP::Header::Authorization) == "Bearer secret"_a;
|
|
}
|
|
|
|
PIHTTP::MessageMutable accessDeniedReply(const PIHTTP::MessageConst &) override {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Unauthorized)
|
|
.addHeader(PIHTTP::Header::WWWAuthenticate, "Bearer realm=\"token\""_a);
|
|
}
|
|
};
|
|
|
|
|
|
//! \~english Test server that denies every protected request and replies 403 with body "no".
|
|
//! \~russian Тестовый сервер, отказывающий во всех защищенных запросах и отвечающий 403 с телом "no".
|
|
class ForbiddenServer: public PIHTTPServerProtected {
|
|
public:
|
|
PIHTTP::AuthInfo authenticate(const PIHTTP::MessageConst &) override { return PIHTTP::AuthInfo(); }
|
|
|
|
PIHTTP::MessageMutable accessDeniedReply(const PIHTTP::MessageConst &) override {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Forbidden).setBody(PIByteArray::fromAscii("no"));
|
|
}
|
|
};
|
|
|
|
|
|
class HttpServerProtectedTest: public ::testing::Test {
|
|
protected:
|
|
struct Reply {
|
|
PIHTTP::Code code = PIHTTP::Code::Unknown;
|
|
PIString body;
|
|
PIString www_authenticate;
|
|
PIString error;
|
|
bool finished = false;
|
|
bool transport_error = false;
|
|
};
|
|
|
|
//! \~english Performs a GET request to "url" with optional "Authorization" header value and
|
|
//! waits for the reply with a 10 seconds timeout.
|
|
//! \~russian Выполняет GET-запрос к "url" с необязательным значением заголовка "Authorization"
|
|
//! и ждет ответ с таймаутом 10 секунд.
|
|
static Reply get(const PIString & url, const PIString & auth) {
|
|
// The reply state and semaphore are heap-allocated: the callbacks run in the
|
|
// HTTP client thread pool and may outlive this function on a wait timeout.
|
|
Reply * rep = new Reply();
|
|
PISemaphore * sem = new PISemaphore();
|
|
PIHTTP::MessageMutable req;
|
|
if (auth.isNotEmpty()) req.addHeader(PIHTTP::Header::Authorization, auth);
|
|
auto client = PIHTTPClient::create(url, PIHTTP::Method::Get, req);
|
|
client->onFinish([rep, sem](const PIHTTP::MessageConst & r) {
|
|
rep->code = r.code();
|
|
rep->body = PIString::fromUTF8(r.body());
|
|
rep->www_authenticate = r.headers().value(PIHTTP::Header::WWWAuthenticate);
|
|
rep->finished = true;
|
|
sem->release();
|
|
});
|
|
client->onError([client, rep, sem](const PIHTTP::MessageConst &) {
|
|
rep->error = client->lastError();
|
|
rep->transport_error = true;
|
|
rep->finished = true;
|
|
sem->release();
|
|
});
|
|
client->start();
|
|
// The client object is owned by the HTTP client thread pool after start()
|
|
// and deleted there, so it must not be touched or deleted here.
|
|
if (sem->tryAcquire(1, 10_s)) {
|
|
Reply out = *rep;
|
|
delete rep;
|
|
delete sem;
|
|
return out;
|
|
}
|
|
// On timeout the callbacks may still run in the client thread pool,
|
|
// so "rep"/"sem" are intentionally leaked (the test fails anyway).
|
|
Reply out;
|
|
return out;
|
|
}
|
|
|
|
//! \~english Builds a Basic-scheme "Authorization" header value from "user:pass".
|
|
//! \~russian Формирует значение заголовка "Authorization" схемы Basic из "user:pass".
|
|
static PIString basicAuth(const char * user_pass) {
|
|
return "Basic %1"_a.arg(PIString::fromAscii(PIByteArray::fromAscii(user_pass).toBase64()));
|
|
}
|
|
|
|
//! \~english Starts listening on the first free port from 18461 and keeps the server
|
|
//! for the teardown. Returns \c false if no port is available.
|
|
//! \~russian Запускает прослушивание на первом свободном порту от 18461 и сохраняет сервер
|
|
//! для завершения теста. Возвращает \c false, если свободный порт не найден.
|
|
bool start(PIHTTPServer * s) {
|
|
server = s;
|
|
for (port = 18461; port < 18561; ++port) {
|
|
if (s->listenAll((ushort)port)) return true;
|
|
}
|
|
port = -1;
|
|
return false;
|
|
}
|
|
|
|
PIString url(const char * path) const { return "http://127.0.0.1:%1"_a.arg(port) + path; }
|
|
|
|
void TearDown() override {
|
|
if (server) {
|
|
server->stop();
|
|
delete server;
|
|
server = nullptr;
|
|
}
|
|
}
|
|
|
|
PIHTTPServer * server = nullptr;
|
|
int port = -1;
|
|
};
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, ProtectedToken_NoCredentials) {
|
|
auto s = new TokenServer();
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
EXPECT_TRUE(rep.www_authenticate.contains("Bearer"));
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, ProtectedToken_Valid) {
|
|
auto s = new TokenServer();
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "Bearer secret");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("ok"_a, rep.body);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Protected_OpenPath) {
|
|
auto s = new TokenServer();
|
|
s->registerPath("/open", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("open"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/open"), "");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("open"_a, rep.body);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Protected_CustomDenied) {
|
|
auto s = new ForbiddenServer();
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "Bearer secret");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Forbidden, rep.code);
|
|
EXPECT_EQ("no"_a, rep.body);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_NoCredentials) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
s->setBasicAuthCallback([](const PIString &, const PIString &) { return PIHTTP::AuthInfo{true}; });
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
EXPECT_TRUE(rep.www_authenticate.contains("realm=\"Restricted\""));
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_Realm) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
s->setRealm("MyAPI");
|
|
s->setBasicAuthCallback([](const PIString &, const PIString &) { return PIHTTP::AuthInfo{true}; });
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
EXPECT_TRUE(rep.www_authenticate.contains("realm=\"MyAPI\""));
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_WrongCreds) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
s->setBasicAuthCallback([](const PIString &, const PIString &) { return PIHTTP::AuthInfo(); });
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), basicAuth("user:pass"));
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_Valid) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
s->setBasicAuthCallback([](const PIString & user, const PIString & pass) {
|
|
return user == "user" && pass == "pass" ? (PIHTTP::AuthInfo{true, 1}) : PIHTTP::AuthInfo();
|
|
});
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), basicAuth("user:pass"));
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("ok"_a, rep.body);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_LowercaseScheme) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
s->setBasicAuthCallback([](const PIString & user, const PIString & pass) {
|
|
return user == "user" && pass == "pass" ? (PIHTTP::AuthInfo{true, 1}) : PIHTTP::AuthInfo();
|
|
});
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
// The auth-scheme is case-insensitive (RFC 7235), so "basic" must be accepted.
|
|
PIString header = "basic "_a + PIString::fromAscii(PIByteArray::fromAscii("user:pass").toBase64());
|
|
auto rep = get(url("/secret"), header);
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("ok"_a, rep.body);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_BearerHeader) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
s->setBasicAuthCallback([](const PIString &, const PIString &) { return PIHTTP::AuthInfo{true}; });
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "Bearer x");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_NoCallback) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
EXPECT_FALSE(s->hasBasicAuthCallback());
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), basicAuth("user:pass"));
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_OpenPath) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
s->setBasicAuthCallback([](const PIString &, const PIString &) { return PIHTTP::AuthInfo{true}; });
|
|
s->registerPath("/open", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("open"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/open"), "");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("open"_a, rep.body);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_EmptyPass) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
s->setBasicAuthCallback([](const PIString & user, const PIString & pass) {
|
|
return user == "user" && pass == "" ? (PIHTTP::AuthInfo{true, 1}) : PIHTTP::AuthInfo();
|
|
});
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), basicAuth("user:"));
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("ok"_a, rep.body);
|
|
}
|
|
|
|
|
|
//! \~english Handler object used to test the object-method protected route overload.
|
|
//! \~russian Объект-обработчик для теста перегрузки защищенного маршрута с методом объекта.
|
|
class WhoamiHandler {
|
|
public:
|
|
//! \~english Replies the user id from the authentication result.
|
|
//! \~russian Отвечает идентификатором пользователя из результата аутентификации.
|
|
PIHTTP::MessageMutable whoami(const PIHTTP::MessageConst &, const PIHTTP::AuthInfo & info) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIString::fromNumber(info.user_id).toByteArray());
|
|
}
|
|
};
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_UserParam) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
s->setBasicAuthCallback([](const PIString &, const PIString &) { return PIHTTP::AuthInfo{true}; });
|
|
s->registerProtectedPath("/whoami", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &, const PIHTTP::AuthInfo & info) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIString::fromNumber(info.user_id).toByteArray());
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/whoami"), basicAuth("user:pass"));
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("0"_a, rep.body);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_MultiUser) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
// Simulates the application-side user table: login and pass -> user id.
|
|
s->setBasicAuthCallback([](const PIString & user, const PIString & pass) {
|
|
if (user == "alice" && pass == "pass1") return PIHTTP::AuthInfo{true, 1};
|
|
if (user == "bob" && pass == "pass2") return PIHTTP::AuthInfo{true, 2};
|
|
return PIHTTP::AuthInfo();
|
|
});
|
|
s->registerProtectedPath("/whoami", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &, const PIHTTP::AuthInfo & info) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIString::fromNumber(info.user_id).toByteArray());
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto alice = get(url("/whoami"), basicAuth("alice:pass1"));
|
|
EXPECT_TRUE(alice.finished);
|
|
EXPECT_FALSE(alice.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, alice.code);
|
|
EXPECT_EQ("1"_a, alice.body);
|
|
|
|
auto bob = get(url("/whoami"), basicAuth("bob:pass2"));
|
|
EXPECT_TRUE(bob.finished);
|
|
EXPECT_FALSE(bob.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, bob.code);
|
|
EXPECT_EQ("2"_a, bob.body);
|
|
|
|
auto stranger = get(url("/whoami"), basicAuth("eve:pass3"));
|
|
EXPECT_TRUE(stranger.finished);
|
|
EXPECT_FALSE(stranger.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, stranger.code);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Basic_UserMethod) {
|
|
auto s = new PIHTTPServerBasicAuth();
|
|
WhoamiHandler handler;
|
|
s->setBasicAuthCallback([](const PIString &, const PIString &) { return PIHTTP::AuthInfo{true, 7}; });
|
|
ASSERT_TRUE(s->registerProtectedPath("/whoami", PIHTTP::Method::Get, &handler, &WhoamiHandler::whoami));
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/whoami"), basicAuth("user:pass"));
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("7"_a, rep.body);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Bearer_NoCredentials) {
|
|
auto s = new PIHTTPServerBearerAuth();
|
|
s->setBearerAuthCallback([](const PIString &) { return PIHTTP::AuthInfo{true}; });
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
EXPECT_TRUE(rep.www_authenticate.contains("Bearer realm=\"Restricted\""));
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Bearer_Realm) {
|
|
auto s = new PIHTTPServerBearerAuth();
|
|
s->setRealm("MyAPI");
|
|
s->setBearerAuthCallback([](const PIString &) { return PIHTTP::AuthInfo{true}; });
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
EXPECT_TRUE(rep.www_authenticate.contains("Bearer realm=\"MyAPI\""));
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Bearer_WrongToken) {
|
|
auto s = new PIHTTPServerBearerAuth();
|
|
s->setBearerAuthCallback([](const PIString &) { return PIHTTP::AuthInfo(); });
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "Bearer secret");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Bearer_Valid) {
|
|
auto s = new PIHTTPServerBearerAuth();
|
|
s->setBearerAuthCallback([](const PIString & token) { return token == "secret" ? (PIHTTP::AuthInfo{true, 1}) : PIHTTP::AuthInfo(); });
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "Bearer secret");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("ok"_a, rep.body);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Bearer_BasicHeader) {
|
|
auto s = new PIHTTPServerBearerAuth();
|
|
s->setBearerAuthCallback([](const PIString &) { return PIHTTP::AuthInfo{true}; });
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), basicAuth("user:pass"));
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Bearer_NoCallback) {
|
|
auto s = new PIHTTPServerBearerAuth();
|
|
EXPECT_FALSE(s->hasBearerAuthCallback());
|
|
s->registerProtectedPath("/secret", PIHTTP::Method::Get, [](const PIHTTP::MessageConst &) {
|
|
return PIHTTP::MessageMutable::fromCode(PIHTTP::Code::Ok).setBody(PIByteArray::fromAscii("ok"));
|
|
});
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/secret"), "Bearer secret");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Unauthorized, rep.code);
|
|
}
|
|
|
|
|
|
TEST_F(HttpServerProtectedTest, Bearer_UserInfo) {
|
|
auto s = new PIHTTPServerBearerAuth();
|
|
WhoamiHandler handler;
|
|
s->setBearerAuthCallback([](const PIString & token) { return token == "secret" ? (PIHTTP::AuthInfo{true, 7}) : PIHTTP::AuthInfo(); });
|
|
ASSERT_TRUE(s->registerProtectedPath("/whoami", PIHTTP::Method::Get, &handler, &WhoamiHandler::whoami));
|
|
ASSERT_TRUE(start(s));
|
|
|
|
auto rep = get(url("/whoami"), "Bearer secret");
|
|
EXPECT_TRUE(rep.finished);
|
|
EXPECT_FALSE(rep.transport_error);
|
|
EXPECT_EQ(PIHTTP::Code::Ok, rep.code);
|
|
EXPECT_EQ("7"_a, rep.body);
|
|
}
|