From: Matthew Barnes Date: Thu, 28 Feb 2013 21:45:49 +0000 (-0500) Subject: Add EGoaPasswordBased. X-Git-Tag: upstream/3.7.91~15 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a2c78375d1d9fec35954ff87d16a4b89c2e02dbe;p=platform%2Fupstream%2Fevolution-data-server.git Add EGoaPasswordBased. This is the GOA-equivalent to ESignonSessionPassword for UOA: a custom EAuthenticationSession subclass that obtains passwords from the Online Accounts service. This class does not use a password prompt. --- diff --git a/modules/gnome-online-accounts/Makefile.am b/modules/gnome-online-accounts/Makefile.am index 855c7c4..0960e31 100644 --- a/modules/gnome-online-accounts/Makefile.am +++ b/modules/gnome-online-accounts/Makefile.am @@ -19,6 +19,8 @@ module_gnome_online_accounts_la_CPPFLAGS = \ module_gnome_online_accounts_la_SOURCES = \ module-gnome-online-accounts.c \ + e-goa-password-based.c \ + e-goa-password-based.h \ goaewsclient.c \ goaewsclient.h \ $(NULL) diff --git a/modules/gnome-online-accounts/e-goa-password-based.c b/modules/gnome-online-accounts/e-goa-password-based.c new file mode 100644 index 0000000..c007caa --- /dev/null +++ b/modules/gnome-online-accounts/e-goa-password-based.c @@ -0,0 +1,272 @@ +/* + * e-goa-password-based.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 + * + */ + +#include "e-goa-password-based.h" + +/* XXX Yeah, yeah... */ +#define GOA_API_IS_SUBJECT_TO_CHANGE + +#include +#include +#include + +#define E_GOA_PASSWORD_BASED_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_GOA_PASSWORD_BASED, EGoaPasswordBasedPrivate)) + +struct _EGoaPasswordBasedPrivate { + gint placeholder; +}; + +G_DEFINE_DYNAMIC_TYPE ( + EGoaPasswordBased, + e_goa_password_based, + E_TYPE_AUTHENTICATION_SESSION) + +static GoaObject * +e_goa_password_based_ref_account (ESourceRegistryServer *server, + ESource *source, + GoaClient *goa_client) +{ + GoaObject *match = NULL; + GList *list, *link; + const gchar *extension_name; + gchar *account_id = NULL; + + extension_name = E_SOURCE_EXTENSION_GOA; + + source = e_source_registry_server_find_extension ( + server, source, extension_name); + + if (source != NULL) { + ESourceGoa *extension; + + extension = e_source_get_extension (source, extension_name); + account_id = e_source_goa_dup_account_id (extension); + + g_object_unref (source); + } + + if (account_id == NULL) + return NULL; + + /* FIXME Use goa_client_lookup_by_id() once we require GOA 3.6. */ + list = goa_client_get_accounts (goa_client); + + for (link = list; link != NULL; link = g_list_next (link)) { + GoaObject *goa_object; + GoaAccount *goa_account; + const gchar *candidate_id; + + goa_object = GOA_OBJECT (link->data); + goa_account = goa_object_get_account (goa_object); + candidate_id = goa_account_get_id (goa_account); + + if (g_strcmp0 (account_id, candidate_id) == 0) + match = g_object_ref (goa_object); + + g_object_unref (goa_account); + + if (match != NULL) + break; + } + + g_list_free_full (list, (GDestroyNotify) g_object_unref); + + return match; +} + +static EAuthenticationSessionResult +e_goa_password_based_execute_sync (EAuthenticationSession *session, + GCancellable *cancellable, + GError **error) +{ +#ifdef HAVE_GOA_PASSWORD_BASED + EAuthenticationSessionResult session_result; + ESourceAuthenticationResult auth_result; + ESourceAuthenticator *authenticator; + ESourceRegistryServer *server; + ESource *source = NULL; + GoaClient *goa_client = NULL; + GoaObject *goa_object = NULL; + GoaAccount *goa_account = NULL; + GoaPasswordBased *goa_password_based = NULL; + GString *password_string; + const gchar *extension_name; + const gchar *source_uid; + gchar *password = NULL; + gboolean use_imap_password; + gboolean use_smtp_password; + gboolean success; + + goa_client = goa_client_new_sync (cancellable, error); + if (goa_client == NULL) { + session_result = E_AUTHENTICATION_SESSION_ERROR; + goto exit; + } + + 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_set_error ( + error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("No such data source for UID '%s'"), + source_uid); + session_result = E_AUTHENTICATION_SESSION_ERROR; + goto exit; + } + + goa_object = e_goa_password_based_ref_account ( + server, source, goa_client); + + if (goa_object == NULL) { + g_set_error ( + error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("Cannot find a corresponding account in " + "the org.gnome.OnlineAccounts service from " + "which to obtain a password for '%s'"), + e_source_get_display_name (source)); + session_result = E_AUTHENTICATION_SESSION_ERROR; + goto exit; + } + + goa_account = goa_object_get_account (goa_object); + goa_password_based = goa_object_get_password_based (goa_object); + + /* XXX We should only be here if the account is password based. */ + g_return_val_if_fail ( + goa_password_based != NULL, + E_AUTHENTICATION_SESSION_ERROR); + + success = goa_account_call_ensure_credentials_sync ( + goa_account, NULL, cancellable, error); + if (!success) { + session_result = E_AUTHENTICATION_SESSION_ERROR; + goto exit; + } + + extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; + use_imap_password = e_source_has_extension (source, extension_name); + + extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT; + use_smtp_password = e_source_has_extension (source, extension_name); + + /* Use a suitable password ID for the ESource. */ + if (use_imap_password) { + goa_password_based_call_get_password_sync ( + goa_password_based, "imap-password", + &password, cancellable, error); + } else if (use_smtp_password) { + goa_password_based_call_get_password_sync ( + goa_password_based, "smtp-password", + &password, cancellable, error); + } else { + /* Generic fallback - password ID is not used. */ + goa_password_based_call_get_password_sync ( + goa_password_based, "", + &password, cancellable, error); + } + + if (password == NULL) { + session_result = E_AUTHENTICATION_SESSION_ERROR; + goto exit; + } + + authenticator = e_authentication_session_get_authenticator (session); + password_string = g_string_new (password); + auth_result = e_source_authenticator_try_password_sync ( + authenticator, password_string, cancellable, error); + g_string_free (password_string, TRUE); + + switch (auth_result) { + case E_SOURCE_AUTHENTICATION_ERROR: + session_result = E_AUTHENTICATION_SESSION_ERROR; + break; + + case E_SOURCE_AUTHENTICATION_ACCEPTED: + session_result = E_AUTHENTICATION_SESSION_SUCCESS; + break; + + case E_SOURCE_AUTHENTICATION_REJECTED: + /* FIXME Apparently applications are expected to post + * a desktop-wide notification about the failed + * authentication attempt. */ + g_set_error ( + error, G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED, + _("Invalid password for '%s'"), + e_source_get_display_name (source)); + session_result = E_AUTHENTICATION_SESSION_ERROR; + break; + + default: + g_warn_if_reached (); + session_result = E_AUTHENTICATION_SESSION_DISMISSED; + break; + } + +exit: + g_clear_object (&source); + g_clear_object (&goa_client); + g_clear_object (&goa_object); + g_clear_object (&goa_account); + g_clear_object (&goa_password_based); + + g_free (password); + + return session_result; +#else + g_return_val_if_reached (E_AUTHENTICATION_SESSION_ERROR); +#endif /* HAVE_GOA_PASSWORD_BASED */ +} + +static void +e_goa_password_based_class_init (EGoaPasswordBasedClass *class) +{ + EAuthenticationSessionClass *authentication_session_class; + + g_type_class_add_private (class, sizeof (EGoaPasswordBasedPrivate)); + + authentication_session_class = + E_AUTHENTICATION_SESSION_CLASS (class); + authentication_session_class->execute_sync = + e_goa_password_based_execute_sync; +} + +static void +e_goa_password_based_class_finalize (EGoaPasswordBasedClass *class) +{ +} + +static void +e_goa_password_based_init (EGoaPasswordBased *session) +{ + session->priv = E_GOA_PASSWORD_BASED_GET_PRIVATE (session); +} + +void +e_goa_password_based_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_goa_password_based_register_type (type_module); +} + diff --git a/modules/gnome-online-accounts/e-goa-password-based.h b/modules/gnome-online-accounts/e-goa-password-based.h new file mode 100644 index 0000000..4fd3d01 --- /dev/null +++ b/modules/gnome-online-accounts/e-goa-password-based.h @@ -0,0 +1,65 @@ +/* + * e-goa-password-based.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 + * + */ + +#ifndef E_GOA_PASSWORD_BASED_H +#define E_GOA_PASSWORD_BASED_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_GOA_PASSWORD_BASED \ + (e_goa_password_based_get_type ()) +#define E_GOA_PASSWORD_BASED(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_GOA_PASSWORD_BASED, EGoaPasswordBased)) +#define E_GOA_PASSWORD_BASED_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_GOA_PASSWORD_BASED, EGoaPasswordBasedClass)) +#define E_IS_GOA_PASSWORD_BASED(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_GOA_PASSWORD_BASED)) +#define E_IS_GOA_PASSWORD_BASED_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_GOA_PASSWORD_BASED)) +#define E_GOA_PASSWORD_BASED_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_GOA_PASSWORD_BASED, EGoaPasswordBasedClass)) + +G_BEGIN_DECLS + +typedef struct _EGoaPasswordBased EGoaPasswordBased; +typedef struct _EGoaPasswordBasedClass EGoaPasswordBasedClass; +typedef struct _EGoaPasswordBasedPrivate EGoaPasswordBasedPrivate; + +struct _EGoaPasswordBased { + EAuthenticationSession parent; + EGoaPasswordBasedPrivate *priv; +}; + +struct _EGoaPasswordBasedClass { + EAuthenticationSessionClass parent_class; +}; + +GType e_goa_password_based_get_type (void) G_GNUC_CONST; +void e_goa_password_based_type_register + (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_GOA_PASSWORD_BASED_H */ + diff --git a/modules/gnome-online-accounts/module-gnome-online-accounts.c b/modules/gnome-online-accounts/module-gnome-online-accounts.c index 0e211c2..45e6f39 100644 --- a/modules/gnome-online-accounts/module-gnome-online-accounts.c +++ b/modules/gnome-online-accounts/module-gnome-online-accounts.c @@ -22,12 +22,12 @@ #include #include #include -#include #include #include #include "goaewsclient.h" +#include "e-goa-password-based.h" /* Standard GObject macros */ #define E_TYPE_GNOME_ONLINE_ACCOUNTS \ @@ -59,25 +59,6 @@ struct _EGnomeOnlineAccountsClass { EExtensionClass parent_class; }; -/* The keyring definintions are copied from e-authentication-session.c */ - -#define KEYRING_ITEM_ATTRIBUTE_NAME "e-source-uid" -#define KEYRING_ITEM_DISPLAY_FORMAT "Evolution Data Source %s" - -#ifdef HAVE_GOA_PASSWORD_BASED -/* XXX Probably want to share this with - * evolution-source-registry-migrate-sources.c */ -static SecretSchema schema = { - "org.gnome.Evolution.DataSource", - SECRET_SCHEMA_DONT_MATCH_NAME, - { - { KEYRING_ITEM_ATTRIBUTE_NAME, - SECRET_SCHEMA_ATTRIBUTE_STRING }, - { NULL, 0 } - } -}; -#endif /* HAVE_GOA_PASSWORD_BASED */ - /* Module Entry Points */ void e_module_load (GTypeModule *type_module); void e_module_unload (GTypeModule *type_module); @@ -582,90 +563,6 @@ gnome_online_accounts_config_oauth2 (EGnomeOnlineAccounts *extension, } static void -gnome_online_accounts_config_password (EGnomeOnlineAccounts *extension, - ESource *source, - GoaObject *goa_object) -{ -#ifdef HAVE_GOA_PASSWORD_BASED - GoaAccount *goa_account; - GoaPasswordBased *goa_password_based; - EAsyncClosure *closure; - GAsyncResult *result; - const gchar *uid; - gchar *arg_id; - gchar *display_name; - gchar *password = NULL; - GError *error = NULL; - - /* If the GNOME Online Account is password-based, we use its - * password to seed our own keyring entry for the collection - * source which avoids having to special-case authentication - * like we do for OAuth. Plus, if the stored password is no - * good we'll prompt for a new one instead of just giving up. */ - - goa_password_based = goa_object_get_password_based (goa_object); - - if (goa_password_based == NULL) - return; - - closure = e_async_closure_new (); - - /* XXX The GOA documentation doesn't explain the string - * argument in goa_password_based_get_password() so - * we'll pass in the identity and hope for the best. */ - goa_account = goa_object_get_account (goa_object); - arg_id = goa_account_dup_identity (goa_account); - g_object_unref (goa_account); - - goa_password_based_call_get_password ( - goa_password_based, arg_id, NULL, - e_async_closure_callback, closure); - - g_free (arg_id); - - result = e_async_closure_wait (closure); - - goa_password_based_call_get_password_finish ( - goa_password_based, &password, result, &error); - - if (error != NULL) { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_error_free (error); - goto exit; - } - - uid = e_source_get_uid (source); - display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid); - - secret_password_store ( - &schema, SECRET_COLLECTION_DEFAULT, - display_name, password, NULL, - e_async_closure_callback, closure, - KEYRING_ITEM_ATTRIBUTE_NAME, uid, - NULL); - - result = e_async_closure_wait (closure); - - secret_password_store_finish (result, &error); - - g_free (display_name); - g_free (password); - - /* If we fail to store the password, we'll just end up prompting - * for a password like normal. Annoying, maybe, but not the end - * of the world. Still leave a breadcrumb for debugging though. */ - if (error != NULL) { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_error_free (error); - } - -exit: - e_async_closure_free (closure); - g_object_unref (goa_password_based); -#endif /* HAVE_GOA_PASSWORD_BASED */ -} - -static void gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension, ESource *source, GoaObject *goa_object) @@ -764,12 +661,20 @@ gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension, /* Handle optional GOA interfaces. */ gnome_online_accounts_config_exchange (extension, source, goa_object); - gnome_online_accounts_config_password (extension, source, goa_object); /* The data source should not be removable by clients. */ e_server_side_source_set_removable ( E_SERVER_SIDE_SOURCE (source), FALSE); +#ifdef HAVE_GOA_PASSWORD_BASED + if (goa_object_peek_password_based (goa_object) != NULL) { + /* Obtain passwords from the OnlineAccounts service. */ + e_server_side_source_set_auth_session_type ( + E_SERVER_SIDE_SOURCE (source), + E_TYPE_GOA_PASSWORD_BASED); + } +#endif /* HAVE_GOA_PASSWORD_BASED */ + if (goa_object_peek_oauth2_based (goa_object) != NULL) { /* This module provides OAuth 2.0 support to the collection. * Note, children of the collection source will automatically @@ -1406,6 +1311,7 @@ e_gnome_online_accounts_init (EGnomeOnlineAccounts *extension) G_MODULE_EXPORT void e_module_load (GTypeModule *type_module) { + e_goa_password_based_type_register (type_module); e_gnome_online_accounts_register_type (type_module); }