HaikuDepot: Implemented logging into haiku-depot-server

UserLoginWindow:
 * Focus nickname text field on tab switches
 * Implement testing the login info, the web-app replies with a token, if
   valid. It could be used for Token Bearer authorization of requests, but
   this is not used yet. Instead the username and password are set on the
   Model. Also after creating a new account successfully.
Model:
 * Use a member instance of WebAppInterface. Set the preferred language
   and the login-info only once.
This commit is contained in:
Stephan Aßmus 2014-09-21 00:09:17 +02:00
parent f511367b33
commit 28075ee427
6 changed files with 163 additions and 24 deletions

View File

@ -21,8 +21,6 @@
#include <Message.h>
#include <Path.h>
#include "WebAppInterface.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Model"
@ -338,6 +336,7 @@ Model::Model()
language.CopyInto(fPreferredLanguage, 0, 2);
}
}
fWebAppInterface.SetPreferredLanguage(fPreferredLanguage);
}
@ -516,8 +515,6 @@ Model::PopulatePackage(const PackageInfoRef& package, uint32 flags)
if ((flags & POPULATE_USER_RATINGS) != 0) {
// Retrieve info from web-app
WebAppInterface interface;
interface.SetPreferredLanguage(fPreferredLanguage);
BMessage info;
BString packageName;
@ -528,7 +525,7 @@ Model::PopulatePackage(const PackageInfoRef& package, uint32 flags)
architecture = package->Architecture();
}
status_t status = interface.RetrieveUserRatings(packageName,
status_t status = fWebAppInterface.RetrieveUserRatings(packageName,
architecture, 0, 50, info);
if (status == B_OK) {
// Parse message
@ -641,6 +638,17 @@ Model::StopPopulatingAllPackages()
}
void
Model::SetAuthorization(const BString& username, const BString& password)
{
BAutolock locker(&fLock);
fWebAppInterface.SetAuthorization(username, password);
}
// #pragma mark - private
int32
Model::_PopulateAllPackagesEntry(void* cookie)
{
@ -727,8 +735,6 @@ Model::_PopulatePackageInfos(PackageList& packages, bool fromCacheOnly,
return;
// Retrieve info from web-app
WebAppInterface interface;
interface.SetPreferredLanguage(fPreferredLanguage);
BMessage info;
StringList packageNames;
@ -739,7 +745,7 @@ Model::_PopulatePackageInfos(PackageList& packages, bool fromCacheOnly,
packageArchitectures.Add(package->Architecture());
}
status_t status = interface.RetrieveBulkPackageInfo(packageNames,
status_t status = fWebAppInterface.RetrieveBulkPackageInfo(packageNames,
packageArchitectures, info);
if (status == B_OK) {
// Parse message
@ -818,11 +824,9 @@ Model::_PopulatePackageInfo(const PackageInfoRef& package, bool fromCacheOnly)
return;
// Retrieve info from web-app
WebAppInterface interface;
interface.SetPreferredLanguage(fPreferredLanguage);
BMessage info;
status_t status = interface.RetrievePackageInfo(package->Title(),
status_t status = fWebAppInterface.RetrievePackageInfo(package->Title(),
package->Architecture(), info);
if (status == B_OK) {
// Parse message
@ -995,10 +999,10 @@ Model::_PopulatePackageIcon(const PackageInfoRef& package, bool fromCacheOnly)
return;
// Retrieve icon from web-app
WebAppInterface interface;
BMallocIO buffer;
status_t status = interface.RetrievePackageIcon(package->Title(), &buffer);
status_t status = fWebAppInterface.RetrievePackageIcon(package->Title(),
&buffer);
if (status == B_OK) {
BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
BAutolock locker(&fLock);
@ -1053,12 +1057,11 @@ Model::_PopulatePackageScreenshot(const PackageInfoRef& package,
return;
// Retrieve screenshot from web-app
WebAppInterface interface;
BMallocIO buffer;
int32 scaledHeight = scaledWidth * info.Height() / info.Width();
status_t status = interface.RetrieveScreenshot(info.Code(),
status_t status = fWebAppInterface.RetrieveScreenshot(info.Code(),
scaledWidth, scaledHeight, &buffer);
if (status == B_OK) {
BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);

View File

@ -8,6 +8,8 @@
#include <Locker.h>
#include "PackageInfo.h"
#include "WebAppInterface.h"
class BMessage;
@ -105,6 +107,9 @@ public:
const BString& PreferredLanguage() const
{ return fPreferredLanguage; }
void SetAuthorization(const BString& username,
const BString& password);
private:
static int32 _PopulateAllPackagesEntry(void* cookie);
void _PopulateAllPackagesThread(bool fromCacheOnly);
@ -168,6 +173,8 @@ private:
thread_id fPopulateAllPackagesThread;
volatile bool fStopPopulatingAllPackages;
BString fPreferredLanguage;
WebAppInterface fWebAppInterface;
};

View File

@ -209,12 +209,14 @@ UserLoginWindow::_SetMode(Mode mode)
case LOGIN:
fTabView->Select((int32)0);
fSendButton->SetLabel(B_TRANSLATE("Log in"));
fUsernameField->MakeFocus();
break;
case CREATE_ACCOUNT:
fTabView->Select(1);
fSendButton->SetLabel(B_TRANSLATE("Create account"));
if (fCaptchaToken.IsEmpty())
_RequestCaptcha();
fNewUsernameField->MakeFocus();
break;
default:
break;
@ -225,15 +227,15 @@ UserLoginWindow::_SetMode(Mode mode)
void
UserLoginWindow::_Login()
{
// TODO: Implement...
BAlert* alert = new BAlert(B_TRANSLATE("Not implemented"),
B_TRANSLATE("Sorry, while the web application would already support "
"logging in, HaikuDepot was not yet updated to use "
"this functionality."),
B_TRANSLATE("Bummer"));
alert->Go(NULL);
BAutolock locker(&fLock);
if (fWorkerThread >= 0)
return;
PostMessage(B_QUIT_REQUESTED);
thread_id thread = spawn_thread(&_AuthenticateThreadEntry,
"Authenticator", B_NORMAL_PRIORITY, this);
if (thread >= 0)
_SetWorkerThread(thread);
}
@ -303,6 +305,79 @@ UserLoginWindow::_SetWorkerThread(thread_id thread)
}
int32
UserLoginWindow::_AuthenticateThreadEntry(void* data)
{
UserLoginWindow* window = reinterpret_cast<UserLoginWindow*>(data);
window->_AuthenticateThread();
return 0;
}
void
UserLoginWindow::_AuthenticateThread()
{
if (!Lock())
return;
BString nickName(fUsernameField->Text());
BString passwordClear(fPasswordField->Text());
Unlock();
WebAppInterface interface;
BMessage info;
status_t status = interface.AuthenticateUser(
nickName, passwordClear, info);
BString error = B_TRANSLATE("Authentication failed. "
"Connection to the service failed.");
BMessage result;
if (status == B_OK && info.FindMessage("result", &result) == B_OK) {
BString token;
if (result.FindString("token", &token) == B_OK && !token.IsEmpty()) {
// We don't care for or store the token for now. The web-service
// supports two methods of authorizing requests. One is via
// Basic Authentication in the HTTP header, the other is via
// Token Bearer. Since the connection is encrypted, it is hopefully
// ok to send the password with each request instead of implementing
// the Token Bearer. See section 5.1.2 in the haiku-depot-web
// documentation.
error = "";
fModel.SetAuthorization(nickName, passwordClear);
} else {
error = B_TRANSLATE("Authentication failed. The user does "
"not exist or the wrong password was supplied.");
}
}
if (!error.IsEmpty()) {
BAlert* alert = new(std::nothrow) BAlert(
B_TRANSLATE("Authentication failed"),
error,
B_TRANSLATE("Close"));
if (alert != NULL)
alert->Go();
_SetWorkerThread(-1);
} else {
_SetWorkerThread(-1);
BMessenger(this).SendMessage(B_QUIT_REQUESTED);
BAlert* alert = new(std::nothrow) BAlert(
B_TRANSLATE("Success"),
B_TRANSLATE("The authentication was successful."),
B_TRANSLATE("Close"));
if (alert != NULL)
alert->Go();
}
}
int32
UserLoginWindow::_RequestCaptchaThreadEntry(void* data)
{
@ -433,6 +508,8 @@ UserLoginWindow::_CreateAccountThread()
fCaptchaToken = "";
_RequestCaptcha();
} else {
fModel.SetAuthorization(nickName, passwordClear);
_SetWorkerThread(-1);
BMessenger(this).SendMessage(B_QUIT_REQUESTED);

View File

@ -40,6 +40,9 @@ private:
void _SetWorkerThread(thread_id thread);
static int32 _AuthenticateThreadEntry(void* data);
void _AuthenticateThread();
static int32 _RequestCaptchaThreadEntry(void* data);
void _RequestCaptchaThread();

View File

@ -238,11 +238,34 @@ WebAppInterface::WebAppInterface()
}
WebAppInterface::WebAppInterface(const WebAppInterface& other)
:
fUsername(other.fUsername),
fPassword(other.fPassword),
fLanguage(other.fLanguage)
{
}
WebAppInterface::~WebAppInterface()
{
}
WebAppInterface&
WebAppInterface::operator=(const WebAppInterface& other)
{
if (this == &other)
return *this;
fUsername = other.fUsername;
fPassword = other.fPassword;
fLanguage = other.fLanguage;
return *this;
}
void
WebAppInterface::SetAuthorization(const BString& username,
const BString& password)
@ -452,6 +475,26 @@ WebAppInterface::CreateUser(const BString& nickName,
}
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();
return _SendJsonRequest("user", jsonString, message);
}
// #pragma mark - private

View File

@ -21,8 +21,11 @@ typedef List<BString, false> StringList;
class WebAppInterface {
public:
WebAppInterface();
WebAppInterface(const WebAppInterface& other);
virtual ~WebAppInterface();
WebAppInterface& operator=(const WebAppInterface& other);
void SetAuthorization(const BString& username,
const BString& password);
void SetPreferredLanguage(const BString& language);
@ -63,6 +66,10 @@ public:
const BString& languageCode,
BMessage& message);
status_t AuthenticateUser(const BString& nickName,
const BString& passwordClear,
BMessage& message);
private:
status_t _SendJsonRequest(const char* domain,
BString jsonString, BMessage& reply) const;
@ -71,7 +78,6 @@ private:
BString fUsername;
BString fPassword;
BString fLanguage;
BString fArchitecture;
static int fRequestIndex;
};