From 28075ee4274d1bd1c4dc57ffc85ecfdfd2afaaa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sun, 21 Sep 2014 00:09:17 +0200 Subject: [PATCH] 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. --- src/apps/haikudepot/Model.cpp | 33 +++++---- src/apps/haikudepot/Model.h | 7 ++ src/apps/haikudepot/UserLoginWindow.cpp | 93 ++++++++++++++++++++++--- src/apps/haikudepot/UserLoginWindow.h | 3 + src/apps/haikudepot/WebAppInterface.cpp | 43 ++++++++++++ src/apps/haikudepot/WebAppInterface.h | 8 ++- 6 files changed, 163 insertions(+), 24 deletions(-) diff --git a/src/apps/haikudepot/Model.cpp b/src/apps/haikudepot/Model.cpp index 2b5b86a09b..d83245600f 100644 --- a/src/apps/haikudepot/Model.cpp +++ b/src/apps/haikudepot/Model.cpp @@ -21,8 +21,6 @@ #include #include -#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); diff --git a/src/apps/haikudepot/Model.h b/src/apps/haikudepot/Model.h index ca8c67a501..c968c1c7a4 100644 --- a/src/apps/haikudepot/Model.h +++ b/src/apps/haikudepot/Model.h @@ -8,6 +8,8 @@ #include #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; }; diff --git a/src/apps/haikudepot/UserLoginWindow.cpp b/src/apps/haikudepot/UserLoginWindow.cpp index 09bbff3ae2..0a525cabba 100644 --- a/src/apps/haikudepot/UserLoginWindow.cpp +++ b/src/apps/haikudepot/UserLoginWindow.cpp @@ -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(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); diff --git a/src/apps/haikudepot/UserLoginWindow.h b/src/apps/haikudepot/UserLoginWindow.h index cdcd813db6..a1788816c9 100644 --- a/src/apps/haikudepot/UserLoginWindow.h +++ b/src/apps/haikudepot/UserLoginWindow.h @@ -40,6 +40,9 @@ private: void _SetWorkerThread(thread_id thread); + static int32 _AuthenticateThreadEntry(void* data); + void _AuthenticateThread(); + static int32 _RequestCaptchaThreadEntry(void* data); void _RequestCaptchaThread(); diff --git a/src/apps/haikudepot/WebAppInterface.cpp b/src/apps/haikudepot/WebAppInterface.cpp index 3eb44f1aed..9e1189db3e 100644 --- a/src/apps/haikudepot/WebAppInterface.cpp +++ b/src/apps/haikudepot/WebAppInterface.cpp @@ -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 diff --git a/src/apps/haikudepot/WebAppInterface.h b/src/apps/haikudepot/WebAppInterface.h index 181bc9f5e3..1fe1b7cf79 100644 --- a/src/apps/haikudepot/WebAppInterface.h +++ b/src/apps/haikudepot/WebAppInterface.h @@ -21,8 +21,11 @@ typedef List 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; };