]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-uoa-auth-handler.c
Updated Dutch translation master
[empathy.git] / libempathy / empathy-uoa-auth-handler.c
index 31c1e6448829b43b46eb8dcecf21844bb11bdbd8..692be2dd93ac27a4322ef3631d3885f3599f75df 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include "config.h"
+#include "empathy-uoa-auth-handler.h"
 
 #include <libaccounts-glib/ag-account.h>
 #include <libaccounts-glib/ag-account-service.h>
 #include <libsignon-glib/signon-identity.h>
 #include <libsignon-glib/signon-auth-session.h>
 
-#define DEBUG_FLAG EMPATHY_DEBUG_SASL
-#include "empathy-debug.h"
+#include <tp-account-widgets/tpaw-keyring.h>
+#include <tp-account-widgets/tpaw-uoa-utils.h>
+
 #include "empathy-utils.h"
-#include "empathy-uoa-auth-handler.h"
 #include "empathy-sasl-mechanisms.h"
 
-#define SERVICE_TYPE "IM"
+#define DEBUG_FLAG EMPATHY_DEBUG_SASL
+#include "empathy-debug.h"
 
 struct _EmpathyUoaAuthHandlerPriv
 {
@@ -50,7 +52,7 @@ 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);
+  self->priv->manager = tpaw_uoa_manager_dup ();
 }
 
 static void
@@ -79,110 +81,198 @@ empathy_uoa_auth_handler_new (void)
   return g_object_new (EMPATHY_TYPE_UOA_AUTH_HANDLER, NULL);
 }
 
+typedef struct
+{
+  TpChannel *channel;
+  AgAccountService *service;
+  AgAuthData *auth_data;
+  SignonIdentity *identity;
+  SignonAuthSession *session;
+
+  gchar *username;
+} AuthContext;
+
+static AuthContext *
+auth_context_new (TpChannel *channel,
+    AgAccountService *service)
+{
+  AuthContext *ctx;
+  guint cred_id;
+
+  ctx = g_slice_new0 (AuthContext);
+  ctx->channel = g_object_ref (channel);
+  ctx->service = g_object_ref (service);
+
+  ctx->auth_data = ag_account_service_get_auth_data (service);
+  if (ctx->auth_data == NULL)
+    goto out;
+
+  cred_id = ag_auth_data_get_credentials_id (ctx->auth_data);
+  if (cred_id == 0)
+    goto out;
+
+  ctx->identity = signon_identity_new_from_db (cred_id);
+  if (ctx->identity == NULL)
+    goto out;
+
+  ctx->session = signon_identity_create_session (ctx->identity,
+      ag_auth_data_get_method (ctx->auth_data), NULL);
+  if (ctx->session == NULL)
+    goto out;
+
+out:
+  return ctx;
+}
+
 static void
-auth_cb (GObject *source,
+auth_context_free (AuthContext *ctx)
+{
+  g_clear_object (&ctx->channel);
+  g_clear_object (&ctx->service);
+  tp_clear_pointer (&ctx->auth_data, ag_auth_data_unref);
+  g_clear_object (&ctx->session);
+  g_clear_object (&ctx->identity);
+  g_free (ctx->username);
+
+  g_slice_free (AuthContext, ctx);
+}
+
+static void
+auth_context_done (AuthContext *ctx)
+{
+  tp_channel_close_async (ctx->channel, NULL, NULL);
+  auth_context_free (ctx);
+}
+
+static void
+request_password_session_process_cb (GObject *source,
     GAsyncResult *result,
     gpointer user_data)
 {
-  TpChannel *channel = (TpChannel *) source;
+  SignonAuthSession *session = (SignonAuthSession *) source;
+  AuthContext *ctx = user_data;
+  GVariant *variant;
   GError *error = NULL;
 
-  if (!empathy_sasl_auth_finish (channel, result, &error))
+  variant = signon_auth_session_process_finish (session, result, &error);
+  if (error != NULL)
     {
-      DEBUG ("SASL Mechanism error: %s", error->message);
+      DEBUG ("Error processing the session to request user's attention: %s",
+          error->message);
       g_clear_error (&error);
     }
 
-  DEBUG ("Auth on %s suceeded", tp_proxy_get_object_path (channel));
+  g_variant_unref (variant);
 
-  tp_channel_close_async (channel, NULL, NULL);
+  auth_context_done (ctx);
 }
 
-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)
+static void
+request_password (AuthContext *ctx)
 {
-  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;
+  GVariantBuilder builder;
+
+  DEBUG ("Invalid credentials, request user action");
+
+  /* Inform SSO that the access token (or password) didn't work and it should
+   * ask user to re-grant access (or retype password). */
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&builder, "{sv}",
+      SIGNON_SESSION_DATA_UI_POLICY,
+      g_variant_new_int32 (SIGNON_POLICY_REQUEST_PASSWORD));
+
+  signon_auth_session_process_async (ctx->session,
+      ag_auth_data_get_login_parameters (ctx->auth_data,
+          g_variant_builder_end (&builder)),
+      ag_auth_data_get_mechanism (ctx->auth_data),
+      NULL,
+      request_password_session_process_cb, ctx);
 }
 
 static void
-query_info_data_free (QueryInfoData *data)
+auth_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_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);
+  TpChannel *channel = (TpChannel *) source;
+  AuthContext *ctx = user_data;
+  GError *error = NULL;
+
+  if (!empathy_sasl_auth_finish (channel, result, &error))
+    {
+      DEBUG ("SASL Mechanism error: %s", error->message);
+      g_clear_error (&error);
+
+      request_password (ctx);
+    }
+  else
+    {
+      DEBUG ("Auth on %s suceeded", tp_proxy_get_object_path (channel));
+      auth_context_done (ctx);
+    }
 }
 
 static void
-session_process_cb (SignonAuthSession *session,
-    GHashTable *session_data,
-    const GError *error,
+session_process_cb (GObject *source,
+    GAsyncResult *result,
     gpointer user_data)
 {
-  QueryInfoData *data = user_data;
+  SignonAuthSession *session = (SignonAuthSession *) source;
+  AuthContext *ctx = user_data;
+  GVariant *session_data;
   const gchar *access_token;
   const gchar *client_id;
+  const gchar *secret;
+  GVariant *params;
+  GError *error = NULL;
 
+  session_data = signon_auth_session_process_finish (session, result, &error);
   if (error != NULL)
     {
       DEBUG ("Error processing the session: %s", error->message);
-      tp_channel_close_async (data->channel, NULL, NULL);
-      query_info_data_free (data);
+      auth_context_done (ctx);
+      g_clear_error (&error);
       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");
+  params = g_variant_ref_sink (
+      ag_auth_data_get_login_parameters (ctx->auth_data, NULL));
+
+  g_variant_lookup (params, "ClientId", "&s", &client_id);
+  g_variant_lookup (session_data, "AccessToken", "&s", &access_token);
+  g_variant_lookup (session_data, "Secret", "&s", &secret);
 
-  switch (empathy_sasl_channel_select_mechanism (data->channel))
+  switch (empathy_sasl_channel_select_mechanism (ctx->channel))
     {
       case EMPATHY_SASL_MECHANISM_FACEBOOK:
-        empathy_sasl_auth_facebook_async (data->channel,
+        empathy_sasl_auth_facebook_async (ctx->channel,
             client_id, access_token,
-            auth_cb, NULL);
+            auth_cb, ctx);
         break;
 
       case EMPATHY_SASL_MECHANISM_WLM:
-        empathy_sasl_auth_wlm_async (data->channel,
+        empathy_sasl_auth_wlm_async (ctx->channel,
             access_token,
-            auth_cb, NULL);
+            auth_cb, ctx);
         break;
 
       case EMPATHY_SASL_MECHANISM_GOOGLE:
-        empathy_sasl_auth_google_async (data->channel,
-            data->username, access_token,
-            auth_cb, NULL);
+        empathy_sasl_auth_google_async (ctx->channel,
+            ctx->username, access_token,
+            auth_cb, ctx);
+        break;
+
+      case EMPATHY_SASL_MECHANISM_PASSWORD:
+        empathy_sasl_auth_password_async (ctx->channel,
+            secret,
+            auth_cb, ctx);
         break;
 
       default:
         g_assert_not_reached ();
     }
 
-  query_info_data_free (data);
+  g_variant_unref (params);
 }
 
 static void
@@ -191,23 +281,54 @@ identity_query_info_cb (SignonIdentity *identity,
     const GError *error,
     gpointer user_data)
 {
-  QueryInfoData *data = user_data;
+  AuthContext *ctx = 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);
+      auth_context_done (ctx);
       return;
     }
 
-  data->username = g_strdup (signon_identity_info_get_username (info));
+  ctx->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),
+  signon_auth_session_process_async (ctx->session,
+      ag_auth_data_get_login_parameters (ctx->auth_data, NULL),
+      ag_auth_data_get_mechanism (ctx->auth_data),
+      NULL,
       session_process_cb,
-      data);
+      ctx);
+}
+
+static void
+set_account_password_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  TpAccount *tp_account = (TpAccount *) source;
+  AuthContext *ctx = user_data;
+  AuthContext *new_ctx;
+  GError *error = NULL;
+
+  if (!tpaw_keyring_set_account_password_finish (tp_account, result, &error))
+    {
+      DEBUG ("Failed to set empty password on UOA account: %s", error->message);
+      auth_context_done (ctx);
+      return;
+    }
+
+  new_ctx = auth_context_new (ctx->channel, ctx->service);
+  auth_context_free (ctx);
+
+  if (new_ctx->session != NULL)
+    {
+      /* The trick worked! */
+      request_password (new_ctx);
+      return;
+    }
+
+  DEBUG ("Still can't get a signon session, even after setting empty pwd");
+  auth_context_done (new_ctx);
 }
 
 void
@@ -220,11 +341,7 @@ empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self,
   AgAccount *account;
   GList *l = NULL;
   AgAccountService *service;
-  AgAuthData *auth_data;
-  guint cred_id;
-  SignonIdentity *identity;
-  SignonAuthSession *session;
-  GError *error = NULL;
+  AuthContext *ctx;
 
   g_return_if_fail (TP_IS_CHANNEL (channel));
   g_return_if_fail (TP_IS_ACCOUNT (tp_account));
@@ -239,7 +356,7 @@ empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self,
 
   account = ag_manager_get_account (self->priv->manager, id);
   if (account != NULL)
-    l = ag_account_list_services_by_type (account, SERVICE_TYPE);
+    l = ag_account_list_services_by_type (account, TPAW_UOA_SERVICE_TYPE);
   if (l == NULL)
     {
       DEBUG ("Couldn't find IM service for AgAccountId %u", id);
@@ -253,29 +370,24 @@ empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self,
   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)
+  ctx = auth_context_new (channel, service);
+  if (ctx->session == NULL)
     {
-      DEBUG ("Error creating a SignonAuthSession: %s", error->message);
-      tp_channel_close_async (channel, NULL, NULL);
-      goto cleanup;
+      /* This (usually?) means we never stored credentials for this account.
+       * To ask user to type his password SSO needs a SignonIdentity bound to
+       * our account. Let's store an empty password. */
+      DEBUG ("Couldn't create a signon session");
+      tpaw_keyring_set_account_password_async (tp_account, "", FALSE,
+          set_account_password_cb, ctx);
+    }
+  else
+    {
+      /* All is fine! Query UOA for more info */
+      signon_identity_query_info (ctx->identity,
+          identity_query_info_cb, ctx);
     }
 
-  /* 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
@@ -297,5 +409,6 @@ empathy_uoa_auth_handler_supports (EmpathyUoaAuthHandler *self,
   mech = empathy_sasl_channel_select_mechanism (channel);
   return mech == EMPATHY_SASL_MECHANISM_FACEBOOK ||
       mech == EMPATHY_SASL_MECHANISM_WLM ||
-      mech == EMPATHY_SASL_MECHANISM_GOOGLE;
+      mech == EMPATHY_SASL_MECHANISM_GOOGLE ||
+      mech == EMPATHY_SASL_MECHANISM_PASSWORD;
 }