[gnome-online-accounts] google: Offer a PasswordBased interface



commit 89c335479c1bb8409af8296c99ffea602a28b71f
Author: Debarshi Ray <debarshir gnome org>
Date:   Sun Nov 4 21:29:22 2012 +0100

    google: Offer a PasswordBased interface
    
    We are not sure when, or if at all, Google will support OAuth2 for
    CalDAV. Instead of spoiling the user experience by asking for the
    password again, lets offer applications a way to fetch it without
    bothering the user.
    
    Fixes: https://bugzilla.gnome.org/687578

 src/goabackend/goagoogleprovider.c |  210 ++++++++++++++++++++++++++++++++++++
 1 files changed, 210 insertions(+), 0 deletions(-)
---
diff --git a/src/goabackend/goagoogleprovider.c b/src/goabackend/goagoogleprovider.c
index e36e2e3..ec66fc5 100644
--- a/src/goabackend/goagoogleprovider.c
+++ b/src/goabackend/goagoogleprovider.c
@@ -30,6 +30,8 @@
 #include "goaprovider.h"
 #include "goaoauth2provider.h"
 #include "goagoogleprovider.h"
+#include "goahttpclient.h"
+#include "goautils.h"
 
 /**
  * GoaGoogleProvider:
@@ -66,6 +68,8 @@ G_DEFINE_TYPE_WITH_CODE (GoaGoogleProvider, goa_google_provider, GOA_TYPE_OAUTH2
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static const gchar *CALDAV_ENDPOINT = "https://www.google.com/calendar/dav/%s/events/";;
+
 static const gchar *
 get_provider_type (GoaProvider *_provider)
 {
@@ -285,8 +289,48 @@ is_identity_node (GoaOAuth2Provider *provider, WebKitDOMHTMLInputElement *elemen
   return ret;
 }
 
+static gboolean
+is_password_node (GoaOAuth2Provider *provider, WebKitDOMHTMLInputElement *element)
+{
+  gboolean ret;
+  gchar *element_type;
+  gchar *id;
+  gchar *name;
+
+  element_type = NULL;
+  id = NULL;
+  name = NULL;
+
+  ret = FALSE;
+
+  g_object_get (element, "type", &element_type, NULL);
+  if (g_strcmp0 (element_type, "password") != 0)
+    goto out;
+
+  id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (element));
+  if (g_strcmp0 (id, "Passwd") != 0)
+    goto out;
+
+  name = webkit_dom_html_input_element_get_name (element);
+  if (g_strcmp0 (name, "Passwd") != 0)
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  g_free (element_type);
+  g_free (id);
+  g_free (name);
+  return ret;
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
+static gboolean on_handle_get_password (GoaPasswordBased      *interface,
+                                        GDBusMethodInvocation *invocation,
+                                        const gchar           *id,
+                                        gpointer               user_data);
+
 static gboolean
 build_object (GoaProvider         *provider,
               GoaObjectSkeleton   *object,
@@ -302,6 +346,7 @@ build_object (GoaProvider         *provider,
   GoaContacts *contacts;
   GoaChat *chat;
   GoaDocuments *documents;
+  GoaPasswordBased *password_based;
   gboolean ret;
   gboolean mail_enabled;
   gboolean calendar_enabled;
@@ -327,6 +372,20 @@ build_object (GoaProvider         *provider,
                                                                             error))
     goto out;
 
+  password_based = goa_object_get_password_based (GOA_OBJECT (object));
+  if (password_based == NULL)
+    {
+      password_based = goa_password_based_skeleton_new ();
+      /* Ensure D-Bus method invocations run in their own thread */
+      g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (password_based),
+                                           G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
+      goa_object_skeleton_set_password_based (object, password_based);
+      g_signal_connect (password_based,
+                        "handle-get-password",
+                        G_CALLBACK (on_handle_get_password),
+                        NULL);
+    }
+
   account = goa_object_get_account (GOA_OBJECT (object));
 
   /* Email */
@@ -479,6 +538,102 @@ build_object (GoaProvider         *provider,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static gboolean
+ensure_credentials_sync (GoaProvider   *provider,
+                         GoaObject     *object,
+                         gint          *out_expires_in,
+                         GCancellable  *cancellable,
+                         GError       **error)
+{
+  GVariant *credentials;
+  GoaAccount *account;
+  GoaHttpClient *http_client;
+  gboolean ret;
+  const gchar *username;
+  gchar *password;
+  gchar *uri_caldav;
+
+  credentials = NULL;
+  http_client = NULL;
+  password = NULL;
+  uri_caldav = NULL;
+
+  ret = FALSE;
+
+  /* Chain up */
+  if (!GOA_PROVIDER_CLASS (goa_google_provider_parent_class)->ensure_credentials_sync (provider,
+                                                                                       object,
+                                                                                       out_expires_in,
+                                                                                       cancellable,
+                                                                                       error))
+    goto out;
+
+  credentials = goa_utils_lookup_credentials_sync (provider,
+                                                   object,
+                                                   cancellable,
+                                                   error);
+  if (credentials == NULL)
+    {
+      if (error != NULL)
+        {
+          g_prefix_error (error,
+                          _("Credentials not found in keyring (%s, %d): "),
+                          g_quark_to_string ((*error)->domain),
+                          (*error)->code);
+          (*error)->domain = GOA_ERROR;
+          (*error)->code = GOA_ERROR_NOT_AUTHORIZED;
+        }
+      goto out;
+    }
+
+  account = goa_object_peek_account (object);
+  username = goa_account_get_presentation_identity (account);
+  uri_caldav = g_strdup_printf (CALDAV_ENDPOINT, username);
+
+  if (!g_variant_lookup (credentials, "password", "s", &password))
+    {
+      if (error != NULL)
+        {
+          *error = g_error_new (GOA_ERROR,
+                                GOA_ERROR_NOT_AUTHORIZED,
+                                _("Did not find password with username `%s' in credentials"),
+                                username);
+        }
+      goto out;
+    }
+
+  http_client = goa_http_client_new ();
+  ret = goa_http_client_check_sync (http_client,
+                                    uri_caldav,
+                                    username,
+                                    password,
+                                    cancellable,
+                                    error);
+  if (!ret)
+    {
+      if (error != NULL)
+        {
+          g_prefix_error (error,
+                          _("Invalid password with username `%s' (%s, %d): "),
+                          username,
+                          g_quark_to_string ((*error)->domain),
+                          (*error)->code);
+          (*error)->domain = GOA_ERROR;
+          (*error)->code = GOA_ERROR_NOT_AUTHORIZED;
+        }
+      goto out;
+    }
+
+ out:
+  g_clear_object (&http_client);
+  g_free (password);
+  g_free (uri_caldav);
+  g_clear_pointer (&credentials, (GDestroyNotify) g_variant_unref);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
 get_use_mobile_browser (GoaOAuth2Provider *provider)
 {
   return TRUE;
@@ -558,6 +713,7 @@ goa_google_provider_class_init (GoaGoogleProviderClass *klass)
   provider_class->get_provider_type          = get_provider_type;
   provider_class->get_provider_name          = get_provider_name;
   provider_class->build_object               = build_object;
+  provider_class->ensure_credentials_sync    = ensure_credentials_sync;
   provider_class->show_account               = show_account;
   provider_class->get_credentials_generation = get_credentials_generation;
 
@@ -571,9 +727,63 @@ goa_google_provider_class_init (GoaGoogleProviderClass *klass)
   oauth2_class->get_scope                 = get_scope;
   oauth2_class->is_deny_node              = is_deny_node;
   oauth2_class->is_identity_node          = is_identity_node;
+  oauth2_class->is_password_node          = is_password_node;
   oauth2_class->get_token_uri             = get_token_uri;
   oauth2_class->get_use_mobile_browser    = get_use_mobile_browser;
   oauth2_class->add_account_key_values    = add_account_key_values;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
+
+/* runs in a thread dedicated to handling @invocation */
+static gboolean
+on_handle_get_password (GoaPasswordBased      *interface,
+                        GDBusMethodInvocation *invocation,
+                        const gchar           *id,
+                        gpointer               user_data)
+{
+  GoaObject *object;
+  GoaAccount *account;
+  GoaProvider *provider;
+  GError *error;
+  GVariant *credentials;
+  gchar *password;
+
+  /* TODO: maybe log what app is requesting access */
+
+  password = NULL;
+  credentials = NULL;
+
+  object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (interface)));
+  account = goa_object_peek_account (object);
+  provider = goa_provider_get_for_provider_type (goa_account_get_provider_type (account));
+
+  error = NULL;
+  credentials = goa_utils_lookup_credentials_sync (provider,
+                                                   object,
+                                                   NULL, /* GCancellable* */
+                                                   &error);
+  if (credentials == NULL)
+    {
+      g_dbus_method_invocation_take_error (invocation, error);
+      goto out;
+    }
+
+  if (!g_variant_lookup (credentials, "password", "s", &password))
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             GOA_ERROR,
+                                             GOA_ERROR_FAILED, /* TODO: more specific */
+                                             _("Did not find password with username `%s' in credentials"),
+                                             id);
+      goto out;
+    }
+
+  goa_password_based_complete_get_password (interface, invocation, password);
+
+ out:
+  g_free (password);
+  g_clear_pointer (&credentials, (GDestroyNotify) g_variant_unref);
+  g_object_unref (provider);
+  return TRUE; /* invocation was handled */
+}



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