[geary/wip/17-noisy-problem-reports: 12/12] Implement client-based password and auth failure status prompting
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/17-noisy-problem-reports: 12/12] Implement client-based password and auth failure status prompting
- Date: Tue, 1 Jan 2019 11:30:44 +0000 (UTC)
commit 2da01b29e436b4cf392e8cb1debf4596bc1736b5
Author: Michael Gratton <mike vee net>
Date: Tue Jan 1 22:19:24 2019 +1100
Implement client-based password and auth failure status prompting
This re-ads support for prompting for service passwords in the client,
and also gracefully handles showing auth failure infobar and getting
multple failures at once.
src/client/application/geary-controller.vala | 113 +++++++++++++++++++++++++--
src/client/components/main-window.vala | 23 ++++--
2 files changed, 124 insertions(+), 12 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 15772f40..59a0394d 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -59,6 +59,7 @@ public class GearyController : Geary.BaseObject {
private const string PROP_ATTEMPT_OPEN_ACCOUNT = "attempt-open-account";
+ private const uint MAX_AUTH_ATTEMPTS = 3;
private static string untitled_file_name;
@@ -81,6 +82,11 @@ public class GearyController : Geary.BaseObject {
public Geary.Account account { get; private set; }
public Geary.Folder? inbox = null;
public Geary.App.EmailStore store { get; private set; }
+
+ public bool authentication_failed = false;
+ public bool authentication_prompting = false;
+ public uint authentication_attempts = 0;
+
public Cancellable cancellable { get; private set; default = new Cancellable(); }
public AccountContext(Geary.Account account) {
@@ -625,6 +631,9 @@ public class GearyController : Geary.BaseObject {
}
private void open_account(Geary.Account account) {
+ account.information.authentication_failure.connect(
+ on_authentication_failure
+ );
account.notify["current-status"].connect(
on_account_status_notify
);
@@ -658,6 +667,9 @@ public class GearyController : Geary.BaseObject {
// Stop updating status and showing errors when closing
// the account - the user doesn't care any more
account.report_problem.disconnect(on_report_problem);
+ account.information.authentication_failure.disconnect(
+ on_authentication_failure
+ );
account.notify["current-status"].disconnect(
on_account_status_notify
);
@@ -890,17 +902,98 @@ public class GearyController : Geary.BaseObject {
}
}
- private void update_account_status() {
- Geary.Account.Status effective_status =
- this.accounts.values.fold<Geary.Account.Status>(
- (ctx, status) => ctx.get_effective_status() | status,
- 0
+ private bool is_currently_prompting() {
+ return this.accounts.values.fold<bool>(
+ (ctx, seed) => ctx.authentication_prompting | seed,
+ false
+ );
+ }
+
+ private async void prompt_for_password(AccountContext context,
+ Geary.ServiceInformation service) {
+ Geary.AccountInformation account = context.account.information;
+ bool is_incoming = (service == account.incoming);
+ Geary.Credentials credentials = is_incoming
+ ? account.incoming.credentials
+ : account.get_outgoing_credentials();
+
+ bool handled = true;
+ if (this.account_manager.is_goa_account(account) ||
+ context.authentication_attempts > MAX_AUTH_ATTEMPTS ||
+ credentials == null) {
+ // A managed account has had a problem, we have run out of
+ // authentication attempts, or have been asked for creds
+ // but don't even have a login. So just bail out
+ // immediately and flag the account as needing attention.
+ handled = false;
+ } else {
+ context.authentication_prompting = true;
+ this.application.present();
+ PasswordDialog password_dialog = new PasswordDialog(
+ this.application.get_active_window(),
+ account,
+ service
);
+ if (password_dialog.run()) {
+ service.credentials = service.credentials.copy_with_token(
+ password_dialog.password
+ );
+ service.remember_password = password_dialog.remember_password;
+
+ // The update the credentials for the service that the
+ // credentials actually came from
+ Geary.ServiceInformation creds_service =
+ credentials == account.incoming.credentials
+ ? account.incoming
+ : account.outgoing;
+ SecretMediator libsecret = (SecretMediator) account.mediator;
+ try {
+ yield libsecret.update_token(
+ account, creds_service, context.cancellable
+ );
+ // Update the actual service in the engine though
+ yield this.application.engine.update_account_service(
+ account, service, context.cancellable
+ );
+ } catch (GLib.IOError.CANCELLED err) {
+ // all good
+ } catch (GLib.Error err) {
+ report_problem(
+ new Geary.ServiceProblemReport(
+ Geary.ProblemType.GENERIC_ERROR,
+ account,
+ service,
+ err
+ )
+ );
+ }
+ context.authentication_attempts++;
+ } else {
+ // User cancelled, bail out unconditionally
+ handled = false;
+ }
+ context.authentication_prompting = false;
+ }
+
+ if (!handled) {
+ context.authentication_attempts = 0;
+ context.authentication_failed = true;
+ update_account_status();
+ }
+ }
+
+ private void update_account_status() {
+ Geary.Account.Status effective_status = 0;
+ bool auth_error = false;
+ foreach (AccountContext context in this.accounts.values) {
+ effective_status |= context.get_effective_status();
+ auth_error |= context.authentication_failed;
+ }
foreach (Gtk.Window window in this.application.get_windows()) {
MainWindow? main = window as MainWindow;
if (main != null) {
- main.update_account_status(effective_status);
+ main.update_account_status(effective_status, auth_error);
}
}
}
@@ -999,6 +1092,14 @@ public class GearyController : Geary.BaseObject {
report_problem(problem);
}
+ private void on_authentication_failure(Geary.AccountInformation account,
+ Geary.ServiceInformation service) {
+ AccountContext? context = this.accounts.get(account);
+ if (context != null && !is_currently_prompting()) {
+ this.prompt_for_password.begin(context, service);
+ }
+ }
+
private void on_retry_service_problem(Geary.ClientService.Status type) {
AccountContext? context = Geary.traverse(this.accounts.values)
.first_matching((ctx) => (
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 1fce807d..9d7e3307 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -104,19 +104,30 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
}
/** Updates the window's account status info bars. */
- public void update_account_status(Geary.Account.Status status) {
+ public void update_account_status(Geary.Account.Status status,
+ bool auth_error) {
// Only ever show one at a time. Offline is primary since
// nothing else can happen when offline. Service problems are
// secondary since auth and cert problems can't be resolved
// when the service isn't talking to the server. Auth and cert
// problems are enabled elsewhere, since the controller might
// be already prompting the user about it.
- this.offline_infobar.set_visible(!status.is_online());
- this.service_problem_infobar.set_visible(
- status.is_online() && status.has_service_problem()
- );
- this.auth_problem_infobar.hide();
+ bool show_offline = false;
+ bool show_service = false;
+ bool show_auth = false;
+
+ if (!status.is_online()) {
+ show_offline = true;
+ } else if (status.has_service_problem()) {
+ show_service = true;
+ } else if (auth_error) {
+ show_auth = true;
+ }
+
+ this.offline_infobar.set_visible(show_offline);
+ this.service_problem_infobar.set_visible(show_service);
this.cert_problem_infobar.hide();
+ this.auth_problem_infobar.set_visible(show_auth);
update_infobar_frame();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]