NetServices: Add support for Basic authentication
Change-Id: I304104f7096fd935212e1bfa3e988e7945cb5cec
This commit is contained in:
parent
f751534257
commit
f9d9d20245
@ -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
|
||||
\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
|
||||
\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)
|
||||
\brief Set additional header \a fields for this request.
|
||||
|
@ -80,6 +80,12 @@ struct BHttpRedirectOptions {
|
||||
};
|
||||
|
||||
|
||||
struct BHttpAuthentication {
|
||||
BString username;
|
||||
BString password;
|
||||
};
|
||||
|
||||
|
||||
class BHttpRequest {
|
||||
public:
|
||||
// Constructors and Destructor
|
||||
@ -95,12 +101,14 @@ public:
|
||||
|
||||
// Access
|
||||
bool IsEmpty() const noexcept;
|
||||
const BHttpAuthentication* Authentication() const noexcept;
|
||||
const BHttpFields& Fields() const noexcept;
|
||||
const BHttpMethod& Method() const noexcept;
|
||||
const BHttpRedirectOptions& Redirect() const noexcept;
|
||||
const BUrl& Url() const noexcept;
|
||||
|
||||
// Named Setters
|
||||
void SetAuthentication(const BHttpAuthentication& authentication);
|
||||
void SetFields(const BHttpFields& fields);
|
||||
void SetMethod(const BHttpMethod& method);
|
||||
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();
|
||||
|
||||
struct BHttpRequest::Data {
|
||||
BUrl url = kDefaultUrl;
|
||||
BHttpMethod method = kDefaultMethod;
|
||||
BHttpRedirectOptions redirectOptions;
|
||||
BHttpFields optionalFields;
|
||||
BUrl url = kDefaultUrl;
|
||||
BHttpMethod method = kDefaultMethod;
|
||||
BHttpRedirectOptions redirectOptions;
|
||||
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
|
||||
|
||||
|
||||
@ -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&
|
||||
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 = {
|
||||
"Host"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) {
|
||||
bytesWritten += _write_to_dataio(target, field.RawField());
|
||||
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
|
||||
|
||||
|
||||
|
@ -494,6 +494,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
|
||||
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
|
||||
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
||||
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
||||
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
|
||||
|
||||
suite.addTest(testCaller);
|
||||
parent.addTest("HttpIntegrationTest", &suite);
|
||||
@ -514,6 +515,7 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
|
||||
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
|
||||
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
|
||||
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
|
||||
testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
|
||||
|
||||
suite.addTest(testCaller);
|
||||
parent.addTest("HttpsIntegrationTest", &suite);
|
||||
@ -680,3 +682,20 @@ HttpIntegrationTest::AutoRedirectTest()
|
||||
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 NoContentTest();
|
||||
void AutoRedirectTest();
|
||||
void BasicAuthTest();
|
||||
|
||||
static void AddTests(BTestSuite& suite);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user