From: Xavier Claessens Date: Mon, 16 Jul 2012 12:15:45 +0000 (+0200) Subject: Add Ubuntu Online Accounts auth handler X-Git-Url: https://git.0d.be/?p=empathy.git;a=commitdiff_plain;h=fa8970d7304e81821dd0b1ed6a6704add1904af9 Add Ubuntu Online Accounts auth handler --- diff --git a/configure.ac b/configure.ac index 290280d1..ed9b560e 100644 --- a/configure.ac +++ b/configure.ac @@ -76,6 +76,7 @@ NETWORK_MANAGER_REQUIRED=0.7.0 CHAMPLAIN_REQUIRED=0.12.1 CHEESE_GTK_REQUIRED=3.4.0 LIBACCOUNTS_REQUIRED=1.1 +LIBSIGNON_REQUIRED=1.1 MC_PLUGINS_REQUIRED=5.13.0 # Use --enable-maintainer-mode to disable deprecated symbols, @@ -514,6 +515,7 @@ if test "x$enable_ubuntu_online_accounts" != "xno"; then account-plugin mission-control-plugins >= $MC_PLUGINS_REQUIRED libaccounts-glib >= $LIBACCOUNTS_REQUIRED + libsignon-glib >= $LIBSIGNON_REQUIRED ], have_uoa="yes", have_uoa="no") # MC plugins dir diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am index 67e64a9f..2274b99e 100644 --- a/libempathy/Makefile.am +++ b/libempathy/Makefile.am @@ -16,6 +16,7 @@ AM_CPPFLAGS = \ $(CONNMAN_CFLAGS) \ $(UDEV_CFLAGS) \ $(GOA_CFLAGS) \ + $(UOA_CFLAGS) \ $(WARN_CFLAGS) \ $(DISABLE_DEPRECATED) @@ -99,6 +100,12 @@ goa_sources = \ empathy-goa-auth-handler.h \ $(NULL) +# these are sources that depend on Ubuntu Online Accounts +uoa_sources = \ + empathy-uoa-auth-handler.c \ + empathy-uoa-auth-handler.h \ + $(NULL) + pkglib_LTLIBRARIES = libempathy.la # libempathy's API is not stable and will never be, so use -release to make the @@ -122,6 +129,7 @@ libempathy_la_LIBADD = \ $(CONNMAN_LIBS) \ $(UDEV_LIBS) \ $(GOA_LIBS) \ + $(UOA_LIBS) \ $(LIBM) dtddir = $(datadir)/empathy @@ -149,6 +157,12 @@ else EXTRA_DIST += $(goa_sources) endif +if HAVE_UOA +libempathy_la_SOURCES += $(uoa_sources) +else +EXTRA_DIST += $(uoa_sources) +endif + # do not distribute generated files nodist_libempathy_la_SOURCES =\ $(BUILT_SOURCES) @@ -156,6 +170,7 @@ nodist_libempathy_la_SOURCES =\ check_c_sources = \ $(libempathy_handwritten_source) \ $(goa_sources) \ + $(uoa_sources) \ $(NULL) include $(top_srcdir)/tools/check-coding-style.mk diff --git a/libempathy/empathy-auth-factory.c b/libempathy/empathy-auth-factory.c index 92252857..a4bc15a6 100644 --- a/libempathy/empathy-auth-factory.c +++ b/libempathy/empathy-auth-factory.c @@ -36,6 +36,10 @@ #include "empathy-goa-auth-handler.h" #endif /* HAVE_GOA */ +#ifdef HAVE_UOA +#include "empathy-uoa-auth-handler.h" +#endif /* HAVE_UOA */ + #include "extensions/extensions.h" G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, TP_TYPE_BASE_CLIENT); @@ -54,6 +58,10 @@ struct _EmpathyAuthFactoryPriv { EmpathyGoaAuthHandler *goa_handler; #endif /* HAVE_GOA */ +#ifdef HAVE_UOA + EmpathyUoaAuthHandler *uoa_handler; +#endif /* HAVE_UOA */ + /* If an account failed to connect and user enters a new password to try, we * store it in this hash table and will try to use it next time the account * attemps to connect. @@ -474,6 +482,32 @@ goa_claim_cb (GObject *source, } #endif /* HAVE_GOA */ +#ifdef HAVE_UOA +static void +uoa_claim_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + ObserveChannelsData *data = user_data; + EmpathyAuthFactory *self = data->self; + GError *error = NULL; + + if (!tp_channel_dispatch_operation_claim_with_finish (data->dispatch_operation, + result, &error)) + { + DEBUG ("Failed to claim: %s", error->message); + g_clear_error (&error); + } + else + { + empathy_uoa_auth_handler_start (self->priv->uoa_handler, + data->channel, data->account); + } + + observe_channels_data_free (data); +} +#endif /* HAVE_UOA */ + static void observe_channels (TpBaseClient *client, TpAccount *account, @@ -522,6 +556,20 @@ observe_channels (TpBaseClient *client, } #endif /* HAVE_GOA */ +#ifdef HAVE_UOA + /* UOA auth? */ + if (empathy_uoa_auth_handler_supports (self->priv->uoa_handler, channel, account)) + { + DEBUG ("Supported UOA account (%s), claim SASL channel", + tp_proxy_get_object_path (account)); + + tp_channel_dispatch_operation_claim_with_async (dispatch_operation, + client, uoa_claim_cb, data); + tp_observe_channels_context_accept (context); + return; + } +#endif /* HAVE_UOA */ + /* Password auth? */ if (empathy_sasl_channel_supports_mechanism (data->channel, "X-TELEPATHY-PASSWORD")) @@ -589,6 +637,10 @@ empathy_auth_factory_init (EmpathyAuthFactory *self) self->priv->goa_handler = empathy_goa_auth_handler_new (); #endif /* HAVE_GOA */ +#ifdef HAVE_UOA + self->priv->uoa_handler = empathy_uoa_auth_handler_new (); +#endif /* HAVE_UOA */ + self->priv->retry_passwords = g_hash_table_new_full (NULL, NULL, g_object_unref, g_free); } @@ -657,6 +709,10 @@ empathy_auth_factory_dispose (GObject *object) g_object_unref (priv->goa_handler); #endif /* HAVE_GOA */ +#ifdef HAVE_UOA + g_object_unref (priv->uoa_handler); +#endif /* HAVE_UOA */ + g_hash_table_unref (priv->retry_passwords); G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object); diff --git a/libempathy/empathy-uoa-auth-handler.c b/libempathy/empathy-uoa-auth-handler.c new file mode 100644 index 00000000..1bfd6e16 --- /dev/null +++ b/libempathy/empathy-uoa-auth-handler.c @@ -0,0 +1,299 @@ +/* + * empathy-auth-uoa.c - Source for Uoa SASL authentication + * Copyright (C) 2012 Collabora Ltd. + * @author Xavier Claessens + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#define DEBUG_FLAG EMPATHY_DEBUG_SASL +#include "empathy-debug.h" +#include "empathy-utils.h" +#include "empathy-uoa-auth-handler.h" +#include "empathy-sasl-mechanisms.h" + +#define SERVICE_TYPE "IM" + +struct _EmpathyUoaAuthHandlerPriv +{ + AgManager *manager; +}; + +G_DEFINE_TYPE (EmpathyUoaAuthHandler, empathy_uoa_auth_handler, G_TYPE_OBJECT); + +static void +empathy_uoa_auth_handler_init (EmpathyUoaAuthHandler *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_UOA_AUTH_HANDLER, EmpathyUoaAuthHandlerPriv); + + self->priv->manager = ag_manager_new_for_service_type (SERVICE_TYPE); +} + +static void +empathy_uoa_auth_handler_dispose (GObject *object) +{ + EmpathyUoaAuthHandler *self = (EmpathyUoaAuthHandler *) object; + + tp_clear_object (&self->priv->manager); + + G_OBJECT_CLASS (empathy_uoa_auth_handler_parent_class)->dispose (object); +} + +static void +empathy_uoa_auth_handler_class_init (EmpathyUoaAuthHandlerClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + oclass->dispose = empathy_uoa_auth_handler_dispose; + + g_type_class_add_private (klass, sizeof (EmpathyUoaAuthHandlerPriv)); +} + +EmpathyUoaAuthHandler * +empathy_uoa_auth_handler_new (void) +{ + return g_object_new (EMPATHY_TYPE_UOA_AUTH_HANDLER, NULL); +} + +static void +auth_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpChannel *channel = (TpChannel *) source; + GError *error = NULL; + + if (!empathy_sasl_auth_finish (channel, result, &error)) + { + DEBUG ("SASL Mechanism error: %s", error->message); + g_clear_error (&error); + } + + tp_channel_close_async (channel, NULL, NULL); +} + +typedef struct +{ + TpChannel *channel; + AgAuthData *auth_data; + SignonAuthSession *session; + SignonIdentity *identity; + + gchar *username; +} QueryInfoData; + +static QueryInfoData * +query_info_data_new (TpChannel *channel, + AgAuthData *auth_data, + SignonAuthSession *session, + SignonIdentity *identity) +{ + QueryInfoData *data; + + data = g_slice_new0 (QueryInfoData); + data->channel = g_object_ref (channel); + data->auth_data = ag_auth_data_ref (auth_data); + data->session = g_object_ref (session); + data->identity = g_object_ref (identity); + + return data; +} + +static void +query_info_data_free (QueryInfoData *data) +{ + g_object_unref (data->channel); + ag_auth_data_unref (data->auth_data); + g_object_unref (data->session); + g_object_unref (data->identity); + g_free (data->username); + g_slice_free (QueryInfoData, data); +} + +static void +session_process_cb (SignonAuthSession *session, + GHashTable *session_data, + const GError *error, + gpointer user_data) +{ + QueryInfoData *data = user_data; + const gchar *access_token; + const gchar *client_id; + + if (error != NULL) + { + DEBUG ("Error processing the session: %s", error->message); + tp_channel_close_async (data->channel, NULL, NULL); + query_info_data_free (data); + return; + } + + access_token = tp_asv_get_string (session_data, "AccessToken"); + client_id = tp_asv_get_string (ag_auth_data_get_parameters (data->auth_data), + "ClientId"); + + switch (empathy_sasl_channel_select_mechanism (data->channel)) + { + case EMPATHY_SASL_MECHANISM_FACEBOOK: + empathy_sasl_auth_facebook_async (data->channel, + client_id, access_token, + auth_cb, NULL); + break; + + case EMPATHY_SASL_MECHANISM_WLM: + empathy_sasl_auth_wlm_async (data->channel, + access_token, + auth_cb, NULL); + break; + + case EMPATHY_SASL_MECHANISM_GOOGLE: + empathy_sasl_auth_google_async (data->channel, + data->username, access_token, + auth_cb, NULL); + break; + + default: + g_assert_not_reached (); + } + + query_info_data_free (data); +} + +static void +identity_query_info_cb (SignonIdentity *identity, + const SignonIdentityInfo *info, + const GError *error, + gpointer user_data) +{ + QueryInfoData *data = user_data; + + if (error != NULL) + { + DEBUG ("Error querying info from identity: %s", error->message); + tp_channel_close_async (data->channel, NULL, NULL); + query_info_data_free (data); + return; + } + + data->username = g_strdup (signon_identity_info_get_username (info)); + + signon_auth_session_process (data->session, + ag_auth_data_get_parameters (data->auth_data), + ag_auth_data_get_mechanism (data->auth_data), + session_process_cb, + data); +} + +void +empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self, + TpChannel *channel, + TpAccount *tp_account) +{ + const GValue *id_value; + AgAccountId id; + AgAccount *account; + GList *l = NULL; + AgAccountService *service; + AgAuthData *auth_data; + guint cred_id; + SignonIdentity *identity; + SignonAuthSession *session; + GError *error = NULL; + + g_return_if_fail (TP_IS_CHANNEL (channel)); + g_return_if_fail (TP_IS_ACCOUNT (tp_account)); + g_return_if_fail (empathy_uoa_auth_handler_supports (self, channel, + tp_account)); + + DEBUG ("Start UOA auth for account: %s", + tp_proxy_get_object_path (tp_account)); + + id_value = tp_account_get_storage_identifier (tp_account); + id = g_value_get_uint (id_value); + + account = ag_manager_get_account (self->priv->manager, id); + if (account != NULL) + l = ag_account_list_services_by_type (account, SERVICE_TYPE); + if (l == NULL) + { + DEBUG ("Couldn't find IM service for AgAccountId %u", id); + g_object_unref (account); + tp_channel_close_async (channel, NULL, NULL); + return; + } + + /* Assume there is only one IM service */ + service = ag_account_service_new (account, l->data); + ag_service_list_free (l); + g_object_unref (account); + + auth_data = ag_account_service_get_auth_data (service); + cred_id = ag_auth_data_get_credentials_id (auth_data); + identity = signon_identity_new_from_db (cred_id); + session = signon_identity_create_session (identity, + ag_auth_data_get_method (auth_data), + &error); + if (session == NULL) + { + DEBUG ("Error creating a SignonAuthSession: %s", error->message); + tp_channel_close_async (channel, NULL, NULL); + goto cleanup; + } + + /* Query UOA for more info */ + signon_identity_query_info (identity, + identity_query_info_cb, + query_info_data_new (channel, auth_data, session, identity)); + +cleanup: + ag_auth_data_unref (auth_data); + g_object_unref (service); + g_object_unref (identity); + g_object_unref (session); +} + +gboolean +empathy_uoa_auth_handler_supports (EmpathyUoaAuthHandler *self, + TpChannel *channel, + TpAccount *account) +{ + const gchar *provider; + EmpathySaslMechanism mech; + + g_return_val_if_fail (TP_IS_CHANNEL (channel), FALSE); + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + + provider = tp_account_get_storage_provider (account); + + if (tp_strdiff (provider, EMPATHY_UOA_PROVIDER)) + return FALSE; + + mech = empathy_sasl_channel_select_mechanism (channel); + return mech == EMPATHY_SASL_MECHANISM_FACEBOOK || + mech == EMPATHY_SASL_MECHANISM_WLM || + mech == EMPATHY_SASL_MECHANISM_GOOGLE; +} diff --git a/libempathy/empathy-uoa-auth-handler.h b/libempathy/empathy-uoa-auth-handler.h new file mode 100644 index 00000000..2c61a760 --- /dev/null +++ b/libempathy/empathy-uoa-auth-handler.h @@ -0,0 +1,72 @@ +/* + * empathy-auth-uoa.h - Header for Uoa SASL authentication + * Copyright (C) 2012 Collabora Ltd. + * @author Xavier Claessens + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __EMPATHY_UOA_AUTH_HANDLER_H__ +#define __EMPATHY_UOA_AUTH_HANDLER_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _EmpathyUoaAuthHandler EmpathyUoaAuthHandler; +typedef struct _EmpathyUoaAuthHandlerClass EmpathyUoaAuthHandlerClass; +typedef struct _EmpathyUoaAuthHandlerPriv EmpathyUoaAuthHandlerPriv; + +struct _EmpathyUoaAuthHandlerClass { + GObjectClass parent_class; +}; + +struct _EmpathyUoaAuthHandler { + GObject parent; + EmpathyUoaAuthHandlerPriv *priv; +}; + +GType empathy_uoa_auth_handler_get_type (void); + +/* TYPE MACROS */ +#define EMPATHY_TYPE_UOA_AUTH_HANDLER \ + (empathy_uoa_auth_handler_get_type ()) +#define EMPATHY_UOA_AUTH_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_UOA_AUTH_HANDLER, \ + EmpathyUoaAuthHandler)) +#define EMPATHY_UOA_AUTH_HANDLER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_UOA_AUTH_HANDLER, \ + EmpathyUoaAuthHandlerClass)) +#define EMPATHY_IS_UOA_AUTH_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_UOA_AUTH_HANDLER)) +#define EMPATHY_IS_UOA_AUTH_HANDLER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_UOA_AUTH_HANDLER)) +#define EMPATHY_UOA_AUTH_HANDLER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_UOA_AUTH_HANDLER, \ + EmpathyUoaAuthHandlerClass)) + +EmpathyUoaAuthHandler *empathy_uoa_auth_handler_new (void); + +void empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self, + TpChannel *channel, + TpAccount *account); + +gboolean empathy_uoa_auth_handler_supports (EmpathyUoaAuthHandler *self, + TpChannel *channel, + TpAccount *account); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_UOA_AUTH_HANDLER_H__*/