[evolution-data-server] uoa: Add ESignonSessionPassword.



commit 88caf0d57025cb11bf4107f57fcf53a556fc770e
Author: Matthew Barnes <mbarnes redhat com>
Date:   Sat Feb 2 11:12:43 2013 -0500

    uoa: Add ESignonSessionPassword.
    
    This is a custom EAuthenticationSession that obtains passwords from
    signond instead of from E-D-S keyring entries.  It also does not use
    GcrSystemPrompt but the SessionDataUiPolicy method of prompting does
    not seem to be working well.  Prompting remains an open issue.

 modules/ubuntu-online-accounts/Makefile.am         |    2 +
 .../e-signon-session-password.c                    |  530 ++++++++++++++++++++
 .../e-signon-session-password.h                    |   66 +++
 .../module-ubuntu-online-accounts.c                |    7 +
 4 files changed, 605 insertions(+), 0 deletions(-)
---
diff --git a/modules/ubuntu-online-accounts/Makefile.am b/modules/ubuntu-online-accounts/Makefile.am
index 2dc77a0..581bbe5 100644
--- a/modules/ubuntu-online-accounts/Makefile.am
+++ b/modules/ubuntu-online-accounts/Makefile.am
@@ -16,6 +16,8 @@ module_ubuntu_online_accounts_la_CPPFLAGS = \
 
 module_ubuntu_online_accounts_la_SOURCES = \
 	module-ubuntu-online-accounts.c \
+	e-signon-session-password.c \
+	e-signon-session-password.h \
 	uoa-utils.c \
 	uoa-utils.h \
 	$(NULL)
diff --git a/modules/ubuntu-online-accounts/e-signon-session-password.c b/modules/ubuntu-online-accounts/e-signon-session-password.c
new file mode 100644
index 0000000..40fd452
--- /dev/null
+++ b/modules/ubuntu-online-accounts/e-signon-session-password.c
@@ -0,0 +1,530 @@
+/*
+ * e-signon-session-password.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-signon-session-password.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+#include <libsignon-glib/signon-glib.h>
+
+#include "uoa-utils.h"
+
+#define SIGNON_METHOD_PASSWORD    "password"
+#define SIGNON_MECHANISM_PASSWORD "password"
+
+#define E_SIGNON_SESSION_PASSWORD_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SIGNON_SESSION_PASSWORD, ESignonSessionPasswordPrivate))
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _ESignonSessionPasswordPrivate {
+	AgManager *ag_manager;
+};
+
+struct _AsyncContext {
+	ESourceAuthenticator *authenticator;
+	SignonAuthSession *signon_auth_session;
+	EAuthenticationSessionResult session_result;
+	AgAuthData *ag_auth_data;
+	GCancellable *cancellable;
+};
+
+/* Forward Declarations */
+static void	signon_session_password_msg
+					(EAuthenticationSession *session,
+					 const gchar *format,
+					 ...) G_GNUC_PRINTF (2, 3);
+static void	signon_session_password_process_cb
+					(GObject *source_object,
+					 GAsyncResult *result,
+					 gpointer user_data);
+
+G_DEFINE_DYNAMIC_TYPE (
+	ESignonSessionPassword,
+	e_signon_session_password,
+	E_TYPE_AUTHENTICATION_SESSION)
+
+static void
+async_context_free (AsyncContext *async_context)
+{
+	if (async_context->authenticator != NULL)
+		g_object_unref (async_context->authenticator);
+
+	if (async_context->signon_auth_session != NULL)
+		g_object_unref (async_context->signon_auth_session);
+
+	if (async_context->ag_auth_data != NULL)
+		ag_auth_data_unref (async_context->ag_auth_data);
+
+	if (async_context->cancellable != NULL)
+		g_object_unref (async_context->cancellable);
+
+	g_slice_free (AsyncContext, async_context);
+}
+
+static void
+signon_session_password_msg (EAuthenticationSession *session,
+                             const gchar *format,
+                             ...)
+{
+	GString *buffer;
+	const gchar *source_uid;
+	va_list args;
+
+	buffer = g_string_sized_new (256);
+
+	source_uid = e_authentication_session_get_source_uid (session);
+	g_string_append_printf (buffer, "AUTH (%s): ", source_uid);
+
+	va_start (args, format);
+	g_string_append_vprintf (buffer, format, args);
+	va_end (args);
+
+	g_print ("%s\n", buffer->str);
+
+	g_string_free (buffer, TRUE);
+}
+
+static void
+signon_session_password_state_changed_cb (SignonAuthSession *signon_auth_session,
+                                          gint state,
+                                          const gchar *message,
+                                          EAuthenticationSession *session)
+{
+	signon_session_password_msg (session, "(signond) %s", message);
+}
+
+static AgAccountService *
+signon_session_password_new_account_service (EAuthenticationSession *session,
+                                             ESource *source,
+                                             GError **error)
+{
+	ESignonSessionPasswordPrivate *priv;
+	ESourceUoa *extension;
+	AgAccountId account_id;
+	AgAccount *ag_account = NULL;
+	AgAccountService *ag_account_service;
+	GList *list;
+	gboolean has_uoa_extension;
+	const gchar *extension_name;
+
+	priv = E_SIGNON_SESSION_PASSWORD_GET_PRIVATE (session);
+
+	/* XXX The ESource should be a collection source with an
+	 *     [Ubuntu Online Accounts] extension.  Verify this. */
+	extension_name = E_SOURCE_EXTENSION_UOA;
+	has_uoa_extension = e_source_has_extension (source, extension_name);
+	g_return_val_if_fail (has_uoa_extension, NULL);
+
+	extension = e_source_get_extension (source, extension_name);
+	account_id = e_source_uoa_get_account_id (extension);
+
+	ag_account = ag_manager_load_account (
+		priv->ag_manager, account_id, error);
+
+	if (ag_account == NULL)
+		return NULL;
+
+	/* XXX We can't accurately determine the appropriate service
+	 *     type from a collection source, but all services for an
+	 *     account should be using the same authentication method
+	 *     and mechanism so any service should work. */
+
+	list = ag_account_list_services (ag_account);
+	g_return_val_if_fail (list != NULL, NULL);
+
+	ag_account_service = ag_account_service_new (ag_account, list->data);
+
+	ag_service_list_free (list);
+
+	g_object_unref (ag_account);
+
+	return ag_account_service;
+}
+
+static void
+signon_session_password_try_password_cb (GObject *source_object,
+                                         GAsyncResult *result,
+                                         gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+	ESourceAuthenticationResult auth_result;
+	GVariantBuilder builder;
+	GVariant *session_data;
+	GError *error = NULL;
+
+	simple = G_SIMPLE_ASYNC_RESULT (user_data);
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	auth_result = e_source_authenticator_try_password_finish (
+		E_SOURCE_AUTHENTICATOR (source_object), result, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_take_error (simple, error);
+		g_simple_async_result_complete (simple);
+		goto exit;
+	}
+
+	if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
+		async_context->session_result =
+			E_AUTHENTICATION_SESSION_SUCCESS;
+		g_simple_async_result_complete (simple);
+		goto exit;
+	}
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+
+	/* Force the signon service to prompt for a password by adding
+	 * SIGNON_POLICY_REQUEST_PASSWORD to the session data dictionary. */
+	g_variant_builder_add (
+		&builder, "{sv}", SIGNON_SESSION_DATA_UI_POLICY,
+		g_variant_new_int32 (SIGNON_POLICY_REQUEST_PASSWORD));
+
+	/* This returns a floating reference. */
+	session_data = ag_auth_data_get_login_parameters (
+		async_context->ag_auth_data,
+		g_variant_builder_end (&builder));
+
+	signon_auth_session_process_async (
+		async_context->signon_auth_session,
+		session_data,
+		SIGNON_MECHANISM_PASSWORD,
+		async_context->cancellable,
+		signon_session_password_process_cb,
+		g_object_ref (simple));
+
+exit:
+	g_object_unref (simple);
+}
+
+static void
+signon_session_password_process_cb (GObject *source_object,
+                                    GAsyncResult *result,
+                                    gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+	GVariant *session_data;
+	GVariant *secret;
+	GString *string = NULL;
+	GError *error = NULL;
+
+	simple = G_SIMPLE_ASYNC_RESULT (user_data);
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	session_data = signon_auth_session_process_finish (
+		SIGNON_AUTH_SESSION (source_object), result, &error);
+
+	/* Sanity check. */
+	g_return_if_fail (
+		((session_data != NULL) && (error == NULL)) ||
+		((session_data == NULL) && (error != NULL)));
+
+	if (error != NULL) {
+		g_simple_async_result_take_error (simple, error);
+		g_simple_async_result_complete (simple);
+		goto exit;
+	}
+
+	secret = g_variant_lookup_value (
+		session_data,
+		SIGNON_SESSION_DATA_SECRET,
+		G_VARIANT_TYPE_STRING);
+
+	g_variant_unref (session_data);
+
+	if (secret == NULL) {
+		g_simple_async_result_set_error (
+			simple, SIGNON_ERROR,
+			SIGNON_ERROR_MISSING_DATA,
+			_("Signon service did not return a secret"));
+		g_simple_async_result_complete (simple);
+		goto exit;
+	}
+
+	/* XXX It occurs to me now a GVariant might have been a better
+	 *     choice for the password parameter in ESourceAuthenticator. */
+	string = g_string_new (g_variant_get_string (secret, NULL));
+
+	e_source_authenticator_try_password (
+		async_context->authenticator,
+		string,
+		async_context->cancellable,
+		signon_session_password_try_password_cb,
+		g_object_ref (simple));
+
+	g_string_free (string, TRUE);
+	g_variant_unref (secret);
+
+exit:
+	g_object_unref (simple);
+}
+
+static void
+signon_session_password_dispose (GObject *object)
+{
+	ESignonSessionPasswordPrivate *priv;
+
+	priv = E_SIGNON_SESSION_PASSWORD_GET_PRIVATE (object);
+
+	g_clear_object (&priv->ag_manager);
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_signon_session_password_parent_class)->
+		dispose (object);
+}
+
+static EAuthenticationSessionResult
+signon_session_password_execute_sync (EAuthenticationSession *session,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+	EAuthenticationSessionResult auth_result;
+	EAsyncClosure *async_closure;
+	GAsyncResult *async_result;
+
+	async_closure = e_async_closure_new ();
+
+	e_authentication_session_execute (
+		session, G_PRIORITY_DEFAULT, cancellable,
+		e_async_closure_callback, async_closure);
+
+	async_result = e_async_closure_wait (async_closure);
+
+	auth_result = e_authentication_session_execute_finish (
+		session, async_result, error);
+
+	e_async_closure_free (async_closure);
+
+	return auth_result;
+}
+
+static void
+signon_session_password_execute (EAuthenticationSession *session,
+                                 gint io_priority,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+	ESourceAuthenticator *authenticator;
+	ESourceRegistryServer *server;
+	ESource *source;
+	AgAccountService *ag_account_service;
+	AgAuthData *ag_auth_data;
+	SignonAuthSession *signon_auth_session;
+	const gchar *source_uid;
+	guint credentials_id;
+	GError *error = NULL;
+
+	signon_session_password_msg (session, "Initiated");
+
+	authenticator = e_authentication_session_get_authenticator (session);
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->authenticator = g_object_ref (authenticator);
+
+	if (G_IS_CANCELLABLE (cancellable))
+		async_context->cancellable = g_object_ref (cancellable);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (session), callback, user_data,
+		signon_session_password_execute);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	server = e_authentication_session_get_server (session);
+	source_uid = e_authentication_session_get_source_uid (session);
+	source = e_source_registry_server_ref_source (server, source_uid);
+
+	if (source == NULL) {
+		g_simple_async_result_set_error (
+			simple, G_IO_ERROR,
+			G_IO_ERROR_NOT_FOUND,
+			_("No such data source for UID '%s'"),
+			source_uid);
+		g_simple_async_result_complete_in_idle (simple);
+		g_object_unref (simple);
+		return;
+	}
+
+	ag_account_service =
+		signon_session_password_new_account_service (
+		session, source, &error);
+
+	g_object_unref (source);
+
+	/* Sanity check. */
+	g_return_if_fail (
+		((ag_account_service != NULL) && (error == NULL)) ||
+		((ag_account_service == NULL) && (error != NULL)));
+
+	if (error != NULL) {
+		g_simple_async_result_take_error (simple, error);
+		g_simple_async_result_complete_in_idle (simple);
+		g_object_unref (simple);
+		return;
+	}
+
+	ag_auth_data = ag_account_service_get_auth_data (ag_account_service);
+	credentials_id = ag_auth_data_get_credentials_id (ag_auth_data);
+
+	/* Hard-code the method and mechanism names.  If they disagree
+	 * with AgAuthData then hopefully the signon process will fail
+	 * with a suitable error message. */
+
+	signon_auth_session = signon_auth_session_new (
+		credentials_id, SIGNON_METHOD_PASSWORD, &error);
+
+	/* Sanity check. */
+	g_return_if_fail (
+		((signon_auth_session != NULL) && (error == NULL)) ||
+		((signon_auth_session == NULL) && (error != NULL)));
+
+	if (signon_auth_session != NULL) {
+		GVariant *session_data;
+
+		g_signal_connect (
+			signon_auth_session, "state-changed",
+			G_CALLBACK (signon_session_password_state_changed_cb),
+			session);
+
+		/* Need to hold on to these in case of retries. */
+		async_context->signon_auth_session = signon_auth_session;
+		async_context->ag_auth_data = ag_auth_data_ref (ag_auth_data);
+
+		/* This returns a floating reference. */
+		session_data = ag_auth_data_get_login_parameters (
+			async_context->ag_auth_data, NULL);
+
+		signon_auth_session_process_async (
+			async_context->signon_auth_session,
+			session_data,
+			SIGNON_MECHANISM_PASSWORD,
+			async_context->cancellable,
+			signon_session_password_process_cb,
+			g_object_ref (simple));
+	} else {
+		g_simple_async_result_take_error (simple, error);
+		g_simple_async_result_complete_in_idle (simple);
+	}
+
+	ag_auth_data_unref (ag_auth_data);
+	g_object_unref (ag_account_service);
+
+	g_object_unref (simple);
+}
+
+static EAuthenticationSessionResult
+signon_session_password_execute_finish (EAuthenticationSession *session,
+                                        GAsyncResult *result,
+                                        GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+	EAuthenticationSessionResult session_result;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (session),
+		signon_session_password_execute),
+		E_AUTHENTICATION_SESSION_DISMISSED);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		session_result = E_AUTHENTICATION_SESSION_ERROR;
+	else
+		session_result = async_context->session_result;
+
+	switch (session_result) {
+		case E_AUTHENTICATION_SESSION_ERROR:
+			if (error != NULL && *error != NULL)
+				signon_session_password_msg (
+					session, "Complete (ERROR - %s)",
+					(*error)->message);
+			else
+				signon_session_password_msg (
+					session, "Complete (ERROR)");
+			break;
+		case E_AUTHENTICATION_SESSION_SUCCESS:
+			signon_session_password_msg (
+				session, "Complete (SUCCESS)");
+			break;
+		case E_AUTHENTICATION_SESSION_DISMISSED:
+			signon_session_password_msg (
+				session, "Complete (DISMISSED)");
+			break;
+		default:
+			g_warn_if_reached ();
+	}
+
+	return session_result;
+}
+
+static void
+e_signon_session_password_class_init (ESignonSessionPasswordClass *class)
+{
+	GObjectClass *object_class;
+	EAuthenticationSessionClass *authentication_session_class;
+
+	g_type_class_add_private (
+		class, sizeof (ESignonSessionPasswordPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->dispose = signon_session_password_dispose;
+
+	authentication_session_class =
+		E_AUTHENTICATION_SESSION_CLASS (class);
+	authentication_session_class->execute_sync =
+		signon_session_password_execute_sync;
+	authentication_session_class->execute =
+		signon_session_password_execute;
+	authentication_session_class->execute_finish =
+		signon_session_password_execute_finish;
+}
+
+static void
+e_signon_session_password_class_finalize (ESignonSessionPasswordClass *class)
+{
+}
+
+static void
+e_signon_session_password_init (ESignonSessionPassword *session)
+{
+	session->priv = E_SIGNON_SESSION_PASSWORD_GET_PRIVATE (session);
+
+	session->priv->ag_manager = ag_manager_new ();
+}
+
+void
+e_signon_session_password_type_register (GTypeModule *type_module)
+{
+	/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+	 *     function, so we have to wrap it with a public function in
+	 *     order to register types from a separate compilation unit. */
+	e_signon_session_password_register_type (type_module);
+}
+
diff --git a/modules/ubuntu-online-accounts/e-signon-session-password.h b/modules/ubuntu-online-accounts/e-signon-session-password.h
new file mode 100644
index 0000000..538b64b
--- /dev/null
+++ b/modules/ubuntu-online-accounts/e-signon-session-password.h
@@ -0,0 +1,66 @@
+/*
+ * e-signon-session-password.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_SIGNON_SESSION_PASSWORD_H
+#define E_SIGNON_SESSION_PASSWORD_H
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SIGNON_SESSION_PASSWORD \
+	(e_signon_session_password_get_type ())
+#define E_SIGNON_SESSION_PASSWORD(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SIGNON_SESSION_PASSWORD, ESignonSessionPassword))
+#define E_SIGNON_SESSION_PASSWORD_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SIGNON_SESSION_PASSWORD, ESignonSessionPasswordClass))
+#define E_IS_SIGNON_SESSION_PASSWORD(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SIGNON_SESSION_PASSWORD))
+#define E_IS_SIGNON_SESSION_PASSWORD_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SIGNON_SESSION_PASSWORD))
+#define E_SIGNON_SESSION_PASSWORD_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SIGNON_SESSION_PASSWORD, ESignonSessionPasswordClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESignonSessionPassword ESignonSessionPassword;
+typedef struct _ESignonSessionPasswordClass ESignonSessionPasswordClass;
+typedef struct _ESignonSessionPasswordPrivate ESignonSessionPasswordPrivate;
+
+struct _ESignonSessionPassword {
+	EAuthenticationSession parent;
+	ESignonSessionPasswordPrivate *priv;
+};
+
+struct _ESignonSessionPasswordClass {
+	EAuthenticationSessionClass parent_class;
+};
+
+GType		e_signon_session_password_get_type
+						(void) G_GNUC_CONST;
+void		e_signon_session_password_type_register
+						(GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_SIGNON_SESSION_PASSWORD_H */
+
diff --git a/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c b/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c
index 7e0e926..d38e48e 100644
--- a/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c
+++ b/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c
@@ -22,6 +22,7 @@
 #include <libaccounts-glib/accounts-glib.h>
 
 #include "uoa-utils.h"
+#include "e-signon-session-password.h"
 
 /* Standard GObject macros */
 #define E_TYPE_UBUNTU_ONLINE_ACCOUNTS \
@@ -372,6 +373,11 @@ ubuntu_online_accounts_config_collection (EUbuntuOnlineAccounts *extension,
 	e_server_side_source_set_removable (
 		E_SERVER_SIDE_SOURCE (source), FALSE);
 
+	/* Obtain passwords from the signond service. */
+	e_server_side_source_set_auth_session_type (
+		E_SERVER_SIDE_SOURCE (source),
+		E_TYPE_SIGNON_SESSION_PASSWORD);
+
 	if (supports_oauth2) {
 		/* This module provides OAuth 2.0 support to the collection.
 		 * Note, children of the collection source will automatically
@@ -1096,6 +1102,7 @@ G_MODULE_EXPORT void
 e_module_load (GTypeModule *type_module)
 {
 	e_ubuntu_online_accounts_register_type (type_module);
+	e_signon_session_password_type_register (type_module);
 }
 
 G_MODULE_EXPORT void



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