HaikuDepot: Restore logged in user across sessions.

* Store the username in the app settings. The password was already stored
   in the keyring. Restore the username upon next launch.
 * Display the logged in user in the main window.
 * Added "Log out" menu entry.
 * When the password could not be retrieved from the keyring (also because
   the user rejects the keyring dialog), unset the username.
 * Allow unsetting the username by passing an empty name.
This commit is contained in:
Stephan Aßmus
2014-09-28 00:33:20 +02:00
parent 3e3d0effdc
commit 8f03a0f9f2
7 changed files with 176 additions and 24 deletions
+52 -10
View File
@@ -30,14 +30,19 @@
static const char* kHaikuDepotKeyring = "HaikuDepot"; static const char* kHaikuDepotKeyring = "HaikuDepot";
// #pragma mark - PackageFilters
PackageFilter::~PackageFilter() PackageFilter::~PackageFilter()
{ {
} }
ModelListener::~ModelListener()
{
}
// #pragma mark - PackageFilters
class AnyFilter : public PackageFilter { class AnyFilter : public PackageFilter {
public: public:
virtual bool AcceptsPackage(const PackageInfoRef& package) const virtual bool AcceptsPackage(const PackageInfoRef& package) const
@@ -368,6 +373,13 @@ Model::~Model()
} }
bool
Model::AddListener(const ModelListenerRef& listener)
{
return fListeners.Add(listener);
}
PackageList PackageList
Model::CreatePackageList() const Model::CreatePackageList() const
{ {
@@ -661,14 +673,27 @@ Model::StopPopulatingAllPackages()
void void
Model::SetUser(const BString& username) Model::SetUsername(BString username)
{ {
BPasswordKey key; BString password;
BKeyStore keyStore; if (username.Length() > 0) {
if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username, BPasswordKey key;
key) == B_OK) { BKeyStore keyStore;
SetAuthorization(username, key.Password(), false); if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
key) == B_OK) {
password = key.Password();
} else {
username = "";
}
} }
SetAuthorization(username, password, false);
}
const BString&
Model::Username() const
{
return fWebAppInterface.Username();
} }
@@ -676,7 +701,7 @@ void
Model::SetAuthorization(const BString& username, const BString& password, Model::SetAuthorization(const BString& username, const BString& password,
bool storePassword) bool storePassword)
{ {
if (storePassword) { if (storePassword && username.Length() > 0 && password.Length() > 0) {
BPasswordKey key(password, B_KEY_PURPOSE_WEB, username); BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
BKeyStore keyStore; BKeyStore keyStore;
keyStore.AddKeyring(kHaikuDepotKeyring); keyStore.AddKeyring(kHaikuDepotKeyring);
@@ -685,6 +710,8 @@ Model::SetAuthorization(const BString& username, const BString& password,
BAutolock locker(&fLock); BAutolock locker(&fLock);
fWebAppInterface.SetAuthorization(username, password); fWebAppInterface.SetAuthorization(username, password);
_NotifyAuthorizationChanged();
} }
@@ -1146,3 +1173,18 @@ Model::_HasNativeIcon(const BMessage& message) const
} }
return false; return false;
} }
// #pragma mark - listener notification methods
void
Model::_NotifyAuthorizationChanged()
{
for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
const ModelListenerRef& listener = fListeners.ItemAtFast(i);
if (listener.Get() != NULL)
listener->AuthorizationChanged();
}
}
+19 -1
View File
@@ -25,6 +25,17 @@ public:
typedef BReference<PackageFilter> PackageFilterRef; typedef BReference<PackageFilter> PackageFilterRef;
class ModelListener : public BReferenceable {
public:
virtual ~ModelListener();
virtual void AuthorizationChanged() = 0;
};
typedef BReference<ModelListener> ModelListenerRef;
typedef List<ModelListenerRef, false> ModelListenerList;
class Model { class Model {
public: public:
Model(); Model();
@@ -33,6 +44,8 @@ public:
BLocker* Lock() BLocker* Lock()
{ return &fLock; } { return &fLock; }
bool AddListener(const ModelListenerRef& listener);
// !Returns new PackageInfoList from current parameters // !Returns new PackageInfoList from current parameters
PackageList CreatePackageList() const; PackageList CreatePackageList() const;
@@ -110,7 +123,8 @@ public:
const BString& PreferredLanguage() const const BString& PreferredLanguage() const
{ return fPreferredLanguage; } { return fPreferredLanguage; }
void SetUser(const BString& username); void SetUsername(BString username);
const BString& Username() const;
void SetAuthorization(const BString& username, void SetAuthorization(const BString& username,
const BString& password, const BString& password,
bool storePassword); bool storePassword);
@@ -139,6 +153,8 @@ private:
int32 scaledWidth, int32 scaledWidth,
bool fromCacheOnly); bool fromCacheOnly);
void _NotifyAuthorizationChanged();
private: private:
BLocker fLock; BLocker fLock;
@@ -182,6 +198,8 @@ private:
BString fPreferredLanguage; BString fPreferredLanguage;
WebAppInterface fWebAppInterface; WebAppInterface fWebAppInterface;
ModelListenerList fListeners;
}; };
@@ -28,6 +28,9 @@ public:
void SetAuthorization(const BString& username, void SetAuthorization(const BString& username,
const BString& password); const BString& password);
const BString& Username() const
{ return fUsername; }
void SetPreferredLanguage(const BString& language); void SetPreferredLanguage(const BString& language);
void SetArchitecture(const BString& architecture); void SetArchitecture(const BString& architecture);
+32 -4
View File
@@ -15,6 +15,7 @@
#include <MenuItem.h> #include <MenuItem.h>
#include <Messenger.h> #include <Messenger.h>
#include <PopUpMenu.h> #include <PopUpMenu.h>
#include <StringView.h>
#include <TextControl.h> #include <TextControl.h>
#include "Model.h" #include "Model.h"
@@ -37,16 +38,23 @@ add_categories_to_menu(const CategoryList& categories, BMenu* menu)
} }
static void
set_small_font(BView* view)
{
BFont font;
view->GetFont(&font);
font.SetSize(ceilf(font.Size() * 0.8));
view->SetFont(&font);
}
static BCheckBox* static BCheckBox*
create_check_box(const char* label, const char* name) create_check_box(const char* label, const char* name)
{ {
BMessage* message = new BMessage(MSG_FILTER_SELECTED); BMessage* message = new BMessage(MSG_FILTER_SELECTED);
message->AddString("name", name); message->AddString("name", name);
BCheckBox* checkBox = new BCheckBox(label, message); BCheckBox* checkBox = new BCheckBox(label, message);
BFont font; set_small_font(checkBox);
checkBox->GetFont(&font);
font.SetSize(ceilf(font.Size() * 0.75));
checkBox->SetFont(&font);
return checkBox; return checkBox;
} }
@@ -90,6 +98,11 @@ FilterView::FilterView()
fSourceCodeCheckBox = create_check_box( fSourceCodeCheckBox = create_check_box(
B_TRANSLATE("Source code"), "source code"); B_TRANSLATE("Source code"), "source code");
// Logged in user label
fUsername = new BStringView("logged in user", "");
set_small_font(fUsername);
fUsername->SetHighColor(80, 80, 80);
// Build layout // Build layout
BLayoutBuilder::Group<>(this) BLayoutBuilder::Group<>(this)
.AddGroup(B_HORIZONTAL) .AddGroup(B_HORIZONTAL)
@@ -107,6 +120,7 @@ FilterView::FilterView()
.Add(fDevelopmentCheckBox) .Add(fDevelopmentCheckBox)
.Add(fSourceCodeCheckBox) .Add(fSourceCodeCheckBox)
.AddGlue(0.5f) .AddGlue(0.5f)
.Add(fUsername)
.End() .End()
.SetInsets(B_USE_DEFAULT_SPACING) .SetInsets(B_USE_DEFAULT_SPACING)
@@ -200,3 +214,17 @@ FilterView::AdoptCheckmarks(const Model& model)
fSourceCodeCheckBox->SetValue(model.ShowSourcePackages()); fSourceCodeCheckBox->SetValue(model.ShowSourcePackages());
} }
void
FilterView::SetUsername(const BString& username)
{
BString label;
if (username.Length() == 0) {
label = B_TRANSLATE("Not logged in");
} else {
label = B_TRANSLATE("Logged in as %User%");
label.ReplaceAll("%User%", username);
}
fUsername->SetText(label);
}
+7 -2
View File
@@ -10,6 +10,7 @@
class BCheckBox; class BCheckBox;
class BMenuField; class BMenuField;
class BStringView;
class BTextControl; class BTextControl;
class Model; class Model;
@@ -30,8 +31,10 @@ public:
virtual void AttachedToWindow(); virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* message); virtual void MessageReceived(BMessage* message);
virtual void AdoptModel(const Model& model); void AdoptModel(const Model& model);
virtual void AdoptCheckmarks(const Model& model); void AdoptCheckmarks(const Model& model);
void SetUsername(const BString& username);
private: private:
BMenuField* fShowField; BMenuField* fShowField;
@@ -42,6 +45,8 @@ private:
BCheckBox* fInstalledCheckBox; BCheckBox* fInstalledCheckBox;
BCheckBox* fDevelopmentCheckBox; BCheckBox* fDevelopmentCheckBox;
BCheckBox* fSourceCodeCheckBox; BCheckBox* fSourceCodeCheckBox;
BStringView* fUsername;
}; };
#endif // FILTER_VIEW_H #endif // FILTER_VIEW_H
+60 -7
View File
@@ -53,12 +53,14 @@
enum { enum {
MSG_MODEL_WORKER_DONE = 'mmwd', MSG_MODEL_WORKER_DONE = 'mmwd',
MSG_REFRESH_DEPOTS = 'mrdp', MSG_REFRESH_DEPOTS = 'mrdp',
MSG_LOG_IN = 'lgin', MSG_LOG_IN = 'lgin',
MSG_PACKAGE_STATE_CHANGED = 'mpsc', MSG_LOG_OUT = 'lgot',
MSG_SHOW_SOURCE_PACKAGES = 'ssrc', MSG_AUTHORIZATION_CHANGED = 'athc',
MSG_SHOW_DEVELOP_PACKAGES = 'sdvl' MSG_PACKAGE_STATE_CHANGED = 'mpsc',
MSG_SHOW_SOURCE_PACKAGES = 'ssrc',
MSG_SHOW_DEVELOP_PACKAGES = 'sdvl'
}; };
@@ -83,11 +85,31 @@ struct RefreshWorkerParameters {
}; };
class MessageModelListener : public ModelListener {
public:
MessageModelListener(const BMessenger& messenger)
:
fMessenger(messenger)
{
}
virtual void AuthorizationChanged()
{
if (fMessenger.IsValid())
fMessenger.SendMessage(MSG_AUTHORIZATION_CHANGED);
}
private:
BMessenger fMessenger;
};
MainWindow::MainWindow(BRect frame, const BMessage& settings) MainWindow::MainWindow(BRect frame, const BMessage& settings)
: :
BWindow(frame, B_TRANSLATE_SYSTEM_NAME("HaikuDepot"), BWindow(frame, B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
fModelListener(new MessageModelListener(BMessenger(this)), true),
fTerminating(false), fTerminating(false),
fModelWorker(B_BAD_THREAD_ID) fModelWorker(B_BAD_THREAD_ID)
{ {
@@ -117,6 +139,8 @@ MainWindow::MainWindow(BRect frame, const BMessage& settings)
fSplitView->SetCollapsible(0, false); fSplitView->SetCollapsible(0, false);
fSplitView->SetCollapsible(1, false); fSplitView->SetCollapsible(1, false);
fModel.AddListener(fModelListener);
// Restore settings // Restore settings
BMessage columnSettings; BMessage columnSettings;
if (settings.FindMessage("column settings", &columnSettings) == B_OK) if (settings.FindMessage("column settings", &columnSettings) == B_OK)
@@ -128,6 +152,13 @@ MainWindow::MainWindow(BRect frame, const BMessage& settings)
if (settings.FindBool("show source packages", &showOption) == B_OK) if (settings.FindBool("show source packages", &showOption) == B_OK)
fModel.SetShowSourcePackages(showOption); fModel.SetShowSourcePackages(showOption);
BString username;
if (settings.FindString("username", &username) == B_OK
&& username.Length() > 0) {
fModel.SetUsername(username);
}
// start worker threads
BPackageRoster().StartWatching(this, BPackageRoster().StartWatching(this,
B_WATCH_PACKAGE_INSTALLATION_LOCATIONS); B_WATCH_PACKAGE_INSTALLATION_LOCATIONS);
@@ -212,6 +243,14 @@ MainWindow::MessageReceived(BMessage* message)
_OpenLoginWindow(); _OpenLoginWindow();
break; break;
case MSG_LOG_OUT:
fModel.SetUsername("");
break;
case MSG_AUTHORIZATION_CHANGED:
_UpdateAuthorization();
break;
case MSG_SHOW_SOURCE_PACKAGES: case MSG_SHOW_SOURCE_PACKAGES:
{ {
BAutolock locker(fModel.Lock()); BAutolock locker(fModel.Lock());
@@ -358,6 +397,8 @@ MainWindow::StoreSettings(BMessage& settings) const
settings.AddBool("show develop packages", fModel.ShowDevelopPackages()); settings.AddBool("show develop packages", fModel.ShowDevelopPackages());
settings.AddBool("show source packages", fModel.ShowSourcePackages()); settings.AddBool("show source packages", fModel.ShowSourcePackages());
settings.AddString("username", fModel.Username());
} }
@@ -401,8 +442,12 @@ MainWindow::_BuildMenu(BMenuBar* menuBar)
BMenu* menu = new BMenu(B_TRANSLATE("Tools")); BMenu* menu = new BMenu(B_TRANSLATE("Tools"));
menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh depots"), menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh depots"),
new BMessage(MSG_REFRESH_DEPOTS))); new BMessage(MSG_REFRESH_DEPOTS)));
menu->AddItem(new BMenuItem(B_TRANSLATE("Log in"), menu->AddSeparatorItem();
menu->AddItem(new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS),
new BMessage(MSG_LOG_IN))); new BMessage(MSG_LOG_IN)));
fLogOutItem = new BMenuItem(B_TRANSLATE("Log out"),
new BMessage(MSG_LOG_OUT));
menu->AddItem(fLogOutItem);
menuBar->AddItem(menu); menuBar->AddItem(menu);
// menu = new BMenu(B_TRANSLATE("Options")); // menu = new BMenu(B_TRANSLATE("Options"));
@@ -858,3 +903,11 @@ MainWindow::_OpenLoginWindow()
window->Show(); window->Show();
} }
void
MainWindow::_UpdateAuthorization()
{
BString username(fModel.Username());
fLogOutItem->SetEnabled(username.Length() > 0);
fFilterView->SetUsername(username);
}
+3
View File
@@ -72,16 +72,19 @@ private:
const char* message); const char* message);
void _OpenLoginWindow(); void _OpenLoginWindow();
void _UpdateAuthorization();
private: private:
FilterView* fFilterView; FilterView* fFilterView;
PackageListView* fPackageListView; PackageListView* fPackageListView;
PackageInfoView* fPackageInfoView; PackageInfoView* fPackageInfoView;
BSplitView* fSplitView; BSplitView* fSplitView;
BMenuItem* fLogOutItem;
BMenuItem* fShowDevelopPackagesItem; BMenuItem* fShowDevelopPackagesItem;
BMenuItem* fShowSourcePackagesItem; BMenuItem* fShowSourcePackagesItem;
Model fModel; Model fModel;
ModelListenerRef fModelListener;
PackageList fVisiblePackages; PackageList fVisiblePackages;
bool fTerminating; bool fTerminating;