HaikuDepot: Refactor of Login
These changes cover a rework of the login and account creation logic before making additional changes related to the user usage conditions. Relates to #15209 Change-Id: I90b7dbcee5b0285476938c6ced0afc89483d6227 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2023 Reviewed-by: Stephan Aßmus <[email protected]> Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
@@ -116,6 +116,8 @@ local applicationSources =
|
||||
App.cpp
|
||||
BarberPole.cpp
|
||||
BitmapView.cpp
|
||||
Captcha.cpp
|
||||
CreateUserDetail.cpp
|
||||
DecisionProvider.cpp
|
||||
FeaturedPackagesView.cpp
|
||||
FilterView.cpp
|
||||
@@ -149,6 +151,8 @@ local applicationSources =
|
||||
UserLoginWindow.cpp
|
||||
UserUsageConditions.cpp
|
||||
UserUsageConditionsWindow.cpp
|
||||
ValidationFailure.cpp
|
||||
ValidationUtils.cpp
|
||||
WorkStatusView.cpp
|
||||
|
||||
# network + server / local processes
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <[email protected]>.
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "Captcha.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <DataIO.h>
|
||||
|
||||
// These are keys that are used to store this object's data into a BMessage
|
||||
// instance.
|
||||
|
||||
#define KEY_TOKEN "token"
|
||||
#define KEY_PNG_IMAGE_DATA "pngImageData"
|
||||
|
||||
|
||||
Captcha::Captcha(BMessage* from)
|
||||
:
|
||||
fToken(""),
|
||||
fPngImageData(NULL)
|
||||
{
|
||||
if (from->FindString(KEY_TOKEN, &fToken) != B_OK)
|
||||
printf("expected key [%s] in the message data when creating a "
|
||||
"Captcha\n", KEY_TOKEN);
|
||||
|
||||
const void* data;
|
||||
ssize_t len;
|
||||
|
||||
if (from->FindData(KEY_PNG_IMAGE_DATA, B_ANY_TYPE, &data, &len) != B_OK)
|
||||
printf("expected key [%s] in the message data\n", KEY_PNG_IMAGE_DATA);
|
||||
else
|
||||
SetPngImageData(data, len);
|
||||
}
|
||||
|
||||
|
||||
Captcha::Captcha()
|
||||
:
|
||||
fToken(""),
|
||||
fPngImageData(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Captcha::~Captcha()
|
||||
{
|
||||
if (fPngImageData != NULL)
|
||||
delete fPngImageData;
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
Captcha::Token() const
|
||||
{
|
||||
return fToken;
|
||||
}
|
||||
|
||||
|
||||
BPositionIO*
|
||||
Captcha::PngImageData() const
|
||||
{
|
||||
return fPngImageData;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Captcha::SetToken(const BString& value)
|
||||
{
|
||||
fToken = value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Captcha::SetPngImageData(const void* data, size_t len)
|
||||
{
|
||||
if (fPngImageData != NULL)
|
||||
delete fPngImageData;
|
||||
fPngImageData = NULL;
|
||||
if (data != NULL) {
|
||||
fPngImageData = new BMallocIO();
|
||||
fPngImageData->Write(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Captcha::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 && fPngImageData != NULL) {
|
||||
result = into->AddData(KEY_PNG_IMAGE_DATA, B_ANY_TYPE,
|
||||
fPngImageData->Buffer(), fPngImageData->BufferLength());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <[email protected]>.
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CAPTCHA_H
|
||||
#define CAPTCHA_H
|
||||
|
||||
|
||||
#include <Archivable.h>
|
||||
#include <String.h>
|
||||
|
||||
class BPositionIO;
|
||||
|
||||
/*! When a user has to perform some sensitive operation, it is necessary to make
|
||||
sure that it is not a 'robot' or software system that is acting as if it
|
||||
were a person. It is necessary to know that a real person is acting. In
|
||||
this case a graphical puzzle is presented to the user that presumably only
|
||||
a human operator could solve. This is called a Captcha.
|
||||
*/
|
||||
|
||||
class Captcha : public BArchivable {
|
||||
public:
|
||||
Captcha(BMessage* from);
|
||||
Captcha();
|
||||
virtual ~Captcha();
|
||||
|
||||
const BString& Token() const;
|
||||
BPositionIO* PngImageData() const;
|
||||
|
||||
void SetToken(const BString& value);
|
||||
void SetPngImageData(const void* data, size_t len);
|
||||
|
||||
status_t Archive(BMessage* into, bool deep = true) const;
|
||||
private:
|
||||
BString fToken;
|
||||
BMallocIO* fPngImageData;
|
||||
};
|
||||
|
||||
|
||||
#endif // CAPTCHA_H
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <[email protected]>.
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "CreateUserDetail.h"
|
||||
|
||||
// These are keys that are used to store this object's data into a BMessage
|
||||
// instance.
|
||||
|
||||
#define KEY_NICKNAME "nickname"
|
||||
#define KEY_PASSWORD_CLEAR "passwordClear"
|
||||
#define KEY_IS_PASSWORD_REPEATED "isPasswordRepeated"
|
||||
#define KEY_EMAIL "email"
|
||||
#define KEY_CAPTCHA_TOKEN "captchaToken"
|
||||
#define KEY_CAPTCHA_RESPONSE "captchaResponse"
|
||||
#define KEY_LANGUAGE_CODE "languageCode"
|
||||
#define KEY_AGREED_USER_USAGE_CONDITIONS_CODE "agreedUserUsageConditionsCode"
|
||||
|
||||
|
||||
CreateUserDetail::CreateUserDetail(BMessage* from)
|
||||
{
|
||||
from->FindString(KEY_NICKNAME, &fNickname);
|
||||
from->FindString(KEY_PASSWORD_CLEAR, &fPasswordClear);
|
||||
from->FindBool(KEY_IS_PASSWORD_REPEATED, &fIsPasswordRepeated);
|
||||
from->FindString(KEY_EMAIL, &fEmail);
|
||||
from->FindString(KEY_CAPTCHA_TOKEN, &fCaptchaToken);
|
||||
from->FindString(KEY_CAPTCHA_RESPONSE, &fCaptchaResponse);
|
||||
from->FindString(KEY_LANGUAGE_CODE, &fLanguageCode);
|
||||
from->FindString(KEY_AGREED_USER_USAGE_CONDITIONS_CODE,
|
||||
&fAgreedUserUsageConditionsCode);
|
||||
}
|
||||
|
||||
|
||||
CreateUserDetail::CreateUserDetail()
|
||||
:
|
||||
fIsPasswordRepeated(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CreateUserDetail::~CreateUserDetail()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
CreateUserDetail::Nickname() const
|
||||
{
|
||||
return fNickname;
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
CreateUserDetail::PasswordClear() const
|
||||
{
|
||||
return fPasswordClear;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
CreateUserDetail::IsPasswordRepeated() const
|
||||
{
|
||||
return fIsPasswordRepeated;
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
CreateUserDetail::Email() const
|
||||
{
|
||||
return fEmail;
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
CreateUserDetail::CaptchaToken() const
|
||||
{
|
||||
return fCaptchaToken;
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
CreateUserDetail::CaptchaResponse() const
|
||||
{
|
||||
return fCaptchaResponse;
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
CreateUserDetail::LanguageCode() const
|
||||
{
|
||||
return fLanguageCode;
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
CreateUserDetail::AgreedToUserUsageConditionsCode() const
|
||||
{
|
||||
return fAgreedUserUsageConditionsCode;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CreateUserDetail::SetNickname(const BString& value)
|
||||
{
|
||||
fNickname = value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CreateUserDetail::SetPasswordClear(const BString& value)
|
||||
{
|
||||
fPasswordClear = value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CreateUserDetail::SetIsPasswordRepeated(bool value)
|
||||
{
|
||||
fIsPasswordRepeated = value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CreateUserDetail::SetEmail(const BString& value)
|
||||
{
|
||||
fEmail = value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CreateUserDetail::SetCaptchaToken(const BString& value)
|
||||
{
|
||||
fCaptchaToken = value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CreateUserDetail::SetCaptchaResponse(const BString& value)
|
||||
{
|
||||
fCaptchaResponse = value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CreateUserDetail::SetLanguageCode(const BString& value)
|
||||
{
|
||||
fLanguageCode = value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CreateUserDetail::SetAgreedToUserUsageConditionsCode(const BString& value)
|
||||
{
|
||||
fAgreedUserUsageConditionsCode = value;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CreateUserDetail::Archive(BMessage* into, bool deep) const
|
||||
{
|
||||
status_t result = B_OK;
|
||||
if (result == B_OK)
|
||||
result = into->AddString(KEY_NICKNAME, fNickname);
|
||||
if (result == B_OK)
|
||||
result = into->AddString(KEY_PASSWORD_CLEAR, fPasswordClear);
|
||||
if (result == B_OK)
|
||||
result = into->AddBool(KEY_IS_PASSWORD_REPEATED, fIsPasswordRepeated);
|
||||
if (result == B_OK)
|
||||
result = into->AddString(KEY_EMAIL, fEmail);
|
||||
if (result == B_OK)
|
||||
result = into->AddString(KEY_CAPTCHA_TOKEN, fCaptchaToken);
|
||||
if (result == B_OK)
|
||||
result = into->AddString(KEY_CAPTCHA_RESPONSE, fCaptchaResponse);
|
||||
if (result == B_OK)
|
||||
result = into->AddString(KEY_LANGUAGE_CODE, fLanguageCode);
|
||||
if (result == B_OK) {
|
||||
result = into->AddString(KEY_AGREED_USER_USAGE_CONDITIONS_CODE,
|
||||
fAgreedUserUsageConditionsCode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <[email protected]>.
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CREATE_USER_DETAIL_H
|
||||
#define CREATE_USER_DETAIL_H
|
||||
|
||||
|
||||
#include <Archivable.h>
|
||||
#include <String.h>
|
||||
|
||||
/*! The operator may choose to create a User. In this case, he or she is
|
||||
required to supply some details such as the nickname, password, email...
|
||||
and those details are then collated and provided to the application server
|
||||
(HDS) in order that the User is created in the application server. This
|
||||
model carries all of those details in an object so that they might be
|
||||
easily conveyed between methods.
|
||||
*/
|
||||
|
||||
class CreateUserDetail : public BArchivable {
|
||||
public:
|
||||
CreateUserDetail(BMessage* from);
|
||||
CreateUserDetail();
|
||||
virtual ~CreateUserDetail();
|
||||
|
||||
const BString& Nickname() const;
|
||||
const BString& PasswordClear() const;
|
||||
bool IsPasswordRepeated() const;
|
||||
const BString& Email() const;
|
||||
const BString& CaptchaToken() const;
|
||||
const BString& CaptchaResponse() const;
|
||||
const BString& LanguageCode() const;
|
||||
const BString& AgreedToUserUsageConditionsCode() const;
|
||||
|
||||
void SetNickname(const BString& value);
|
||||
void SetPasswordClear(const BString& value);
|
||||
void SetIsPasswordRepeated(bool value);
|
||||
void SetEmail(const BString& value);
|
||||
void SetCaptchaToken(const BString& value);
|
||||
void SetCaptchaResponse(const BString& value);
|
||||
void SetLanguageCode(const BString& value);
|
||||
void SetAgreedToUserUsageConditionsCode(
|
||||
const BString& value);
|
||||
|
||||
status_t Archive(BMessage* into, bool deep = true) const;
|
||||
private:
|
||||
BString fNickname;
|
||||
BString fPasswordClear;
|
||||
bool fIsPasswordRepeated;
|
||||
BString fEmail;
|
||||
BString fCaptchaToken;
|
||||
BString fCaptchaResponse;
|
||||
BString fLanguageCode;
|
||||
BString fAgreedUserUsageConditionsCode;
|
||||
};
|
||||
|
||||
|
||||
#endif // CREATE_USER_DETAIL_H
|
||||
@@ -32,6 +32,15 @@ UserCredentials::UserCredentials(const BString& nickname,
|
||||
}
|
||||
|
||||
|
||||
UserCredentials::UserCredentials(const UserCredentials& other)
|
||||
:
|
||||
fNickname(other.Nickname()),
|
||||
fPasswordClear(other.PasswordClear()),
|
||||
fIsSuccessful(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
UserCredentials::UserCredentials()
|
||||
:
|
||||
fNickname(),
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
#include <String.h>
|
||||
|
||||
|
||||
/*! This object represents the tuple of the user's nickname (username) and
|
||||
password. It also carries a boolean that indicates if an authentication
|
||||
with these credentials was successful or failed.
|
||||
/*! This object represents the tuple of the user's nickname (username) and
|
||||
password. It also carries a boolean that indicates if an authentication
|
||||
with these credentials was successful or failed.
|
||||
*/
|
||||
|
||||
class UserCredentials : public BArchivable {
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
UserCredentials(BMessage* from);
|
||||
UserCredentials(const BString& nickname,
|
||||
const BString& passwordClear);
|
||||
UserCredentials(const UserCredentials& other);
|
||||
UserCredentials();
|
||||
virtual ~UserCredentials();
|
||||
|
||||
@@ -42,4 +43,5 @@ private:
|
||||
bool fIsSuccessful;
|
||||
};
|
||||
|
||||
|
||||
#endif // USER_CREDENTIALS_H
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
#include <String.h>
|
||||
|
||||
|
||||
/*! A user in the HDS system should have agreed to user usage conditions when
|
||||
they created their user on the server. This object represents the user
|
||||
usage conditions that either they have agreed to or that they could agree
|
||||
to. Each set of user usage conditions has a code that uniquely identifies
|
||||
a given set of conditions.
|
||||
/*! A user in the HDS system should have agreed to user usage conditions when
|
||||
they created their user on the server. This object represents the user
|
||||
usage conditions that either they have agreed to or that they could agree
|
||||
to. Each set of user usage conditions has a code that uniquely identifies
|
||||
a given set of conditions.
|
||||
*/
|
||||
|
||||
class UserUsageConditions : public BArchivable {
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <[email protected]>.
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "ValidationFailure.h"
|
||||
|
||||
// These are keys that are used to store this object's data into a BMessage
|
||||
// instance.
|
||||
|
||||
#define KEY_PROPERTY "property"
|
||||
#define KEY_PREFIX_MESSAGE "message_"
|
||||
#define KEY_PREFIX_ITEM "item_"
|
||||
|
||||
|
||||
// #pragma mark - Single Validation Failure
|
||||
|
||||
|
||||
ValidationFailure::ValidationFailure(BMessage* from)
|
||||
{
|
||||
from->FindString(KEY_PROPERTY, &fProperty);
|
||||
|
||||
if (fProperty.IsEmpty())
|
||||
debugger("illegal state; missing property in message");
|
||||
|
||||
status_t result = B_OK;
|
||||
BString name;
|
||||
BString message;
|
||||
|
||||
for (int32 i = 0; result == B_OK; i++) {
|
||||
name.SetToFormat("%s%" B_PRId32, KEY_PREFIX_MESSAGE, i);
|
||||
result = from->FindString(name, &message);
|
||||
|
||||
if (result == B_OK)
|
||||
AddMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ValidationFailure::ValidationFailure(const BString& property)
|
||||
{
|
||||
fProperty = property;
|
||||
}
|
||||
|
||||
|
||||
ValidationFailure::~ValidationFailure()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const BString&
|
||||
ValidationFailure::Property() const
|
||||
{
|
||||
return fProperty;
|
||||
}
|
||||
|
||||
|
||||
const BStringList&
|
||||
ValidationFailure::Messages() const
|
||||
{
|
||||
return fMessages;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ValidationFailure::IsEmpty() const
|
||||
{
|
||||
return fMessages.IsEmpty();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ValidationFailure::Contains(const BString& message) const
|
||||
{
|
||||
return fMessages.HasString(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationFailure::AddMessage(const BString& value)
|
||||
{
|
||||
fMessages.Add(value);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ValidationFailure::Archive(BMessage* into, bool deep) const
|
||||
{
|
||||
status_t result = B_OK;
|
||||
BString key;
|
||||
if (result == B_OK)
|
||||
result = into->AddString(KEY_PROPERTY, fProperty);
|
||||
for (int32 i = 0; result == B_OK && i < fMessages.CountStrings(); i++) {
|
||||
key.SetToFormat("%s%" B_PRId32, KEY_PREFIX_MESSAGE, i);
|
||||
result = into->AddString(key, fMessages.StringAt(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Collections of Validation Failures
|
||||
|
||||
|
||||
ValidationFailures::ValidationFailures(BMessage* from)
|
||||
:
|
||||
fItems(20, true)
|
||||
{
|
||||
_AddFromMessage(from);
|
||||
}
|
||||
|
||||
|
||||
ValidationFailures::ValidationFailures()
|
||||
:
|
||||
fItems(20, true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ValidationFailures::~ValidationFailures()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationFailures::AddFailure(const BString& property, const BString& message)
|
||||
{
|
||||
_GetOrCreateFailure(property)->AddMessage(message);
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
ValidationFailures::CountFailures() const
|
||||
{
|
||||
return fItems.CountItems();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ValidationFailures::IsEmpty() const
|
||||
{
|
||||
return fItems.IsEmpty();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ValidationFailures::Contains(const BString& property) const
|
||||
{
|
||||
ValidationFailure* failure = _GetFailure(property);
|
||||
return failure != NULL && !failure->IsEmpty();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ValidationFailures::Contains(const BString& property,
|
||||
const BString& message) const
|
||||
{
|
||||
ValidationFailure* failure = _GetFailure(property);
|
||||
return failure != NULL && failure->Contains(message);
|
||||
}
|
||||
|
||||
|
||||
ValidationFailure*
|
||||
ValidationFailures::FailureAtIndex(int32 index) const
|
||||
{
|
||||
return fItems.ItemAt(index);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ValidationFailures::Archive(BMessage* into, bool deep) const
|
||||
{
|
||||
status_t result = B_OK;
|
||||
BString key;
|
||||
|
||||
for (int32 i = 0; i < fItems.CountItems() && result == B_OK; i++) {
|
||||
ValidationFailure* item = fItems.ItemAt(i);
|
||||
BMessage itemMessage;
|
||||
result = item->Archive(&itemMessage);
|
||||
if (result == B_OK) {
|
||||
key.SetToFormat("%s%" B_PRId32, KEY_PREFIX_ITEM, i);
|
||||
result = into->AddMessage(key, &itemMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ValidationFailure*
|
||||
ValidationFailures::_GetFailure(const BString& property) const
|
||||
{
|
||||
for (int32 i = 0; i < fItems.CountItems(); i++) {
|
||||
ValidationFailure* item = fItems.ItemAt(i);
|
||||
if (item->Property() == property)
|
||||
return item;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ValidationFailure*
|
||||
ValidationFailures::_GetOrCreateFailure(const BString& property)
|
||||
{
|
||||
ValidationFailure* item = _GetFailure(property);
|
||||
|
||||
if (item == NULL) {
|
||||
item = new ValidationFailure(property);
|
||||
fItems.AddItem(item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationFailures::_AddFromMessage(const BMessage* from)
|
||||
{
|
||||
int32 i = 0;
|
||||
BString key;
|
||||
|
||||
while (true) {
|
||||
BMessage itemMessage;
|
||||
key.SetToFormat("%s%" B_PRId32, KEY_PREFIX_ITEM, i);
|
||||
if (from->FindMessage(key, &itemMessage) != B_OK)
|
||||
return;
|
||||
fItems.AddItem(new ValidationFailure(&itemMessage));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <[email protected]>.
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef VALIDATION_FAILURE_H
|
||||
#define VALIDATION_FAILURE_H
|
||||
|
||||
|
||||
#include <Archivable.h>
|
||||
#include <ObjectList.h>
|
||||
#include <String.h>
|
||||
#include <StringList.h>
|
||||
|
||||
|
||||
/*! This objects marries together a 'key' into some data together with an
|
||||
error message. The error message is not intended to be human readable, but
|
||||
is a key into a message.
|
||||
*/
|
||||
|
||||
class ValidationFailure : public BArchivable {
|
||||
public:
|
||||
ValidationFailure(BMessage* from);
|
||||
ValidationFailure(const BString& property);
|
||||
virtual ~ValidationFailure();
|
||||
|
||||
const BString& Property() const;
|
||||
const BStringList& Messages() const;
|
||||
bool IsEmpty() const;
|
||||
bool Contains(const BString& message) const;
|
||||
|
||||
void AddMessage(const BString& value);
|
||||
|
||||
status_t Archive(BMessage* into, bool deep = true) const;
|
||||
private:
|
||||
BString fProperty;
|
||||
BStringList fMessages;
|
||||
};
|
||||
|
||||
|
||||
class ValidationFailures : public BArchivable {
|
||||
public:
|
||||
ValidationFailures(BMessage* from);
|
||||
ValidationFailures();
|
||||
virtual ~ValidationFailures();
|
||||
|
||||
void AddFailure(const BString& property,
|
||||
const BString& message);
|
||||
int32 CountFailures() const;
|
||||
ValidationFailure* FailureAtIndex(int32 index) const;
|
||||
bool IsEmpty() const;
|
||||
bool Contains(const BString& property) const;
|
||||
bool Contains(const BString& property,
|
||||
const BString& message) const;
|
||||
|
||||
status_t Archive(BMessage* into, bool deep = true) const;
|
||||
|
||||
private:
|
||||
void _AddFromMessage(const BMessage* from);
|
||||
ValidationFailure* _GetFailure(const BString& property) const;
|
||||
ValidationFailure* _GetOrCreateFailure(const BString& property);
|
||||
|
||||
private:
|
||||
BObjectList<ValidationFailure>
|
||||
fItems;
|
||||
};
|
||||
|
||||
|
||||
#endif // VALIDATION_FAILURE_H
|
||||
@@ -27,29 +27,30 @@
|
||||
#define KEY_HEADER_MINIMUM_VERSION "X-Desktop-Application-Minimum-Version"
|
||||
|
||||
|
||||
/*! This method will cause an alert to be shown to the user regarding a
|
||||
/*! \brief This method will cause an alert to be shown to the user regarding a
|
||||
JSON-RPC error that has been sent from the application server. It will
|
||||
send a message to the application looper which will then relay the message
|
||||
to the looper and then onto the user to see.
|
||||
\param responsePayload The top level payload returned from the server.
|
||||
*/
|
||||
|
||||
/*static*/ void
|
||||
ServerHelper::NotifyServerJsonRpcError(BMessage& error)
|
||||
ServerHelper::NotifyServerJsonRpcError(BMessage& responsePayload)
|
||||
{
|
||||
BMessage message(MSG_SERVER_ERROR);
|
||||
message.AddMessage("error", &error);
|
||||
message.AddMessage("error", &responsePayload);
|
||||
be_app->PostMessage(&message);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
ServerHelper::AlertServerJsonRpcError(BMessage* message)
|
||||
ServerHelper::AlertServerJsonRpcError(BMessage* responseEnvelopeMessage)
|
||||
{
|
||||
BMessage error;
|
||||
BMessage errorMessage;
|
||||
int32 errorCode = 0;
|
||||
|
||||
if (message->FindMessage("error", &error) == B_OK)
|
||||
errorCode = WebAppInterface::ErrorCodeFromResponse(error);
|
||||
if (responseEnvelopeMessage->FindMessage("error", &errorMessage) == B_OK)
|
||||
errorCode = WebAppInterface::ErrorCodeFromResponse(errorMessage);
|
||||
|
||||
BString alertText;
|
||||
|
||||
@@ -210,4 +211,78 @@ ServerHelper::IsPlatformNetworkAvailable()
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*! If the response is an error and the error is a validation failure then
|
||||
* various validation errors may be carried in the error data. These are
|
||||
* copied into the supplied failures. An abridged example input JSON structure
|
||||
* would be;
|
||||
*
|
||||
* \code
|
||||
* {
|
||||
* ...
|
||||
* "error": {
|
||||
* "code": -32800,
|
||||
* "data": {
|
||||
"validationfailures": [
|
||||
{ "property": "nickname", "message": "required" },
|
||||
...
|
||||
]
|
||||
},
|
||||
...
|
||||
* }
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* \param failures is the object into which the validation failures are to be
|
||||
* written.
|
||||
* \param responseEnvelopeMessage is a representation of the entire JSON-RPC
|
||||
* response sent back from the server when the error occurred.
|
||||
*
|
||||
*/
|
||||
|
||||
/*static*/ void
|
||||
ServerHelper::GetFailuresFromJsonRpcError(
|
||||
ValidationFailures& failures, BMessage& responseEnvelopeMessage)
|
||||
{
|
||||
BMessage errorMessage;
|
||||
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(
|
||||
responseEnvelopeMessage);
|
||||
|
||||
if (responseEnvelopeMessage.FindMessage("error", &errorMessage) == B_OK) {
|
||||
BMessage dataMessage;
|
||||
|
||||
if (errorMessage.FindMessage("data", &dataMessage) == B_OK) {
|
||||
BMessage validationFailuresMessage;
|
||||
|
||||
if (dataMessage.FindMessage("validationfailures",
|
||||
&validationFailuresMessage) == B_OK) {
|
||||
_GetFailuresFromJsonRpcFailures(failures,
|
||||
validationFailuresMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
ServerHelper::_GetFailuresFromJsonRpcFailures(
|
||||
ValidationFailures& failures, BMessage& jsonRpcFailures)
|
||||
{
|
||||
int32 index = 0;
|
||||
while (true) {
|
||||
BString name;
|
||||
name << index++;
|
||||
BMessage failure;
|
||||
if (jsonRpcFailures.FindMessage(name, &failure) != B_OK)
|
||||
break;
|
||||
|
||||
BString property;
|
||||
BString message;
|
||||
if (failure.FindString("property", &property) == B_OK
|
||||
&& failure.FindString("message", &message) == B_OK) {
|
||||
failures.AddFailure(property, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
|
||||
* Copyright 2017-2019, Andrew Lindesay <[email protected]>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef SERVER_HELPER_H
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <HttpHeaders.h>
|
||||
|
||||
#include "ValidationFailure.h"
|
||||
|
||||
|
||||
class BMessage;
|
||||
|
||||
@@ -27,7 +29,15 @@ public:
|
||||
static void NotifyServerJsonRpcError(
|
||||
BMessage& error);
|
||||
static void AlertServerJsonRpcError(
|
||||
BMessage* message);
|
||||
BMessage* responseEnvelopeMessage);
|
||||
static void GetFailuresFromJsonRpcError(
|
||||
ValidationFailures& failures,
|
||||
BMessage& responseEnvelopeMessage);
|
||||
|
||||
private:
|
||||
static void _GetFailuresFromJsonRpcFailures(
|
||||
ValidationFailures& failures,
|
||||
BMessage& jsonRpcFailures);
|
||||
};
|
||||
|
||||
#endif // SERVER_HELPER_H
|
||||
|
||||
@@ -8,29 +8,22 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <AppFileInfo.h>
|
||||
#include <Application.h>
|
||||
#include <AutoDeleter.h>
|
||||
#include <Autolock.h>
|
||||
#include <File.h>
|
||||
#include <HttpHeaders.h>
|
||||
#include <HttpRequest.h>
|
||||
#include <Json.h>
|
||||
#include <JsonTextWriter.h>
|
||||
#include <JsonMessageWriter.h>
|
||||
#include <Message.h>
|
||||
#include <Roster.h>
|
||||
#include <Url.h>
|
||||
#include <UrlContext.h>
|
||||
#include <UrlProtocolListener.h>
|
||||
#include <UrlProtocolRoster.h>
|
||||
|
||||
#include "AutoLocker.h"
|
||||
#include "DataIOUtils.h"
|
||||
#include "HaikuDepotConstants.h"
|
||||
#include "List.h"
|
||||
#include "Logger.h"
|
||||
#include "PackageInfo.h"
|
||||
#include "ServerSettings.h"
|
||||
#include "ServerHelper.h"
|
||||
|
||||
@@ -40,159 +33,6 @@
|
||||
#define LOG_PAYLOAD_LIMIT 8192
|
||||
|
||||
|
||||
class JsonBuilder {
|
||||
public:
|
||||
JsonBuilder()
|
||||
:
|
||||
fString("{"),
|
||||
fInList(false)
|
||||
{
|
||||
}
|
||||
|
||||
JsonBuilder& AddObject()
|
||||
{
|
||||
fString << '{';
|
||||
fInList = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonBuilder& AddObject(const char* name)
|
||||
{
|
||||
_StartName(name);
|
||||
fString << '{';
|
||||
fInList = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonBuilder& EndObject()
|
||||
{
|
||||
fString << '}';
|
||||
fInList = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonBuilder& AddArray(const char* name)
|
||||
{
|
||||
_StartName(name);
|
||||
fString << '[';
|
||||
fInList = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonBuilder& EndArray()
|
||||
{
|
||||
fString << ']';
|
||||
fInList = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonBuilder& AddStrings(const StringList& strings)
|
||||
{
|
||||
for (int i = 0; i < strings.CountItems(); i++)
|
||||
AddItem(strings.ItemAtFast(i));
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonBuilder& AddItem(const char* item)
|
||||
{
|
||||
return AddItem(item, false);
|
||||
}
|
||||
|
||||
JsonBuilder& AddItem(const char* item, bool nullIfEmpty)
|
||||
{
|
||||
if (item == NULL || (nullIfEmpty && strlen(item) == 0)) {
|
||||
if (fInList)
|
||||
fString << ",null";
|
||||
else
|
||||
fString << "null";
|
||||
} else {
|
||||
if (fInList)
|
||||
fString << ",\"";
|
||||
else
|
||||
fString << '"';
|
||||
fString << _EscapeString(item);
|
||||
fString << '"';
|
||||
}
|
||||
fInList = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonBuilder& AddValue(const char* name, const char* value)
|
||||
{
|
||||
return AddValue(name, value, false);
|
||||
}
|
||||
|
||||
JsonBuilder& AddValue(const char* name, const char* value,
|
||||
bool nullIfEmpty)
|
||||
{
|
||||
_StartName(name);
|
||||
if (value == NULL || (nullIfEmpty && strlen(value) == 0)) {
|
||||
fString << "null";
|
||||
} else {
|
||||
fString << '"';
|
||||
fString << _EscapeString(value);
|
||||
fString << '"';
|
||||
}
|
||||
fInList = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonBuilder& AddValue(const char* name, int value)
|
||||
{
|
||||
_StartName(name);
|
||||
fString << value;
|
||||
fInList = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonBuilder& AddValue(const char* name, bool value)
|
||||
{
|
||||
_StartName(name);
|
||||
if (value)
|
||||
fString << "true";
|
||||
else
|
||||
fString << "false";
|
||||
fInList = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const BString& End()
|
||||
{
|
||||
fString << "}\n";
|
||||
return fString;
|
||||
}
|
||||
|
||||
private:
|
||||
void _StartName(const char* name)
|
||||
{
|
||||
if (fInList)
|
||||
fString << ",\"";
|
||||
else
|
||||
fString << '"';
|
||||
fString << _EscapeString(name);
|
||||
fString << "\":";
|
||||
}
|
||||
|
||||
BString _EscapeString(const char* original) const
|
||||
{
|
||||
BString string(original);
|
||||
string.ReplaceAll("\\", "\\\\");
|
||||
string.ReplaceAll("\"", "\\\"");
|
||||
string.ReplaceAll("/", "\\/");
|
||||
string.ReplaceAll("\b", "\\b");
|
||||
string.ReplaceAll("\f", "\\f");
|
||||
string.ReplaceAll("\n", "\\n");
|
||||
string.ReplaceAll("\r", "\\r");
|
||||
string.ReplaceAll("\t", "\\t");
|
||||
return string;
|
||||
}
|
||||
|
||||
private:
|
||||
BString fString;
|
||||
bool fInList;
|
||||
};
|
||||
|
||||
|
||||
class ProtocolListener : public BUrlProtocolListener {
|
||||
public:
|
||||
ProtocolListener(bool traceLogging)
|
||||
@@ -314,18 +154,27 @@ WebAppInterface::Nickname() const
|
||||
status_t
|
||||
WebAppInterface::GetChangelog(const BString& packageName, BMessage& message)
|
||||
{
|
||||
BString jsonString = JsonBuilder()
|
||||
.AddValue("jsonrpc", "2.0")
|
||||
.AddValue("id", ++fRequestIndex)
|
||||
.AddValue("method", "getPkgChangelog")
|
||||
.AddArray("params")
|
||||
.AddObject()
|
||||
.AddValue("pkgName", packageName)
|
||||
.EndObject()
|
||||
.EndArray()
|
||||
.End();
|
||||
BMallocIO* requestEnvelopeData = new BMallocIO();
|
||||
// BHttpRequest later takes ownership of this.
|
||||
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
|
||||
|
||||
return _SendJsonRequest("pkg", jsonString, 0, message);
|
||||
requestEnvelopeWriter.WriteObjectStart();
|
||||
_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
|
||||
"getPkgChangelog");
|
||||
requestEnvelopeWriter.WriteObjectName("params");
|
||||
requestEnvelopeWriter.WriteArrayStart();
|
||||
requestEnvelopeWriter.WriteObjectStart();
|
||||
|
||||
requestEnvelopeWriter.WriteObjectName("pkgName");
|
||||
requestEnvelopeWriter.WriteString(packageName.String());
|
||||
|
||||
requestEnvelopeWriter.WriteObjectEnd();
|
||||
requestEnvelopeWriter.WriteArrayEnd();
|
||||
requestEnvelopeWriter.WriteObjectEnd();
|
||||
|
||||
return _SendJsonRequest("pkg", requestEnvelopeData,
|
||||
_LengthAndSeekToZero(requestEnvelopeData), 0,
|
||||
message);
|
||||
}
|
||||
|
||||
|
||||
@@ -429,10 +278,10 @@ WebAppInterface::RetreiveUserRatingForPackageAndVersionByUser(
|
||||
}
|
||||
|
||||
|
||||
/*! This method will fill out the supplied UserDetail object with information
|
||||
about the user that is supplied in the credentials. Importantly it will
|
||||
also authenticate the request with the details of the credentials and will
|
||||
not use the credentials that are configured in 'fCredentials'.
|
||||
/*! This method will fill out the supplied UserDetail object with information
|
||||
about the user that is supplied in the credentials. Importantly it will
|
||||
also authenticate the request with the details of the credentials and will
|
||||
not use the credentials that are configured in 'fCredentials'.
|
||||
*/
|
||||
|
||||
status_t
|
||||
@@ -468,8 +317,8 @@ WebAppInterface::RetrieveUserDetailForCredentials(
|
||||
}
|
||||
|
||||
|
||||
/*! This method will return the credentials for the currently authenticated
|
||||
user.
|
||||
/*! This method will return the credentials for the currently authenticated
|
||||
user.
|
||||
*/
|
||||
|
||||
status_t
|
||||
@@ -479,9 +328,9 @@ WebAppInterface::RetrieveCurrentUserDetail(BMessage& message)
|
||||
}
|
||||
|
||||
|
||||
/*! When the user requests user detail, the server sends back an envelope of
|
||||
response data. This method will unpack the data into a model object.
|
||||
\return Not B_OK if something went wrong.
|
||||
/*! When the user requests user detail, the server sends back an envelope of
|
||||
response data. This method will unpack the data into a model object.
|
||||
\return Not B_OK if something went wrong.
|
||||
*/
|
||||
|
||||
/*static*/ status_t
|
||||
@@ -533,14 +382,14 @@ WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage,
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Returns data relating to the user usage conditions
|
||||
/*! \brief Returns data relating to the user usage conditions
|
||||
|
||||
\param code defines the version of the data to return or if empty then the
|
||||
latest is returned.
|
||||
|
||||
This method will go to the server and get details relating to the user usage
|
||||
conditions. It does this in two API calls; first gets the details (the
|
||||
minimum age) and in the second call, the text of the conditions is returned.
|
||||
This method will go to the server and get details relating to the user usage
|
||||
conditions. It does this in two API calls; first gets the details (the
|
||||
minimum age) and in the second call, the text of the conditions is returned.
|
||||
*/
|
||||
|
||||
status_t
|
||||
@@ -782,17 +631,23 @@ WebAppInterface::RetrieveScreenshot(const BString& code,
|
||||
status_t
|
||||
WebAppInterface::RequestCaptcha(BMessage& message)
|
||||
{
|
||||
BString jsonString = JsonBuilder()
|
||||
.AddValue("jsonrpc", "2.0")
|
||||
.AddValue("id", ++fRequestIndex)
|
||||
.AddValue("method", "generateCaptcha")
|
||||
.AddArray("params")
|
||||
.AddObject()
|
||||
.EndObject()
|
||||
.EndArray()
|
||||
.End();
|
||||
BMallocIO* requestEnvelopeData = new BMallocIO();
|
||||
// BHttpRequest later takes ownership of this.
|
||||
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
|
||||
|
||||
return _SendJsonRequest("captcha", jsonString, 0, message);
|
||||
requestEnvelopeWriter.WriteObjectStart();
|
||||
_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
|
||||
"generateCaptcha");
|
||||
requestEnvelopeWriter.WriteObjectName("params");
|
||||
requestEnvelopeWriter.WriteArrayStart();
|
||||
requestEnvelopeWriter.WriteObjectStart();
|
||||
requestEnvelopeWriter.WriteObjectEnd();
|
||||
requestEnvelopeWriter.WriteArrayEnd();
|
||||
requestEnvelopeWriter.WriteObjectEnd();
|
||||
|
||||
return _SendJsonRequest("captcha", requestEnvelopeData,
|
||||
_LengthAndSeekToZero(requestEnvelopeData), 0,
|
||||
message);
|
||||
}
|
||||
|
||||
|
||||
@@ -845,28 +700,38 @@ status_t
|
||||
WebAppInterface::AuthenticateUser(const BString& nickName,
|
||||
const BString& passwordClear, BMessage& message)
|
||||
{
|
||||
BString jsonString = JsonBuilder()
|
||||
.AddValue("jsonrpc", "2.0")
|
||||
.AddValue("id", ++fRequestIndex)
|
||||
.AddValue("method", "authenticateUser")
|
||||
.AddArray("params")
|
||||
.AddObject()
|
||||
.AddValue("nickname", nickName)
|
||||
.AddValue("passwordClear", passwordClear)
|
||||
.EndObject()
|
||||
.EndArray()
|
||||
.End();
|
||||
BMallocIO* requestEnvelopeData = new BMallocIO();
|
||||
// BHttpRequest later takes ownership of this.
|
||||
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
|
||||
|
||||
return _SendJsonRequest("user", jsonString, 0, message);
|
||||
requestEnvelopeWriter.WriteObjectStart();
|
||||
_WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
|
||||
"authenticateUser");
|
||||
requestEnvelopeWriter.WriteObjectName("params");
|
||||
requestEnvelopeWriter.WriteArrayStart();
|
||||
requestEnvelopeWriter.WriteObjectStart();
|
||||
|
||||
requestEnvelopeWriter.WriteObjectName("nickname");
|
||||
requestEnvelopeWriter.WriteString(nickName.String());
|
||||
requestEnvelopeWriter.WriteObjectName("passwordClear");
|
||||
requestEnvelopeWriter.WriteString(passwordClear.String());
|
||||
|
||||
requestEnvelopeWriter.WriteObjectEnd();
|
||||
requestEnvelopeWriter.WriteArrayEnd();
|
||||
requestEnvelopeWriter.WriteObjectEnd();
|
||||
|
||||
return _SendJsonRequest("user", requestEnvelopeData,
|
||||
_LengthAndSeekToZero(requestEnvelopeData), 0,
|
||||
message);
|
||||
}
|
||||
|
||||
|
||||
/*! JSON-RPC invocations return a response. The response may be either
|
||||
a result or it may be an error depending on the response structure.
|
||||
If it is an error then there may be additional detail that is the
|
||||
error code and message. This method will extract the error code
|
||||
from the response. This method will return 0 if the payload does
|
||||
not look like an error.
|
||||
/*! JSON-RPC invocations return a response. The response may be either
|
||||
a result or it may be an error depending on the response structure.
|
||||
If it is an error then there may be additional detail that is the
|
||||
error code and message. This method will extract the error code
|
||||
from the response. This method will return 0 if the payload does
|
||||
not look like an error.
|
||||
*/
|
||||
|
||||
int32
|
||||
@@ -1107,9 +972,9 @@ WebAppInterface::_LogPayload(BPositionIO* requestData, size_t size)
|
||||
}
|
||||
|
||||
|
||||
/*! This will get the position of the data to get the length an then sets the
|
||||
offset to zero so that it can be re-read for reading the payload in to log
|
||||
or send.
|
||||
/*! This will get the position of the data to get the length an then sets the
|
||||
offset to zero so that it can be re-read for reading the payload in to log
|
||||
or send.
|
||||
*/
|
||||
|
||||
off_t
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,10 @@
|
||||
#include <Messenger.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "CreateUserDetail.h"
|
||||
#include "PackageInfo.h"
|
||||
#include "UserCredentials.h"
|
||||
#include "ValidationFailure.h"
|
||||
|
||||
|
||||
class BButton;
|
||||
@@ -19,6 +22,7 @@ class BMenuField;
|
||||
class BTabView;
|
||||
class BTextControl;
|
||||
class BitmapView;
|
||||
class Captcha;
|
||||
class LinkView;
|
||||
class Model;
|
||||
class UserUsageConditions;
|
||||
@@ -30,6 +34,7 @@ public:
|
||||
Model& model);
|
||||
virtual ~UserLoginWindow();
|
||||
|
||||
virtual bool QuitRequested();
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
void SetOnSuccessMessage(
|
||||
@@ -45,45 +50,85 @@ private:
|
||||
};
|
||||
|
||||
void _SetMode(Mode mode);
|
||||
bool _ValidateCreateAccountFields(
|
||||
bool alertProblems = false);
|
||||
void _Login();
|
||||
void _SetWorkerThread(thread_id thread);
|
||||
void _SetWorkerThreadLocked(thread_id thread);
|
||||
|
||||
void _Authenticate();
|
||||
void _Authenticate(
|
||||
const UserCredentials& credentials);
|
||||
static int32 _AuthenticateThreadEntry(void* data);
|
||||
void _AuthenticateThread(
|
||||
UserCredentials& credentials);
|
||||
void _UnpackAuthenticationToken(
|
||||
BMessage& responsePayload, BString& token);
|
||||
void _HandleAuthenticationFailed();
|
||||
void _HandleAuthenticationSuccess(
|
||||
const UserCredentials & credentials);
|
||||
void _HandleAuthenticationError();
|
||||
|
||||
void _CreateAccount();
|
||||
void _AssembleCreateUserDetail(
|
||||
CreateUserDetail& detail);
|
||||
void _ValidateCreateUserDetail(
|
||||
CreateUserDetail& detail,
|
||||
ValidationFailures& failures);
|
||||
void _AlertCreateUserValidationFailure(
|
||||
const ValidationFailures& failures);
|
||||
static BString _CreateAlertTextFromValidationFailure(
|
||||
const BString& property,
|
||||
const BString& message);
|
||||
void _MarkCreateUserInvalidFields();
|
||||
void _MarkCreateUserInvalidFields(
|
||||
const ValidationFailures& failures);
|
||||
static int32 _CreateAccountThreadEntry(void* data);
|
||||
void _CreateAccountThread(CreateUserDetail* detail);
|
||||
void _HandleCreateAccountSuccess(
|
||||
const UserCredentials& credentials);
|
||||
void _HandleCreateAccountFailure(
|
||||
const ValidationFailures& failures);
|
||||
void _HandleCreateAccountError();
|
||||
|
||||
void _CreateAccountSetup(uint32 mask);
|
||||
void _CreateAccountSetupIfNecessary();
|
||||
void _LoginSuccessful(const BString& message);
|
||||
|
||||
void _SetWorkerThread(thread_id thread);
|
||||
|
||||
static int32 _AuthenticateThreadEntry(void* data);
|
||||
void _AuthenticateThread();
|
||||
|
||||
static int32 _CreateAccountSetupThreadEntry(void* data);
|
||||
void _CreateAccountCaptchaSetupThread();
|
||||
void _CreateAccountUserUsageConditionsSetupThread();
|
||||
status_t _CreateAccountCaptchaSetupThread(
|
||||
Captcha& captcha);
|
||||
status_t _CreateAccountUserUsageConditionsSetupThread(
|
||||
UserUsageConditions& userUsageConditions);
|
||||
status_t _UnpackCaptcha(BMessage& responsePayload,
|
||||
Captcha& captcha);
|
||||
void _HandleCreateAccountSetupSuccess(
|
||||
BMessage* message);
|
||||
|
||||
void _SetCaptcha(Captcha* captcha);
|
||||
void _SetUserUsageConditions(
|
||||
UserUsageConditions* userUsageConditions);
|
||||
|
||||
static int32 _CreateAccountThreadEntry(void* data);
|
||||
void _CreateAccountThread();
|
||||
|
||||
void _CollectValidationFailures(
|
||||
const BMessage& result,
|
||||
BString& error) const;
|
||||
|
||||
void _ViewUserUsageConditions();
|
||||
|
||||
void _TakeUpCredentialsAndQuit(
|
||||
const UserCredentials& credentials);
|
||||
|
||||
void _EnableMutableControls(bool enabled);
|
||||
|
||||
static void _ValidationFailuresToString(
|
||||
const ValidationFailures& failures,
|
||||
BString& output);
|
||||
|
||||
private:
|
||||
BMessenger fOnSuccessTarget;
|
||||
BMessage fOnSuccessMessage;
|
||||
|
||||
BTabView* fTabView;
|
||||
|
||||
BTextControl* fUsernameField;
|
||||
BTextControl* fNicknameField;
|
||||
BTextControl* fPasswordField;
|
||||
|
||||
BTextControl* fNewUsernameField;
|
||||
BTextControl* fNewNicknameField;
|
||||
BTextControl* fNewPasswordField;
|
||||
BTextControl* fRepeatPasswordField;
|
||||
BTextControl* fEmailField;
|
||||
@@ -97,19 +142,18 @@ private:
|
||||
BButton* fSendButton;
|
||||
BButton* fCancelButton;
|
||||
|
||||
BString fCaptchaToken;
|
||||
BitmapRef fCaptchaImage;
|
||||
UserUsageConditions*
|
||||
fUserUsageConditions;
|
||||
Captcha* fCaptcha;
|
||||
BString fPreferredLanguageCode;
|
||||
|
||||
Model& fModel;
|
||||
|
||||
Mode fMode;
|
||||
|
||||
UserUsageConditions*
|
||||
fUserUsageConditions;
|
||||
|
||||
BLocker fLock;
|
||||
thread_id fWorkerThread;
|
||||
bool fQuitRequestedDuringWorkerThread;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "ValidationUtils.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <UnicodeChar.h>
|
||||
|
||||
|
||||
#define MIN_LENGTH_NICKNAME 4
|
||||
#define MAX_LENGTH_NICKNAME 16
|
||||
|
||||
#define MIN_LENGTH_PASSWORD_CLEAR 8
|
||||
#define MIN_UPPER_PASSWORD_CLEAR 2
|
||||
#define MIN_DIGITS_PASSWORD_CLEAR 2
|
||||
|
||||
/*! 1 if the character would be suitable for use in an email address mailbox
|
||||
or domain part.
|
||||
*/
|
||||
|
||||
static int
|
||||
hd_is_email_domain_or_mailbox_part(int c)
|
||||
{
|
||||
if (0 == isspace(c) && c != 0x40)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*! Returns true if the entire string is lower case alpha numeric.
|
||||
*/
|
||||
|
||||
static int
|
||||
hd_is_lower_alnum(int c)
|
||||
{
|
||||
if ((c >= 0x30 && c <= 0x39) || (c >= 0x60 && c <= 0x7a))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
hd_str_all_matches_fn(const BString& string, int (*hd_match_c)(int c))
|
||||
{
|
||||
const char* c = string.String();
|
||||
for (int32 i = 0; i < string.CountChars(); i++) {
|
||||
if (0 == hd_match_c(c[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
hd_str_count_upper_case(const BString& string)
|
||||
{
|
||||
int32 upperCaseLetters = 0;
|
||||
const char* c = string.String();
|
||||
for (int32 i = 0; i < string.CountChars(); i++) {
|
||||
uint32 unicodeChar = BUnicodeChar::FromUTF8(&c);
|
||||
if (BUnicodeChar::IsUpper(unicodeChar))
|
||||
upperCaseLetters++;
|
||||
}
|
||||
return upperCaseLetters;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
hd_str_count_digit(const BString& string)
|
||||
{
|
||||
int32 digits = 0;
|
||||
const char* c = string.String();
|
||||
for (int32 i = 0; i < string.CountChars(); i++) {
|
||||
uint32 unicodeChar = BUnicodeChar::FromUTF8(&c);
|
||||
if (BUnicodeChar::IsDigit(unicodeChar))
|
||||
digits++;
|
||||
}
|
||||
return digits;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ bool
|
||||
ValidationUtils::IsValidNickname(const BString& value)
|
||||
{
|
||||
return hd_str_all_matches_fn(value, &hd_is_lower_alnum)
|
||||
&& value.CountChars() >= MIN_LENGTH_NICKNAME
|
||||
&& value.CountChars() <= MAX_LENGTH_NICKNAME;
|
||||
}
|
||||
|
||||
|
||||
/*! Email addresses are quite difficult to validate 100% correctly so go fairly
|
||||
light on the enforcement here; it should be a string with an '@' symbol,
|
||||
something either side of the '@' and there should be no whitespace.
|
||||
*/
|
||||
|
||||
/*static*/ bool
|
||||
ValidationUtils::IsValidEmail(const BString& value)
|
||||
{
|
||||
const char* c = value.String();
|
||||
size_t len = strlen(c);
|
||||
bool foundAt = false;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (c[i] == 0x40 && !foundAt) {
|
||||
if (i == 0 || i == len - 1)
|
||||
return false;
|
||||
foundAt = true;
|
||||
}
|
||||
else {
|
||||
if (0 == hd_is_email_domain_or_mailbox_part(c[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return foundAt;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ bool
|
||||
ValidationUtils::IsValidPasswordClear(const BString& value)
|
||||
{
|
||||
return value.Length() >= MIN_LENGTH_PASSWORD_CLEAR
|
||||
&& hd_str_count_digit(value) >= MIN_DIGITS_PASSWORD_CLEAR
|
||||
&& hd_str_count_upper_case(value) >= MIN_UPPER_PASSWORD_CLEAR;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef VALIDATION_UTILS_H
|
||||
#define VALIDATION_UTILS_H
|
||||
|
||||
|
||||
#include <String.h>
|
||||
|
||||
|
||||
class ValidationUtils {
|
||||
|
||||
public:
|
||||
static bool IsValidEmail(const BString& value);
|
||||
static bool IsValidNickname(const BString& value);
|
||||
static bool IsValidPasswordClear(const BString& value);
|
||||
};
|
||||
|
||||
#endif // VALIDATION_UTILS_H
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017, Andrew Lindesay, apl@lindesay.co.nz
|
||||
* Copyright 2017-2019, Andrew Lindesay, apl@lindesay.co.nz
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "StandardMetaDataJsonEventListenerTest.h"
|
||||
#include "DumpExportRepositoryJsonListenerTest.h"
|
||||
#include "ValidationFailureTest.h"
|
||||
#include "ValidationUtilsTest.h"
|
||||
#include "ListTest.h"
|
||||
|
||||
BTestSuite*
|
||||
@@ -17,6 +19,8 @@ getTestSuite()
|
||||
|
||||
StandardMetaDataJsonEventListenerTest::AddTests(*suite);
|
||||
DumpExportRepositoryJsonListenerTest::AddTests(*suite);
|
||||
ValidationFailureTest::AddTests(*suite);
|
||||
ValidationUtilsTest::AddTests(*suite);
|
||||
ListTest::AddTests(*suite);
|
||||
|
||||
return suite;
|
||||
|
||||
@@ -3,14 +3,18 @@ SubDir HAIKU_TOP src tests apps haikudepot ;
|
||||
AddSubDirSupportedPlatforms libbe_test ;
|
||||
|
||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot ] ;
|
||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot model ] ;
|
||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot server ] ;
|
||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot server dumpexportrepository ] ;
|
||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot util ] ;
|
||||
|
||||
UsePrivateHeaders shared ;
|
||||
|
||||
local sourceDirs =
|
||||
model
|
||||
server
|
||||
server/dumpexportrepository
|
||||
util
|
||||
;
|
||||
|
||||
local sourceDir ;
|
||||
@@ -37,5 +41,11 @@ UnitTestLib haikudepottest.so :
|
||||
StandardMetaDataJsonEventListener.cpp
|
||||
StandardMetaDataJsonEventListenerTest.cpp
|
||||
|
||||
ValidationFailure.cpp
|
||||
ValidationFailureTest.cpp
|
||||
|
||||
ValidationUtils.cpp
|
||||
ValidationUtilsTest.cpp
|
||||
|
||||
: be shared bnetapi package [ TargetLibstdc++ ] [ TargetLibsupc++ ]
|
||||
;
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "ValidationFailureTest.h"
|
||||
|
||||
#include <cppunit/TestCaller.h>
|
||||
#include <cppunit/TestSuite.h>
|
||||
|
||||
#include "ValidationFailure.h"
|
||||
|
||||
|
||||
ValidationFailureTest::ValidationFailureTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ValidationFailureTest::~ValidationFailureTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationFailureTest::TestDearchive()
|
||||
{
|
||||
BMessage nicknameMessage;
|
||||
nicknameMessage.AddString("property", "nickname");
|
||||
nicknameMessage.AddString("message_0", "malformed");
|
||||
nicknameMessage.AddString("message_1", "required");
|
||||
|
||||
BMessage passwordClearMessage;
|
||||
passwordClearMessage.AddString("property", "passwordClear");
|
||||
passwordClearMessage.AddString("message_0", "required");
|
||||
|
||||
BMessage validationFailuresMessage;
|
||||
validationFailuresMessage.AddMessage("item_0", &nicknameMessage);
|
||||
validationFailuresMessage.AddMessage("item_1", &passwordClearMessage);
|
||||
|
||||
// ----------------------
|
||||
ValidationFailures validationFailures(&validationFailuresMessage);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Count failures", (int32) 2,
|
||||
validationFailures.CountFailures());
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Contains 'nickname'", true,
|
||||
validationFailures.Contains("nickname"));
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Contains 'nickname:required'", true,
|
||||
validationFailures.Contains("nickname", "required"));
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Contains 'nickname:malformed'", true,
|
||||
validationFailures.Contains("nickname", "malformed"));
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Contains 'passwordClear:required'", true,
|
||||
validationFailures.Contains("passwordClear", "required"));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationFailureTest::TestArchive()
|
||||
{
|
||||
ValidationFailures failures;
|
||||
failures.AddFailure("nickname", "malformed");
|
||||
failures.AddFailure("nickname", "required");
|
||||
failures.AddFailure("passwordClear", "required");
|
||||
BMessage validationFailuresMessage;
|
||||
|
||||
// ----------------------
|
||||
status_t archiveResult = failures.Archive(&validationFailuresMessage);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Archive failure", B_OK, archiveResult);
|
||||
BMessage validationFailureNicknameMessage;
|
||||
BMessage validationFailurePasswordClearMessage;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Unable to find 'nickname'", B_OK,
|
||||
FindMessageWithProperty("nickname", validationFailuresMessage,
|
||||
validationFailureNicknameMessage));
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Unable to find 'passwordClear'", B_OK,
|
||||
FindMessageWithProperty("passwordClear", validationFailuresMessage,
|
||||
validationFailurePasswordClearMessage));
|
||||
|
||||
BStringList validationFailureMessagesNickname;
|
||||
BStringList validationFailureMessagesPasswordClear;
|
||||
FindValidationMessages(validationFailureNicknameMessage,
|
||||
validationFailureMessagesNickname);
|
||||
FindValidationMessages(validationFailurePasswordClearMessage,
|
||||
validationFailureMessagesPasswordClear);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Unable to find 'nickname:malformed'",
|
||||
true, validationFailureMessagesNickname.HasString("malformed"));
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Unable to find 'nickname:required'",
|
||||
true, validationFailureMessagesNickname.HasString("required"));
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Unexpected validation messages 'nickname'",
|
||||
(int32) 2, validationFailureMessagesNickname.CountStrings());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Unable to find 'passwordClear:required'",
|
||||
true, validationFailureMessagesPasswordClear.HasString("required"));
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Unexpected validation messages 'nickname'",
|
||||
(int32) 1, validationFailureMessagesPasswordClear.CountStrings());
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
ValidationFailureTest::AddTests(BTestSuite& parent)
|
||||
{
|
||||
CppUnit::TestSuite& suite = *new CppUnit::TestSuite(
|
||||
"ValidationFailureTest");
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<ValidationFailureTest>(
|
||||
"ValidationFailureTest::TestArchive",
|
||||
&ValidationFailureTest::TestArchive));
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<ValidationFailureTest>(
|
||||
"ValidationFailureTest::TestDearchive",
|
||||
&ValidationFailureTest::TestDearchive));
|
||||
|
||||
parent.addTest("ValidationFailureTest", &suite);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ status_t
|
||||
ValidationFailureTest::FindMessageWithProperty(
|
||||
const char* property, BMessage& validationFailuresMessage,
|
||||
BMessage& validationFailureMessage)
|
||||
{
|
||||
status_t result = B_OK;
|
||||
|
||||
for (int32 i = 0; result == B_OK; i++) {
|
||||
BString name = "item_";
|
||||
name << i;
|
||||
result = validationFailuresMessage.FindMessage(name,
|
||||
&validationFailureMessage);
|
||||
|
||||
if (result == B_OK) {
|
||||
BString messageProperty;
|
||||
result = validationFailureMessage.FindString("property",
|
||||
&messageProperty);
|
||||
|
||||
if (result == B_OK && messageProperty == property)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
ValidationFailureTest::FindValidationMessages(
|
||||
BMessage& validationFailureMessage, BStringList& validationMessages)
|
||||
{
|
||||
status_t result = B_OK;
|
||||
|
||||
for (int32 i = 0; result == B_OK; i++) {
|
||||
BString validationMessage;
|
||||
BString name = "message_";
|
||||
name << i;
|
||||
result = validationFailureMessage.FindString(name,
|
||||
&validationMessage);
|
||||
|
||||
if (result == B_OK) {
|
||||
validationMessages.Add(validationMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef VALIDATION_FAILURE_TEST_H
|
||||
#define VALIDATION_FAILURE_TEST_H
|
||||
|
||||
#include "Message.h"
|
||||
|
||||
#include <TestCase.h>
|
||||
#include <TestSuite.h>
|
||||
|
||||
|
||||
class ValidationFailureTest : public CppUnit::TestCase {
|
||||
public:
|
||||
ValidationFailureTest();
|
||||
virtual ~ValidationFailureTest();
|
||||
|
||||
void TestArchive();
|
||||
void TestDearchive();
|
||||
|
||||
static void AddTests(BTestSuite& suite);
|
||||
|
||||
private:
|
||||
static status_t FindMessageWithProperty(
|
||||
const char* property,
|
||||
BMessage& validationFailuresMessage,
|
||||
BMessage& validationFailureMessage);
|
||||
static void FindValidationMessages(
|
||||
BMessage& validationFailureMessage,
|
||||
BStringList& validationMessages);
|
||||
};
|
||||
|
||||
|
||||
#endif // VALIDATION_FAILURE_TEST_H
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "ValidationUtilsTest.h"
|
||||
|
||||
#include <cppunit/TestCaller.h>
|
||||
#include <cppunit/TestSuite.h>
|
||||
|
||||
#include "ValidationUtils.h"
|
||||
|
||||
|
||||
ValidationUtilsTest::ValidationUtilsTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ValidationUtilsTest::~ValidationUtilsTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationUtilsTest::TestEmailValid()
|
||||
{
|
||||
BString email("[email protected]");
|
||||
|
||||
// ----------------------
|
||||
bool result = ValidationUtils::IsValidEmail(email);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Email valid", true, result);
|
||||
}
|
||||
|
||||
|
||||
void TestEmailInvalidNoAt();
|
||||
void TestEmailInvalidNoMailbox();
|
||||
void TestEmailInvalidNoDomain();
|
||||
void TestEmailInvalidTwoAts();
|
||||
|
||||
void
|
||||
ValidationUtilsTest::TestEmailInvalidNoAt()
|
||||
{
|
||||
BString email("wetaexample.com");
|
||||
|
||||
// ----------------------
|
||||
bool result = ValidationUtils::IsValidEmail(email);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Email invalid - no @", false, result);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationUtilsTest::TestEmailInvalidNoMailbox()
|
||||
{
|
||||
BString email("@example.com");
|
||||
|
||||
// ----------------------
|
||||
bool result = ValidationUtils::IsValidEmail(email);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Email invalid - no mailbox", false, result);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationUtilsTest::TestEmailInvalidNoDomain()
|
||||
{
|
||||
BString email("fredric@");
|
||||
|
||||
// ----------------------
|
||||
bool result = ValidationUtils::IsValidEmail(email);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Email invalid - no domain", false, result);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationUtilsTest::TestNicknameValid()
|
||||
{
|
||||
BString nickname("erik55");
|
||||
|
||||
// ----------------------
|
||||
bool result = ValidationUtils::IsValidNickname(nickname);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Nickname valid", true, result);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationUtilsTest::TestNicknameInvalid()
|
||||
{
|
||||
BString nickname("not a Nickname!");
|
||||
|
||||
// ----------------------
|
||||
bool result = ValidationUtils::IsValidNickname(nickname);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Nickname invalid", false, result);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationUtilsTest::TestNicknameInvalidBadChars()
|
||||
{
|
||||
BString nickname("erik!!10");
|
||||
|
||||
// ----------------------
|
||||
bool result = ValidationUtils::IsValidNickname(nickname);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Nickname invalid (bad chars)",
|
||||
false, result);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationUtilsTest::TestPasswordClearValid()
|
||||
{
|
||||
BString passwordClear("P4NhelQoad4");
|
||||
|
||||
// ----------------------
|
||||
bool result = ValidationUtils::IsValidPasswordClear(passwordClear);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Password clear valid", true, result);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValidationUtilsTest::TestPasswordClearInvalid()
|
||||
{
|
||||
BString passwordClear("only has lower case letters");
|
||||
// needs some numbers / upper case characters in there too
|
||||
|
||||
// ----------------------
|
||||
bool result = ValidationUtils::IsValidPasswordClear(passwordClear);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("!Password clear invalid", false, result);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
ValidationUtilsTest::AddTests(BTestSuite& parent)
|
||||
{
|
||||
CppUnit::TestSuite& suite = *new CppUnit::TestSuite(
|
||||
"ValidationUtilsTest");
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<ValidationUtilsTest>(
|
||||
"ValidationUtilsTest::TestEmailInvalid",
|
||||
&ValidationUtilsTest::TestEmailInvalid));
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<ValidationUtilsTest>(
|
||||
"ValidationUtilsTest::TestEmailValid",
|
||||
&ValidationUtilsTest::TestEmailValid));
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<ValidationUtilsTest>(
|
||||
"ValidationUtilsTest::TestNicknameInvalid",
|
||||
&ValidationUtilsTest::TestNicknameInvalid));
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<ValidationUtilsTest>(
|
||||
"ValidationUtilsTest::TestNicknameValid",
|
||||
&ValidationUtilsTest::TestNicknameValid));
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<ValidationUtilsTest>(
|
||||
"ValidationUtilsTest::TestNicknameInvalidBadChars",
|
||||
&ValidationUtilsTest::TestNicknameInvalidBadChars));
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<ValidationUtilsTest>(
|
||||
"ValidationUtilsTest::TestPasswordClearInvalid",
|
||||
&ValidationUtilsTest::TestPasswordClearInvalid));
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<ValidationUtilsTest>(
|
||||
"ValidationUtilsTest::TestPasswordClearValid",
|
||||
&ValidationUtilsTest::TestPasswordClearValid));
|
||||
|
||||
parent.addTest("ValidationUtilsTest", &suite);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef VALIDATION_UTILS_TEST_H
|
||||
#define VALIDATION_UTILS_TEST_H
|
||||
|
||||
#include "Message.h"
|
||||
|
||||
#include <TestCase.h>
|
||||
#include <TestSuite.h>
|
||||
|
||||
|
||||
class ValidationUtilsTest : public CppUnit::TestCase {
|
||||
public:
|
||||
ValidationUtilsTest();
|
||||
virtual ~ValidationUtilsTest();
|
||||
|
||||
void TestEmailValid();
|
||||
void TestEmailInvalidNoAt();
|
||||
void TestEmailInvalidNoMailbox();
|
||||
void TestEmailInvalidNoDomain();
|
||||
void TestEmailInvalidTwoAts();
|
||||
|
||||
void TestNicknameValid();
|
||||
void TestNicknameInvalid();
|
||||
void TestNicknameInvalidBadChars();
|
||||
|
||||
void TestPasswordClearValid();
|
||||
void TestPasswordClearInvalid();
|
||||
|
||||
static void AddTests(BTestSuite& suite);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // VALIDATION_UTILS_TEST_H
|
||||
Reference in New Issue
Block a user