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
|
App.cpp
|
||||||
BarberPole.cpp
|
BarberPole.cpp
|
||||||
BitmapView.cpp
|
BitmapView.cpp
|
||||||
|
Captcha.cpp
|
||||||
|
CreateUserDetail.cpp
|
||||||
DecisionProvider.cpp
|
DecisionProvider.cpp
|
||||||
FeaturedPackagesView.cpp
|
FeaturedPackagesView.cpp
|
||||||
FilterView.cpp
|
FilterView.cpp
|
||||||
@@ -149,6 +151,8 @@ local applicationSources =
|
|||||||
UserLoginWindow.cpp
|
UserLoginWindow.cpp
|
||||||
UserUsageConditions.cpp
|
UserUsageConditions.cpp
|
||||||
UserUsageConditionsWindow.cpp
|
UserUsageConditionsWindow.cpp
|
||||||
|
ValidationFailure.cpp
|
||||||
|
ValidationUtils.cpp
|
||||||
WorkStatusView.cpp
|
WorkStatusView.cpp
|
||||||
|
|
||||||
# network + server / local processes
|
# 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()
|
UserCredentials::UserCredentials()
|
||||||
:
|
:
|
||||||
fNickname(),
|
fNickname(),
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
|
|
||||||
/*! This object represents the tuple of the user's nickname (username) and
|
/*! This object represents the tuple of the user's nickname (username) and
|
||||||
password. It also carries a boolean that indicates if an authentication
|
password. It also carries a boolean that indicates if an authentication
|
||||||
with these credentials was successful or failed.
|
with these credentials was successful or failed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class UserCredentials : public BArchivable {
|
class UserCredentials : public BArchivable {
|
||||||
@@ -21,6 +21,7 @@ public:
|
|||||||
UserCredentials(BMessage* from);
|
UserCredentials(BMessage* from);
|
||||||
UserCredentials(const BString& nickname,
|
UserCredentials(const BString& nickname,
|
||||||
const BString& passwordClear);
|
const BString& passwordClear);
|
||||||
|
UserCredentials(const UserCredentials& other);
|
||||||
UserCredentials();
|
UserCredentials();
|
||||||
virtual ~UserCredentials();
|
virtual ~UserCredentials();
|
||||||
|
|
||||||
@@ -42,4 +43,5 @@ private:
|
|||||||
bool fIsSuccessful;
|
bool fIsSuccessful;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // USER_CREDENTIALS_H
|
#endif // USER_CREDENTIALS_H
|
||||||
|
|||||||
@@ -11,11 +11,11 @@
|
|||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
|
|
||||||
/*! A user in the HDS system should have agreed to user usage conditions when
|
/*! 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
|
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
|
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
|
to. Each set of user usage conditions has a code that uniquely identifies
|
||||||
a given set of conditions.
|
a given set of conditions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class UserUsageConditions : public BArchivable {
|
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"
|
#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
|
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
|
send a message to the application looper which will then relay the message
|
||||||
to the looper and then onto the user to see.
|
to the looper and then onto the user to see.
|
||||||
|
\param responsePayload The top level payload returned from the server.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*static*/ void
|
/*static*/ void
|
||||||
ServerHelper::NotifyServerJsonRpcError(BMessage& error)
|
ServerHelper::NotifyServerJsonRpcError(BMessage& responsePayload)
|
||||||
{
|
{
|
||||||
BMessage message(MSG_SERVER_ERROR);
|
BMessage message(MSG_SERVER_ERROR);
|
||||||
message.AddMessage("error", &error);
|
message.AddMessage("error", &responsePayload);
|
||||||
be_app->PostMessage(&message);
|
be_app->PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*static*/ void
|
/*static*/ void
|
||||||
ServerHelper::AlertServerJsonRpcError(BMessage* message)
|
ServerHelper::AlertServerJsonRpcError(BMessage* responseEnvelopeMessage)
|
||||||
{
|
{
|
||||||
BMessage error;
|
BMessage errorMessage;
|
||||||
int32 errorCode = 0;
|
int32 errorCode = 0;
|
||||||
|
|
||||||
if (message->FindMessage("error", &error) == B_OK)
|
if (responseEnvelopeMessage->FindMessage("error", &errorMessage) == B_OK)
|
||||||
errorCode = WebAppInterface::ErrorCodeFromResponse(error);
|
errorCode = WebAppInterface::ErrorCodeFromResponse(errorMessage);
|
||||||
|
|
||||||
BString alertText;
|
BString alertText;
|
||||||
|
|
||||||
@@ -210,4 +211,78 @@ ServerHelper::IsPlatformNetworkAvailable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef SERVER_HELPER_H
|
#ifndef SERVER_HELPER_H
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include <HttpHeaders.h>
|
#include <HttpHeaders.h>
|
||||||
|
|
||||||
|
#include "ValidationFailure.h"
|
||||||
|
|
||||||
|
|
||||||
class BMessage;
|
class BMessage;
|
||||||
|
|
||||||
@@ -27,7 +29,15 @@ public:
|
|||||||
static void NotifyServerJsonRpcError(
|
static void NotifyServerJsonRpcError(
|
||||||
BMessage& error);
|
BMessage& error);
|
||||||
static void AlertServerJsonRpcError(
|
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
|
#endif // SERVER_HELPER_H
|
||||||
|
|||||||
@@ -8,29 +8,22 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <AppFileInfo.h>
|
|
||||||
#include <Application.h>
|
#include <Application.h>
|
||||||
#include <AutoDeleter.h>
|
|
||||||
#include <Autolock.h>
|
|
||||||
#include <File.h>
|
|
||||||
#include <HttpHeaders.h>
|
#include <HttpHeaders.h>
|
||||||
#include <HttpRequest.h>
|
#include <HttpRequest.h>
|
||||||
#include <Json.h>
|
#include <Json.h>
|
||||||
#include <JsonTextWriter.h>
|
#include <JsonTextWriter.h>
|
||||||
#include <JsonMessageWriter.h>
|
#include <JsonMessageWriter.h>
|
||||||
#include <Message.h>
|
#include <Message.h>
|
||||||
#include <Roster.h>
|
|
||||||
#include <Url.h>
|
#include <Url.h>
|
||||||
#include <UrlContext.h>
|
#include <UrlContext.h>
|
||||||
#include <UrlProtocolListener.h>
|
#include <UrlProtocolListener.h>
|
||||||
#include <UrlProtocolRoster.h>
|
#include <UrlProtocolRoster.h>
|
||||||
|
|
||||||
#include "AutoLocker.h"
|
|
||||||
#include "DataIOUtils.h"
|
#include "DataIOUtils.h"
|
||||||
#include "HaikuDepotConstants.h"
|
#include "HaikuDepotConstants.h"
|
||||||
#include "List.h"
|
#include "List.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "PackageInfo.h"
|
|
||||||
#include "ServerSettings.h"
|
#include "ServerSettings.h"
|
||||||
#include "ServerHelper.h"
|
#include "ServerHelper.h"
|
||||||
|
|
||||||
@@ -40,159 +33,6 @@
|
|||||||
#define LOG_PAYLOAD_LIMIT 8192
|
#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 {
|
class ProtocolListener : public BUrlProtocolListener {
|
||||||
public:
|
public:
|
||||||
ProtocolListener(bool traceLogging)
|
ProtocolListener(bool traceLogging)
|
||||||
@@ -314,18 +154,27 @@ WebAppInterface::Nickname() const
|
|||||||
status_t
|
status_t
|
||||||
WebAppInterface::GetChangelog(const BString& packageName, BMessage& message)
|
WebAppInterface::GetChangelog(const BString& packageName, BMessage& message)
|
||||||
{
|
{
|
||||||
BString jsonString = JsonBuilder()
|
BMallocIO* requestEnvelopeData = new BMallocIO();
|
||||||
.AddValue("jsonrpc", "2.0")
|
// BHttpRequest later takes ownership of this.
|
||||||
.AddValue("id", ++fRequestIndex)
|
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
|
||||||
.AddValue("method", "getPkgChangelog")
|
|
||||||
.AddArray("params")
|
|
||||||
.AddObject()
|
|
||||||
.AddValue("pkgName", packageName)
|
|
||||||
.EndObject()
|
|
||||||
.EndArray()
|
|
||||||
.End();
|
|
||||||
|
|
||||||
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
|
/*! This method will fill out the supplied UserDetail object with information
|
||||||
about the user that is supplied in the credentials. Importantly it will
|
about the user that is supplied in the credentials. Importantly it will
|
||||||
also authenticate the request with the details of the credentials and will
|
also authenticate the request with the details of the credentials and will
|
||||||
not use the credentials that are configured in 'fCredentials'.
|
not use the credentials that are configured in 'fCredentials'.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
@@ -468,8 +317,8 @@ WebAppInterface::RetrieveUserDetailForCredentials(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! This method will return the credentials for the currently authenticated
|
/*! This method will return the credentials for the currently authenticated
|
||||||
user.
|
user.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
@@ -479,9 +328,9 @@ WebAppInterface::RetrieveCurrentUserDetail(BMessage& message)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! When the user requests user detail, the server sends back an envelope of
|
/*! 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.
|
response data. This method will unpack the data into a model object.
|
||||||
\return Not B_OK if something went wrong.
|
\return Not B_OK if something went wrong.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*static*/ status_t
|
/*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
|
\param code defines the version of the data to return or if empty then the
|
||||||
latest is returned.
|
latest is returned.
|
||||||
|
|
||||||
This method will go to the server and get details relating to the user usage
|
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
|
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.
|
minimum age) and in the second call, the text of the conditions is returned.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
@@ -782,17 +631,23 @@ WebAppInterface::RetrieveScreenshot(const BString& code,
|
|||||||
status_t
|
status_t
|
||||||
WebAppInterface::RequestCaptcha(BMessage& message)
|
WebAppInterface::RequestCaptcha(BMessage& message)
|
||||||
{
|
{
|
||||||
BString jsonString = JsonBuilder()
|
BMallocIO* requestEnvelopeData = new BMallocIO();
|
||||||
.AddValue("jsonrpc", "2.0")
|
// BHttpRequest later takes ownership of this.
|
||||||
.AddValue("id", ++fRequestIndex)
|
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
|
||||||
.AddValue("method", "generateCaptcha")
|
|
||||||
.AddArray("params")
|
|
||||||
.AddObject()
|
|
||||||
.EndObject()
|
|
||||||
.EndArray()
|
|
||||||
.End();
|
|
||||||
|
|
||||||
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,
|
WebAppInterface::AuthenticateUser(const BString& nickName,
|
||||||
const BString& passwordClear, BMessage& message)
|
const BString& passwordClear, BMessage& message)
|
||||||
{
|
{
|
||||||
BString jsonString = JsonBuilder()
|
BMallocIO* requestEnvelopeData = new BMallocIO();
|
||||||
.AddValue("jsonrpc", "2.0")
|
// BHttpRequest later takes ownership of this.
|
||||||
.AddValue("id", ++fRequestIndex)
|
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
|
||||||
.AddValue("method", "authenticateUser")
|
|
||||||
.AddArray("params")
|
|
||||||
.AddObject()
|
|
||||||
.AddValue("nickname", nickName)
|
|
||||||
.AddValue("passwordClear", passwordClear)
|
|
||||||
.EndObject()
|
|
||||||
.EndArray()
|
|
||||||
.End();
|
|
||||||
|
|
||||||
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
|
/*! JSON-RPC invocations return a response. The response may be either
|
||||||
a result or it may be an error depending on the response structure.
|
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
|
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
|
error code and message. This method will extract the error code
|
||||||
from the response. This method will return 0 if the payload does
|
from the response. This method will return 0 if the payload does
|
||||||
not look like an error.
|
not look like an error.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int32
|
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
|
/*! 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
|
offset to zero so that it can be re-read for reading the payload in to log
|
||||||
or send.
|
or send.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
off_t
|
off_t
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,10 @@
|
|||||||
#include <Messenger.h>
|
#include <Messenger.h>
|
||||||
#include <Window.h>
|
#include <Window.h>
|
||||||
|
|
||||||
|
#include "CreateUserDetail.h"
|
||||||
#include "PackageInfo.h"
|
#include "PackageInfo.h"
|
||||||
|
#include "UserCredentials.h"
|
||||||
|
#include "ValidationFailure.h"
|
||||||
|
|
||||||
|
|
||||||
class BButton;
|
class BButton;
|
||||||
@@ -19,6 +22,7 @@ class BMenuField;
|
|||||||
class BTabView;
|
class BTabView;
|
||||||
class BTextControl;
|
class BTextControl;
|
||||||
class BitmapView;
|
class BitmapView;
|
||||||
|
class Captcha;
|
||||||
class LinkView;
|
class LinkView;
|
||||||
class Model;
|
class Model;
|
||||||
class UserUsageConditions;
|
class UserUsageConditions;
|
||||||
@@ -30,6 +34,7 @@ public:
|
|||||||
Model& model);
|
Model& model);
|
||||||
virtual ~UserLoginWindow();
|
virtual ~UserLoginWindow();
|
||||||
|
|
||||||
|
virtual bool QuitRequested();
|
||||||
virtual void MessageReceived(BMessage* message);
|
virtual void MessageReceived(BMessage* message);
|
||||||
|
|
||||||
void SetOnSuccessMessage(
|
void SetOnSuccessMessage(
|
||||||
@@ -45,45 +50,85 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void _SetMode(Mode mode);
|
void _SetMode(Mode mode);
|
||||||
bool _ValidateCreateAccountFields(
|
void _SetWorkerThread(thread_id thread);
|
||||||
bool alertProblems = false);
|
void _SetWorkerThreadLocked(thread_id thread);
|
||||||
void _Login();
|
|
||||||
|
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 _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 _CreateAccountSetup(uint32 mask);
|
||||||
void _CreateAccountSetupIfNecessary();
|
void _CreateAccountSetupIfNecessary();
|
||||||
void _LoginSuccessful(const BString& message);
|
|
||||||
|
|
||||||
void _SetWorkerThread(thread_id thread);
|
|
||||||
|
|
||||||
static int32 _AuthenticateThreadEntry(void* data);
|
|
||||||
void _AuthenticateThread();
|
|
||||||
|
|
||||||
static int32 _CreateAccountSetupThreadEntry(void* data);
|
static int32 _CreateAccountSetupThreadEntry(void* data);
|
||||||
void _CreateAccountCaptchaSetupThread();
|
status_t _CreateAccountCaptchaSetupThread(
|
||||||
void _CreateAccountUserUsageConditionsSetupThread();
|
Captcha& captcha);
|
||||||
|
status_t _CreateAccountUserUsageConditionsSetupThread(
|
||||||
|
UserUsageConditions& userUsageConditions);
|
||||||
|
status_t _UnpackCaptcha(BMessage& responsePayload,
|
||||||
|
Captcha& captcha);
|
||||||
|
void _HandleCreateAccountSetupSuccess(
|
||||||
|
BMessage* message);
|
||||||
|
|
||||||
|
void _SetCaptcha(Captcha* captcha);
|
||||||
void _SetUserUsageConditions(
|
void _SetUserUsageConditions(
|
||||||
UserUsageConditions* userUsageConditions);
|
UserUsageConditions* userUsageConditions);
|
||||||
|
|
||||||
static int32 _CreateAccountThreadEntry(void* data);
|
|
||||||
void _CreateAccountThread();
|
|
||||||
|
|
||||||
void _CollectValidationFailures(
|
void _CollectValidationFailures(
|
||||||
const BMessage& result,
|
const BMessage& result,
|
||||||
BString& error) const;
|
BString& error) const;
|
||||||
|
|
||||||
void _ViewUserUsageConditions();
|
void _ViewUserUsageConditions();
|
||||||
|
|
||||||
|
void _TakeUpCredentialsAndQuit(
|
||||||
|
const UserCredentials& credentials);
|
||||||
|
|
||||||
|
void _EnableMutableControls(bool enabled);
|
||||||
|
|
||||||
|
static void _ValidationFailuresToString(
|
||||||
|
const ValidationFailures& failures,
|
||||||
|
BString& output);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BMessenger fOnSuccessTarget;
|
BMessenger fOnSuccessTarget;
|
||||||
BMessage fOnSuccessMessage;
|
BMessage fOnSuccessMessage;
|
||||||
|
|
||||||
BTabView* fTabView;
|
BTabView* fTabView;
|
||||||
|
|
||||||
BTextControl* fUsernameField;
|
BTextControl* fNicknameField;
|
||||||
BTextControl* fPasswordField;
|
BTextControl* fPasswordField;
|
||||||
|
|
||||||
BTextControl* fNewUsernameField;
|
BTextControl* fNewNicknameField;
|
||||||
BTextControl* fNewPasswordField;
|
BTextControl* fNewPasswordField;
|
||||||
BTextControl* fRepeatPasswordField;
|
BTextControl* fRepeatPasswordField;
|
||||||
BTextControl* fEmailField;
|
BTextControl* fEmailField;
|
||||||
@@ -97,19 +142,18 @@ private:
|
|||||||
BButton* fSendButton;
|
BButton* fSendButton;
|
||||||
BButton* fCancelButton;
|
BButton* fCancelButton;
|
||||||
|
|
||||||
BString fCaptchaToken;
|
UserUsageConditions*
|
||||||
BitmapRef fCaptchaImage;
|
fUserUsageConditions;
|
||||||
|
Captcha* fCaptcha;
|
||||||
BString fPreferredLanguageCode;
|
BString fPreferredLanguageCode;
|
||||||
|
|
||||||
Model& fModel;
|
Model& fModel;
|
||||||
|
|
||||||
Mode fMode;
|
Mode fMode;
|
||||||
|
|
||||||
UserUsageConditions*
|
|
||||||
fUserUsageConditions;
|
|
||||||
|
|
||||||
BLocker fLock;
|
BLocker fLock;
|
||||||
thread_id fWorkerThread;
|
thread_id fWorkerThread;
|
||||||
|
bool fQuitRequestedDuringWorkerThread;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019, Andrew Lindesay <[email protected]>.
|
||||||
|
* 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 <[email protected]>.
|
||||||
|
* 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, [email protected]
|
* Copyright 2017-2019, Andrew Lindesay, [email protected]
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include "StandardMetaDataJsonEventListenerTest.h"
|
#include "StandardMetaDataJsonEventListenerTest.h"
|
||||||
#include "DumpExportRepositoryJsonListenerTest.h"
|
#include "DumpExportRepositoryJsonListenerTest.h"
|
||||||
|
#include "ValidationFailureTest.h"
|
||||||
|
#include "ValidationUtilsTest.h"
|
||||||
#include "ListTest.h"
|
#include "ListTest.h"
|
||||||
|
|
||||||
BTestSuite*
|
BTestSuite*
|
||||||
@@ -17,6 +19,8 @@ getTestSuite()
|
|||||||
|
|
||||||
StandardMetaDataJsonEventListenerTest::AddTests(*suite);
|
StandardMetaDataJsonEventListenerTest::AddTests(*suite);
|
||||||
DumpExportRepositoryJsonListenerTest::AddTests(*suite);
|
DumpExportRepositoryJsonListenerTest::AddTests(*suite);
|
||||||
|
ValidationFailureTest::AddTests(*suite);
|
||||||
|
ValidationUtilsTest::AddTests(*suite);
|
||||||
ListTest::AddTests(*suite);
|
ListTest::AddTests(*suite);
|
||||||
|
|
||||||
return suite;
|
return suite;
|
||||||
|
|||||||
@@ -3,14 +3,18 @@ SubDir HAIKU_TOP src tests apps haikudepot ;
|
|||||||
AddSubDirSupportedPlatforms libbe_test ;
|
AddSubDirSupportedPlatforms libbe_test ;
|
||||||
|
|
||||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot ] ;
|
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 ] ;
|
||||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot server dumpexportrepository ] ;
|
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot server dumpexportrepository ] ;
|
||||||
|
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot util ] ;
|
||||||
|
|
||||||
UsePrivateHeaders shared ;
|
UsePrivateHeaders shared ;
|
||||||
|
|
||||||
local sourceDirs =
|
local sourceDirs =
|
||||||
|
model
|
||||||
server
|
server
|
||||||
server/dumpexportrepository
|
server/dumpexportrepository
|
||||||
|
util
|
||||||
;
|
;
|
||||||
|
|
||||||
local sourceDir ;
|
local sourceDir ;
|
||||||
@@ -37,5 +41,11 @@ UnitTestLib haikudepottest.so :
|
|||||||
StandardMetaDataJsonEventListener.cpp
|
StandardMetaDataJsonEventListener.cpp
|
||||||
StandardMetaDataJsonEventListenerTest.cpp
|
StandardMetaDataJsonEventListenerTest.cpp
|
||||||
|
|
||||||
|
ValidationFailure.cpp
|
||||||
|
ValidationFailureTest.cpp
|
||||||
|
|
||||||
|
ValidationUtils.cpp
|
||||||
|
ValidationUtilsTest.cpp
|
||||||
|
|
||||||
: be shared bnetapi package [ TargetLibstdc++ ] [ TargetLibsupc++ ]
|
: be shared bnetapi package [ TargetLibstdc++ ] [ TargetLibsupc++ ]
|
||||||
;
|
;
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019, Andrew Lindesay <[email protected]>.
|
||||||
|
* 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 <[email protected]>
|
||||||
|
* 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 <[email protected]>.
|
||||||
|
* 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 <[email protected]>
|
||||||
|
* 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