*/
#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 "empathy-keyring.h"
#include "empathy-utils.h"
-#include "empathy-uoa-auth-handler.h"
#include "empathy-uoa-utils.h"
#include "empathy-sasl-mechanisms.h"
+#define DEBUG_FLAG EMPATHY_DEBUG_SASL
+#include "empathy-debug.h"
+
struct _EmpathyUoaAuthHandlerPriv
{
AgManager *manager;
typedef struct
{
TpChannel *channel;
+ AgAccountService *service;
AgAuthData *auth_data;
- SignonAuthSession *session;
SignonIdentity *identity;
+ SignonAuthSession *session;
gchar *username;
-} QueryInfoData;
+} AuthContext;
-static QueryInfoData *
-query_info_data_new (TpChannel *channel,
- AgAuthData *auth_data,
- SignonAuthSession *session,
- SignonIdentity *identity)
+static AuthContext *
+auth_context_new (TpChannel *channel,
+ AgAccountService *service)
{
- QueryInfoData *data;
+ 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;
- 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);
+ ctx->session = signon_identity_create_session (ctx->identity,
+ ag_auth_data_get_method (ctx->auth_data), NULL);
+ if (ctx->session == NULL)
+ goto out;
- return data;
+out:
+ return ctx;
+}
+
+static void
+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
-query_info_data_free (QueryInfoData *data)
+auth_context_done (AuthContext *ctx)
{
- 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);
+ tp_channel_close_async (ctx->channel, NULL, NULL);
+ auth_context_free (ctx);
+}
+
+static void
+request_password_session_process_cb (SignonAuthSession *session,
+ GHashTable *session_data,
+ const GError *error,
+ gpointer user_data)
+{
+ AuthContext *ctx = user_data;
+
+ if (error != NULL)
+ {
+ DEBUG ("Error processing the session to request user's attention: %s",
+ error->message);
+ }
+
+ auth_context_done (ctx);
+}
+
+static void
+request_password (AuthContext *ctx)
+{
+ GHashTable *extra_params;
+
+ 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). */
+ extra_params = tp_asv_new (
+ SIGNON_SESSION_DATA_UI_POLICY, G_TYPE_INT,
+ SIGNON_POLICY_REQUEST_PASSWORD,
+ NULL);
+
+ ag_auth_data_insert_parameters (ctx->auth_data, extra_params);
+
+ signon_auth_session_process (ctx->session,
+ ag_auth_data_get_parameters (ctx->auth_data),
+ ag_auth_data_get_mechanism (ctx->auth_data),
+ request_password_session_process_cb, ctx);
+
+ g_hash_table_unref (extra_params);
}
static void
gpointer user_data)
{
TpChannel *channel = (TpChannel *) source;
- QueryInfoData *data = user_data;
+ AuthContext *ctx = user_data;
GError *error = NULL;
if (!empathy_sasl_auth_finish (channel, result, &error))
{
- GHashTable *extra_params;
-
DEBUG ("SASL Mechanism error: %s", error->message);
g_clear_error (&error);
- /* Inform SSO that the access token didn't work and it should ask user
- * to re-grant access. */
- extra_params = tp_asv_new (
- SIGNON_SESSION_DATA_UI_POLICY, G_TYPE_INT,
- SIGNON_POLICY_REQUEST_PASSWORD,
- NULL);
-
- ag_auth_data_insert_parameters (data->auth_data, extra_params);
-
- signon_auth_session_process (data->session,
- ag_auth_data_get_parameters (data->auth_data),
- ag_auth_data_get_mechanism (data->auth_data),
- NULL, NULL);
-
- g_hash_table_unref (extra_params);
+ request_password (ctx);
}
else
{
DEBUG ("Auth on %s suceeded", tp_proxy_get_object_path (channel));
+ auth_context_done (ctx);
}
-
- tp_channel_close_async (channel, NULL, NULL);
- query_info_data_free (data);
}
static void
const GError *error,
gpointer user_data)
{
- QueryInfoData *data = user_data;
+ AuthContext *ctx = 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);
+ auth_context_done (ctx);
return;
}
access_token = tp_asv_get_string (session_data, "AccessToken");
- client_id = tp_asv_get_string (ag_auth_data_get_parameters (data->auth_data),
+ client_id = tp_asv_get_string (ag_auth_data_get_parameters (ctx->auth_data),
"ClientId");
- 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, data);
+ 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, data);
+ auth_cb, ctx);
break;
case EMPATHY_SASL_MECHANISM_GOOGLE:
- empathy_sasl_auth_google_async (data->channel,
- data->username, access_token,
- auth_cb, data);
+ 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,
+ tp_asv_get_string (session_data, "Secret"),
+ auth_cb, ctx);
break;
default:
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 (ctx->session,
+ ag_auth_data_get_parameters (ctx->auth_data),
+ ag_auth_data_get_mechanism (ctx->auth_data),
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 (!empathy_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
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));
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");
+ empathy_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
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;
}