NetServices: Add support for Basic authentication
Change-Id: I304104f7096fd935212e1bfa3e988e7945cb5cec
This commit is contained in:
@@ -308,6 +308,34 @@ namespace Network {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\struct BHttpAuthentication
|
||||||
|
\ingroup netservices
|
||||||
|
\brief Describe username and password for basic authentication for the request.
|
||||||
|
|
||||||
|
\see These options are used by \ref BHttpRequest::Authentication() and
|
||||||
|
\ref BHttpRequest::SetAuthentication()
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\var BString BHttpAuthentication::username
|
||||||
|
\brief The username for the request.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\var BString BHttpAuthentication::password
|
||||||
|
\brief The password for the request.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class BHttpRequest
|
\class BHttpRequest
|
||||||
\ingroup netservices
|
\ingroup netservices
|
||||||
@@ -478,6 +506,18 @@ namespace Network {
|
|||||||
//! @{
|
//! @{
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn const BHttpAuthentication* BHttpRequest::Authentication() const noexcept
|
||||||
|
\brief Get the credentials for the authentication for the request.
|
||||||
|
|
||||||
|
\return When no credentials are set for this request, the method returns a \c nullptr.
|
||||||
|
Otherwise, it will return a pointer to the current BHttpAuthentication data set for this
|
||||||
|
request.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn const BHttpFields& BHttpRequest::Fields() const noexcept
|
\fn const BHttpFields& BHttpRequest::Fields() const noexcept
|
||||||
\brief Get the additional header fields set for the request.
|
\brief Get the additional header fields set for the request.
|
||||||
@@ -531,6 +571,21 @@ namespace Network {
|
|||||||
//! @{
|
//! @{
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void BHttpRequest::SetAuthentication(const BHttpAuthentication &authentication)
|
||||||
|
\brief Set the credentials to enable basic authentication for the request.
|
||||||
|
|
||||||
|
The Basic authorization line is added to the request upon setting the request details. There is
|
||||||
|
no support for other authentication schemes, like digest authentication.
|
||||||
|
|
||||||
|
\param authentication The credentials to apply to the request.
|
||||||
|
|
||||||
|
\exception std::bad_alloc This exception may be raised if it is impossible to allocate memory.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn void BHttpRequest::SetFields(const BHttpFields &fields)
|
\fn void BHttpRequest::SetFields(const BHttpFields &fields)
|
||||||
\brief Set additional header \a fields for this request.
|
\brief Set additional header \a fields for this request.
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ struct BHttpRedirectOptions {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct BHttpAuthentication {
|
||||||
|
BString username;
|
||||||
|
BString password;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class BHttpRequest {
|
class BHttpRequest {
|
||||||
public:
|
public:
|
||||||
// Constructors and Destructor
|
// Constructors and Destructor
|
||||||
@@ -95,12 +101,14 @@ public:
|
|||||||
|
|
||||||
// Access
|
// Access
|
||||||
bool IsEmpty() const noexcept;
|
bool IsEmpty() const noexcept;
|
||||||
|
const BHttpAuthentication* Authentication() const noexcept;
|
||||||
const BHttpFields& Fields() const noexcept;
|
const BHttpFields& Fields() const noexcept;
|
||||||
const BHttpMethod& Method() const noexcept;
|
const BHttpMethod& Method() const noexcept;
|
||||||
const BHttpRedirectOptions& Redirect() const noexcept;
|
const BHttpRedirectOptions& Redirect() const noexcept;
|
||||||
const BUrl& Url() const noexcept;
|
const BUrl& Url() const noexcept;
|
||||||
|
|
||||||
// Named Setters
|
// Named Setters
|
||||||
|
void SetAuthentication(const BHttpAuthentication& authentication);
|
||||||
void SetFields(const BHttpFields& fields);
|
void SetFields(const BHttpFields& fields);
|
||||||
void SetMethod(const BHttpMethod& method);
|
void SetMethod(const BHttpMethod& method);
|
||||||
void SetRedirect(const BHttpRedirectOptions& redirectOptions);
|
void SetRedirect(const BHttpRedirectOptions& redirectOptions);
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
BString encode_to_base64(const BString& string);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,13 +160,30 @@ static const BHttpRedirectOptions kDefaultRedirectOptions = BHttpRedirectOptions
|
|||||||
static const BHttpFields kDefaultOptionalFields = BHttpFields();
|
static const BHttpFields kDefaultOptionalFields = BHttpFields();
|
||||||
|
|
||||||
struct BHttpRequest::Data {
|
struct BHttpRequest::Data {
|
||||||
BUrl url = kDefaultUrl;
|
BUrl url = kDefaultUrl;
|
||||||
BHttpMethod method = kDefaultMethod;
|
BHttpMethod method = kDefaultMethod;
|
||||||
BHttpRedirectOptions redirectOptions;
|
BHttpRedirectOptions redirectOptions;
|
||||||
BHttpFields optionalFields;
|
BHttpFields optionalFields;
|
||||||
|
std::optional<BHttpAuthentication> authentication;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -- BHttpRequest helper functions
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Build basic authentication header
|
||||||
|
*/
|
||||||
|
static inline BString
|
||||||
|
build_basic_http_header(const BString& username, const BString& password)
|
||||||
|
{
|
||||||
|
BString basicEncode, result;
|
||||||
|
basicEncode << username << ":" << password;
|
||||||
|
result << "Basic " << encode_to_base64(basicEncode);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -- BHttpRequest
|
// #pragma mark -- BHttpRequest
|
||||||
|
|
||||||
|
|
||||||
@@ -201,6 +218,15 @@ BHttpRequest::IsEmpty() const noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const BHttpAuthentication*
|
||||||
|
BHttpRequest::Authentication() const noexcept
|
||||||
|
{
|
||||||
|
if (fData && fData->authentication)
|
||||||
|
return std::addressof(*fData->authentication);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const BHttpFields&
|
const BHttpFields&
|
||||||
BHttpRequest::Fields() const noexcept
|
BHttpRequest::Fields() const noexcept
|
||||||
{
|
{
|
||||||
@@ -237,6 +263,16 @@ BHttpRequest::Url() const noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BHttpRequest::SetAuthentication(const BHttpAuthentication& authentication)
|
||||||
|
{
|
||||||
|
if (!fData)
|
||||||
|
fData = std::make_unique<Data>();
|
||||||
|
|
||||||
|
fData->authentication = authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static constexpr std::array<std::string_view, 4> fReservedOptionalFieldNames = {
|
static constexpr std::array<std::string_view, 4> fReservedOptionalFieldNames = {
|
||||||
"Host"sv,
|
"Host"sv,
|
||||||
"Accept"sv,
|
"Accept"sv,
|
||||||
@@ -357,6 +393,13 @@ BHttpRequest::SerializeHeaderTo(BDataIO* target) const
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fData->authentication) {
|
||||||
|
// This request will add a Basic authorization header
|
||||||
|
BString authorization = build_basic_http_header(fData->authentication->username,
|
||||||
|
fData->authentication->password);
|
||||||
|
outputFields.AddField("Authorization"sv, std::string_view(authorization.String()));
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& field: outputFields) {
|
for (const auto& field: outputFields) {
|
||||||
bytesWritten += _write_to_dataio(target, field.RawField());
|
bytesWritten += _write_to_dataio(target, field.RawField());
|
||||||
bytesWritten += _write_to_dataio(target, "\r\n"sv);
|
bytesWritten += _write_to_dataio(target, "\r\n"sv);
|
||||||
|
|||||||
@@ -152,6 +152,52 @@ BNetworkRequestError::ErrorCode() const noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -- Public functions
|
||||||
|
|
||||||
|
|
||||||
|
static const char* kBase64Symbols
|
||||||
|
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
encode_to_base64(const BString& string)
|
||||||
|
{
|
||||||
|
BString result;
|
||||||
|
BString tmpString = string;
|
||||||
|
|
||||||
|
while (tmpString.Length()) {
|
||||||
|
char in[3] = { 0, 0, 0 };
|
||||||
|
char out[4] = { 0, 0, 0, 0 };
|
||||||
|
int8 remaining = tmpString.Length();
|
||||||
|
|
||||||
|
tmpString.MoveInto(in, 0, 3);
|
||||||
|
|
||||||
|
out[0] = (in[0] & 0xFC) >> 2;
|
||||||
|
out[1] = ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4);
|
||||||
|
out[2] = ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6);
|
||||||
|
out[3] = in[2] & 0x3F;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
out[i] = kBase64Symbols[(int)out[i]];
|
||||||
|
|
||||||
|
// Add padding if the input length is not a multiple
|
||||||
|
// of 3
|
||||||
|
switch (remaining) {
|
||||||
|
case 1:
|
||||||
|
out[2] = '=';
|
||||||
|
// Fall through
|
||||||
|
case 2:
|
||||||
|
out[3] = '=';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Append(out, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -- Private functions and data
|
// #pragma mark -- Private functions and data
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -494,6 +494,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
|
|||||||
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
|
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
|
||||||
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
||||||
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
||||||
|
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
|
||||||
|
|
||||||
suite.addTest(testCaller);
|
suite.addTest(testCaller);
|
||||||
parent.addTest("HttpIntegrationTest", &suite);
|
parent.addTest("HttpIntegrationTest", &suite);
|
||||||
@@ -514,6 +515,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
|
|||||||
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
|
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
|
||||||
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
||||||
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
||||||
|
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
|
||||||
|
|
||||||
suite.addTest(testCaller);
|
suite.addTest(testCaller);
|
||||||
parent.addTest("HttpsIntegrationTest", &suite);
|
parent.addTest("HttpsIntegrationTest", &suite);
|
||||||
@@ -680,3 +682,20 @@ HttpIntegrationTest::AutoRedirectTest()
|
|||||||
CPPUNIT_FAIL(e.DebugMessage().String());
|
CPPUNIT_FAIL(e.DebugMessage().String());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
HttpIntegrationTest::BasicAuthTest()
|
||||||
|
{
|
||||||
|
// Basic Authentication
|
||||||
|
auto request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/auth/basic/walter/secret"));
|
||||||
|
request.SetAuthentication({"walter", "secret"});
|
||||||
|
auto result = fSession.Execute(std::move(request));
|
||||||
|
CPPUNIT_ASSERT(result.Status().code == 200);
|
||||||
|
|
||||||
|
// Basic Authentication with incorrect credentials
|
||||||
|
request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/auth/basic/walter/secret"));
|
||||||
|
request.SetAuthentication({"invaliduser", "invalidpassword"});
|
||||||
|
result = fSession.Execute(std::move(request));
|
||||||
|
CPPUNIT_ASSERT(result.Status().code == 401);
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public:
|
|||||||
void HeadTest();
|
void HeadTest();
|
||||||
void NoContentTest();
|
void NoContentTest();
|
||||||
void AutoRedirectTest();
|
void AutoRedirectTest();
|
||||||
|
void BasicAuthTest();
|
||||||
|
|
||||||
static void AddTests(BTestSuite& suite);
|
static void AddTests(BTestSuite& suite);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user