[gnome-initial-setup] Improve keyring handling



commit dd0e394b44092035a7d8a5874c663156e9badae8
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Mar 2 02:06:59 2014 -0500

    Improve keyring handling
    
    We're having the problem again where the keyring dialog
    pops up when goa tries to save the gmail credentials.
    To fix this, ensure that a keyring is present before
    we get to the goa page, and prevent keyring dialogs
    from appearing by installing a no-op prompter for
    gnome-keyring.

 configure.ac                              |    2 +
 gnome-initial-setup/Makefile.am           |    6 +-
 gnome-initial-setup/gis-keyring.c         |  127 ++++++++++++
 gnome-initial-setup/gis-keyring.h         |   34 +++
 gnome-initial-setup/gis-prompt.c          |  313 +++++++++++++++++++++++++++++
 gnome-initial-setup/gis-prompt.h          |   51 +++++
 gnome-initial-setup/gnome-initial-setup.c |    2 +
 gnome-initial-setup/gnome-initial-setup.h |    1 +
 8 files changed, 535 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 38050a5..6fa9659 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,8 @@ PKG_CHECK_MODULES(INITIAL_SETUP,
                   pango >= $PANGO_REQUIRED_VERSION
                   rest-0.7
                   json-glib-1.0
+                 libsecret-1
+                 gcr-3
                   pwquality)
 
 PKG_CHECK_MODULES(CHEESE,
diff --git a/gnome-initial-setup/Makefile.am b/gnome-initial-setup/Makefile.am
index 8bcd46d..acb80a2 100644
--- a/gnome-initial-setup/Makefile.am
+++ b/gnome-initial-setup/Makefile.am
@@ -6,6 +6,8 @@ uidir = $(datadir)/gnome-initial-setup
 
 AM_CPPFLAGS = \
        $(INITIAL_SETUP_CFLAGS) \
+       -DSECRET_API_SUBJECT_TO_CHANGE \
+       -DGCR_API_SUBJECT_TO_CHANGE \
        -DUIDIR="\"$(uidir)\"" \
        -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
        -DLIBLOCALEDIR=\""$(prefix)/lib/locale"\" \
@@ -25,7 +27,9 @@ gnome_initial_setup_SOURCES = \
        gnome-initial-setup.c gnome-initial-setup.h \
        gis-assistant.c gis-assistant.h \
        gis-page.c gis-page.h \
-       gis-driver.c gis-driver.h
+       gis-driver.c gis-driver.h \
+       gis-keyring.c gis-keyring.h \
+       gis-prompt.c gis-prompt.h
 
 gnome_initial_setup_LDADD =    \
        pages/language/libgislanguage.la \
diff --git a/gnome-initial-setup/gis-keyring.c b/gnome-initial-setup/gis-keyring.c
new file mode 100644
index 0000000..a7de651
--- /dev/null
+++ b/gnome-initial-setup/gis-keyring.c
@@ -0,0 +1,127 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2014 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ *     Matthias Clasen <mclasen redhat com>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gis-keyring.h"
+
+#include <libsecret/secret.h>
+#include <gcr/gcr.h>
+
+#include "gis-prompt.h"
+
+/* We never want to see a keyring dialog, but we need to make
+ * sure a keyring is present.
+ *
+ * To achieve this, install a prompter for gnome-keyring that
+ * never shows any UI, and create a keyring, if one does not
+ * exist yet.
+ */
+
+#define GCR_DBUS_PROMPTER_SYSTEM_BUS_NAME "org.gnome.keyring.SystemPrompter"
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const gchar     *name,
+                 gpointer         user_data)
+{
+  GcrSystemPrompter *prompter;
+
+  prompter = gcr_system_prompter_new (GCR_SYSTEM_PROMPTER_SINGLE, GIS_TYPE_PROMPT);
+  gcr_system_prompter_register (prompter, connection);
+}
+
+static void
+created_collection (GObject      *source,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  SecretCollection *collection;
+  GError *error = NULL;
+
+  collection = secret_collection_create_finish (result, &error);
+  if (collection)
+    {
+      g_debug ("Created keyring '%s', %s\n",
+               secret_collection_get_label (collection),
+               secret_collection_get_locked (collection) ? "locked" : "unlocked");
+      g_object_unref (collection);
+    }
+  else
+    {
+      g_warning ("Failed to create keyring: %s\n", error->message);
+      g_error_free (error);
+    }
+}
+
+static void
+got_alias (GObject      *source,
+           GAsyncResult *result,
+           gpointer      user_data)
+{
+  SecretCollection *collection;
+
+  collection = secret_collection_for_alias_finish (result, NULL);
+  if (collection)
+    {
+      g_debug ("Found default keyring '%s', %s\n",
+               secret_collection_get_label (collection),
+               secret_collection_get_locked (collection) ? "locked" : "unlocked");
+      g_object_unref (collection);
+    }
+  else
+    {
+      secret_collection_create (NULL, "login", SECRET_COLLECTION_DEFAULT, 0, NULL, created_collection, NULL);
+    }
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar     *name,
+                  gpointer         user_data)
+{
+  g_debug ("Got " GCR_DBUS_PROMPTER_SYSTEM_BUS_NAME "\n");
+
+  secret_collection_for_alias (NULL, SECRET_COLLECTION_DEFAULT, SECRET_COLLECTION_NONE, NULL, got_alias, 
NULL);
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar     *name,
+              gpointer         user_data)
+{
+  g_debug ("Lost " GCR_DBUS_PROMPTER_SYSTEM_BUS_NAME "\n");
+}
+
+void
+gis_ensure_keyring (void)
+{
+  g_bus_own_name (G_BUS_TYPE_SESSION,
+                  GCR_DBUS_PROMPTER_SYSTEM_BUS_NAME,
+                  G_BUS_NAME_OWNER_FLAGS_REPLACE,
+                  on_bus_acquired,
+                  on_name_acquired,
+                  on_name_lost,
+                  NULL, NULL);
+}
+
diff --git a/gnome-initial-setup/gis-keyring.h b/gnome-initial-setup/gis-keyring.h
new file mode 100644
index 0000000..607417f
--- /dev/null
+++ b/gnome-initial-setup/gis-keyring.h
@@ -0,0 +1,34 @@
+
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2014 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ *     Matthias Clasen <mclasen redhat com>
+ */
+
+#ifndef __GIS_KEYRING_H__
+#define __GIS_KEYRING_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void   gis_ensure_keyring      (void);
+
+G_END_DECLS
+
+#endif /* __GIS_KEYRING_H__ */
diff --git a/gnome-initial-setup/gis-prompt.c b/gnome-initial-setup/gis-prompt.c
new file mode 100644
index 0000000..d4d5159
--- /dev/null
+++ b/gnome-initial-setup/gis-prompt.c
@@ -0,0 +1,313 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2014 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ *     Matthias Clasen <mclasen redhat com>
+ */
+
+#include "config.h"
+
+#include "gis-prompt.h"
+
+#include <gcr/gcr.h>
+
+/* This code is inspired by GcrMockPrompt - we never show any UI,
+ * and just return TRUE for confirmations and "gis" for passwords.
+ */
+
+enum {
+        PROP_0,
+
+        PROP_TITLE,
+        PROP_MESSAGE,
+        PROP_DESCRIPTION,
+        PROP_WARNING,
+        PROP_PASSWORD_NEW,
+        PROP_PASSWORD_STRENGTH,
+        PROP_CHOICE_LABEL,
+        PROP_CHOICE_CHOSEN,
+        PROP_CALLER_WINDOW,
+        PROP_CONTINUE_LABEL,
+        PROP_CANCEL_LABEL
+};
+
+static void    gis_prompt_iface     (GcrPromptIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GisPrompt, gis_prompt, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT, gis_prompt_iface))
+
+static void
+property_free (gpointer data)
+{
+        GParameter *param = data;
+        g_value_unset (&param->value);
+        g_free (param);
+}
+
+static void
+blank_string_property (GHashTable *properties,
+                       const gchar *property)
+{
+        GParameter *param;
+
+        param = g_new0 (GParameter, 1);
+        param->name = property;
+        g_value_init (&param->value, G_TYPE_STRING);
+        g_value_set_string (&param->value, "");
+        g_hash_table_insert (properties, (gpointer)param->name, param);
+}
+
+static void
+blank_boolean_property (GHashTable *properties,
+                        const gchar *property)
+{
+        GParameter *param;
+
+        param = g_new0 (GParameter, 1);
+        param->name = property;
+        g_value_init (&param->value, G_TYPE_BOOLEAN);
+        g_value_set_boolean (&param->value, FALSE);
+        g_hash_table_insert (properties, (gpointer)param->name, param);
+}
+
+static void
+blank_int_property (GHashTable *properties,
+                    const gchar *property)
+{
+        GParameter *param;
+
+        param = g_new0 (GParameter, 1);
+        param->name = property;
+        g_value_init (&param->value, G_TYPE_INT);
+        g_value_set_int (&param->value, 0);
+        g_hash_table_insert (properties, (gpointer)param->name, param);
+}
+
+static void
+gis_prompt_init (GisPrompt *self)
+{
+        self->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                  NULL, property_free);
+
+        blank_string_property (self->properties, "title");
+        blank_string_property (self->properties, "message");
+        blank_string_property (self->properties, "description");
+        blank_string_property (self->properties, "warning");
+        blank_string_property (self->properties, "choice-label");
+        blank_string_property (self->properties, "caller-window");
+        blank_string_property (self->properties, "continue-label");
+        blank_string_property (self->properties, "cancel-label");
+
+        blank_boolean_property (self->properties, "choice-chosen");
+        blank_boolean_property (self->properties, "password-new");
+
+        blank_int_property (self->properties, "password-strength");
+}
+
+static void
+gis_prompt_set_property (GObject *obj,
+                         guint prop_id,
+                         const GValue *value,
+                         GParamSpec *pspec)
+{
+        GisPrompt *self = GIS_PROMPT (obj);
+        GParameter *param;
+
+        switch (prop_id) {
+        case PROP_TITLE:
+        case PROP_MESSAGE:
+        case PROP_DESCRIPTION:
+        case PROP_WARNING:
+        case PROP_PASSWORD_NEW:
+        case PROP_CHOICE_LABEL:
+        case PROP_CHOICE_CHOSEN:
+        case PROP_CALLER_WINDOW:
+        case PROP_CONTINUE_LABEL:
+        case PROP_CANCEL_LABEL:
+                param = g_new0 (GParameter, 1);
+                param->name = pspec->name;
+                g_value_init (&param->value, pspec->value_type);
+                g_value_copy (value, &param->value);
+                g_hash_table_replace (self->properties, (gpointer)param->name, param);
+                g_object_notify (G_OBJECT (self), param->name);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gis_prompt_get_property (GObject *obj,
+                         guint prop_id,
+                         GValue *value,
+                         GParamSpec *pspec)
+{
+        GisPrompt *self = GIS_PROMPT (obj);
+        GParameter *param;
+
+        switch (prop_id) {
+        case PROP_TITLE:
+        case PROP_MESSAGE:
+        case PROP_DESCRIPTION:
+        case PROP_WARNING:
+        case PROP_PASSWORD_NEW:
+        case PROP_PASSWORD_STRENGTH:
+        case PROP_CHOICE_LABEL:
+        case PROP_CHOICE_CHOSEN:
+        case PROP_CALLER_WINDOW:
+        case PROP_CONTINUE_LABEL:
+        case PROP_CANCEL_LABEL:
+                param = g_hash_table_lookup (self->properties, pspec->name);
+                g_return_if_fail (param != NULL);
+                g_value_copy (&param->value, value);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gis_prompt_finalize (GObject *obj)
+{
+        GisPrompt *self = GIS_PROMPT (obj);
+
+        g_hash_table_destroy (self->properties);
+
+        G_OBJECT_CLASS (gis_prompt_parent_class)->finalize (obj);
+}
+
+static void
+gis_prompt_class_init (GisPromptClass *klass)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+        gobject_class->get_property = gis_prompt_get_property;
+        gobject_class->set_property = gis_prompt_set_property;
+        gobject_class->finalize = gis_prompt_finalize;
+
+        g_object_class_override_property (gobject_class, PROP_TITLE, "title");
+        g_object_class_override_property (gobject_class, PROP_MESSAGE, "message");
+        g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description");
+        g_object_class_override_property (gobject_class, PROP_WARNING, "warning");
+        g_object_class_override_property (gobject_class, PROP_CALLER_WINDOW, "caller-window");
+        g_object_class_override_property (gobject_class, PROP_CHOICE_LABEL, "choice-label");
+        g_object_class_override_property (gobject_class, PROP_CHOICE_CHOSEN, "choice-chosen");
+        g_object_class_override_property (gobject_class, PROP_PASSWORD_NEW, "password-new");
+        g_object_class_override_property (gobject_class, PROP_PASSWORD_STRENGTH, "password-strength");
+        g_object_class_override_property (gobject_class, PROP_CONTINUE_LABEL, "continue-label");
+        g_object_class_override_property (gobject_class, PROP_CANCEL_LABEL, "cancel-label");
+}
+
+static gboolean
+on_timeout_complete (gpointer data)
+{
+        GSimpleAsyncResult *res = data;
+        g_simple_async_result_complete (res);
+        return FALSE;
+}
+
+static void
+destroy_unref_source (gpointer source)
+{
+        if (!g_source_is_destroyed (source))
+                g_source_destroy (source);
+        g_source_unref (source);
+}
+
+static void
+gis_prompt_confirm_async (GcrPrompt *prompt,
+                          GCancellable *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+        GisPrompt *self = GIS_PROMPT (prompt);
+        GSourceFunc complete_func = on_timeout_complete;
+        GSimpleAsyncResult *res;
+        GSource *source;
+        guint delay_msec;
+
+        res = g_simple_async_result_new (G_OBJECT (prompt), callback, user_data,
+                                         gis_prompt_confirm_async);
+        g_simple_async_result_set_op_res_gboolean (res, TRUE);
+
+        source = g_idle_source_new ();
+        g_source_set_callback (source, on_timeout_complete, g_object_ref (res), g_object_unref);
+        g_source_attach (source, NULL);
+        g_object_set_data_full (G_OBJECT (self), "delay-source", source, destroy_unref_source);
+
+        g_object_unref (res);
+}
+
+static GcrPromptReply
+gis_prompt_confirm_finish (GcrPrompt *prompt,
+                           GAsyncResult *result,
+                           GError **error)
+{
+        g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
+                              gis_prompt_confirm_async), GCR_PROMPT_REPLY_CANCEL);
+
+        return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)) ?
+                       GCR_PROMPT_REPLY_CONTINUE : GCR_PROMPT_REPLY_CANCEL;
+}
+
+
+static void
+gis_prompt_password_async (GcrPrompt *prompt,
+                           GCancellable *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
+{
+        GisPrompt *self = GIS_PROMPT (prompt);
+        GSourceFunc complete_func = on_timeout_complete;
+        GSimpleAsyncResult *res;
+        GSource *source;
+        guint delay_msec;
+
+        res = g_simple_async_result_new (G_OBJECT (prompt), callback, user_data,
+                                         gis_prompt_password_async);
+
+        g_simple_async_result_set_op_res_gpointer (res, "gis", NULL);
+
+        source = g_idle_source_new ();
+        g_source_set_callback (source, on_timeout_complete, g_object_ref (res), g_object_unref);
+        g_source_attach (source, NULL);
+        g_object_set_data_full (G_OBJECT (self), "delay-source", source, destroy_unref_source);
+
+        g_object_unref (res);
+}
+
+static const gchar *
+gis_prompt_password_finish (GcrPrompt *prompt,
+                            GAsyncResult *result,
+                            GError **error)
+{
+        g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
+                              gis_prompt_password_async), NULL);
+
+        return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+static void
+gis_prompt_iface (GcrPromptIface *iface)
+{
+        iface->prompt_confirm_async = gis_prompt_confirm_async;
+        iface->prompt_confirm_finish = gis_prompt_confirm_finish;
+        iface->prompt_password_async = gis_prompt_password_async;
+        iface->prompt_password_finish = gis_prompt_password_finish;
+}
diff --git a/gnome-initial-setup/gis-prompt.h b/gnome-initial-setup/gis-prompt.h
new file mode 100644
index 0000000..92353ac
--- /dev/null
+++ b/gnome-initial-setup/gis-prompt.h
@@ -0,0 +1,51 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2014 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ *     Matthias Clasen <mclasen redhat com>
+ */
+
+#ifndef __GIS_PROMPT_H__
+#define __GIS_PROMPT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+GType   gis_prompt_get_type       (void) G_GNUC_CONST;
+#define GIS_TYPE_PROMPT            (gis_prompt_get_type ())
+#define GIS_PROMPT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_PROMPT, GisPrompt))
+#define GIS_IS_PROMPT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_PROMPT))
+#define GIS_IS_PROMPT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_PROMPT))
+#define GIS_PROMPT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_PROMPT, GisPromptClass))
+#define GIS_PROMPT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_PROMPT, GisPromptClass))
+
+typedef struct _GisPrompt GisPrompt;
+typedef struct _GisPromptClass GisPromptClass;
+
+struct _GisPrompt {
+       GObject parent;
+       GHashTable *properties;
+};
+
+struct _GisPromptClass {
+       GObjectClass parent_class;
+};
+
+G_END_DECLS
+
+#endif /* __GIS_PROMPT_H__ */
diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c
index 32eee26..b159c9c 100644
--- a/gnome-initial-setup/gnome-initial-setup.c
+++ b/gnome-initial-setup/gnome-initial-setup.c
@@ -217,6 +217,8 @@ main (int argc, char *argv[])
   }
 #endif
 
+  gis_ensure_keyring ();
+
   driver = gis_driver_new (get_mode ());
   g_signal_connect (driver, "rebuild-pages", G_CALLBACK (rebuild_pages_cb), NULL);
   status = g_application_run (G_APPLICATION (driver), argc, argv);
diff --git a/gnome-initial-setup/gnome-initial-setup.h b/gnome-initial-setup/gnome-initial-setup.h
index ebfe6c6..6dce853 100644
--- a/gnome-initial-setup/gnome-initial-setup.h
+++ b/gnome-initial-setup/gnome-initial-setup.h
@@ -33,6 +33,7 @@ typedef struct _GisPage      GisPage;
 #include "gis-driver.h"
 #include "gis-assistant.h"
 #include "gis-page.h"
+#include "gis-keyring.h"
 
 #endif /* __GNOME_INITIAL_SETUP_H__ */
 


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