[gnome-online-accounts/wip/kerberos: 1/5] Allow for transient, "non-permanent" accounts



commit 73d2953abec61f1391eca48a067246195a6bfe35
Author: Ray Strode <rstrode redhat com>
Date:   Sun Aug 12 20:41:42 2012 -0400

    Allow for transient, "non-permanent" accounts
    
    One prerequisite for adding kerberos support to online accounts
    is for it to allowr accounts to show up that weren't explicitly
    previously added by the user from control-center. For instance,
    if a user runs "kinit" they should still be able to see their
    kerberos tickets in the dialog, and even destroy the credentials
    and remove the account.
    
    Of course these accounts have a lifetime limited to the current
    session.  We don't want a user to unintentionally trigger permanent
    behavior by just doing a one off kinit.

 data/dbus-interfaces.xml     |   22 +++++
 src/daemon/goadaemon.c       |  141 ++++++++++++++++++++++++++++---
 src/goabackend/goaprovider.c |  194 ++++++++++++++++++++++++++++++++++++++++++
 src/goabackend/goaprovider.h |   18 ++++-
 4 files changed, 362 insertions(+), 13 deletions(-)
---
diff --git a/data/dbus-interfaces.xml b/data/dbus-interfaces.xml
index 48ad569..9881d81 100644
--- a/data/dbus-interfaces.xml
+++ b/data/dbus-interfaces.xml
@@ -69,6 +69,20 @@
       -->
     <property name="Id" type="s" access="read"/>
 
+    <!-- IsPermanent:
+
+         Whether or not the account is remembered from session to session.
+
+         Non-permanent accounts are added implicitly when the user is granted credentials
+         from some mechanism other than Online Accounts, but that Online Accounts
+         still knows how to deal with.
+
+         They are specific to the machine/session and are silently disregarded after logout.
+
+         Accounts are permanant by default unless created with "IsPermanent" "false" detail.
+      -->
+    <property name="IsPermanent" type="b" access="read"/>
+
     <!-- AttentionNeeded: Set to %TRUE if the account is in need of attention.
 
          This is used when a human operator is needed to service the
@@ -177,6 +191,14 @@
       <arg name="expires_in" type="i" direction="out"/>
     </method>
 
+    <!--
+        EnsurePermanent:
+
+        Ensures an account is permanent. See the
+        #org.gnome.OnlineAccounts.Account:IsPermanent property
+        for more information on what that details.
+    -->
+    <method name="EnsurePermanent"/>
   </interface>
 
   <!--
diff --git a/src/daemon/goadaemon.c b/src/daemon/goadaemon.c
index e2cca23..e2aa37a 100644
--- a/src/daemon/goadaemon.c
+++ b/src/daemon/goadaemon.c
@@ -78,6 +78,10 @@ static gboolean on_account_handle_ensure_credentials (GoaAccount            *acc
                                                       GDBusMethodInvocation *invocation,
                                                       gpointer               user_data);
 
+static gboolean on_account_handle_ensure_permanent (GoaAccount            *account,
+                                                    GDBusMethodInvocation *invocation,
+                                                    gpointer               user_data);
+
 static void goa_daemon_reload_configuration (GoaDaemon *daemon);
 
 static void on_notification_closed (NotifyNotification *notification,
@@ -330,7 +334,8 @@ key_file_data_new (GKeyFile    *key_file,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
-add_config_file (const gchar   *path,
+add_config_file (GoaDaemon     *daemon,
+                 const gchar   *path,
                  GHashTable    *group_name_to_key_file_data,
                  GList        **key_files_to_free)
 {
@@ -357,14 +362,48 @@ add_config_file (const gchar   *path,
   else
     {
       gchar **groups;
+      const char *guid;
       gsize num_groups;
       guint n;
 
+      guid = g_dbus_connection_get_guid (daemon->connection);
       groups = g_key_file_get_groups (key_file, &num_groups);
       for (n = 0; n < num_groups; n++)
         {
           if (g_str_has_prefix (groups[n], "Account "))
             {
+              gboolean is_permanent;
+              char *session_id;
+
+              is_permanent = g_key_file_get_boolean (key_file,
+                                                     groups[n],
+                                                     "IsPermanent",
+                                                     NULL);
+
+              if (!is_permanent)
+                {
+                  session_id = g_key_file_get_string (key_file,
+                                                      groups[n],
+                                                      "SessionId",
+                                                      NULL);
+
+                  /* discard non-permanent accounts from older sessions */
+                  if (session_id != NULL &&
+                      g_strcmp0 (session_id, guid) != 0)
+                    {
+                      goa_debug ("ignoring account \"%s\" in file %s because it's stale",
+                                 groups[n], path);
+                      g_free (groups[n]);
+                      g_free (session_id);
+                      continue;
+                    }
+                  g_free (session_id);
+                }
+              else
+                {
+                  g_key_file_remove_key (key_file, groups[n], "SessionId", NULL);
+                }
+
               g_hash_table_insert (group_name_to_key_file_data,
                                    groups[n], /* steals string */
                                    key_file_data_new (key_file, path));
@@ -585,6 +624,10 @@ process_config_entries (GoaDaemon  *daemon,
                             "handle-ensure-credentials",
                             G_CALLBACK (on_account_handle_ensure_credentials),
                             daemon);
+          g_signal_connect (goa_object_peek_account (GOA_OBJECT (object)),
+                            "handle-ensure-permanent",
+                            G_CALLBACK (on_account_handle_ensure_permanent),
+                            daemon);
         }
       g_object_unref (object);
       g_free (group);
@@ -619,6 +662,9 @@ process_config_entries (GoaDaemon  *daemon,
           g_signal_handlers_disconnect_by_func (goa_object_peek_account (object),
                                                 G_CALLBACK (on_account_handle_ensure_credentials),
                                                 daemon);
+          g_signal_handlers_disconnect_by_func (goa_object_peek_account (object),
+                                                G_CALLBACK (on_account_handle_ensure_permanent),
+                                                daemon);
           g_warn_if_fail (g_dbus_object_manager_server_unexport (daemon->object_manager, object_path));
         }
       g_object_unref (object);
@@ -657,7 +703,7 @@ goa_daemon_reload_configuration (GoaDaemon *daemon)
 
   /* Read the main user config file at $HOME/.config/goa-1.0/accounts.conf */
   path = g_strdup_printf ("%s/goa-1.0/accounts.conf", g_get_user_config_dir ());
-  add_config_file (path, group_name_to_key_file_data, &key_files_to_free);
+  add_config_file (daemon, path, group_name_to_key_file_data, &key_files_to_free);
   g_free (path);
 
   /* now process the group_name_to_key_file_data hash table */
@@ -788,6 +834,21 @@ on_manager_handle_add_account (GoaManager             *manager,
   g_variant_iter_init (&iter, details);
   while (g_variant_iter_next (&iter, "{&s&s}", &key, &value))
     {
+      /* We treat IsPermanent special.  If it's false we add in
+       * the current session guid, so it can be ignored after
+       * the session is over.
+       */
+      if (g_strcmp0 (key, "IsPermanent") == 0)
+        {
+          if (g_strcmp0 (value, "false") == 0)
+            {
+              const char *guid;
+
+              guid = g_dbus_connection_get_guid (daemon->connection);
+              g_key_file_set_string (key_file, group, "SessionId", guid);
+            }
+        }
+
       g_key_file_set_string (key_file, group, key, value);
     }
 
@@ -1105,15 +1166,15 @@ typedef struct
   GoaDaemon *daemon;
   GoaObject *object;
   GDBusMethodInvocation *invocation;
-} EnsureCredentialsData;
+} EnsureData;
 
-static EnsureCredentialsData *
-ensure_credentials_data_new (GoaDaemon             *daemon,
+static EnsureData *
+ensure_data_new (GoaDaemon             *daemon,
                              GoaObject             *object,
                              GDBusMethodInvocation *invocation)
 {
-  EnsureCredentialsData *data;
-  data = g_slice_new0 (EnsureCredentialsData);
+  EnsureData *data;
+  data = g_slice_new0 (EnsureData);
   data->daemon = g_object_ref (daemon);
   data->object = g_object_ref (object);
   data->invocation = invocation;
@@ -1121,11 +1182,11 @@ ensure_credentials_data_new (GoaDaemon             *daemon,
 }
 
 static void
-ensure_credentials_data_unref (EnsureCredentialsData *data)
+ensure_data_unref (EnsureData *data)
 {
   g_object_unref (data->daemon);
   g_object_unref (data->object);
-  g_slice_free (EnsureCredentialsData, data);
+  g_slice_free (EnsureData, data);
 }
 
 static gboolean
@@ -1154,7 +1215,7 @@ ensure_credentials_cb (GoaProvider   *provider,
                        GAsyncResult  *res,
                        gpointer       user_data)
 {
-  EnsureCredentialsData *data = user_data;
+  EnsureData *data = user_data;
   gint expires_in;
   GError *error;
 
@@ -1195,7 +1256,7 @@ ensure_credentials_cb (GoaProvider   *provider,
                                                data->invocation,
                                                expires_in);
     }
-  ensure_credentials_data_unref (data);
+  ensure_data_unref (data);
 }
 
 static gboolean
@@ -1224,7 +1285,63 @@ on_account_handle_ensure_credentials (GoaAccount            *account,
                                    object,
                                    NULL, /* GCancellable */
                                    (GAsyncReadyCallback) ensure_credentials_cb,
-                                   ensure_credentials_data_new (daemon, object, invocation));
+                                   ensure_data_new (daemon, object, invocation));
+
+ out:
+  return TRUE; /* invocation was handled */
+}
+
+static void
+ensure_permanent_cb (GoaProvider   *provider,
+                     GAsyncResult  *res,
+                     gpointer       user_data)
+{
+  EnsureData *data = user_data;
+  GError *error;
+
+  error= NULL;
+  if (!goa_provider_ensure_permanent_finish (provider, res, &error))
+    {
+      g_dbus_method_invocation_return_gerror (data->invocation, error);
+      g_error_free (error);
+    }
+  else
+    {
+      GoaAccount *account;
+      account = goa_object_peek_account (data->object);
+
+      goa_account_complete_ensure_permanent (account, data->invocation);
+    }
+  ensure_data_unref (data);
+}
+static gboolean
+on_account_handle_ensure_permanent (GoaAccount            *account,
+                                    GDBusMethodInvocation *invocation,
+                                    gpointer               user_data)
+{
+  GoaDaemon *daemon = GOA_DAEMON (user_data);
+  GoaProvider *provider;
+  GoaObject *object;
+
+  object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (account)));
+  provider = goa_provider_get_for_provider_type (goa_account_get_provider_type (account));
+  if (provider == NULL)
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             GOA_ERROR,
+                                             GOA_ERROR_FAILED,
+                                             "Unsupported account type %s for id %s (no provider)",
+                                             goa_account_get_provider_type (account),
+                                             goa_account_get_id (account));
+      goto out;
+    }
+
+  goa_provider_ensure_permanent (provider,
+                                 object,
+                                 NULL, /* GCancellable */
+                                 (GAsyncReadyCallback)
+                                 ensure_permanent_cb,
+                                 ensure_data_new (daemon, object, invocation));
 
  out:
   return TRUE; /* invocation was handled */
diff --git a/src/goabackend/goaprovider.c b/src/goabackend/goaprovider.c
index fc92d7f..41ebbdb 100644
--- a/src/goabackend/goaprovider.c
+++ b/src/goabackend/goaprovider.c
@@ -50,6 +50,11 @@ static gboolean goa_provider_ensure_credentials_sync_real (GoaProvider   *provid
                                                            GCancellable  *cancellable,
                                                            GError       **error);
 
+static gboolean goa_provider_ensure_permanent_sync_real (GoaProvider   *provider,
+                                                         GoaObject     *object,
+                                                         GCancellable  *cancellable,
+                                                         GError       **error);
+
 static gboolean goa_provider_build_object_real (GoaProvider         *provider,
                                                 GoaObjectSkeleton   *object,
                                                 GKeyFile            *key_file,
@@ -64,6 +69,7 @@ static void goa_provider_show_account_real (GoaProvider         *provider,
                                             GtkGrid             *left,
                                             GtkGrid             *right);
 
+static void goa_util_remove_keyfile_key (GoaAccount *account, const gchar *key);
 static guint goa_provider_get_credentials_generation_real (GoaProvider *provider);
 
 static GIcon *goa_provider_get_provider_icon_default (GoaProvider *provider,
@@ -81,6 +87,7 @@ goa_provider_class_init (GoaProviderClass *klass)
 {
   klass->build_object = goa_provider_build_object_real;
   klass->ensure_credentials_sync = goa_provider_ensure_credentials_sync_real;
+  klass->ensure_permanent_sync = goa_provider_ensure_permanent_sync_real;
   klass->show_account = goa_provider_show_account_real;
   klass->get_credentials_generation = goa_provider_get_credentials_generation_real;
   klass->get_provider_icon = goa_provider_get_provider_icon_default;
@@ -521,6 +528,129 @@ goa_provider_ensure_credentials_sync (GoaProvider     *provider,
   return GOA_PROVIDER_GET_CLASS (provider)->ensure_credentials_sync (provider, object, out_expires_in, cancellable, error);
 }
 
+static void
+ensure_permanent_in_thread_func (GSimpleAsyncResult *simple,
+                                 GoaProvider        *provider,
+                                 GCancellable       *cancellable)
+{
+  GoaObject *object;
+  GError *error;
+
+  object = g_simple_async_result_get_op_res_gpointer (simple);
+
+  error = NULL;
+  if (!goa_provider_ensure_permanent_sync (provider,
+                                           object,
+                                           cancellable,
+                                           &error))
+    g_simple_async_result_take_error (simple, error);
+}
+
+
+/**
+ * goa_provider_ensure_permanent:
+ * @provider: A #GoaProvider.
+ * @object: A #GoaObject with a #GoaAccount interface.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: The function to call when the request is satisfied.
+ * @user_data: Pointer to pass to @callback.
+ *
+ * Ensures that credentials for @object will be reacquired on login.
+ *
+ * When the result is ready, @callback will be called in the the <link
+ * linkend="g-main-context-push-thread-default">thread-default main
+ * loop</link> this function was called from. You can then call
+ * goa_provider_ensure_permanent_finish() to get the result
+ * of the operation.
+ *
+ * This is a virtual method where the default implementation does
+ * all the necessary work. A subclass may chain up to this default
+ * implementation.
+ */
+void
+goa_provider_ensure_permanent (GoaProvider          *provider,
+                               GoaObject            *object,
+                               GCancellable         *cancellable,
+                               GAsyncReadyCallback   callback,
+                               gpointer              user_data)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (GOA_IS_PROVIDER (provider));
+  g_return_if_fail (GOA_IS_OBJECT (object));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+  simple = g_simple_async_result_new (G_OBJECT (provider),
+                                      callback,
+                                      user_data,
+                                      goa_provider_ensure_permanent);
+  g_simple_async_result_set_op_res_gpointer (simple,
+                                             g_object_ref (object),
+                                             (GDestroyNotify)
+                                             g_object_unref);
+  g_simple_async_result_run_in_thread (simple,
+                                       (GSimpleAsyncThreadFunc)
+                                       ensure_permanent_in_thread_func,
+                                       G_PRIORITY_DEFAULT,
+                                       cancellable);
+  g_object_unref (simple);
+}
+
+/**
+ * goa_provider_ensure_permanent_finish:
+ * @provider: A #GoaProvider.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to goa_provider_ensure_credentials().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with goa_provider_ensure_permanent().
+ *
+ * Returns: %TRUE if the account for the passed #GoaObject is now permanent, %FALSE if @error is set.
+ */
+gboolean
+goa_provider_ensure_permanent_finish (GoaProvider   *provider,
+                                      GAsyncResult  *res,
+                                      GError       **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+
+  g_return_val_if_fail (GOA_IS_PROVIDER (provider), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == goa_provider_ensure_permanent);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * goa_provider_ensure_permanent_sync:
+ * @provider: A #GoaProvider.
+ * @object: A #GoaObject with a #GoaAccount interface.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Like goa_provider_ensure_permanent() but blocks the
+ * calling thread until an answer is received.
+ *
+ * Returns: %TRUE if the credentials for the passed #GoaObject are valid, %FALSE if @error is set.
+ */
+gboolean
+goa_provider_ensure_permanent_sync (GoaProvider     *provider,
+                                    GoaObject       *object,
+                                    GCancellable    *cancellable,
+                                    GError         **error)
+{
+  g_return_val_if_fail (GOA_IS_PROVIDER (provider), FALSE);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  return GOA_PROVIDER_GET_CLASS (provider)->ensure_permanent_sync (provider, object, cancellable, error);
+}
+
 static gboolean
 goa_provider_ensure_credentials_sync_real (GoaProvider   *provider,
                                            GoaObject     *object,
@@ -537,6 +667,20 @@ goa_provider_ensure_credentials_sync_real (GoaProvider   *provider,
 }
 
 static gboolean
+goa_provider_ensure_permanent_sync_real (GoaProvider   *provider,
+                                         GoaObject     *object,
+                                         GCancellable  *cancellable,
+                                         GError       **error)
+{
+  GoaAccount *account;
+
+  account = goa_object_peek_account (object);
+  goa_util_remove_keyfile_key (account, "SessionId");
+
+  return TRUE;
+}
+
+static gboolean
 goa_provider_build_object_real (GoaProvider         *provider,
                                 GoaObjectSkeleton   *object,
                                 GKeyFile            *key_file,
@@ -852,6 +996,56 @@ goa_util_lookup_keyfile_boolean (GoaObject    *object,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+goa_util_remove_keyfile_key (GoaAccount *account, const gchar *key)
+{
+  GError *error;
+  GKeyFile *key_file;
+  gchar *contents;
+  gchar *group;
+  gchar *path;
+  gsize length;
+
+  contents = NULL;
+
+  path = g_strdup_printf ("%s/goa-1.0/accounts.conf", g_get_user_config_dir ());
+  group = g_strdup_printf ("Account %s", goa_account_get_id (account));
+
+  key_file = g_key_file_new ();
+  error = NULL;
+  if (!g_key_file_load_from_file (key_file,
+                                  path,
+                                  G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+                                  &error))
+    {
+      goa_warning ("Error loading keyfile %s: %s (%s, %d)",
+                   path,
+                   error->message,
+                   g_quark_to_string (error->domain),
+                   error->code);
+      g_error_free (error);
+      goto out;
+    }
+
+  g_key_file_remove_key (key_file, group, key, NULL);
+  contents = g_key_file_to_data (key_file, &length, NULL);
+
+  error = NULL;
+  if (!g_file_set_contents (path, contents, length, &error))
+    {
+      g_prefix_error (&error, "Error writing key-value-file %s: ", path);
+      goa_warning ("%s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code);
+      g_error_free (error);
+      goto out;
+    }
+
+ out:
+  g_free (contents);
+  g_key_file_free (key_file);
+  g_free (group);
+  g_free (path);
+}
+
+static void
 goa_util_set_keyfile_boolean (GoaAccount *account, const gchar *key, gboolean value)
 {
   GError *error;
diff --git a/src/goabackend/goaprovider.h b/src/goabackend/goaprovider.h
index b5e186e..0dcee5e 100644
--- a/src/goabackend/goaprovider.h
+++ b/src/goabackend/goaprovider.h
@@ -95,13 +95,16 @@ struct _GoaProviderClass
                                      const gchar        *group,
                                      gboolean            just_added,
                                      GError            **error);
-
   /* virtual but with default implementation */
   gboolean (*ensure_credentials_sync) (GoaProvider         *provider,
                                        GoaObject           *object,
                                        gint                *out_expires_in,
                                        GCancellable        *cancellable,
                                        GError             **error);
+  gboolean (*ensure_permanent_sync)   (GoaProvider         *provider,
+                                       GoaObject           *object,
+                                       GCancellable        *cancellable,
+                                       GError             **error);
   void     (*show_account)            (GoaProvider         *provider,
                                        GoaClient           *client,
                                        GoaObject           *object,
@@ -143,6 +146,19 @@ gboolean     goa_provider_build_object              (GoaProvider         *provid
                                                      const gchar         *group,
                                                      gboolean             just_added,
                                                      GError             **error);
+
+void         goa_provider_ensure_permanent          (GoaProvider         *provider,
+                                                     GoaObject           *object,
+                                                     GCancellable        *cancellable,
+                                                     GAsyncReadyCallback  callback,
+                                                     gpointer             user_data);
+gboolean     goa_provider_ensure_permanent_finish   (GoaProvider         *provider,
+                                                     GAsyncResult        *res,
+                                                     GError             **error);
+gboolean     goa_provider_ensure_permanent_sync     (GoaProvider         *provider,
+                                                     GoaObject           *object,
+                                                     GCancellable        *cancellable,
+                                                     GError             **error);
 void         goa_provider_ensure_credentials        (GoaProvider         *provider,
                                                      GoaObject           *object,
                                                      GCancellable        *cancellable,



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