[gdm] Add ability to retry login on failures



commit 378ed9c6423a801f552fdf8ff774f6b2b6b09897
Author: William Jon McCann <jmccann redhat com>
Date:   Mon Jun 21 19:00:27 2010 -0400

    Add ability to retry login on failures
    
    https://bugzilla.gnome.org/show_bug.cgi?id=622326

 daemon/gdm-greeter-server.c                   |    9 ++
 daemon/gdm-greeter-server.h                   |    1 +
 daemon/gdm-simple-slave.c                     |   32 +++++-
 gui/simple-greeter/gdm-greeter-client.c       |   24 ++++
 gui/simple-greeter/gdm-greeter-client.h       |    1 +
 gui/simple-greeter/gdm-greeter-login-window.c |  156 ++++++++++++++++--------
 gui/simple-greeter/gdm-greeter-login-window.h |    1 +
 gui/simple-greeter/gdm-greeter-session.c      |   33 +++++
 8 files changed, 204 insertions(+), 53 deletions(-)
---
diff --git a/daemon/gdm-greeter-server.c b/daemon/gdm-greeter-server.c
index cecce83..b9d3b2e 100644
--- a/daemon/gdm-greeter-server.c
+++ b/daemon/gdm-greeter-server.c
@@ -239,6 +239,13 @@ gdm_greeter_server_problem (GdmGreeterServer *greeter_server,
 }
 
 gboolean
+gdm_greeter_server_authentication_failed (GdmGreeterServer *greeter_server)
+{
+        send_dbus_void_signal (greeter_server, "AuthenticationFailed");
+        return TRUE;
+}
+
+gboolean
 gdm_greeter_server_reset (GdmGreeterServer *greeter_server)
 {
         send_dbus_void_signal (greeter_server, "Reset");
@@ -762,6 +769,8 @@ do_introspect (DBusConnection *connection,
                                "    </signal>\n"
                                "    <signal name=\"Reset\">\n"
                                "    </signal>\n"
+                               "    <signal name=\"AuthenticationFailed\">\n"
+                               "    </signal>\n"
                                "    <signal name=\"UserAuthorized\">\n"
                                "    </signal>\n"
                                "  </interface>\n");
diff --git a/daemon/gdm-greeter-server.h b/daemon/gdm-greeter-server.h
index 6e92100..f9c157f 100644
--- a/daemon/gdm-greeter-server.h
+++ b/daemon/gdm-greeter-server.h
@@ -84,6 +84,7 @@ gboolean            gdm_greeter_server_info                  (GdmGreeterServer *
                                                               const char       *text);
 gboolean            gdm_greeter_server_problem               (GdmGreeterServer *greeter_server,
                                                               const char       *text);
+gboolean            gdm_greeter_server_authentication_failed (GdmGreeterServer *greeter_server);
 gboolean            gdm_greeter_server_reset                 (GdmGreeterServer *greeter_server);
 gboolean            gdm_greeter_server_ready                 (GdmGreeterServer *greeter_server);
 void                gdm_greeter_server_selected_user_changed (GdmGreeterServer *greeter_server,
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 1fb5773..e370ba9 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -204,6 +204,22 @@ greeter_reset_timeout (GdmSimpleSlave *slave)
         return FALSE;
 }
 
+static gboolean
+auth_failed_reset_timeout (GdmSimpleSlave *slave)
+{
+        g_debug ("GdmSimpleSlave: auth failed resetting slave");
+
+        if (slave->priv->greeter_server != NULL) {
+                gdm_greeter_server_authentication_failed (slave->priv->greeter_server);
+                reset_session (slave);
+        } else {
+                start_greeter (slave);
+                create_new_session (slave);
+        }
+        slave->priv->greeter_reset_id = 0;
+        return FALSE;
+}
+
 static void
 queue_greeter_reset (GdmSimpleSlave *slave)
 {
@@ -215,6 +231,17 @@ queue_greeter_reset (GdmSimpleSlave *slave)
 }
 
 static void
+queue_auth_failed_reset (GdmSimpleSlave *slave)
+{
+        /* use the greeter reset idle id so we don't do both at once */
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
+
+        slave->priv->greeter_reset_id = g_idle_add ((GSourceFunc)auth_failed_reset_timeout, slave);
+}
+
+static void
 on_session_setup_complete (GdmSession     *session,
                            GdmSimpleSlave *slave)
 {
@@ -266,8 +293,11 @@ on_session_authentication_failed (GdmSession     *session,
                 gdm_greeter_server_problem (slave->priv->greeter_server,
                                             message != NULL ? message : _("Unable to authenticate user"));
         }
+
         destroy_session (slave);
-        queue_greeter_reset (slave);
+
+        g_debug ("GdmSimpleSlave: Authentication failed - may retry");
+        queue_auth_failed_reset (slave);
 }
 
 static void
diff --git a/gui/simple-greeter/gdm-greeter-client.c b/gui/simple-greeter/gdm-greeter-client.c
index ed4dd95..d02b3d1 100644
--- a/gui/simple-greeter/gdm-greeter-client.c
+++ b/gui/simple-greeter/gdm-greeter-client.c
@@ -65,6 +65,7 @@ enum {
         SECRET_INFO_QUERY,
         READY,
         RESET,
+        AUTHENTICATION_FAILED,
         SELECTED_USER_CHANGED,
         DEFAULT_LANGUAGE_NAME_CHANGED,
         DEFAULT_LAYOUT_NAME_CHANGED,
@@ -255,6 +256,17 @@ on_reset (GdmGreeterClient *client,
                        0);
 }
 
+static void
+on_authentication_failed (GdmGreeterClient *client,
+                          DBusMessage      *message)
+{
+        g_debug ("GdmGreeterClient: Authentication failed");
+
+        g_signal_emit (client,
+                       gdm_greeter_client_signals[AUTHENTICATION_FAILED],
+                       0);
+}
+
 static gboolean
 send_dbus_string_method (DBusConnection *connection,
                          const char     *method,
@@ -645,6 +657,8 @@ client_dbus_handle_message (DBusConnection *connection,
                 on_ready (client, message);
         } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "Reset")) {
                 on_reset (client, message);
+        } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "AuthenticationFailed")) {
+                on_authentication_failed (client, message);
         } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "SelectedUserChanged")) {
                 on_selected_user_changed (client, message);
         } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "DefaultLanguageNameChanged")) {
@@ -893,6 +907,16 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
                               g_cclosure_marshal_VOID__VOID,
                               G_TYPE_NONE,
                               0);
+        gdm_greeter_client_signals[AUTHENTICATION_FAILED] =
+                g_signal_new ("authentication-failed",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmGreeterClientClass, authentication_failed),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE,
+                              0);
         gdm_greeter_client_signals[SELECTED_USER_CHANGED] =
                 g_signal_new ("selected-user-changed",
                               G_OBJECT_CLASS_TYPE (object_class),
diff --git a/gui/simple-greeter/gdm-greeter-client.h b/gui/simple-greeter/gdm-greeter-client.h
index 08deabd..16ccef9 100644
--- a/gui/simple-greeter/gdm-greeter-client.h
+++ b/gui/simple-greeter/gdm-greeter-client.h
@@ -57,6 +57,7 @@ typedef struct
                                           const char        *problem);
         void (* ready)                   (GdmGreeterClient  *client);
         void (* reset)                   (GdmGreeterClient  *client);
+        void (* authentication_failed)   (GdmGreeterClient  *client);
         void (* selected_user_changed)   (GdmGreeterClient  *client,
                                           const char        *username);
 
diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index f4794c8..fc9c7e3 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -546,34 +546,90 @@ switch_mode (GdmGreeterLoginWindow *login_window,
 }
 
 static void
-reset_dialog (GdmGreeterLoginWindow *login_window)
+choose_user (GdmGreeterLoginWindow *login_window,
+             const char            *user_name)
+{
+        g_assert (user_name != NULL);
+
+        g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
+                       0, user_name);
+
+        if (strcmp (user_name, GDM_USER_CHOOSER_USER_OTHER) == 0) {
+                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0);
+        } else if (strcmp (user_name, GDM_USER_CHOOSER_USER_GUEST) == 0) {
+                /* FIXME: handle guest account stuff */
+        } else if (strcmp (user_name, GDM_USER_CHOOSER_USER_AUTO) == 0) {
+                g_signal_emit (login_window, signals[BEGIN_AUTO_LOGIN], 0,
+                               login_window->priv->timed_login_username);
+
+                login_window->priv->timed_login_enabled = TRUE;
+                restart_timed_login_timeout (login_window);
+
+                /* just wait for the user to select language and stuff */
+                set_log_in_button_mode (login_window, LOGIN_BUTTON_TIMED_LOGIN);
+                set_message (login_window, _("Select language and click Log In"));
+        } else {
+                g_signal_emit (login_window, signals[BEGIN_VERIFICATION_FOR_USER], 0, user_name);
+        }
+
+        switch_mode (login_window, MODE_AUTHENTICATION);
+}
+
+static void
+retry_login (GdmGreeterLoginWindow *login_window)
+{
+        GtkWidget  *entry;
+        char       *user_name;
+
+        user_name = gdm_user_chooser_widget_get_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser));
+        if (user_name == NULL) {
+                return;
+        }
+
+        g_debug ("GdmGreeterLoginWindow: Retrying login for %s", user_name);
+
+        entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-entry"));
+        gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
+
+        choose_user (login_window, user_name);
+
+        g_free (user_name);
+}
+
+static void
+reset_dialog (GdmGreeterLoginWindow *login_window,
+              guint                  dialog_mode)
 {
         GtkWidget  *entry;
         GtkWidget  *label;
 
-        g_debug ("GdmGreeterLoginWindow: Resetting dialog");
+        g_debug ("GdmGreeterLoginWindow: Resetting dialog to mode %u", dialog_mode);
         set_busy (login_window);
         set_sensitive (login_window, FALSE);
 
-        login_window->priv->show_cancel_button = FALSE;
+        if (dialog_mode == MODE_SELECTION) {
+                login_window->priv->show_cancel_button = FALSE;
 
-        if (login_window->priv->timed_login_enabled) {
-                gdm_chooser_widget_set_item_timer (GDM_CHOOSER_WIDGET (login_window->priv->user_chooser),
-                                                   GDM_USER_CHOOSER_USER_AUTO, 0);
-                remove_timed_login_timeout (login_window);
-                login_window->priv->timed_login_enabled = FALSE;
-        }
-        _gdm_greeter_login_window_set_interactive (login_window, FALSE);
+                if (login_window->priv->timed_login_enabled) {
+                        gdm_chooser_widget_set_item_timer (GDM_CHOOSER_WIDGET (login_window->priv->user_chooser),
+                                                           GDM_USER_CHOOSER_USER_AUTO, 0);
+                        remove_timed_login_timeout (login_window);
+                        login_window->priv->timed_login_enabled = FALSE;
+                }
+                _gdm_greeter_login_window_set_interactive (login_window, FALSE);
 
-        g_signal_handlers_block_by_func (G_OBJECT (login_window->priv->user_chooser),
-                                         G_CALLBACK (on_user_unchosen), login_window);
-        gdm_user_chooser_widget_set_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser), NULL);
-        g_signal_handlers_unblock_by_func (G_OBJECT (login_window->priv->user_chooser),
-                                           G_CALLBACK (on_user_unchosen), login_window);
+                g_signal_handlers_block_by_func (G_OBJECT (login_window->priv->user_chooser),
+                                                 G_CALLBACK (on_user_unchosen), login_window);
+                gdm_user_chooser_widget_set_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser), NULL);
+                g_signal_handlers_unblock_by_func (G_OBJECT (login_window->priv->user_chooser),
+                                                   G_CALLBACK (on_user_unchosen), login_window);
 
-        if (login_window->priv->start_session_handler_id > 0) {
-                g_signal_handler_disconnect (login_window, login_window->priv->start_session_handler_id);
-                login_window->priv->start_session_handler_id = 0;
+                if (login_window->priv->start_session_handler_id > 0) {
+                        g_signal_handler_disconnect (login_window, login_window->priv->start_session_handler_id);
+                        login_window->priv->start_session_handler_id = 0;
+                }
+
+                set_message (login_window, "");
         }
 
         entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-entry"));
@@ -581,7 +637,6 @@ reset_dialog (GdmGreeterLoginWindow *login_window)
         gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
 
         gtk_entry_set_visibility (GTK_ENTRY (entry), TRUE);
-        set_message (login_window, "");
 
         label = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-label"));
         gtk_label_set_text (GTK_LABEL (label), "");
@@ -589,7 +644,7 @@ reset_dialog (GdmGreeterLoginWindow *login_window)
         if (login_window->priv->user_list_disabled) {
                 switch_mode (login_window, MODE_AUTHENTICATION);
         } else {
-                switch_mode (login_window, MODE_SELECTION);
+                switch_mode (login_window, dialog_mode);
         }
 
         set_sensitive (login_window, TRUE);
@@ -597,6 +652,7 @@ reset_dialog (GdmGreeterLoginWindow *login_window)
         set_focus (GDM_GREETER_LOGIN_WINDOW (login_window));
         update_banner_message (login_window);
         adjust_other_login_visibility (login_window);
+
         if (gdm_chooser_widget_get_number_of_items (GDM_CHOOSER_WIDGET (login_window->priv->user_chooser)) >= 1) {
                 gdm_chooser_widget_propagate_pending_key_events (GDM_CHOOSER_WIDGET (login_window->priv->user_chooser));
         }
@@ -617,29 +673,46 @@ gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window)
 {
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
-        reset_dialog (login_window);
-
         set_sensitive (GDM_GREETER_LOGIN_WINDOW (login_window), TRUE);
         set_ready (GDM_GREETER_LOGIN_WINDOW (login_window));
         set_focus (GDM_GREETER_LOGIN_WINDOW (login_window));
 
-        /* If the user list is disabled, then start the PAM conversation */
-        if (login_window->priv->user_list_disabled) {
-                g_debug ("Starting PAM conversation since user list disabled");
-                g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
-                               0, GDM_USER_CHOOSER_USER_OTHER);
-                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0);
+        /* If we are retrying a previously selected user */
+        if (login_window->priv->dialog_mode != MODE_SELECTION) {
+                retry_login (login_window);
+        } else {
+                /* If the user list is disabled, then start the PAM conversation */
+                if (login_window->priv->user_list_disabled) {
+                        g_debug ("Starting PAM conversation since user list disabled");
+                        g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
+                                       0, GDM_USER_CHOOSER_USER_OTHER);
+                        g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0);
+                }
         }
 
         return TRUE;
 }
 
 gboolean
+gdm_greeter_login_window_authentication_failed (GdmGreeterLoginWindow *login_window)
+{
+        g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
+
+        g_debug ("GdmGreeterLoginWindow: got authentication failed");
+
+        /* FIXME: shake? */
+        reset_dialog (login_window, MODE_AUTHENTICATION);
+
+        return TRUE;
+}
+
+gboolean
 gdm_greeter_login_window_reset (GdmGreeterLoginWindow *login_window)
 {
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
-        reset_dialog (GDM_GREETER_LOGIN_WINDOW (login_window));
+        g_debug ("GdmGreeterLoginWindow: got reset");
+        reset_dialog (login_window, MODE_SELECTION);
 
         return TRUE;
 }
@@ -675,7 +748,7 @@ static void
 handle_request_timed_login (GdmGreeterLoginWindow *login_window)
 {
         if (login_window->priv->dialog_mode != MODE_SELECTION) {
-                reset_dialog (login_window);
+                reset_dialog (login_window, MODE_SELECTION);
         }
         gdm_user_chooser_widget_set_show_user_auto (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser), TRUE);
 
@@ -939,28 +1012,7 @@ on_user_chosen (GdmUserChooserWidget  *user_chooser,
                 return;
         }
 
-        g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
-                       0, user_name);
-
-        if (strcmp (user_name, GDM_USER_CHOOSER_USER_OTHER) == 0) {
-                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0);
-        } else if (strcmp (user_name, GDM_USER_CHOOSER_USER_GUEST) == 0) {
-                /* FIXME: handle guest account stuff */
-        } else if (strcmp (user_name, GDM_USER_CHOOSER_USER_AUTO) == 0) {
-                g_signal_emit (login_window, signals[BEGIN_AUTO_LOGIN], 0,
-                               login_window->priv->timed_login_username);
-
-                login_window->priv->timed_login_enabled = TRUE;
-                restart_timed_login_timeout (login_window);
-
-                /* just wait for the user to select language and stuff */
-                set_log_in_button_mode (login_window, LOGIN_BUTTON_TIMED_LOGIN);
-                set_message (login_window, _("Select language and click Log In"));
-        } else {
-                g_signal_emit (login_window, signals[BEGIN_VERIFICATION_FOR_USER], 0, user_name);
-        }
-
-        switch_mode (login_window, MODE_AUTHENTICATION);
+        choose_user (login_window, user_name);
 
         g_free (user_name);
 }
diff --git a/gui/simple-greeter/gdm-greeter-login-window.h b/gui/simple-greeter/gdm-greeter-login-window.h
index 817d0a2..504b075 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.h
+++ b/gui/simple-greeter/gdm-greeter-login-window.h
@@ -66,6 +66,7 @@ GtkWidget *         gdm_greeter_login_window_new                (gboolean displa
 
 
 gboolean            gdm_greeter_login_window_reset              (GdmGreeterLoginWindow *login_window);
+gboolean            gdm_greeter_login_window_authentication_failed (GdmGreeterLoginWindow *login_window);
 gboolean            gdm_greeter_login_window_ready              (GdmGreeterLoginWindow *login_window);
 gboolean            gdm_greeter_login_window_info_query         (GdmGreeterLoginWindow *login_window,
                                                                  const char *text);
diff --git a/gui/simple-greeter/gdm-greeter-session.c b/gui/simple-greeter/gdm-greeter-session.c
index 41336c6..7e5da1d 100644
--- a/gui/simple-greeter/gdm-greeter-session.c
+++ b/gui/simple-greeter/gdm-greeter-session.c
@@ -43,12 +43,16 @@
 
 #define GDM_GREETER_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER_SESSION, GdmGreeterSessionPrivate))
 
+#define MAX_LOGIN_TRIES 3
+
 struct GdmGreeterSessionPrivate
 {
         GdmGreeterClient      *client;
 
         GtkWidget             *login_window;
         GtkWidget             *panel;
+
+        guint                  num_tries;
 };
 
 enum {
@@ -98,11 +102,36 @@ on_reset (GdmGreeterClient  *client,
 {
         g_debug ("GdmGreeterSession: Reset");
 
+        session->priv->num_tries = 0;
+
         gdm_greeter_panel_reset (GDM_GREETER_PANEL (session->priv->panel));
         gdm_greeter_login_window_reset (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
 }
 
 static void
+on_authentication_failed (GdmGreeterClient  *client,
+                          GdmGreeterSession *session)
+{
+        g_debug ("GdmGreeterSession: Authentication failed");
+
+        session->priv->num_tries++;
+
+        if (session->priv->num_tries < MAX_LOGIN_TRIES) {
+                g_debug ("GdmGreeterSession: Retrying login (%d)",
+                         session->priv->num_tries);
+
+                gdm_greeter_login_window_authentication_failed (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
+        } else {
+                g_debug ("GdmGreeterSession: Maximum number of login tries exceeded (%d) - resetting",
+                         session->priv->num_tries - 1);
+                session->priv->num_tries = 0;
+
+                gdm_greeter_panel_reset (GDM_GREETER_PANEL (session->priv->panel));
+                gdm_greeter_login_window_reset (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
+        }
+}
+
+static void
 show_or_hide_user_options (GdmGreeterSession *session,
                            const char        *username)
 {
@@ -569,6 +598,10 @@ gdm_greeter_session_init (GdmGreeterSession *session)
                           G_CALLBACK (on_reset),
                           session);
         g_signal_connect (session->priv->client,
+                          "authentication-failed",
+                          G_CALLBACK (on_authentication_failed),
+                          session);
+        g_signal_connect (session->priv->client,
                           "selected-user-changed",
                           G_CALLBACK (on_selected_user_changed),
                           session);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]