mirror of
https://review.haiku-os.org/haiku
synced 2024-11-23 07:18:40 +01:00
HaikuDepot: Token Based Authentication
This switches the application over from using basic authentication to using token-based authentication in preparation for later using Open-ID based authentication flows. The application version is also bumped in order that the server can detect this version at some later date in the future when it no longer supports basic authentication itself. Change-Id: I7addde1d57503c58d6bcd54908f22f66830c0c59 Reviewed-on: https://review.haiku-os.org/c/haiku/+/6944 Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org> Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
parent
d8bca3564a
commit
4b347fccb2
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 Haiku Inc. All rights reserved.
|
||||
* Copyright 2010-2023 Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _B_HTTP_AUTHENTICATION_H_
|
||||
@ -24,8 +24,10 @@ enum BHttpAuthenticationMethod {
|
||||
// Basic base64 authentication method (unsecure)
|
||||
B_HTTP_AUTHENTICATION_DIGEST = 2,
|
||||
// Digest authentication
|
||||
B_HTTP_AUTHENTICATION_IE_DIGEST = 4
|
||||
B_HTTP_AUTHENTICATION_IE_DIGEST = 4,
|
||||
// Slightly modified digest authentication to mimic old IE one
|
||||
B_HTTP_AUTHENTICATION_BEARER = 5
|
||||
// Bearer authentication used to convey a token
|
||||
};
|
||||
|
||||
|
||||
@ -56,6 +58,7 @@ public:
|
||||
// Field modification
|
||||
void SetUserName(const BString& username);
|
||||
void SetPassword(const BString& password);
|
||||
void SetToken(const BString& token);
|
||||
void SetMethod(
|
||||
BHttpAuthenticationMethod type);
|
||||
status_t Initialize(const BString& wwwAuthenticate);
|
||||
@ -63,6 +66,7 @@ public:
|
||||
// Field access
|
||||
const BString& UserName() const;
|
||||
const BString& Password() const;
|
||||
const BString& Token() const;
|
||||
BHttpAuthenticationMethod Method() const;
|
||||
|
||||
BString Authorization(const BUrl& url,
|
||||
@ -89,6 +93,7 @@ private:
|
||||
BHttpAuthenticationMethod fAuthenticationMethod;
|
||||
BString fUserName;
|
||||
BString fPassword;
|
||||
BString fToken;
|
||||
|
||||
BString fRealm;
|
||||
BString fDigestNonce;
|
||||
|
@ -8,7 +8,7 @@ resource app_flags B_SINGLE_LAUNCH;
|
||||
resource app_version {
|
||||
major = 0,
|
||||
middle = 0,
|
||||
minor = 6,
|
||||
minor = 7,
|
||||
|
||||
variety = B_APPV_ALPHA,
|
||||
internal = 1,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2021, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2018-2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef HAIKU_DEPOT_CONSTANTS_H
|
||||
@ -66,6 +66,7 @@ enum BitmapSize {
|
||||
#define HD_CLIENT_TOO_OLD (HD_ERROR_BASE + 2)
|
||||
#define HD_ERR_NOT_MODIFIED (HD_ERROR_BASE + 3)
|
||||
#define HD_ERR_NO_DATA (HD_ERROR_BASE + 4)
|
||||
#define HD_AUTHENTICATION_FAILED (HD_ERROR_BASE + 5)
|
||||
|
||||
|
||||
#define REPOSITORY_NAME_SYSTEM "system"
|
||||
|
@ -113,6 +113,7 @@ local textDocumentSources =
|
||||
|
||||
local applicationSources =
|
||||
App.cpp
|
||||
AccessToken.cpp
|
||||
BitmapView.cpp
|
||||
Captcha.cpp
|
||||
CreateUserDetail.cpp
|
||||
@ -124,6 +125,7 @@ local applicationSources =
|
||||
IconTarPtr.cpp
|
||||
IncrementViewCounterProcess.cpp
|
||||
JobStateListener.cpp
|
||||
JwtTokenHelper.cpp
|
||||
LanguageModel.cpp
|
||||
LinkView.cpp
|
||||
LinkedBitmapView.cpp
|
||||
|
134
src/apps/haikudepot/model/AccessToken.cpp
Normal file
134
src/apps/haikudepot/model/AccessToken.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "AccessToken.h"
|
||||
|
||||
#include "JwtTokenHelper.h"
|
||||
#include "Logger.h"
|
||||
|
||||
// These are keys that are used to store this object's data into a BMessage instance.
|
||||
|
||||
#define KEY_TOKEN "token"
|
||||
#define KEY_EXPIRY_TIMESTAMP "expiryTimestamp"
|
||||
|
||||
|
||||
AccessToken::AccessToken(BMessage* from)
|
||||
:
|
||||
fToken(""),
|
||||
fExpiryTimestamp(0)
|
||||
{
|
||||
if (from->FindString(KEY_TOKEN, &fToken) != B_OK) {
|
||||
HDERROR("expected key [%s] in the message data when creating an access"
|
||||
" token", KEY_TOKEN);
|
||||
}
|
||||
|
||||
if (from->FindUInt64(KEY_EXPIRY_TIMESTAMP, &fExpiryTimestamp) != B_OK) {
|
||||
HDERROR("expected key [%s] in the message data when creating an access"
|
||||
" token", KEY_EXPIRY_TIMESTAMP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AccessToken::AccessToken()
|
||||
:
|
||||
fToken(""),
|
||||
fExpiryTimestamp(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AccessToken::~AccessToken()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AccessToken&
|
||||
AccessToken::operator=(const AccessToken& other)
|
||||
{
|
||||
fToken = other.fToken;
|
||||
fExpiryTimestamp = other.fExpiryTimestamp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
AccessToken::operator==(const AccessToken& other) const
|
||||
{
|
||||
return fToken == other.fToken;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
AccessToken::operator!=(const AccessToken& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
AccessToken::Token() const
|
||||
{
|
||||
return fToken;
|
||||
}
|
||||
|
||||
|
||||
uint64
|
||||
AccessToken::ExpiryTimestamp() const
|
||||
{
|
||||
return fExpiryTimestamp;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AccessToken::SetToken(const BString& value)
|
||||
{
|
||||
fToken = value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AccessToken::SetExpiryTimestamp(uint64 value)
|
||||
{
|
||||
fExpiryTimestamp = value;
|
||||
}
|
||||
|
||||
|
||||
/*! The access token may have a value or may be the empty string. This method
|
||||
will check that the token appears to be an access token.
|
||||
*/
|
||||
|
||||
bool
|
||||
AccessToken::IsValid() const
|
||||
{
|
||||
return JwtTokenHelper::IsValid(fToken);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
AccessToken::IsValid(uint64 currentTimestamp) const
|
||||
{
|
||||
return IsValid() && (fExpiryTimestamp == 0 || fExpiryTimestamp > currentTimestamp);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AccessToken::Clear()
|
||||
{
|
||||
fToken = "";
|
||||
fExpiryTimestamp = 0;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AccessToken::Archive(BMessage* into, bool deep) const
|
||||
{
|
||||
status_t result = B_OK;
|
||||
if (result == B_OK && into == NULL)
|
||||
result = B_ERROR;
|
||||
if (result == B_OK)
|
||||
result = into->AddString(KEY_TOKEN, fToken);
|
||||
if (result == B_OK)
|
||||
result = into->AddUInt64(KEY_EXPIRY_TIMESTAMP, fExpiryTimestamp);
|
||||
return result;
|
||||
}
|
51
src/apps/haikudepot/model/AccessToken.h
Normal file
51
src/apps/haikudepot/model/AccessToken.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef ACCESS_TOKEN_H
|
||||
#define ACCESS_TOKEN_H
|
||||
|
||||
|
||||
#include <Archivable.h>
|
||||
#include <String.h>
|
||||
|
||||
class BPositionIO;
|
||||
|
||||
/*! When a user authenticates with the HDS system, the authentication API will
|
||||
return a JWT access token which can then be later used with other APIs. This
|
||||
object models the token. The reason why the token is modelled like
|
||||
this is that the access token is not an opaque string; it contains a number
|
||||
of key-value pairs that are known as "claims". Some of the claims are used to
|
||||
detect, for example, when the access token has expired.
|
||||
*/
|
||||
|
||||
class AccessToken : public BArchivable {
|
||||
public:
|
||||
AccessToken(BMessage* from);
|
||||
AccessToken();
|
||||
virtual ~AccessToken();
|
||||
|
||||
AccessToken& operator=(const AccessToken& other);
|
||||
bool operator==(const AccessToken& other) const;
|
||||
bool operator!=(const AccessToken& other) const;
|
||||
|
||||
const BString& Token() const;
|
||||
uint64 ExpiryTimestamp() const;
|
||||
|
||||
void SetToken(const BString& value);
|
||||
void SetExpiryTimestamp(uint64 value);
|
||||
|
||||
bool IsValid() const;
|
||||
bool IsValid(uint64 currentTimestamp) const;
|
||||
|
||||
void Clear();
|
||||
|
||||
status_t Archive(BMessage* into, bool deep = true) const;
|
||||
private:
|
||||
BString fToken;
|
||||
uint64 fExpiryTimestamp;
|
||||
// milliseconds since epoc UTC
|
||||
};
|
||||
|
||||
|
||||
#endif // ACCESS_TOKEN_H
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2014, Axel Dörfler <axeld@pinc-software.de>.
|
||||
* Copyright 2016-2022, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2016-2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@ -567,7 +567,7 @@ Model::PopulatePackage(const PackageInfoRef& package, uint32 flags)
|
||||
}
|
||||
|
||||
status_t status = fWebAppInterface
|
||||
.RetreiveUserRatingsForPackageForDisplay(packageName,
|
||||
.RetrieveUserRatingsForPackageForDisplay(packageName,
|
||||
webAppRepositoryCode, webAppRepositorySourceCode, 0,
|
||||
PACKAGE_INFO_MAX_USER_RATINGS, info);
|
||||
if (status == B_OK) {
|
||||
@ -780,19 +780,19 @@ Model::SetNickname(BString nickname)
|
||||
nickname = "";
|
||||
}
|
||||
|
||||
SetAuthorization(nickname, password, false);
|
||||
SetCredentials(nickname, password, false);
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
Model::Nickname() const
|
||||
Model::Nickname()
|
||||
{
|
||||
return fWebAppInterface.Nickname();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::SetAuthorization(const BString& nickname, const BString& passwordClear,
|
||||
Model::SetCredentials(const BString& nickname, const BString& passwordClear,
|
||||
bool storePassword)
|
||||
{
|
||||
BString existingNickname = Nickname();
|
||||
@ -820,7 +820,7 @@ Model::SetAuthorization(const BString& nickname, const BString& passwordClear,
|
||||
}
|
||||
|
||||
BAutolock locker(&fLock);
|
||||
fWebAppInterface.SetAuthorization(UserCredentials(nickname, passwordClear));
|
||||
fWebAppInterface.SetCredentials(UserCredentials(nickname, passwordClear));
|
||||
|
||||
if (nickname != existingNickname)
|
||||
_NotifyAuthorizationChanged();
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2016-2021, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2016-2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef MODEL_H
|
||||
@ -148,14 +148,13 @@ public:
|
||||
uint32 flags);
|
||||
|
||||
void SetNickname(BString nickname);
|
||||
const BString& Nickname() const;
|
||||
void SetAuthorization(const BString& nickname,
|
||||
const BString& Nickname();
|
||||
void SetCredentials(const BString& nickname,
|
||||
const BString& passwordClear,
|
||||
bool storePassword);
|
||||
|
||||
WebAppInterface&
|
||||
GetWebAppInterface()
|
||||
{ return fWebAppInterface; }
|
||||
WebAppInterface* GetWebAppInterface()
|
||||
{ return &fWebAppInterface; }
|
||||
|
||||
status_t IconTarPath(BPath& path) const;
|
||||
status_t DumpExportReferenceDataPath(BPath& path);
|
||||
|
@ -55,6 +55,31 @@ UserCredentials::~UserCredentials()
|
||||
}
|
||||
|
||||
|
||||
UserCredentials&
|
||||
UserCredentials::operator=(const UserCredentials& other)
|
||||
{
|
||||
fNickname = other.fNickname;
|
||||
fPasswordClear = other.fPasswordClear;
|
||||
fIsSuccessful = other.fIsSuccessful;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
UserCredentials::operator==(const UserCredentials& other) const
|
||||
{
|
||||
return fNickname == other.fNickname && fPasswordClear == other.fPasswordClear
|
||||
&& fIsSuccessful == other.fIsSuccessful;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
UserCredentials::operator!=(const UserCredentials& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
UserCredentials::Nickname() const
|
||||
{
|
||||
@ -104,16 +129,6 @@ UserCredentials::SetIsSuccessful(bool value)
|
||||
}
|
||||
|
||||
|
||||
UserCredentials&
|
||||
UserCredentials::operator=(const UserCredentials& other)
|
||||
{
|
||||
fNickname = other.fNickname;
|
||||
fPasswordClear = other.fPasswordClear;
|
||||
fIsSuccessful = other.fIsSuccessful;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UserCredentials::Archive(BMessage* into, bool deep) const
|
||||
{
|
||||
|
@ -25,6 +25,10 @@ public:
|
||||
UserCredentials();
|
||||
virtual ~UserCredentials();
|
||||
|
||||
UserCredentials& operator=(const UserCredentials& other);
|
||||
bool operator==(const UserCredentials& other) const;
|
||||
bool operator!=(const UserCredentials& other) const;
|
||||
|
||||
const BString& Nickname() const;
|
||||
const BString& PasswordClear() const;
|
||||
const bool IsSuccessful() const;
|
||||
@ -34,8 +38,6 @@ public:
|
||||
void SetPasswordClear(const BString& value);
|
||||
void SetIsSuccessful(bool value);
|
||||
|
||||
UserCredentials& operator=(const UserCredentials& other);
|
||||
|
||||
status_t Archive(BMessage* into, bool deep = true) const;
|
||||
private:
|
||||
BString fNickname;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2021-2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "IncrementViewCounterProcess.h"
|
||||
@ -81,13 +81,11 @@ IncrementViewCounterProcess::RunInternal()
|
||||
|
||||
while (attempts > 0 && !WasStopped()) {
|
||||
BMessage resultEnvelope;
|
||||
WebAppInterface& webAppInterface = fModel->GetWebAppInterface();
|
||||
result = webAppInterface.IncrementViewCounter(fPackage, depot,
|
||||
resultEnvelope);
|
||||
WebAppInterface* webAppInterface = fModel->GetWebAppInterface();
|
||||
result = webAppInterface->IncrementViewCounter(fPackage, depot, resultEnvelope);
|
||||
|
||||
if (result == B_OK) {
|
||||
int32 errorCode = webAppInterface.ErrorCodeFromResponse(
|
||||
resultEnvelope);
|
||||
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(resultEnvelope);
|
||||
switch (errorCode) {
|
||||
case ERROR_CODE_NONE:
|
||||
HDINFO("did increment the view counter for [%s]",
|
||||
|
78
src/apps/haikudepot/server/JwtTokenHelper.cpp
Normal file
78
src/apps/haikudepot/server/JwtTokenHelper.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "JwtTokenHelper.h"
|
||||
|
||||
#include "DataIOUtils.h"
|
||||
#include "Json.h"
|
||||
#include "JsonMessageWriter.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
/*static*/ bool
|
||||
JwtTokenHelper::IsValid(const BString& value)
|
||||
{
|
||||
int countDots = 0;
|
||||
|
||||
for (int i = 0; i < value.Length(); i++) {
|
||||
char ch = value[i];
|
||||
|
||||
if ('.' == ch)
|
||||
countDots++;
|
||||
else {
|
||||
if (!_IsBase64(ch))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return 2 == countDots;
|
||||
}
|
||||
|
||||
|
||||
/*! A JWT token is split into three parts separated by a '.' character. The
|
||||
middle section is a base-64 encoded string and within the string is JSON
|
||||
structured data. The JSON data contains key-value pairs which carry data
|
||||
about the token. This method will take a JWT token, will find the middle
|
||||
section and will parse the claims into the supplied 'message' parameter.
|
||||
*/
|
||||
|
||||
/*static*/ status_t
|
||||
JwtTokenHelper::ParseClaims(const BString& token, BMessage& message)
|
||||
{
|
||||
int firstDot = token.FindFirst('.');
|
||||
|
||||
if (firstDot == B_ERROR)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// find the end of the first section by looking for the next dot.
|
||||
|
||||
int secondDot = token.FindFirst('.', firstDot + 1);
|
||||
|
||||
if (secondDot == B_ERROR)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
BMemoryIO memoryIo(&(token.String())[firstDot + 1], (secondDot - firstDot) - 1);
|
||||
Base64DecodingDataIO base64DecodingIo(&memoryIo, '-', '_');
|
||||
BJsonMessageWriter writer(message);
|
||||
BJson::Parse(&base64DecodingIo, &writer);
|
||||
|
||||
return writer.ErrorStatus();
|
||||
}
|
||||
|
||||
|
||||
/*! Note this is base64 "url" standard that disallows "/" and "+" and instead
|
||||
uses "-" and "_".
|
||||
*/
|
||||
|
||||
/*static*/ bool
|
||||
JwtTokenHelper::_IsBase64(char ch)
|
||||
{
|
||||
return isalnum(ch)
|
||||
|| '=' == ch
|
||||
|| '-' == ch
|
||||
|| '_' == ch;
|
||||
}
|
37
src/apps/haikudepot/server/JwtTokenHelper.h
Normal file
37
src/apps/haikudepot/server/JwtTokenHelper.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef JWT_TOKEN_HELPER_H
|
||||
#define JWT_TOKEN_HELPER_H
|
||||
|
||||
|
||||
#include <String.h>
|
||||
|
||||
|
||||
class BMessage;
|
||||
|
||||
|
||||
/*! The term "JWT" is short for "Java Web Token" and is a token format typically used to convey
|
||||
proof of an authentication. The token is structured and contains three parts separated by the
|
||||
full-stop character ".". The first part is a header, the middle part contains some data (which
|
||||
is termed the "claims") and the last part is a signature proving the authenticity of the claims.
|
||||
|
||||
The claims are base-64 encoded JSON and the JSON data typically conveys some key-value pairs
|
||||
with a number of the key names being well-known through standards.
|
||||
|
||||
This class contains a number of helper methods for working with JWT tokens.
|
||||
*/
|
||||
|
||||
class JwtTokenHelper {
|
||||
public:
|
||||
static bool IsValid(const BString& value);
|
||||
|
||||
static status_t ParseClaims(const BString& token,
|
||||
BMessage& message);
|
||||
|
||||
private:
|
||||
static bool _IsBase64(char ch);
|
||||
};
|
||||
|
||||
#endif // JWT_TOKEN_HELPER_H
|
@ -99,18 +99,18 @@ UserDetailVerifierProcess::_ShouldVerify()
|
||||
status_t
|
||||
UserDetailVerifierProcess::_TryFetchUserDetail(UserDetail& userDetail)
|
||||
{
|
||||
WebAppInterface interface = fModel->GetWebAppInterface();
|
||||
WebAppInterface* interface = fModel->GetWebAppInterface();
|
||||
BMessage userDetailResponse;
|
||||
status_t result;
|
||||
|
||||
result = interface.RetrieveCurrentUserDetail(userDetailResponse);
|
||||
result = interface->RetrieveCurrentUserDetail(userDetailResponse);
|
||||
if (result != B_OK) {
|
||||
HDERROR("a problem has arisen retrieving the current user detail: %s",
|
||||
strerror(result));
|
||||
}
|
||||
|
||||
if (result == B_OK) {
|
||||
int32 errorCode = interface.ErrorCodeFromResponse(userDetailResponse);
|
||||
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(userDetailResponse);
|
||||
switch (errorCode) {
|
||||
case ERROR_CODE_NONE:
|
||||
break;
|
||||
@ -132,7 +132,7 @@ UserDetailVerifierProcess::_TryFetchUserDetail(UserDetail& userDetail)
|
||||
// worked, it is now necessary to check to see that the user has agreed
|
||||
// to the most recent user-usage conditions.
|
||||
|
||||
result = interface.UnpackUserDetail(userDetailResponse, userDetail);
|
||||
result = interface->UnpackUserDetail(userDetailResponse, userDetail);
|
||||
if (result != B_OK)
|
||||
HDERROR("it was not possible to unpack the user details.");
|
||||
}
|
||||
|
@ -6,21 +6,24 @@
|
||||
|
||||
#include "WebAppInterface.h"
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <Application.h>
|
||||
#include <Message.h>
|
||||
#include <Url.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <AutoLocker.h>
|
||||
#include <HttpHeaders.h>
|
||||
#include <HttpRequest.h>
|
||||
#include <Json.h>
|
||||
#include <JsonTextWriter.h>
|
||||
#include <JsonMessageWriter.h>
|
||||
#include <Message.h>
|
||||
#include <Url.h>
|
||||
#include <UrlContext.h>
|
||||
#include <UrlProtocolListener.h>
|
||||
#include <UrlProtocolRoster.h>
|
||||
|
||||
#include "DataIOUtils.h"
|
||||
#include "HaikuDepotConstants.h"
|
||||
#include "JwtTokenHelper.h"
|
||||
#include "Logger.h"
|
||||
#include "ServerSettings.h"
|
||||
#include "ServerHelper.h"
|
||||
@ -103,10 +106,6 @@ make_http_request(const BUrl& url, BDataIO* output,
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
WebAppInterface::fRequestIndex = 0;
|
||||
|
||||
|
||||
enum {
|
||||
NEEDS_AUTHORIZATION = 1 << 0,
|
||||
};
|
||||
@ -117,38 +116,26 @@ WebAppInterface::WebAppInterface()
|
||||
}
|
||||
|
||||
|
||||
WebAppInterface::WebAppInterface(const WebAppInterface& other)
|
||||
:
|
||||
fCredentials(other.fCredentials)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
WebAppInterface::~WebAppInterface()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
WebAppInterface&
|
||||
WebAppInterface::operator=(const WebAppInterface& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
fCredentials = other.fCredentials;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebAppInterface::SetAuthorization(const UserCredentials& value)
|
||||
WebAppInterface::SetCredentials(const UserCredentials& value)
|
||||
{
|
||||
fCredentials = value;
|
||||
AutoLocker<BLocker> lock(&fLock);
|
||||
if (fCredentials != value) {
|
||||
fCredentials = value;
|
||||
fAccessToken.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
WebAppInterface::Nickname() const
|
||||
WebAppInterface::Nickname()
|
||||
{
|
||||
AutoLocker<BLocker> lock(&fLock);
|
||||
return fCredentials.Nickname();
|
||||
}
|
||||
|
||||
@ -172,7 +159,7 @@ WebAppInterface::GetChangelog(const BString& packageName, BMessage& message)
|
||||
|
||||
|
||||
status_t
|
||||
WebAppInterface::RetreiveUserRatingsForPackageForDisplay(
|
||||
WebAppInterface::RetrieveUserRatingsForPackageForDisplay(
|
||||
const BString& packageName,
|
||||
const BString& webAppRepositoryCode,
|
||||
const BString& webAppRepositorySourceCode,
|
||||
@ -209,7 +196,7 @@ WebAppInterface::RetreiveUserRatingsForPackageForDisplay(
|
||||
|
||||
|
||||
status_t
|
||||
WebAppInterface::RetreiveUserRatingForPackageAndVersionByUser(
|
||||
WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser(
|
||||
const BString& packageName, const BPackageVersion& version,
|
||||
const BString& architecture,
|
||||
const BString& webAppRepositoryCode,
|
||||
@ -283,19 +270,40 @@ WebAppInterface::RetrieveUserDetailForCredentials(
|
||||
"to obtain the user detail");
|
||||
}
|
||||
|
||||
// BHttpRequest later takes ownership of this.
|
||||
BMallocIO* requestEnvelopeData = new BMallocIO();
|
||||
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
|
||||
status_t result = B_OK;
|
||||
|
||||
requestEnvelopeWriter.WriteObjectStart();
|
||||
requestEnvelopeWriter.WriteObjectName("nickname");
|
||||
requestEnvelopeWriter.WriteString(credentials.Nickname().String());
|
||||
requestEnvelopeWriter.WriteObjectEnd();
|
||||
// authenticate the user and obtain a token to use with the latter
|
||||
// request.
|
||||
|
||||
status_t result = _SendJsonRequest("user/get-user", credentials,
|
||||
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
|
||||
NEEDS_AUTHORIZATION, message);
|
||||
// note that the credentials used here are passed in as args.
|
||||
BMessage authenticateResponseEnvelopeMessage;
|
||||
|
||||
if (result == B_OK) {
|
||||
result = AuthenticateUser(
|
||||
credentials.Nickname(),
|
||||
credentials.PasswordClear(),
|
||||
authenticateResponseEnvelopeMessage);
|
||||
}
|
||||
|
||||
AccessToken accessToken;
|
||||
|
||||
if (result == B_OK)
|
||||
result = UnpackAccessToken(authenticateResponseEnvelopeMessage, accessToken);
|
||||
|
||||
if (result == B_OK) {
|
||||
// BHttpRequest later takes ownership of this.
|
||||
BMallocIO* requestEnvelopeData = new BMallocIO();
|
||||
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
|
||||
|
||||
requestEnvelopeWriter.WriteObjectStart();
|
||||
requestEnvelopeWriter.WriteObjectName("nickname");
|
||||
requestEnvelopeWriter.WriteString(credentials.Nickname().String());
|
||||
requestEnvelopeWriter.WriteObjectEnd();
|
||||
|
||||
result = _SendJsonRequest("user/get-user", accessToken,
|
||||
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData),
|
||||
NEEDS_AUTHORIZATION, message);
|
||||
// note that the credentials used here are passed in as args.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -308,7 +316,8 @@ WebAppInterface::RetrieveUserDetailForCredentials(
|
||||
status_t
|
||||
WebAppInterface::RetrieveCurrentUserDetail(BMessage& message)
|
||||
{
|
||||
return RetrieveUserDetailForCredentials(fCredentials, message);
|
||||
UserCredentials credentials = _Credentials();
|
||||
return RetrieveUserDetailForCredentials(credentials, message);
|
||||
}
|
||||
|
||||
|
||||
@ -366,6 +375,66 @@ WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage,
|
||||
}
|
||||
|
||||
|
||||
/*! When an authentication API call is made, the response (if successful) will
|
||||
return an access token in the response. This method will take the response
|
||||
from the server and will parse out the access token data into the supplied
|
||||
object.
|
||||
*/
|
||||
|
||||
/*static*/ status_t
|
||||
WebAppInterface::UnpackAccessToken(BMessage& responseEnvelopeMessage,
|
||||
AccessToken& accessToken)
|
||||
{
|
||||
status_t result;
|
||||
|
||||
BMessage resultMessage;
|
||||
result = responseEnvelopeMessage.FindMessage(
|
||||
"result", &resultMessage);
|
||||
|
||||
if (result != B_OK) {
|
||||
HDERROR("bad response envelope missing 'result' entry");
|
||||
return result;
|
||||
}
|
||||
|
||||
BString token;
|
||||
result = resultMessage.FindString("token", &token);
|
||||
|
||||
if (result != B_OK || token.IsEmpty()) {
|
||||
HDINFO("failure to authenticate");
|
||||
return HD_AUTHENTICATION_FAILED;
|
||||
}
|
||||
|
||||
// The token should be present in three parts; the header, the claims and
|
||||
// then a digital signature. The logic here wants to extract some data
|
||||
// from the claims part.
|
||||
|
||||
BMessage claimsMessage;
|
||||
result = JwtTokenHelper::ParseClaims(token, claimsMessage);
|
||||
|
||||
if (Logger::IsTraceEnabled()) {
|
||||
HDTRACE("start; token claims...");
|
||||
claimsMessage.PrintToStream();
|
||||
HDTRACE("...end; token claims");
|
||||
}
|
||||
|
||||
if (B_OK == result) {
|
||||
accessToken.SetToken(token);
|
||||
accessToken.SetExpiryTimestamp(0);
|
||||
|
||||
double expiryTimestampDouble;
|
||||
|
||||
// The claims should have parsed but it could transpire that there is
|
||||
// no expiry. This should not be the case, but it is theoretically
|
||||
// possible.
|
||||
|
||||
if (claimsMessage.FindDouble("exp", &expiryTimestampDouble) == B_OK)
|
||||
accessToken.SetExpiryTimestamp(1000 * static_cast<uint64>(expiryTimestampDouble));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Returns data relating to the user usage conditions
|
||||
|
||||
\param code defines the version of the data to return or if empty then the
|
||||
@ -432,7 +501,7 @@ WebAppInterface::AgreeUserUsageConditions(const BString& code,
|
||||
requestEnvelopeWriter.WriteObjectName("userUsageConditionsCode");
|
||||
requestEnvelopeWriter.WriteString(code.String());
|
||||
requestEnvelopeWriter.WriteObjectName("nickname");
|
||||
requestEnvelopeWriter.WriteString(fCredentials.Nickname());
|
||||
requestEnvelopeWriter.WriteString(Nickname());
|
||||
requestEnvelopeWriter.WriteObjectEnd();
|
||||
|
||||
// now fetch this information into an object.
|
||||
@ -504,7 +573,7 @@ WebAppInterface::CreateUserRating(const BString& packageName,
|
||||
requestEnvelopeWriter.WriteObjectName("pkgVersionType");
|
||||
requestEnvelopeWriter.WriteString("SPECIFIC");
|
||||
requestEnvelopeWriter.WriteObjectName("userNickname");
|
||||
requestEnvelopeWriter.WriteString(fCredentials.Nickname());
|
||||
requestEnvelopeWriter.WriteString(Nickname());
|
||||
|
||||
if (!version.Major().IsEmpty()) {
|
||||
requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
|
||||
@ -668,6 +737,47 @@ WebAppInterface::CreateUser(const BString& nickName,
|
||||
}
|
||||
|
||||
|
||||
/*! This method will authenticate the user set in the credentials and will
|
||||
retain the resultant access token for authenticating any latter API calls.
|
||||
*/
|
||||
|
||||
status_t
|
||||
WebAppInterface::AuthenticateUserRetainingAccessToken()
|
||||
{
|
||||
UserCredentials userCredentials = _Credentials();
|
||||
|
||||
if (!userCredentials.IsValid()) {
|
||||
HDINFO("unable to get a new access token as there are no credentials");
|
||||
return B_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return _AuthenticateUserRetainingAccessToken(userCredentials.Nickname(),
|
||||
userCredentials.PasswordClear());
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WebAppInterface::_AuthenticateUserRetainingAccessToken(const BString& nickName,
|
||||
const BString& passwordClear) {
|
||||
AutoLocker<BLocker> lock(&fLock);
|
||||
|
||||
fAccessToken.Clear();
|
||||
|
||||
BMessage responseEnvelopeMessage;
|
||||
status_t result = AuthenticateUser(nickName, passwordClear, responseEnvelopeMessage);
|
||||
|
||||
AccessToken accessToken;
|
||||
|
||||
if (result == B_OK)
|
||||
result = UnpackAccessToken(responseEnvelopeMessage, accessToken);
|
||||
|
||||
if (result == B_OK)
|
||||
fAccessToken = accessToken;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WebAppInterface::AuthenticateUser(const BString& nickName,
|
||||
const BString& passwordClear, BMessage& message)
|
||||
@ -800,7 +910,7 @@ WebAppInterface::_RetrievePasswordRequirementsMeta(BMessage& message)
|
||||
not look like an error.
|
||||
*/
|
||||
|
||||
int32
|
||||
/*static*/ int32
|
||||
WebAppInterface::ErrorCodeFromResponse(BMessage& responseEnvelopeMessage)
|
||||
{
|
||||
BMessage error;
|
||||
@ -821,17 +931,23 @@ WebAppInterface::ErrorCodeFromResponse(BMessage& responseEnvelopeMessage)
|
||||
status_t
|
||||
WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
|
||||
BPositionIO* requestData, size_t requestDataSize, uint32 flags,
|
||||
BMessage& reply) const
|
||||
BMessage& reply)
|
||||
{
|
||||
return _SendJsonRequest(urlPathComponents, fCredentials, requestData,
|
||||
bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0;
|
||||
AccessToken accessToken;
|
||||
|
||||
if (needsAuthorization)
|
||||
accessToken = _ObtainValidAccessToken();
|
||||
|
||||
return _SendJsonRequest(urlPathComponents, accessToken, requestData,
|
||||
requestDataSize, flags, reply);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
/*static*/ status_t
|
||||
WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
|
||||
UserCredentials credentials, BPositionIO* requestData,
|
||||
size_t requestDataSize, uint32 flags, BMessage& reply) const
|
||||
const AccessToken& accessToken, BPositionIO* requestData,
|
||||
size_t requestDataSize, uint32 flags, BMessage& reply)
|
||||
{
|
||||
if (requestDataSize == 0) {
|
||||
HDINFO("%s; empty request payload", PROTOCOL_NAME);
|
||||
@ -852,6 +968,15 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
|
||||
return HD_CLIENT_TOO_OLD;
|
||||
}
|
||||
|
||||
bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0;
|
||||
|
||||
if (needsAuthorization && !accessToken.IsValid()) {
|
||||
HDDEBUG("%s; dropping request to ...[%s] as access token is not valid",
|
||||
PROTOCOL_NAME, urlPathComponents);
|
||||
delete requestData;
|
||||
return B_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
BUrl url = ServerSettings::CreateFullUrl(BString("/__api/v2/")
|
||||
<< urlPathComponents);
|
||||
HDDEBUG("%s; will make request to [%s]", PROTOCOL_NAME,
|
||||
@ -883,13 +1008,10 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
|
||||
request->SetMethod(B_HTTP_POST);
|
||||
request->SetHeaders(headers);
|
||||
|
||||
// Authentication via Basic Authentication
|
||||
// The other way would be to obtain a token and then use the Token Bearer
|
||||
// header.
|
||||
if (((flags & NEEDS_AUTHORIZATION) != 0) && credentials.IsValid()) {
|
||||
BHttpAuthentication authentication(credentials.Nickname(),
|
||||
credentials.PasswordClear());
|
||||
authentication.SetMethod(B_HTTP_AUTHENTICATION_BASIC);
|
||||
if (needsAuthorization) {
|
||||
BHttpAuthentication authentication;
|
||||
authentication.SetMethod(B_HTTP_AUTHENTICATION_BEARER);
|
||||
authentication.SetToken(accessToken.Token());
|
||||
context.AddAuthentication(url, authentication);
|
||||
}
|
||||
|
||||
@ -948,7 +1070,7 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
|
||||
|
||||
status_t
|
||||
WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
|
||||
const BString& jsonString, uint32 flags, BMessage& reply) const
|
||||
const BString& jsonString, uint32 flags, BMessage& reply)
|
||||
{
|
||||
// gets 'adopted' by the subsequent http request.
|
||||
BMemoryIO* data = new BMemoryIO(jsonString.String(),
|
||||
@ -1042,3 +1164,32 @@ WebAppInterface::_LengthAndSeekToZero(BPositionIO* data)
|
||||
data->Seek(0, SEEK_SET);
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
|
||||
UserCredentials
|
||||
WebAppInterface::_Credentials()
|
||||
{
|
||||
AutoLocker<BLocker> lock(&fLock);
|
||||
return fCredentials;
|
||||
}
|
||||
|
||||
|
||||
AccessToken
|
||||
WebAppInterface::_ObtainValidAccessToken()
|
||||
{
|
||||
AutoLocker<BLocker> lock(&fLock);
|
||||
|
||||
uint64 now = static_cast<uint64>(time(NULL)) * 1000;
|
||||
|
||||
if (!fAccessToken.IsValid(now)) {
|
||||
HDINFO("clearing cached access token as it is no longer valid");
|
||||
fAccessToken.Clear();
|
||||
}
|
||||
|
||||
if (!fAccessToken.IsValid()) {
|
||||
HDINFO("no cached access token present; will obtain a new one");
|
||||
AuthenticateUserRetainingAccessToken();
|
||||
}
|
||||
|
||||
return fAccessToken;
|
||||
}
|
||||
|
@ -9,9 +9,11 @@
|
||||
|
||||
#include <Application.h>
|
||||
#include <JsonWriter.h>
|
||||
#include <Locker.h>
|
||||
#include <String.h>
|
||||
#include <package/PackageVersion.h>
|
||||
|
||||
#include "AccessToken.h"
|
||||
#include "PackageInfo.h"
|
||||
#include "PasswordRequirements.h"
|
||||
#include "UserCredentials.h"
|
||||
@ -43,26 +45,23 @@ using BPackageKit::BPackageVersion;
|
||||
class WebAppInterface {
|
||||
public:
|
||||
WebAppInterface();
|
||||
WebAppInterface(const WebAppInterface& other);
|
||||
virtual ~WebAppInterface();
|
||||
|
||||
WebAppInterface& operator=(const WebAppInterface& other);
|
||||
|
||||
void SetAuthorization(const UserCredentials& value);
|
||||
const BString& Nickname() const;
|
||||
void SetCredentials(const UserCredentials& value);
|
||||
const BString& Nickname();
|
||||
|
||||
status_t GetChangelog(
|
||||
const BString& packageName,
|
||||
BMessage& message);
|
||||
|
||||
status_t RetreiveUserRatingsForPackageForDisplay(
|
||||
status_t RetrieveUserRatingsForPackageForDisplay(
|
||||
const BString& packageName,
|
||||
const BString& webAppRepositoryCode,
|
||||
const BString& webAppRepositorySourceCode,
|
||||
int resultOffset, int maxResults,
|
||||
BMessage& message);
|
||||
|
||||
status_t RetreiveUserRatingForPackageAndVersionByUser(
|
||||
status_t RetrieveUserRatingForPackageAndVersionByUser(
|
||||
const BString& packageName,
|
||||
const BPackageVersion& version,
|
||||
const BString& architecture,
|
||||
@ -121,6 +120,8 @@ public:
|
||||
const BString& userUsageConditionsCode,
|
||||
BMessage& message);
|
||||
|
||||
status_t AuthenticateUserRetainingAccessToken();
|
||||
|
||||
status_t AuthenticateUser(const BString& nickName,
|
||||
const BString& passwordClear,
|
||||
BMessage& message);
|
||||
@ -139,7 +140,17 @@ public:
|
||||
static status_t UnpackUserDetail(
|
||||
BMessage& responseEnvelopeMessage,
|
||||
UserDetail& userDetail);
|
||||
|
||||
static status_t UnpackAccessToken(
|
||||
BMessage& responseEnvelopeMessage,
|
||||
AccessToken& accessToken);
|
||||
private:
|
||||
UserCredentials _Credentials();
|
||||
|
||||
AccessToken _ObtainValidAccessToken();
|
||||
|
||||
status_t _AuthenticateUserRetainingAccessToken(const BString& nickName,
|
||||
const BString& passwordClear);
|
||||
|
||||
status_t _RetrievePasswordRequirementsMeta(
|
||||
BMessage& message);
|
||||
@ -151,16 +162,16 @@ private:
|
||||
|
||||
status_t _SendJsonRequest(const char* urlPathComponents,
|
||||
const BString& jsonString, uint32 flags,
|
||||
BMessage& reply) const;
|
||||
status_t _SendJsonRequest(const char* urlPathComponents,
|
||||
UserCredentials credentials,
|
||||
BPositionIO* requestData,
|
||||
size_t requestDataSize, uint32 flags,
|
||||
BMessage& reply) const;
|
||||
BMessage& reply);
|
||||
status_t _SendJsonRequest(const char* urlPathComponents,
|
||||
BPositionIO* requestData,
|
||||
size_t requestDataSize, uint32 flags,
|
||||
BMessage& reply) const;
|
||||
BMessage& reply);
|
||||
static status_t _SendJsonRequest(const char* urlPathComponents,
|
||||
const AccessToken& accessToken,
|
||||
BPositionIO* requestData,
|
||||
size_t requestDataSize, uint32 flags,
|
||||
BMessage& reply);
|
||||
|
||||
status_t _SendRawGetRequest(
|
||||
const BString urlPathComponents,
|
||||
@ -171,7 +182,8 @@ private:
|
||||
|
||||
private:
|
||||
UserCredentials fCredentials;
|
||||
static int fRequestIndex;
|
||||
AccessToken fAccessToken;
|
||||
BLocker fLock;
|
||||
};
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2013, Rene Gollent, rene@gollent.com.
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Copyright 2016-2022, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2016-2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
@ -653,7 +653,7 @@ main_window_str_to_package_list_view_mode(const BString& str)
|
||||
|
||||
|
||||
void
|
||||
MainWindow::StoreSettings(BMessage& settings) const
|
||||
MainWindow::StoreSettings(BMessage& settings)
|
||||
{
|
||||
settings.AddRect(_WindowFrameName(), Frame());
|
||||
if (!fSinglePackageMode) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2013, Rene Gollent <rene@gollent.com>.
|
||||
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
|
||||
* Copyright 2017-2022, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2017-2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef MAIN_WINDOW_H
|
||||
@ -48,7 +48,7 @@ public:
|
||||
virtual bool QuitRequested();
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
void StoreSettings(BMessage& message) const;
|
||||
void StoreSettings(BMessage& message);
|
||||
|
||||
// ProcessCoordinatorConsumer
|
||||
virtual void Consume(ProcessCoordinator *item);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2016-2022, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2016-2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@ -544,7 +544,8 @@ RatePackageWindow::_QueryRatingThread()
|
||||
return;
|
||||
}
|
||||
|
||||
WebAppInterface interface;
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
|
||||
BMessage info;
|
||||
const DepotInfo* depot = fModel.DepotForName(package->DepotName());
|
||||
BString webAppRepositoryCode;
|
||||
@ -561,16 +562,15 @@ RatePackageWindow::_QueryRatingThread()
|
||||
"code for depot; %s", package->DepotName().String());
|
||||
BMessenger(this).SendMessage(B_QUIT_REQUESTED);
|
||||
} else {
|
||||
status_t status = interface
|
||||
.RetreiveUserRatingForPackageAndVersionByUser(package->Name(),
|
||||
package->Version(), package->Architecture(),
|
||||
status_t status = interface->RetrieveUserRatingForPackageAndVersionByUser(
|
||||
package->Name(), package->Version(), package->Architecture(),
|
||||
webAppRepositoryCode, webAppRepositorySourceCode,
|
||||
nickname, info);
|
||||
|
||||
if (status == B_OK) {
|
||||
// could be an error or could be a valid response envelope
|
||||
// containing data.
|
||||
switch (interface.ErrorCodeFromResponse(info)) {
|
||||
switch (WebAppInterface::ErrorCodeFromResponse(info)) {
|
||||
case ERROR_CODE_NONE:
|
||||
{
|
||||
//info.PrintToStream();
|
||||
@ -647,7 +647,7 @@ RatePackageWindow::_SendRatingThread()
|
||||
webAppRepositorySourceCode = depot->WebAppRepositorySourceCode();
|
||||
}
|
||||
|
||||
WebAppInterface interface = fModel.GetWebAppInterface();
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
|
||||
Unlock();
|
||||
|
||||
@ -672,11 +672,11 @@ RatePackageWindow::_SendRatingThread()
|
||||
BMessage info;
|
||||
if (ratingID.Length() > 0) {
|
||||
HDINFO("will update the existing user rating [%s]", ratingID.String());
|
||||
status = interface.UpdateUserRating(ratingID,
|
||||
status = interface->UpdateUserRating(ratingID,
|
||||
languageCode, comment, stability, rating, active, info);
|
||||
} else {
|
||||
HDINFO("will create a new user rating for pkg [%s]", package.String());
|
||||
status = interface.CreateUserRating(package, fPackage->Version(),
|
||||
status = interface->CreateUserRating(package, fPackage->Version(),
|
||||
architecture, webAppRepositoryCode, webAppRepositorySourceCode,
|
||||
languageCode, comment, stability, rating, info);
|
||||
}
|
||||
@ -684,7 +684,7 @@ RatePackageWindow::_SendRatingThread()
|
||||
if (status == B_OK) {
|
||||
// could be an error or could be a valid response envelope
|
||||
// containing data.
|
||||
switch (interface.ErrorCodeFromResponse(info)) {
|
||||
switch (WebAppInterface::ErrorCodeFromResponse(info)) {
|
||||
case ERROR_CODE_NONE:
|
||||
{
|
||||
if (ratingID.Length() > 0)
|
||||
|
@ -293,9 +293,9 @@ void
|
||||
ToLatestUserUsageConditionsWindow::_FetchDataPerform()
|
||||
{
|
||||
UserUsageConditions conditions;
|
||||
WebAppInterface interface = fModel.GetWebAppInterface();
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
|
||||
if (interface.RetrieveUserUsageConditions("", conditions) == B_OK) {
|
||||
if (interface->RetrieveUserUsageConditions("", conditions) == B_OK) {
|
||||
BMessage userUsageConditionsMessage;
|
||||
conditions.Archive(&userUsageConditionsMessage, true);
|
||||
BMessage dataMessage(MSG_USER_USAGE_CONDITIONS_DATA);
|
||||
@ -362,16 +362,15 @@ ToLatestUserUsageConditionsWindow::_AgreePerform()
|
||||
{
|
||||
BMessenger messenger(this);
|
||||
BMessage responsePayload;
|
||||
WebAppInterface webApp = fModel.GetWebAppInterface();
|
||||
status_t result = webApp.AgreeUserUsageConditions(
|
||||
WebAppInterface* webApp = fModel.GetWebAppInterface();
|
||||
status_t result = webApp->AgreeUserUsageConditions(
|
||||
fUserUsageConditions.Code(), responsePayload);
|
||||
|
||||
if (result != B_OK) {
|
||||
ServerHelper::NotifyTransportError(result);
|
||||
messenger.SendMessage(MSG_AGREE_FAILED);
|
||||
} else {
|
||||
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(
|
||||
responsePayload);
|
||||
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(responsePayload);
|
||||
if (errorCode == ERROR_CODE_NONE) {
|
||||
AppUtils::NotifySimpleError(
|
||||
B_TRANSLATE("Usage conditions agreed"),
|
||||
|
@ -518,14 +518,14 @@ void
|
||||
UserLoginWindow::_AuthenticateThread(UserCredentials& userCredentials)
|
||||
{
|
||||
BMessage responsePayload;
|
||||
WebAppInterface interface = fModel.GetWebAppInterface();
|
||||
status_t status = interface.AuthenticateUser(
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
status_t status = interface->AuthenticateUser(
|
||||
userCredentials.Nickname(), userCredentials.PasswordClear(),
|
||||
responsePayload);
|
||||
BString token;
|
||||
|
||||
if (status == B_OK) {
|
||||
int32 errorCode = interface.ErrorCodeFromResponse(responsePayload);
|
||||
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(responsePayload);
|
||||
|
||||
if (errorCode == ERROR_CODE_NONE)
|
||||
_UnpackAuthenticationToken(responsePayload, token);
|
||||
@ -648,7 +648,7 @@ UserLoginWindow::_TakeUpCredentialsAndQuit(const UserCredentials& credentials)
|
||||
{
|
||||
{
|
||||
AutoLocker<BLocker> locker(fModel.Lock());
|
||||
fModel.SetAuthorization(credentials.Nickname(),
|
||||
fModel.SetCredentials(credentials.Nickname(),
|
||||
credentials.PasswordClear(), true);
|
||||
}
|
||||
|
||||
@ -815,9 +815,8 @@ status_t
|
||||
UserLoginWindow::_CreateAccountUserUsageConditionsSetupThread(
|
||||
UserUsageConditions& userUsageConditions)
|
||||
{
|
||||
WebAppInterface interface = fModel.GetWebAppInterface();
|
||||
status_t result = interface.RetrieveUserUsageConditions(
|
||||
NULL, userUsageConditions);
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
status_t result = interface->RetrieveUserUsageConditions(NULL, userUsageConditions);
|
||||
|
||||
if (result != B_OK) {
|
||||
AppUtils::NotifySimpleError(
|
||||
@ -836,9 +835,8 @@ status_t
|
||||
UserLoginWindow::_CreateAccountPasswordRequirementsSetupThread(
|
||||
PasswordRequirements& passwordRequirements)
|
||||
{
|
||||
WebAppInterface interface = fModel.GetWebAppInterface();
|
||||
status_t result = interface.RetrievePasswordRequirements(
|
||||
passwordRequirements);
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
status_t result = interface->RetrievePasswordRequirements(passwordRequirements);
|
||||
|
||||
if (result != B_OK) {
|
||||
AppUtils::NotifySimpleError(
|
||||
@ -856,10 +854,10 @@ UserLoginWindow::_CreateAccountPasswordRequirementsSetupThread(
|
||||
status_t
|
||||
UserLoginWindow::_CreateAccountCaptchaSetupThread(Captcha& captcha)
|
||||
{
|
||||
WebAppInterface interface = fModel.GetWebAppInterface();
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
BMessage responsePayload;
|
||||
|
||||
status_t status = interface.RequestCaptcha(responsePayload);
|
||||
status_t status = interface->RequestCaptcha(responsePayload);
|
||||
|
||||
// check for transport related errors.
|
||||
|
||||
@ -873,7 +871,7 @@ UserLoginWindow::_CreateAccountCaptchaSetupThread(Captcha& captcha)
|
||||
// check for server-generated errors.
|
||||
|
||||
if (status == B_OK) {
|
||||
if (interface.ErrorCodeFromResponse(responsePayload)
|
||||
if (WebAppInterface::ErrorCodeFromResponse(responsePayload)
|
||||
!= ERROR_CODE_NONE) {
|
||||
ServerHelper::AlertTransportError(&responsePayload);
|
||||
status = B_ERROR;
|
||||
@ -1307,11 +1305,11 @@ UserLoginWindow::_CreateAccountThreadEntry(void* data)
|
||||
void
|
||||
UserLoginWindow::_CreateAccountThread(CreateUserDetail* detail)
|
||||
{
|
||||
WebAppInterface interface = fModel.GetWebAppInterface();
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
BMessage responsePayload;
|
||||
BMessenger messenger(this);
|
||||
|
||||
status_t status = interface.CreateUser(
|
||||
status_t status = interface->CreateUser(
|
||||
detail->Nickname(),
|
||||
detail->PasswordClear(),
|
||||
detail->Email(),
|
||||
@ -1325,7 +1323,7 @@ UserLoginWindow::_CreateAccountThread(CreateUserDetail* detail)
|
||||
"There was a puzzling response from the web service.");
|
||||
|
||||
if (status == B_OK) {
|
||||
int32 errorCode = interface.ErrorCodeFromResponse(responsePayload);
|
||||
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(responsePayload);
|
||||
|
||||
switch (errorCode) {
|
||||
case ERROR_CODE_NONE:
|
||||
|
@ -270,7 +270,7 @@ UserUsageConditionsWindow::_FetchDataPerform()
|
||||
{
|
||||
UserDetail userDetail;
|
||||
UserUsageConditions conditions;
|
||||
WebAppInterface interface = fModel.GetWebAppInterface();
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
BString code;
|
||||
status_t status = _FetchUserUsageConditionsCodePerform(userDetail, code);
|
||||
|
||||
@ -291,7 +291,7 @@ UserUsageConditionsWindow::_FetchDataPerform()
|
||||
}
|
||||
|
||||
if (status == B_OK) {
|
||||
if (interface.RetrieveUserUsageConditions(code, conditions) == B_OK) {
|
||||
if (interface->RetrieveUserUsageConditions(code, conditions) == B_OK) {
|
||||
BMessage userUsageConditionsMessage;
|
||||
BMessage userDetailMessage;
|
||||
conditions.Archive(&userUsageConditionsMessage, true);
|
||||
@ -334,20 +334,19 @@ status_t
|
||||
UserUsageConditionsWindow::_FetchUserUsageConditionsCodeForUserPerform(
|
||||
UserDetail& userDetail, BString& code)
|
||||
{
|
||||
WebAppInterface interface = fModel.GetWebAppInterface();
|
||||
WebAppInterface* interface = fModel.GetWebAppInterface();
|
||||
|
||||
if (interface.Nickname().IsEmpty())
|
||||
if (interface->Nickname().IsEmpty())
|
||||
debugger("attempt to get user details for the current user, but"
|
||||
" there is no current user");
|
||||
|
||||
BMessage responseEnvelopeMessage;
|
||||
status_t result = interface.RetrieveCurrentUserDetail(
|
||||
responseEnvelopeMessage);
|
||||
status_t result = interface->RetrieveCurrentUserDetail(responseEnvelopeMessage);
|
||||
|
||||
if (result == B_OK) {
|
||||
// could be an error or could be a valid response envelope
|
||||
// containing data.
|
||||
switch (interface.ErrorCodeFromResponse(responseEnvelopeMessage)) {
|
||||
switch (WebAppInterface::ErrorCodeFromResponse(responseEnvelopeMessage)) {
|
||||
case ERROR_CODE_NONE:
|
||||
result = WebAppInterface::UnpackUserDetail(
|
||||
responseEnvelopeMessage, userDetail);
|
||||
@ -368,12 +367,12 @@ UserUsageConditionsWindow::_FetchUserUsageConditionsCodeForUserPerform(
|
||||
if (result == B_OK) {
|
||||
BString userUsageConditionsCode = userDetail.Agreement().Code();
|
||||
HDDEBUG("the user [%s] has agreed to uuc [%s]",
|
||||
interface.Nickname().String(),
|
||||
interface->Nickname().String(),
|
||||
userUsageConditionsCode.String());
|
||||
code.SetTo(userUsageConditionsCode);
|
||||
} else {
|
||||
HDDEBUG("unable to get details of the user [%s]",
|
||||
interface.Nickname().String());
|
||||
interface->Nickname().String());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2020, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2018-2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "DataIOUtils.h"
|
||||
@ -31,6 +31,8 @@ DataIOUtils::CopyAll(BDataIO* target, BDataIO* source)
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - ConstraintedDataIO
|
||||
|
||||
ConstraintedDataIO::ConstraintedDataIO(BDataIO* delegate, size_t limit)
|
||||
:
|
||||
fDelegate(delegate),
|
||||
@ -71,3 +73,120 @@ ConstraintedDataIO::Flush()
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Base64DecodingDataIO
|
||||
|
||||
Base64DecodingDataIO::Base64DecodingDataIO(BDataIO* delegate, char char62, char char63)
|
||||
:
|
||||
fDelegate(delegate),
|
||||
fChar62(char62),
|
||||
fChar63(char63),
|
||||
fNextByteAssembly(0),
|
||||
fNextByteAssemblyBits(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Base64DecodingDataIO::~Base64DecodingDataIO()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Base64DecodingDataIO::_CharToInt(uint8 ch, uint8* value)
|
||||
{
|
||||
if (ch >= 0x41 && ch <= 0x5A) {
|
||||
*value = (ch - 0x41);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (ch >= 0x61 && ch <= 0x7a) {
|
||||
*value = (ch - 0x61) + 26;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (ch >= 0x30 && ch <= 0x39) {
|
||||
*value = (ch - 0x30) + 52;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (ch == fChar62) {
|
||||
*value = 62;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (ch == fChar63) {
|
||||
*value = 63;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (ch == '=') {
|
||||
*value = 0;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_BAD_DATA;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Base64DecodingDataIO::_ReadSingleByte(void* buffer)
|
||||
{
|
||||
uint8 delegateRead;
|
||||
uint8 delegateReadInt;
|
||||
status_t result = B_OK;
|
||||
|
||||
if (result == B_OK)
|
||||
result = fDelegate->ReadExactly(&delegateRead, 1);
|
||||
|
||||
if (result == B_OK)
|
||||
result = _CharToInt(delegateRead, &delegateReadInt);
|
||||
|
||||
if (result == B_OK && 0 == fNextByteAssemblyBits) {
|
||||
fNextByteAssembly = delegateReadInt;
|
||||
fNextByteAssemblyBits = 6;
|
||||
return _ReadSingleByte(buffer);
|
||||
}
|
||||
|
||||
if (result == B_OK) {
|
||||
uint8 followingNextByteAssemblyBits = (6 - (8 - fNextByteAssemblyBits));
|
||||
*((uint8 *) buffer) = fNextByteAssembly << (8 - fNextByteAssemblyBits)
|
||||
| (delegateReadInt >> followingNextByteAssemblyBits);
|
||||
fNextByteAssembly = delegateReadInt & (0x3f >> (6 - followingNextByteAssemblyBits));
|
||||
fNextByteAssemblyBits = followingNextByteAssemblyBits;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
Base64DecodingDataIO::Read(void* buffer, size_t size)
|
||||
{
|
||||
size_t readSize = 0;
|
||||
status_t result = B_OK;
|
||||
|
||||
while (result == B_OK && readSize < size) {
|
||||
result = _ReadSingleByte(&((uint8_t*) buffer)[readSize]);
|
||||
|
||||
if (result == B_OK)
|
||||
readSize++;
|
||||
}
|
||||
|
||||
return readSize++;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
Base64DecodingDataIO::Write(const void* buffer, size_t size)
|
||||
{
|
||||
return B_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Base64DecodingDataIO::Flush()
|
||||
{
|
||||
return B_OK;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2020, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2018-2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef DATA_IO_UTILS_H
|
||||
@ -40,4 +40,29 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class Base64DecodingDataIO : public BDataIO {
|
||||
public:
|
||||
Base64DecodingDataIO(BDataIO* delegate,
|
||||
char char62 = '+', char char63 = '/');
|
||||
virtual ~Base64DecodingDataIO();
|
||||
|
||||
virtual ssize_t Read(void* buffer, size_t size);
|
||||
virtual ssize_t Write(const void* buffer, size_t size);
|
||||
|
||||
virtual status_t Flush();
|
||||
|
||||
private:
|
||||
status_t _ReadSingleByte(void* buffer);
|
||||
status_t _CharToInt(uint8 ch, uint8* value);
|
||||
|
||||
private:
|
||||
BDataIO* fDelegate;
|
||||
char fChar62;
|
||||
char fChar63;
|
||||
|
||||
uint8 fNextByteAssembly;
|
||||
uint8 fNextByteAssemblyBits;
|
||||
};
|
||||
|
||||
|
||||
#endif // DATA_IO_UTILS_H
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 Haiku, Inc. All rights reserved.
|
||||
* Copyright 2010-2023 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
@ -60,6 +60,7 @@ BHttpAuthentication::BHttpAuthentication(const BHttpAuthentication& other)
|
||||
fAuthenticationMethod(other.fAuthenticationMethod),
|
||||
fUserName(other.fUserName),
|
||||
fPassword(other.fPassword),
|
||||
fToken(other.fToken),
|
||||
fRealm(other.fRealm),
|
||||
fDigestNonce(other.fDigestNonce),
|
||||
fDigestCnonce(other.fDigestCnonce),
|
||||
@ -79,6 +80,7 @@ BHttpAuthentication& BHttpAuthentication::operator=(
|
||||
fAuthenticationMethod = other.fAuthenticationMethod;
|
||||
fUserName = other.fUserName;
|
||||
fPassword = other.fPassword;
|
||||
fToken = other.fToken;
|
||||
fRealm = other.fRealm;
|
||||
fDigestNonce = other.fDigestNonce;
|
||||
fDigestCnonce = other.fDigestCnonce;
|
||||
@ -113,6 +115,15 @@ BHttpAuthentication::SetPassword(const BString& password)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BHttpAuthentication::SetToken(const BString& token)
|
||||
{
|
||||
fLock.Lock();
|
||||
fToken = token;
|
||||
fLock.Unlock();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BHttpAuthentication::SetMethod(BHttpAuthenticationMethod method)
|
||||
{
|
||||
@ -151,7 +162,9 @@ BHttpAuthentication::Initialize(const BString& wwwAuthenticate)
|
||||
else if (authRequired == "digest") {
|
||||
fAuthenticationMethod = B_HTTP_AUTHENTICATION_DIGEST;
|
||||
fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5;
|
||||
} else
|
||||
} else if (authRequired == "bearer")
|
||||
fAuthenticationMethod = B_HTTP_AUTHENTICATION_BEARER;
|
||||
else
|
||||
return B_ERROR;
|
||||
|
||||
|
||||
@ -206,6 +219,8 @@ BHttpAuthentication::Initialize(const BString& wwwAuthenticate)
|
||||
|
||||
if (fAuthenticationMethod == B_HTTP_AUTHENTICATION_BASIC)
|
||||
return B_OK;
|
||||
else if (fAuthenticationMethod == B_HTTP_AUTHENTICATION_BEARER)
|
||||
return B_OK;
|
||||
else if (fAuthenticationMethod == B_HTTP_AUTHENTICATION_DIGEST
|
||||
&& fDigestNonce.Length() > 0
|
||||
&& fDigestAlgorithm != B_HTTP_AUTHENTICATION_ALGORITHM_NONE) {
|
||||
@ -260,6 +275,10 @@ BHttpAuthentication::Authorization(const BUrl& url, const BString& method) const
|
||||
break;
|
||||
}
|
||||
|
||||
case B_HTTP_AUTHENTICATION_BEARER:
|
||||
authorizationString << "Bearer " << fToken;
|
||||
break;
|
||||
|
||||
case B_HTTP_AUTHENTICATION_DIGEST:
|
||||
case B_HTTP_AUTHENTICATION_IE_DIGEST:
|
||||
authorizationString << "Digest " << "username=\"" << fUserName
|
||||
@ -384,6 +403,7 @@ BHttpAuthentication::_DigestResponse(const BString& uri, const BString& method)
|
||||
PRINT(("HttpAuth: Computing digest response: \n"));
|
||||
PRINT(("HttpAuth: > username = %s\n", fUserName.String()));
|
||||
PRINT(("HttpAuth: > password = %s\n", fPassword.String()));
|
||||
PRINT(("HttpAuth: > token = %s\n", fToken.String()));
|
||||
PRINT(("HttpAuth: > realm = %s\n", fRealm.String()));
|
||||
PRINT(("HttpAuth: > nonce = %s\n", fDigestNonce.String()));
|
||||
PRINT(("HttpAuth: > cnonce = %s\n", fDigestCnonce.String()));
|
||||
|
128
src/tests/apps/haikudepot/DataIOUtilsTest.cpp
Normal file
128
src/tests/apps/haikudepot/DataIOUtilsTest.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "DataIOUtilsTest.h"
|
||||
|
||||
#include <String.h>
|
||||
|
||||
#include <cppunit/TestCaller.h>
|
||||
#include <cppunit/TestSuite.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "DataIOUtils.h"
|
||||
|
||||
|
||||
DataIOUtilsTest::DataIOUtilsTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
DataIOUtilsTest::~DataIOUtilsTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DataIOUtilsTest::TestReadBase64JwtClaims_1()
|
||||
{
|
||||
const char* jwtToken = "eyJpc3MiOiJkZXYuaGRzIiwic3ViIjoiZXJpazY0QGhkcyIs"
|
||||
"ImV4cCI6MTY5MzE5MTMzMiwiaWF0IjoxNjkzMTkxMDMyfQ";
|
||||
BMemoryIO memoryIo(jwtToken, strlen(jwtToken));
|
||||
Base64DecodingDataIO base64DecodingIo(&memoryIo, '-', '_');
|
||||
char actualOutputBuffer[71];
|
||||
size_t actualReadBytes;
|
||||
|
||||
bzero(actualOutputBuffer, 71);
|
||||
|
||||
// ----------------------
|
||||
status_t result = base64DecodingIo.ReadExactly(actualOutputBuffer, 70, &actualReadBytes);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, result);
|
||||
CPPUNIT_ASSERT_EQUAL(70, actualReadBytes);
|
||||
actualOutputBuffer[actualReadBytes] = 0;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(0x7b, (uint8) actualOutputBuffer[0]);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
BString("{\"iss\":\"dev.hds\",\"sub\":\"erik64@hds\",\"exp\":1693191332,\"iat\""
|
||||
":1693191032}"),
|
||||
BString(actualOutputBuffer));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DataIOUtilsTest::TestReadBase64JwtClaims_2()
|
||||
{
|
||||
const char* jwtToken = "eyJpc3MiOiJkZXYuaGRzIiwic3ViIjoidG93ZWxkb3dudGVhQ"
|
||||
"GhkcyIsImV4cCI6MTY5MzczODgyNiwiaWF0IjoxNjkzNzM4NTI2fQ";
|
||||
BMemoryIO memoryIo(jwtToken, strlen(jwtToken));
|
||||
Base64DecodingDataIO base64DecodingIo(&memoryIo, '-', '_');
|
||||
char actualOutputBuffer[77];
|
||||
size_t actualReadBytes;
|
||||
|
||||
bzero(actualOutputBuffer, 77);
|
||||
|
||||
// ----------------------
|
||||
status_t result = base64DecodingIo.ReadExactly(actualOutputBuffer, 76, &actualReadBytes);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, result);
|
||||
CPPUNIT_ASSERT_EQUAL(76, actualReadBytes);
|
||||
actualOutputBuffer[actualReadBytes] = 0;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(0x7b, (uint8) actualOutputBuffer[0]);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
BString("{\"iss\":\"dev.hds\",\"sub\":\"toweldowntea@hds\",\"exp\":1693738826,\"iat\""
|
||||
":1693738526}"),
|
||||
BString(actualOutputBuffer));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DataIOUtilsTest::TestCorrupt()
|
||||
{
|
||||
const char* jwtToken = "QW5k$mV3";
|
||||
// note that '$' is not a valid base64 character
|
||||
BMemoryIO memoryIo(jwtToken, strlen(jwtToken));
|
||||
Base64DecodingDataIO base64DecodingIo(&memoryIo, '-', '_');
|
||||
char actualOutputBuffer[7];
|
||||
size_t actualReadBytes;
|
||||
|
||||
bzero(actualOutputBuffer, 7);
|
||||
|
||||
// ----------------------
|
||||
status_t result = base64DecodingIo.ReadExactly(actualOutputBuffer, 6, &actualReadBytes);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT(B_OK != result);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
DataIOUtilsTest::AddTests(BTestSuite& parent)
|
||||
{
|
||||
CppUnit::TestSuite& suite = *new CppUnit::TestSuite("DataIOUtilsTest");
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<DataIOUtilsTest>(
|
||||
"DataIOUtilsTest::TestReadBase64JwtClaims_1",
|
||||
&DataIOUtilsTest::TestReadBase64JwtClaims_1));
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<DataIOUtilsTest>(
|
||||
"DataIOUtilsTest::TestReadBase64JwtClaims_2",
|
||||
&DataIOUtilsTest::TestReadBase64JwtClaims_2));
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<DataIOUtilsTest>(
|
||||
"DataIOUtilsTest::TestCorrupt",
|
||||
&DataIOUtilsTest::TestCorrupt));
|
||||
|
||||
parent.addTest("DataIOUtilsTest", &suite);
|
||||
}
|
26
src/tests/apps/haikudepot/DataIOUtilsTest.h
Normal file
26
src/tests/apps/haikudepot/DataIOUtilsTest.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef DATA_IO_UTILS_TEST_H
|
||||
#define DATA_IO_UTILS_TEST_H
|
||||
|
||||
|
||||
#include <TestCase.h>
|
||||
#include <TestSuite.h>
|
||||
|
||||
|
||||
class DataIOUtilsTest : public CppUnit::TestCase {
|
||||
public:
|
||||
DataIOUtilsTest();
|
||||
virtual ~DataIOUtilsTest();
|
||||
|
||||
void TestReadBase64JwtClaims_1();
|
||||
void TestReadBase64JwtClaims_2();
|
||||
void TestCorrupt();
|
||||
|
||||
static void AddTests(BTestSuite& suite);
|
||||
};
|
||||
|
||||
|
||||
#endif // DATA_IO_UTILS_TEST_H
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2020, Andrew Lindesay, apl@lindesay.co.nz
|
||||
* Copyright 2017-2023, Andrew Lindesay, apl@lindesay.co.nz
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@ -8,7 +8,9 @@
|
||||
#include <TestSuiteAddon.h>
|
||||
|
||||
#include "StandardMetaDataJsonEventListenerTest.h"
|
||||
#include "DataIOUtilsTest.h"
|
||||
#include "DumpExportRepositoryJsonListenerTest.h"
|
||||
#include "JwtTokenHelperTest.h"
|
||||
#include "ValidationFailureTest.h"
|
||||
#include "ValidationUtilsTest.h"
|
||||
#include "StorageUtilsTest.h"
|
||||
@ -21,7 +23,10 @@ getTestSuite()
|
||||
BTestSuite* suite = new BTestSuite("HaikuDepot");
|
||||
|
||||
StandardMetaDataJsonEventListenerTest::AddTests(*suite);
|
||||
DataIOUtilsTest::AddTests(*suite);
|
||||
DumpExportRepositoryJsonListenerTest::AddTests(*suite);
|
||||
DumpExportRepositoryJsonListenerTest::AddTests(*suite);
|
||||
JwtTokenHelperTest::AddTests(*suite);
|
||||
ValidationFailureTest::AddTests(*suite);
|
||||
ValidationUtilsTest::AddTests(*suite);
|
||||
StorageUtilsTest::AddTests(*suite);
|
||||
|
@ -33,6 +33,7 @@ UnitTestLib haikudepottest.so :
|
||||
HaikuDepotTestAddon.cpp
|
||||
|
||||
DataIOUtils.cpp
|
||||
DataIOUtilsTest.cpp
|
||||
|
||||
DumpExportRepositorySource.cpp
|
||||
DumpExportRepositorySourceMirror.cpp
|
||||
@ -40,6 +41,9 @@ UnitTestLib haikudepottest.so :
|
||||
DumpExportRepositoryJsonListener.cpp
|
||||
DumpExportRepositoryJsonListenerTest.cpp
|
||||
|
||||
JwtTokenHelper.cpp
|
||||
JwtTokenHelperTest.cpp
|
||||
|
||||
Logger.cpp
|
||||
|
||||
StandardMetaData.cpp
|
||||
|
102
src/tests/apps/haikudepot/JwtTokenHelperTest.cpp
Normal file
102
src/tests/apps/haikudepot/JwtTokenHelperTest.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "JwtTokenHelperTest.h"
|
||||
|
||||
#include <String.h>
|
||||
|
||||
#include <cppunit/TestCaller.h>
|
||||
#include <cppunit/TestSuite.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "JwtTokenHelper.h"
|
||||
|
||||
|
||||
JwtTokenHelperTest::JwtTokenHelperTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
JwtTokenHelperTest::~JwtTokenHelperTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
JwtTokenHelperTest::TestParseTokenClaims()
|
||||
{
|
||||
const char* jwtToken = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJkZXYuaGRzIiwic3ViIj"
|
||||
"oiZXJpazY0QGhkcyIsImV4cCI6MTY5MzkwNzM1NywiaWF0IjoxNjkzOTA3MDU3fQ.DJOz0"
|
||||
"TmgN0Ya8De-oV0mBwWb-8FYavLbaFUFhCLqr-s";
|
||||
BMessage actualMessage;
|
||||
|
||||
// ----------------------
|
||||
status_t result = JwtTokenHelper::ParseClaims(BString(jwtToken), actualMessage);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, result);
|
||||
|
||||
_AssertStringValue(actualMessage, "iss", "dev.hds");
|
||||
_AssertStringValue(actualMessage, "sub", "erik64@hds");
|
||||
_AssertDoubleValue(actualMessage, "exp", 1693907357);
|
||||
_AssertDoubleValue(actualMessage, "iat", 1693907057);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
JwtTokenHelperTest::TestCorrupt()
|
||||
{
|
||||
const char* jwtToken = "application/json";
|
||||
BMessage actualMessage;
|
||||
|
||||
// ----------------------
|
||||
status_t result = JwtTokenHelper::ParseClaims(BString(jwtToken), actualMessage);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT(B_OK != result);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
JwtTokenHelperTest::AddTests(BTestSuite& parent)
|
||||
{
|
||||
CppUnit::TestSuite& suite = *new CppUnit::TestSuite("JwtTokenHelperTest");
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<JwtTokenHelperTest>(
|
||||
"JwtTokenHelperTest::TestParseTokenClaims",
|
||||
&JwtTokenHelperTest::TestParseTokenClaims));
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<JwtTokenHelperTest>(
|
||||
"JwtTokenHelperTest::TestCorrupt",
|
||||
&JwtTokenHelperTest::TestCorrupt));
|
||||
|
||||
parent.addTest("JwtTokenHelperTest", &suite);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
JwtTokenHelperTest::_AssertStringValue(const BMessage& message, const char* key,
|
||||
const char* expectedValue) const
|
||||
{
|
||||
BString value;
|
||||
status_t result = message.FindString(key, &value);
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, result);
|
||||
CPPUNIT_ASSERT_EQUAL(BString(expectedValue), value);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
JwtTokenHelperTest::_AssertDoubleValue(const BMessage& message, const char* key,
|
||||
int64 expectedValue) const
|
||||
{
|
||||
double value;
|
||||
status_t result = message.FindDouble(key, &value);
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, result);
|
||||
CPPUNIT_ASSERT_EQUAL((double) expectedValue, value);
|
||||
}
|
33
src/tests/apps/haikudepot/JwtTokenHelperTest.h
Normal file
33
src/tests/apps/haikudepot/JwtTokenHelperTest.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef JWT_TOKEN_HELPER_TEST_H
|
||||
#define JWT_TOKEN_HELPER_TEST_H
|
||||
|
||||
|
||||
#include <Message.h>
|
||||
|
||||
#include <TestCase.h>
|
||||
#include <TestSuite.h>
|
||||
|
||||
|
||||
class JwtTokenHelperTest : public CppUnit::TestCase {
|
||||
public:
|
||||
JwtTokenHelperTest();
|
||||
virtual ~JwtTokenHelperTest();
|
||||
|
||||
void TestParseTokenClaims();
|
||||
void TestCorrupt();
|
||||
|
||||
static void AddTests(BTestSuite& suite);
|
||||
|
||||
private:
|
||||
void _AssertStringValue(const BMessage& message, const char* key,
|
||||
const char* expectedValue) const;
|
||||
void _AssertDoubleValue(const BMessage& message, const char* key,
|
||||
int64 expectedValue) const;
|
||||
};
|
||||
|
||||
|
||||
#endif // JWT_TOKEN_HELPER_TEST_H
|
Loading…
Reference in New Issue
Block a user