Updated Czech translation
[nijm-empathy.git] / libempathy / empathy-uoa-auth-handler.c
blob692be2dd93ac27a4322ef3631d3885f3599f75df
1 /*
2 * empathy-auth-uoa.c - Source for Uoa SASL authentication
3 * Copyright (C) 2012 Collabora Ltd.
4 * @author Xavier Claessens <xavier.claessens@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "config.h"
22 #include "empathy-uoa-auth-handler.h"
24 #include <libaccounts-glib/ag-account.h>
25 #include <libaccounts-glib/ag-account-service.h>
26 #include <libaccounts-glib/ag-auth-data.h>
27 #include <libaccounts-glib/ag-manager.h>
28 #include <libaccounts-glib/ag-service.h>
30 #include <libsignon-glib/signon-identity.h>
31 #include <libsignon-glib/signon-auth-session.h>
33 #include <tp-account-widgets/tpaw-keyring.h>
34 #include <tp-account-widgets/tpaw-uoa-utils.h>
36 #include "empathy-utils.h"
37 #include "empathy-sasl-mechanisms.h"
39 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
40 #include "empathy-debug.h"
42 struct _EmpathyUoaAuthHandlerPriv
44 AgManager *manager;
47 G_DEFINE_TYPE (EmpathyUoaAuthHandler, empathy_uoa_auth_handler, G_TYPE_OBJECT);
49 static void
50 empathy_uoa_auth_handler_init (EmpathyUoaAuthHandler *self)
52 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
53 EMPATHY_TYPE_UOA_AUTH_HANDLER, EmpathyUoaAuthHandlerPriv);
55 self->priv->manager = tpaw_uoa_manager_dup ();
58 static void
59 empathy_uoa_auth_handler_dispose (GObject *object)
61 EmpathyUoaAuthHandler *self = (EmpathyUoaAuthHandler *) object;
63 tp_clear_object (&self->priv->manager);
65 G_OBJECT_CLASS (empathy_uoa_auth_handler_parent_class)->dispose (object);
68 static void
69 empathy_uoa_auth_handler_class_init (EmpathyUoaAuthHandlerClass *klass)
71 GObjectClass *oclass = G_OBJECT_CLASS (klass);
73 oclass->dispose = empathy_uoa_auth_handler_dispose;
75 g_type_class_add_private (klass, sizeof (EmpathyUoaAuthHandlerPriv));
78 EmpathyUoaAuthHandler *
79 empathy_uoa_auth_handler_new (void)
81 return g_object_new (EMPATHY_TYPE_UOA_AUTH_HANDLER, NULL);
84 typedef struct
86 TpChannel *channel;
87 AgAccountService *service;
88 AgAuthData *auth_data;
89 SignonIdentity *identity;
90 SignonAuthSession *session;
92 gchar *username;
93 } AuthContext;
95 static AuthContext *
96 auth_context_new (TpChannel *channel,
97 AgAccountService *service)
99 AuthContext *ctx;
100 guint cred_id;
102 ctx = g_slice_new0 (AuthContext);
103 ctx->channel = g_object_ref (channel);
104 ctx->service = g_object_ref (service);
106 ctx->auth_data = ag_account_service_get_auth_data (service);
107 if (ctx->auth_data == NULL)
108 goto out;
110 cred_id = ag_auth_data_get_credentials_id (ctx->auth_data);
111 if (cred_id == 0)
112 goto out;
114 ctx->identity = signon_identity_new_from_db (cred_id);
115 if (ctx->identity == NULL)
116 goto out;
118 ctx->session = signon_identity_create_session (ctx->identity,
119 ag_auth_data_get_method (ctx->auth_data), NULL);
120 if (ctx->session == NULL)
121 goto out;
123 out:
124 return ctx;
127 static void
128 auth_context_free (AuthContext *ctx)
130 g_clear_object (&ctx->channel);
131 g_clear_object (&ctx->service);
132 tp_clear_pointer (&ctx->auth_data, ag_auth_data_unref);
133 g_clear_object (&ctx->session);
134 g_clear_object (&ctx->identity);
135 g_free (ctx->username);
137 g_slice_free (AuthContext, ctx);
140 static void
141 auth_context_done (AuthContext *ctx)
143 tp_channel_close_async (ctx->channel, NULL, NULL);
144 auth_context_free (ctx);
147 static void
148 request_password_session_process_cb (GObject *source,
149 GAsyncResult *result,
150 gpointer user_data)
152 SignonAuthSession *session = (SignonAuthSession *) source;
153 AuthContext *ctx = user_data;
154 GVariant *variant;
155 GError *error = NULL;
157 variant = signon_auth_session_process_finish (session, result, &error);
158 if (error != NULL)
160 DEBUG ("Error processing the session to request user's attention: %s",
161 error->message);
162 g_clear_error (&error);
165 g_variant_unref (variant);
167 auth_context_done (ctx);
170 static void
171 request_password (AuthContext *ctx)
173 GVariantBuilder builder;
175 DEBUG ("Invalid credentials, request user action");
177 /* Inform SSO that the access token (or password) didn't work and it should
178 * ask user to re-grant access (or retype password). */
179 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
180 g_variant_builder_add (&builder, "{sv}",
181 SIGNON_SESSION_DATA_UI_POLICY,
182 g_variant_new_int32 (SIGNON_POLICY_REQUEST_PASSWORD));
184 signon_auth_session_process_async (ctx->session,
185 ag_auth_data_get_login_parameters (ctx->auth_data,
186 g_variant_builder_end (&builder)),
187 ag_auth_data_get_mechanism (ctx->auth_data),
188 NULL,
189 request_password_session_process_cb, ctx);
192 static void
193 auth_cb (GObject *source,
194 GAsyncResult *result,
195 gpointer user_data)
197 TpChannel *channel = (TpChannel *) source;
198 AuthContext *ctx = user_data;
199 GError *error = NULL;
201 if (!empathy_sasl_auth_finish (channel, result, &error))
203 DEBUG ("SASL Mechanism error: %s", error->message);
204 g_clear_error (&error);
206 request_password (ctx);
208 else
210 DEBUG ("Auth on %s suceeded", tp_proxy_get_object_path (channel));
211 auth_context_done (ctx);
215 static void
216 session_process_cb (GObject *source,
217 GAsyncResult *result,
218 gpointer user_data)
220 SignonAuthSession *session = (SignonAuthSession *) source;
221 AuthContext *ctx = user_data;
222 GVariant *session_data;
223 const gchar *access_token;
224 const gchar *client_id;
225 const gchar *secret;
226 GVariant *params;
227 GError *error = NULL;
229 session_data = signon_auth_session_process_finish (session, result, &error);
230 if (error != NULL)
232 DEBUG ("Error processing the session: %s", error->message);
233 auth_context_done (ctx);
234 g_clear_error (&error);
235 return;
238 params = g_variant_ref_sink (
239 ag_auth_data_get_login_parameters (ctx->auth_data, NULL));
241 g_variant_lookup (params, "ClientId", "&s", &client_id);
242 g_variant_lookup (session_data, "AccessToken", "&s", &access_token);
243 g_variant_lookup (session_data, "Secret", "&s", &secret);
245 switch (empathy_sasl_channel_select_mechanism (ctx->channel))
247 case EMPATHY_SASL_MECHANISM_FACEBOOK:
248 empathy_sasl_auth_facebook_async (ctx->channel,
249 client_id, access_token,
250 auth_cb, ctx);
251 break;
253 case EMPATHY_SASL_MECHANISM_WLM:
254 empathy_sasl_auth_wlm_async (ctx->channel,
255 access_token,
256 auth_cb, ctx);
257 break;
259 case EMPATHY_SASL_MECHANISM_GOOGLE:
260 empathy_sasl_auth_google_async (ctx->channel,
261 ctx->username, access_token,
262 auth_cb, ctx);
263 break;
265 case EMPATHY_SASL_MECHANISM_PASSWORD:
266 empathy_sasl_auth_password_async (ctx->channel,
267 secret,
268 auth_cb, ctx);
269 break;
271 default:
272 g_assert_not_reached ();
275 g_variant_unref (params);
278 static void
279 identity_query_info_cb (SignonIdentity *identity,
280 const SignonIdentityInfo *info,
281 const GError *error,
282 gpointer user_data)
284 AuthContext *ctx = user_data;
286 if (error != NULL)
288 DEBUG ("Error querying info from identity: %s", error->message);
289 auth_context_done (ctx);
290 return;
293 ctx->username = g_strdup (signon_identity_info_get_username (info));
295 signon_auth_session_process_async (ctx->session,
296 ag_auth_data_get_login_parameters (ctx->auth_data, NULL),
297 ag_auth_data_get_mechanism (ctx->auth_data),
298 NULL,
299 session_process_cb,
300 ctx);
303 static void
304 set_account_password_cb (GObject *source,
305 GAsyncResult *result,
306 gpointer user_data)
308 TpAccount *tp_account = (TpAccount *) source;
309 AuthContext *ctx = user_data;
310 AuthContext *new_ctx;
311 GError *error = NULL;
313 if (!tpaw_keyring_set_account_password_finish (tp_account, result, &error))
315 DEBUG ("Failed to set empty password on UOA account: %s", error->message);
316 auth_context_done (ctx);
317 return;
320 new_ctx = auth_context_new (ctx->channel, ctx->service);
321 auth_context_free (ctx);
323 if (new_ctx->session != NULL)
325 /* The trick worked! */
326 request_password (new_ctx);
327 return;
330 DEBUG ("Still can't get a signon session, even after setting empty pwd");
331 auth_context_done (new_ctx);
334 void
335 empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self,
336 TpChannel *channel,
337 TpAccount *tp_account)
339 const GValue *id_value;
340 AgAccountId id;
341 AgAccount *account;
342 GList *l = NULL;
343 AgAccountService *service;
344 AuthContext *ctx;
346 g_return_if_fail (TP_IS_CHANNEL (channel));
347 g_return_if_fail (TP_IS_ACCOUNT (tp_account));
348 g_return_if_fail (empathy_uoa_auth_handler_supports (self, channel,
349 tp_account));
351 DEBUG ("Start UOA auth for account: %s",
352 tp_proxy_get_object_path (tp_account));
354 id_value = tp_account_get_storage_identifier (tp_account);
355 id = g_value_get_uint (id_value);
357 account = ag_manager_get_account (self->priv->manager, id);
358 if (account != NULL)
359 l = ag_account_list_services_by_type (account, TPAW_UOA_SERVICE_TYPE);
360 if (l == NULL)
362 DEBUG ("Couldn't find IM service for AgAccountId %u", id);
363 g_object_unref (account);
364 tp_channel_close_async (channel, NULL, NULL);
365 return;
368 /* Assume there is only one IM service */
369 service = ag_account_service_new (account, l->data);
370 ag_service_list_free (l);
371 g_object_unref (account);
373 ctx = auth_context_new (channel, service);
374 if (ctx->session == NULL)
376 /* This (usually?) means we never stored credentials for this account.
377 * To ask user to type his password SSO needs a SignonIdentity bound to
378 * our account. Let's store an empty password. */
379 DEBUG ("Couldn't create a signon session");
380 tpaw_keyring_set_account_password_async (tp_account, "", FALSE,
381 set_account_password_cb, ctx);
383 else
385 /* All is fine! Query UOA for more info */
386 signon_identity_query_info (ctx->identity,
387 identity_query_info_cb, ctx);
390 g_object_unref (service);
393 gboolean
394 empathy_uoa_auth_handler_supports (EmpathyUoaAuthHandler *self,
395 TpChannel *channel,
396 TpAccount *account)
398 const gchar *provider;
399 EmpathySaslMechanism mech;
401 g_return_val_if_fail (TP_IS_CHANNEL (channel), FALSE);
402 g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
404 provider = tp_account_get_storage_provider (account);
406 if (tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
407 return FALSE;
409 mech = empathy_sasl_channel_select_mechanism (channel);
410 return mech == EMPATHY_SASL_MECHANISM_FACEBOOK ||
411 mech == EMPATHY_SASL_MECHANISM_WLM ||
412 mech == EMPATHY_SASL_MECHANISM_GOOGLE ||
413 mech == EMPATHY_SASL_MECHANISM_PASSWORD;