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