]> git.0d.be Git - empathy.git/blob - libempathy/empathy-uoa-auth-handler.c
UOA auth handler: Inform SSO when access token didn't work
[empathy.git] / libempathy / empathy-uoa-auth-handler.c
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>
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #include <libaccounts-glib/ag-account.h>
24 #include <libaccounts-glib/ag-account-service.h>
25 #include <libaccounts-glib/ag-auth-data.h>
26 #include <libaccounts-glib/ag-manager.h>
27 #include <libaccounts-glib/ag-service.h>
28
29 #include <libsignon-glib/signon-identity.h>
30 #include <libsignon-glib/signon-auth-session.h>
31
32 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
33 #include "empathy-debug.h"
34 #include "empathy-utils.h"
35 #include "empathy-uoa-auth-handler.h"
36 #include "empathy-sasl-mechanisms.h"
37
38 #define SERVICE_TYPE "IM"
39
40 struct _EmpathyUoaAuthHandlerPriv
41 {
42   AgManager *manager;
43 };
44
45 G_DEFINE_TYPE (EmpathyUoaAuthHandler, empathy_uoa_auth_handler, G_TYPE_OBJECT);
46
47 static void
48 empathy_uoa_auth_handler_init (EmpathyUoaAuthHandler *self)
49 {
50   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
51       EMPATHY_TYPE_UOA_AUTH_HANDLER, EmpathyUoaAuthHandlerPriv);
52
53   self->priv->manager = ag_manager_new_for_service_type (SERVICE_TYPE);
54 }
55
56 static void
57 empathy_uoa_auth_handler_dispose (GObject *object)
58 {
59   EmpathyUoaAuthHandler *self = (EmpathyUoaAuthHandler *) object;
60
61   tp_clear_object (&self->priv->manager);
62
63   G_OBJECT_CLASS (empathy_uoa_auth_handler_parent_class)->dispose (object);
64 }
65
66 static void
67 empathy_uoa_auth_handler_class_init (EmpathyUoaAuthHandlerClass *klass)
68 {
69   GObjectClass *oclass = G_OBJECT_CLASS (klass);
70
71   oclass->dispose = empathy_uoa_auth_handler_dispose;
72
73   g_type_class_add_private (klass, sizeof (EmpathyUoaAuthHandlerPriv));
74 }
75
76 EmpathyUoaAuthHandler *
77 empathy_uoa_auth_handler_new (void)
78 {
79   return g_object_new (EMPATHY_TYPE_UOA_AUTH_HANDLER, NULL);
80 }
81
82 typedef struct
83 {
84   TpChannel *channel;
85   AgAuthData *auth_data;
86   SignonAuthSession *session;
87   SignonIdentity *identity;
88
89   gchar *username;
90 } QueryInfoData;
91
92 static QueryInfoData *
93 query_info_data_new (TpChannel *channel,
94     AgAuthData *auth_data,
95     SignonAuthSession *session,
96     SignonIdentity *identity)
97 {
98   QueryInfoData *data;
99
100   data = g_slice_new0 (QueryInfoData);
101   data->channel = g_object_ref (channel);
102   data->auth_data = ag_auth_data_ref (auth_data);
103   data->session = g_object_ref (session);
104   data->identity = g_object_ref (identity);
105
106   return data;
107 }
108
109 static void
110 query_info_data_free (QueryInfoData *data)
111 {
112   g_object_unref (data->channel);
113   ag_auth_data_unref (data->auth_data);
114   g_object_unref (data->session);
115   g_object_unref (data->identity);
116   g_free (data->username);
117   g_slice_free (QueryInfoData, data);
118 }
119
120 static void
121 auth_cb (GObject *source,
122     GAsyncResult *result,
123     gpointer user_data)
124 {
125   TpChannel *channel = (TpChannel *) source;
126   QueryInfoData *data = user_data;
127   GError *error = NULL;
128
129   if (!empathy_sasl_auth_finish (channel, result, &error))
130     {
131       GHashTable *extra_params;
132
133       DEBUG ("SASL Mechanism error: %s", error->message);
134       g_clear_error (&error);
135
136       /* Inform SSO that the access token didn't work and it should ask user
137        * to re-grant access. */
138       extra_params = tp_asv_new (
139           SIGNON_SESSION_DATA_UI_POLICY, G_TYPE_INT,
140               SIGNON_POLICY_REQUEST_PASSWORD,
141           NULL);
142
143       ag_auth_data_insert_parameters (data->auth_data, extra_params);
144
145       signon_auth_session_process (data->session,
146           ag_auth_data_get_parameters (data->auth_data),
147           ag_auth_data_get_mechanism (data->auth_data),
148           NULL, NULL);
149
150       g_hash_table_unref (extra_params);
151     }
152   else
153     {
154       DEBUG ("Auth on %s suceeded", tp_proxy_get_object_path (channel));
155     }
156
157   tp_channel_close_async (channel, NULL, NULL);
158   query_info_data_free (data);
159 }
160
161 static void
162 session_process_cb (SignonAuthSession *session,
163     GHashTable *session_data,
164     const GError *error,
165     gpointer user_data)
166 {
167   QueryInfoData *data = user_data;
168   const gchar *access_token;
169   const gchar *client_id;
170
171   if (error != NULL)
172     {
173       DEBUG ("Error processing the session: %s", error->message);
174       tp_channel_close_async (data->channel, NULL, NULL);
175       query_info_data_free (data);
176       return;
177     }
178
179   access_token = tp_asv_get_string (session_data, "AccessToken");
180   client_id = tp_asv_get_string (ag_auth_data_get_parameters (data->auth_data),
181       "ClientId");
182
183   switch (empathy_sasl_channel_select_mechanism (data->channel))
184     {
185       case EMPATHY_SASL_MECHANISM_FACEBOOK:
186         empathy_sasl_auth_facebook_async (data->channel,
187             client_id, access_token,
188             auth_cb, data);
189         break;
190
191       case EMPATHY_SASL_MECHANISM_WLM:
192         empathy_sasl_auth_wlm_async (data->channel,
193             access_token,
194             auth_cb, data);
195         break;
196
197       case EMPATHY_SASL_MECHANISM_GOOGLE:
198         empathy_sasl_auth_google_async (data->channel,
199             data->username, access_token,
200             auth_cb, data);
201         break;
202
203       default:
204         g_assert_not_reached ();
205     }
206 }
207
208 static void
209 identity_query_info_cb (SignonIdentity *identity,
210     const SignonIdentityInfo *info,
211     const GError *error,
212     gpointer user_data)
213 {
214   QueryInfoData *data = user_data;
215
216   if (error != NULL)
217     {
218       DEBUG ("Error querying info from identity: %s", error->message);
219       tp_channel_close_async (data->channel, NULL, NULL);
220       query_info_data_free (data);
221       return;
222     }
223
224   data->username = g_strdup (signon_identity_info_get_username (info));
225
226   signon_auth_session_process (data->session,
227       ag_auth_data_get_parameters (data->auth_data),
228       ag_auth_data_get_mechanism (data->auth_data),
229       session_process_cb,
230       data);
231 }
232
233 void
234 empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self,
235     TpChannel *channel,
236     TpAccount *tp_account)
237 {
238   const GValue *id_value;
239   AgAccountId id;
240   AgAccount *account;
241   GList *l = NULL;
242   AgAccountService *service;
243   AgAuthData *auth_data;
244   guint cred_id;
245   SignonIdentity *identity;
246   SignonAuthSession *session;
247   GError *error = NULL;
248
249   g_return_if_fail (TP_IS_CHANNEL (channel));
250   g_return_if_fail (TP_IS_ACCOUNT (tp_account));
251   g_return_if_fail (empathy_uoa_auth_handler_supports (self, channel,
252       tp_account));
253
254   DEBUG ("Start UOA auth for account: %s",
255       tp_proxy_get_object_path (tp_account));
256
257   id_value = tp_account_get_storage_identifier (tp_account);
258   id = g_value_get_uint (id_value);
259
260   account = ag_manager_get_account (self->priv->manager, id);
261   if (account != NULL)
262     l = ag_account_list_services_by_type (account, SERVICE_TYPE);
263   if (l == NULL)
264     {
265       DEBUG ("Couldn't find IM service for AgAccountId %u", id);
266       g_object_unref (account);
267       tp_channel_close_async (channel, NULL, NULL);
268       return;
269     }
270
271   /* Assume there is only one IM service */
272   service = ag_account_service_new (account, l->data);
273   ag_service_list_free (l);
274   g_object_unref (account);
275
276   auth_data = ag_account_service_get_auth_data (service);
277   cred_id = ag_auth_data_get_credentials_id (auth_data);
278   identity = signon_identity_new_from_db (cred_id);
279   session = signon_identity_create_session (identity,
280       ag_auth_data_get_method (auth_data),
281       &error);
282   if (session == NULL)
283     {
284       DEBUG ("Error creating a SignonAuthSession: %s", error->message);
285       tp_channel_close_async (channel, NULL, NULL);
286       goto cleanup;
287     }
288
289   /* Query UOA for more info */
290   signon_identity_query_info (identity,
291       identity_query_info_cb,
292       query_info_data_new (channel, auth_data, session, identity));
293
294 cleanup:
295   ag_auth_data_unref (auth_data);
296   g_object_unref (service);
297   g_object_unref (identity);
298   g_object_unref (session);
299 }
300
301 gboolean
302 empathy_uoa_auth_handler_supports (EmpathyUoaAuthHandler *self,
303     TpChannel *channel,
304     TpAccount *account)
305 {
306   const gchar *provider;
307   EmpathySaslMechanism mech;
308
309   g_return_val_if_fail (TP_IS_CHANNEL (channel), FALSE);
310   g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
311
312   provider = tp_account_get_storage_provider (account);
313
314   if (tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
315     return FALSE;
316
317   mech = empathy_sasl_channel_select_mechanism (channel);
318   return mech == EMPATHY_SASL_MECHANISM_FACEBOOK ||
319       mech == EMPATHY_SASL_MECHANISM_WLM ||
320       mech == EMPATHY_SASL_MECHANISM_GOOGLE;
321 }