/*
* e-authentication-session.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see
*
*/
/**
* SECTION: e-authentication-session
* @include: libebackend/libebackend.h
* @short_description: Centralized authentication management
*
* #EAuthenticationSession provides centralized password management and
* password prompting for all clients of the registry D-Bus service.
*
* An #EAuthenticationSession is created within the registry D-Bus service
* when the service receives a request to authenticate some data source.
* Clients can issue requests by calling e_source_registry_authenticate().
* Requests can also come from any #ECollectionBackend running within the
* service itself.
*
* An #EAuthenticationSession requires some object implementing the
* #ESourceAuthenticator interface to verify stored or user-provided
* passwords. #EAuthenticationMediator is used for client-issued
* authentication requests. Custom collection backends derived from
* #ECollectionBackend usually implement the #ESourceAuthenticator
* interface themselves.
*
* The #EAuthenticationSession is then handed to #ESourceRegistryServer
* through e_source_registry_server_authenticate() where it waits in line
* behind other previously added authentication sessions. When its turn
* comes, the server calls e_authentication_session_execute() to begin
* the interactive authentication session.
**/
#include "e-authentication-session.h"
/* XXX Yeah, yeah... */
#define GCR_API_SUBJECT_TO_CHANGE
#include
#include
#include
#include
#include
/* Private D-Bus classes. */
#include
#include
#include
#include
#include
#define E_AUTHENTICATION_SESSION_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_AUTHENTICATION_SESSION, EAuthenticationSessionPrivate))
/* Wait forever for a system prompt. */
#define SYSTEM_PROMPT_TIMEOUT (-1)
#define KEYRING_ITEM_ATTRIBUTE_NAME "e-source-uid"
#define KEYRING_ITEM_DISPLAY_FORMAT "Evolution Data Source %s"
typedef struct _AsyncContext AsyncContext;
struct _EAuthenticationSessionPrivate {
ESourceRegistryServer *server;
ESourceAuthenticator *authenticator;
gchar *source_uid;
/* These are for configuring system prompts. */
GMutex property_lock;
gchar *prompt_title;
gchar *prompt_message;
gchar *prompt_description;
};
struct _AsyncContext {
EAuthenticationSessionResult auth_result;
gchar *password;
gboolean permanently;
};
enum {
PROP_0,
PROP_AUTHENTICATOR,
PROP_PROMPT_DESCRIPTION,
PROP_PROMPT_MESSAGE,
PROP_PROMPT_TITLE,
PROP_SERVER,
PROP_SOURCE_UID
};
static SecretSchema schema = {
"org.gnome.Evolution.Data.Source",
SECRET_SCHEMA_DONT_MATCH_NAME,
{
{ KEYRING_ITEM_ATTRIBUTE_NAME,
SECRET_SCHEMA_ATTRIBUTE_STRING },
{ NULL, 0 }
}
};
/* Forward Declarations */
static void authentication_session_msg
(EAuthenticationSession *session,
const gchar *format,
...) G_GNUC_PRINTF (2, 3);
G_DEFINE_TYPE (
EAuthenticationSession,
e_authentication_session,
G_TYPE_OBJECT)
static void
async_context_free (AsyncContext *async_context)
{
g_free (async_context->password);
g_slice_free (AsyncContext, async_context);
}
static void
authentication_session_msg (EAuthenticationSession *session,
const gchar *format,
...)
{
GString *buffer;
va_list args;
buffer = g_string_sized_new (256);
g_string_append_printf (
buffer, "AUTH (%s): ",
session->priv->source_uid);
va_start (args, format);
g_string_append_vprintf (buffer, format, args);
va_end (args);
g_print ("%s\n", buffer->str);
g_string_free (buffer, TRUE);
}
static void
authentication_session_set_authenticator (EAuthenticationSession *session,
ESourceAuthenticator *authenticator)
{
g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (authenticator));
g_return_if_fail (session->priv->authenticator == NULL);
session->priv->authenticator = g_object_ref (authenticator);
}
static void
authentication_session_set_server (EAuthenticationSession *session,
ESourceRegistryServer *server)
{
g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
g_return_if_fail (session->priv->server == NULL);
session->priv->server = g_object_ref (server);
}
static void
authentication_session_set_source_uid (EAuthenticationSession *session,
const gchar *source_uid)
{
g_return_if_fail (source_uid != NULL);
g_return_if_fail (session->priv->source_uid == NULL);
session->priv->source_uid = g_strdup (source_uid);
}
static void
authentication_session_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_AUTHENTICATOR:
authentication_session_set_authenticator (
E_AUTHENTICATION_SESSION (object),
g_value_get_object (value));
return;
case PROP_PROMPT_DESCRIPTION:
e_authentication_session_set_prompt_description (
E_AUTHENTICATION_SESSION (object),
g_value_get_string (value));
return;
case PROP_PROMPT_MESSAGE:
e_authentication_session_set_prompt_message (
E_AUTHENTICATION_SESSION (object),
g_value_get_string (value));
return;
case PROP_PROMPT_TITLE:
e_authentication_session_set_prompt_title (
E_AUTHENTICATION_SESSION (object),
g_value_get_string (value));
return;
case PROP_SERVER:
authentication_session_set_server (
E_AUTHENTICATION_SESSION (object),
g_value_get_object (value));
return;
case PROP_SOURCE_UID:
authentication_session_set_source_uid (
E_AUTHENTICATION_SESSION (object),
g_value_get_string (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
authentication_session_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_AUTHENTICATOR:
g_value_set_object (
value,
e_authentication_session_get_authenticator (
E_AUTHENTICATION_SESSION (object)));
return;
case PROP_PROMPT_DESCRIPTION:
g_value_take_string (
value,
e_authentication_session_dup_prompt_description (
E_AUTHENTICATION_SESSION (object)));
return;
case PROP_PROMPT_MESSAGE:
g_value_take_string (
value,
e_authentication_session_dup_prompt_message (
E_AUTHENTICATION_SESSION (object)));
return;
case PROP_PROMPT_TITLE:
g_value_take_string (
value,
e_authentication_session_dup_prompt_title (
E_AUTHENTICATION_SESSION (object)));
return;
case PROP_SERVER:
g_value_set_object (
value,
e_authentication_session_get_server (
E_AUTHENTICATION_SESSION (object)));
return;
case PROP_SOURCE_UID:
g_value_set_string (
value,
e_authentication_session_get_source_uid (
E_AUTHENTICATION_SESSION (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
authentication_session_dispose (GObject *object)
{
EAuthenticationSessionPrivate *priv;
priv = E_AUTHENTICATION_SESSION_GET_PRIVATE (object);
if (priv->server != NULL) {
g_object_unref (priv->server);
priv->server = NULL;
}
if (priv->authenticator != NULL) {
g_object_unref (priv->authenticator);
priv->authenticator = NULL;
}
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_authentication_session_parent_class)->
dispose (object);
}
static void
authentication_session_finalize (GObject *object)
{
EAuthenticationSessionPrivate *priv;
priv = E_AUTHENTICATION_SESSION_GET_PRIVATE (object);
g_mutex_clear (&priv->property_lock);
g_free (priv->source_uid);
g_free (priv->prompt_title);
g_free (priv->prompt_message);
g_free (priv->prompt_description);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_authentication_session_parent_class)->
finalize (object);
}
static void
authentication_session_constructed (GObject *object)
{
EAuthenticationSession *session;
ESourceAuthenticator *authenticator;
ESourceRegistryServer *server;
ESource *source;
const gchar *source_uid;
session = E_AUTHENTICATION_SESSION (object);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_authentication_session_parent_class)->
constructed (object);
/* If the server knows about the data source UID we've been
* given, then we can auto-configure our own prompt strings. */
server = e_authentication_session_get_server (session);
source_uid = e_authentication_session_get_source_uid (session);
authenticator = e_authentication_session_get_authenticator (session);
source = e_source_registry_server_ref_source (server, source_uid);
if (source != NULL) {
gchar *prompt_title = NULL;
gchar *prompt_message = NULL;
gchar *prompt_description = NULL;
e_source_authenticator_get_prompt_strings (
authenticator, source,
&prompt_title,
&prompt_message,
&prompt_description);
g_object_set (
session,
"prompt-title", prompt_title,
"prompt-message", prompt_message,
"prompt-description", prompt_description,
NULL);
g_free (prompt_title);
g_free (prompt_message);
g_free (prompt_description);
g_object_unref (source);
}
}
/* Helper for authentication_session_execute() */
static void
authentication_session_execute_thread (GSimpleAsyncResult *simple,
GObject *object,
GCancellable *cancellable)
{
AsyncContext *async_context;
GError *error = NULL;
async_context = g_simple_async_result_get_op_res_gpointer (simple);
async_context->auth_result =
e_authentication_session_execute_sync (
E_AUTHENTICATION_SESSION (object),
cancellable, &error);
if (error != NULL)
g_simple_async_result_take_error (simple, error);
}
static EAuthenticationSessionResult
authentication_session_execute_sync (EAuthenticationSession *session,
GCancellable *cancellable,
GError **error)
{
ESourceAuthenticator *authenticator;
EAuthenticationSessionResult session_result;
ESourceAuthenticationResult auth_result;
ESourceRegistryServer *server;
ESource *source;
GcrPrompt *prompt;
GString *password_string;
gboolean allow_auth_prompt;
const gchar *label;
const gchar *source_uid;
const gchar *prompt_password;
gchar *stored_password = NULL;
gboolean success;
gboolean remember_password = TRUE;
GError *local_error = NULL;
/* XXX I moved the execute() operation into a class method thinking
* we might someday want to subclass EAuthenticationSession and
* override this method to make it behave differently.
*
* It would be a little premature to do this now, but we might
* also want to use the basic algorithm here as a template and
* turn the password lookup/delete/store operations into class
* methods that could also be overridden. I reserved adequate
* space in the class struct for this should the need arise.
*
* For now though we'll keep it simple. I don't want to over-
* engineer this too much in trying to make it future-proof.
*/
authentication_session_msg (session, "Initiated");
server = e_authentication_session_get_server (session);
source_uid = e_authentication_session_get_source_uid (session);
authenticator = e_authentication_session_get_authenticator (session);
if (e_source_authenticator_get_without_password (authenticator)) {
password_string = g_string_new ("");
auth_result = e_source_authenticator_try_password_sync (
authenticator, password_string, cancellable, &local_error);
g_string_free (password_string, TRUE);
password_string = NULL;
if (auth_result == E_SOURCE_AUTHENTICATION_ERROR &&
g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_propagate_error (error, local_error);
session_result = E_AUTHENTICATION_SESSION_ERROR;
goto exit;
}
g_clear_error (&local_error);
if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
session_result = E_AUTHENTICATION_SESSION_SUCCESS;
goto exit;
}
/* if an empty password fails, then ask a user for it */
}
success = e_authentication_session_lookup_password_sync (
session, cancellable, &stored_password, error);
if (!success) {
session_result = E_AUTHENTICATION_SESSION_ERROR;
goto exit;
}
auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
/* If we found a stored password, signal the client without
* interrupting the user. Note, if the client responds with
* REJECTED, we'll have to interrupt the user after all. */
if (stored_password != NULL) {
password_string = g_string_new (stored_password);
auth_result = e_source_authenticator_try_password_sync (
authenticator, password_string, cancellable, error);
g_string_free (password_string, TRUE);
password_string = NULL;
g_free (stored_password);
stored_password = NULL;
}
if (auth_result == E_SOURCE_AUTHENTICATION_ERROR) {
session_result = E_AUTHENTICATION_SESSION_ERROR;
goto exit;
}
if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
session_result = E_AUTHENTICATION_SESSION_SUCCESS;
goto exit;
}
g_warn_if_fail (auth_result == E_SOURCE_AUTHENTICATION_REJECTED);
/* The stored password is bad so delete it from the keyring.
* Failure here does not affect the outcome of this operation,
* but leave a breadcrumb as evidence that something went wrong. */
e_authentication_session_delete_password_sync (
session, cancellable, &local_error);
if (local_error != NULL) {
g_warning ("%s: %s", G_STRFUNC, local_error->message);
g_clear_error (&local_error);
}
/* This will return NULL if the authenticating data source
* has not yet been submitted to the D-Bus registry service. */
source = e_source_registry_server_ref_source (server, source_uid);
if (source != NULL) {
ESourceExtension *extension;
const gchar *extension_name;
allow_auth_prompt =
e_server_side_source_get_allow_auth_prompt (
E_SERVER_SIDE_SOURCE (source));
extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
extension = e_source_get_extension (source, extension_name);
remember_password =
e_source_authentication_get_remember_password (
E_SOURCE_AUTHENTICATION (extension));
g_object_unref (source);
} else {
allow_auth_prompt = TRUE;
}
/* Check if we're allowed to interrupt the user for a password.
* If not, we have no choice but to dismiss the authentication
* request. */
if (!allow_auth_prompt) {
session_result = E_AUTHENTICATION_SESSION_DISMISSED;
goto exit;
}
/* Configure a system prompt. */
prompt = gcr_system_prompt_open (
SYSTEM_PROMPT_TIMEOUT, cancellable, error);
if (prompt == NULL) {
session_result = E_AUTHENTICATION_SESSION_ERROR;
goto exit;
}
g_object_bind_property (
session, "prompt-title",
prompt, "title",
G_BINDING_SYNC_CREATE);
g_object_bind_property (
session, "prompt-message",
prompt, "message",
G_BINDING_SYNC_CREATE);
g_object_bind_property (
session, "prompt-description",
prompt, "description",
G_BINDING_SYNC_CREATE);
label = _("Add this password to your keyring");
gcr_prompt_set_choice_label (prompt, label);
gcr_prompt_set_choice_chosen (prompt, remember_password);
try_again:
/* Prompt the user for a password. */
prompt_password = gcr_prompt_password (
prompt, cancellable, &local_error);
if (local_error != NULL) {
session_result = E_AUTHENTICATION_SESSION_ERROR;
g_propagate_error (error, local_error);
local_error = NULL;
goto close_prompt;
}
/* No password and no error indicates a dismissal. */
if (prompt_password == NULL) {
session_result = E_AUTHENTICATION_SESSION_DISMISSED;
goto close_prompt;
}
source = e_source_registry_server_ref_source (server, source_uid);
if (source != NULL) {
ESourceExtension *extension;
const gchar *extension_name;
extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
extension = e_source_get_extension (source, extension_name);
e_source_authentication_set_remember_password (
E_SOURCE_AUTHENTICATION (extension),
gcr_prompt_get_choice_chosen (prompt));
g_object_unref (source);
}
/* Attempt authentication with the provided password. */
password_string = g_string_new (prompt_password);
auth_result = e_source_authenticator_try_password_sync (
authenticator, password_string, cancellable, error);
g_string_free (password_string, TRUE);
password_string = NULL;
if (auth_result == E_SOURCE_AUTHENTICATION_ERROR) {
session_result = E_AUTHENTICATION_SESSION_ERROR;
goto close_prompt;
}
if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
gboolean permanently;
gchar *password_copy;
permanently = gcr_prompt_get_choice_chosen (prompt);
session_result = E_AUTHENTICATION_SESSION_SUCCESS;
/* Close our prompt before storing the password in
* the keyring. If the keyring is locked, it will
* need to prompt the user for a keyring password,
* but it can't do that if our password prompt is
* still open since both prompts are system-modal.
* Not sure what would happen next; probably the
* store operation would either fail or deadlock. */
/* XXX Not sure if it's safe to use the prompt's
* password string after closing the prompt,
* so make a copy here just to be safe. */
password_copy = gcr_secure_memory_strdup (prompt_password);
/* Failure here does not affect the outcome of this
* operation, but leave a breadcrumb as evidence that
* something went wrong. */
gcr_system_prompt_close (
GCR_SYSTEM_PROMPT (prompt),
cancellable, &local_error);
if (local_error != NULL) {
g_warning ("%s: %s", G_STRFUNC, local_error->message);
g_clear_error (&local_error);
}
g_object_unref (prompt);
e_authentication_session_store_password_sync (
session, password_copy, permanently,
cancellable, &local_error);
if (local_error != NULL) {
g_warning ("%s: %s", G_STRFUNC, local_error->message);
g_clear_error (&local_error);
}
gcr_secure_memory_strfree (password_copy);
goto exit;
}
g_warn_if_fail (auth_result == E_SOURCE_AUTHENTICATION_REJECTED);
gcr_prompt_set_warning (prompt, _("Password was incorrect"));
goto try_again;
close_prompt:
/* Failure here does not affect the outcome of this operation,
* but leave a breadcrumb as evidence that something went wrong. */
gcr_system_prompt_close (
GCR_SYSTEM_PROMPT (prompt),
cancellable, &local_error);
if (local_error != NULL) {
g_warning ("%s: %s", G_STRFUNC, local_error->message);
g_clear_error (&local_error);
}
g_object_unref (prompt);
exit:
switch (session_result) {
case E_AUTHENTICATION_SESSION_ERROR:
if (error != NULL && *error != NULL)
authentication_session_msg (
session, "Complete (ERROR - %s)",
(*error)->message);
else
authentication_session_msg (
session, "Complete (ERROR)");
break;
case E_AUTHENTICATION_SESSION_SUCCESS:
authentication_session_msg (
session, "Complete (SUCCESS)");
break;
case E_AUTHENTICATION_SESSION_DISMISSED:
authentication_session_msg (
session, "Complete (DISMISSED)");
break;
/* coverity[dead_error_begin] */
default:
g_warn_if_reached ();
}
return session_result;
}
static void
authentication_session_execute (EAuthenticationSession *session,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
async_context = g_slice_new0 (AsyncContext);
simple = g_simple_async_result_new (
G_OBJECT (session), callback, user_data,
e_authentication_session_execute);
g_simple_async_result_set_check_cancellable (simple, cancellable);
g_simple_async_result_set_op_res_gpointer (
simple, async_context, (GDestroyNotify) async_context_free);
g_simple_async_result_run_in_thread (
simple, authentication_session_execute_thread,
io_priority, cancellable);
g_object_unref (simple);
}
static EAuthenticationSessionResult
authentication_session_execute_finish (EAuthenticationSession *session,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
g_return_val_if_fail (
g_simple_async_result_is_valid (
result, G_OBJECT (session),
e_authentication_session_execute),
E_AUTHENTICATION_SESSION_DISMISSED);
simple = G_SIMPLE_ASYNC_RESULT (result);
async_context = g_simple_async_result_get_op_res_gpointer (simple);
if (g_simple_async_result_propagate_error (simple, error))
return E_AUTHENTICATION_SESSION_ERROR;
return async_context->auth_result;
}
static void
e_authentication_session_class_init (EAuthenticationSessionClass *class)
{
GObjectClass *object_class;
g_type_class_add_private (
class, sizeof (EAuthenticationSessionPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->set_property = authentication_session_set_property;
object_class->get_property = authentication_session_get_property;
object_class->dispose = authentication_session_dispose;
object_class->finalize = authentication_session_finalize;
object_class->constructed = authentication_session_constructed;
class->execute_sync = authentication_session_execute_sync;
class->execute = authentication_session_execute;
class->execute_finish = authentication_session_execute_finish;
g_object_class_install_property (
object_class,
PROP_AUTHENTICATOR,
g_param_spec_object (
"authenticator",
"Authenticator",
"Handles authentication attempts",
E_TYPE_SOURCE_AUTHENTICATOR,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (
object_class,
PROP_PROMPT_DESCRIPTION,
g_param_spec_string (
"prompt-description",
"Prompt Description",
"The detailed description of the prompt",
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (
object_class,
PROP_PROMPT_MESSAGE,
g_param_spec_string (
"prompt-message",
"Prompt Message",
"The prompt message for the user",
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (
object_class,
PROP_PROMPT_TITLE,
g_param_spec_string (
"prompt-title",
"Prompt Title",
"The title of the prompt",
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (
object_class,
PROP_SERVER,
g_param_spec_object (
"server",
"Server",
"The server to which the session belongs",
E_TYPE_SOURCE_REGISTRY_SERVER,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (
object_class,
PROP_SOURCE_UID,
g_param_spec_string (
"source-uid",
"Source UID",
"Unique ID of the data source being authenticated",
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
}
static void
e_authentication_session_init (EAuthenticationSession *session)
{
session->priv = E_AUTHENTICATION_SESSION_GET_PRIVATE (session);
g_mutex_init (&session->priv->property_lock);
}
G_DEFINE_QUARK (
e - authentication - session - error - quark,
e_authentication_session_error);
/**
* e_authentication_session_new:
* @server: an #ESourceRegistryServer
* @authenticator: an #ESourceAuthenticator
* @source_uid: a data source identifier
*
* Creates a new #EAuthenticationSession instance for @server using
* @authenticator to handle authentication attempts.
*
* Note that @source_uid does not necessarily have to be known to the
* @server, as in the case when configuring a new data source, but it
* still has to be unique.
*
* Returns: a newly-created #EAuthenticationSession
*
* Since: 3.6
*
* Deprecated: 3.8: Use e_source_registry_server_new_auth_session() instead.
**/
EAuthenticationSession *
e_authentication_session_new (ESourceRegistryServer *server,
ESourceAuthenticator *authenticator,
const gchar *source_uid)
{
g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATOR (authenticator), NULL);
g_return_val_if_fail (source_uid != NULL, NULL);
return g_object_new (
E_TYPE_AUTHENTICATION_SESSION,
"server", server,
"authenticator", authenticator,
"source-uid", source_uid, NULL);
}
/**
* e_authentication_session_get_server:
* @session: an #EAuthenticationSession
*
* Returns the #ESourceRegistryServer to which @session belongs.
*
* Returns: the #ESourceRegistryServer for @session
*
* Since: 3.6
**/
ESourceRegistryServer *
e_authentication_session_get_server (EAuthenticationSession *session)
{
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
return session->priv->server;
}
/**
* e_authentication_session_get_authenticator:
* @session: an #EAuthenticationSession
*
* Returns the #ESourceAuthenticator handling authentication attempts for
* @session. This is usually an #EAuthenticationMediator but can also be
* a custom collection backend derived from #ECollectionBackend.
*
* Returns: the #ESourceAuthenticator for @session
*
* Since: 3.6
**/
ESourceAuthenticator *
e_authentication_session_get_authenticator (EAuthenticationSession *session)
{
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
return session->priv->authenticator;
}
/**
* e_authentication_session_get_source_uid:
* @session: an #EAuthenticationSession
*
* Returns the #ESource:uid of the authenticating data source. The data
* source may or may not be known to the #EAuthenticationSession:server.
*
* Returns: the UID of the authenticating data source
*
* Since: 3.6
**/
const gchar *
e_authentication_session_get_source_uid (EAuthenticationSession *session)
{
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
return session->priv->source_uid;
}
/**
* e_authentication_session_get_prompt_title:
* @session: an #EAuthenticationSession
*
* Returns the text used for the password prompt title should prompting
* be necessary. See #GcrPrompt for more details about password prompts.
*
* Returns: the password prompt title
*
* Since: 3.6
**/
const gchar *
e_authentication_session_get_prompt_title (EAuthenticationSession *session)
{
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
return session->priv->prompt_title;
}
/**
* e_authentication_session_dup_prompt_title:
* @session: an #EAuthenticationSession
*
* Thread-safe variation of e_authentication_session_get_prompt_title().
* Use this function when accessing @session from multiple threads.
*
* The returned string should be freed with g_free() when no longer needed.
*
* Returns: a newly-allocated copy of #EAuthenticationSession:prompt-title
*
* Since: 3.6
**/
gchar *
e_authentication_session_dup_prompt_title (EAuthenticationSession *session)
{
const gchar *protected;
gchar *duplicate;
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
g_mutex_lock (&session->priv->property_lock);
protected = e_authentication_session_get_prompt_title (session);
duplicate = g_strdup (protected);
g_mutex_unlock (&session->priv->property_lock);
return duplicate;
}
/**
* e_authentication_session_set_prompt_title:
* @session: an #EAuthenticationSession
* @prompt_title: the password prompt title, or %NULL
*
* Sets the text used for the password prompt title should prompting be
* necessary. See #GcrPrompt for more details about password prompts.
*
* Since: 3.6
**/
void
e_authentication_session_set_prompt_title (EAuthenticationSession *session,
const gchar *prompt_title)
{
g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
g_mutex_lock (&session->priv->property_lock);
if (g_strcmp0 (session->priv->prompt_title, prompt_title) == 0) {
g_mutex_unlock (&session->priv->property_lock);
return;
}
g_free (session->priv->prompt_title);
session->priv->prompt_title = g_strdup (prompt_title);
g_mutex_unlock (&session->priv->property_lock);
g_object_notify (G_OBJECT (session), "prompt-title");
}
/**
* e_authentication_session_get_prompt_message:
* @session: an #EAuthenticationSession
*
* Returns the text used for the password prompt message should prompting
* be necessary. See #GcrPrompt for more details about password prompts.
*
* Returns: the password prompt message
*
* Since: 3.6
**/
const gchar *
e_authentication_session_get_prompt_message (EAuthenticationSession *session)
{
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
return session->priv->prompt_message;
}
/**
* e_authentication_session_dup_prompt_message:
* @session: an #EAuthenticationSession
*
* Thread-safe variation of e_authentication_session_get_prompt_message().
* Use this function when accessing @session from multiple threads.
*
* The returned string should be freed with g_free() when no longer needed.
*
* Returns: a newly-allocated copy of #EAuthenticationSession:prompt-message
*
* Since: 3.6
**/
gchar *
e_authentication_session_dup_prompt_message (EAuthenticationSession *session)
{
const gchar *protected;
gchar *duplicate;
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
g_mutex_lock (&session->priv->property_lock);
protected = e_authentication_session_get_prompt_message (session);
duplicate = g_strdup (protected);
g_mutex_unlock (&session->priv->property_lock);
return duplicate;
}
/**
* e_authentication_session_set_prompt_message:
* @session: an #EAuthenticationSession
* @prompt_message: the password prompt message, or %NULL
*
* Sets the text used for the password prompt message should prompting be
* necessary. See #GcrPrompt for more details about password prompts.
*
* Since: 3.6
**/
void
e_authentication_session_set_prompt_message (EAuthenticationSession *session,
const gchar *prompt_message)
{
g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
g_mutex_lock (&session->priv->property_lock);
if (g_strcmp0 (session->priv->prompt_message, prompt_message) == 0) {
g_mutex_unlock (&session->priv->property_lock);
return;
}
g_free (session->priv->prompt_message);
session->priv->prompt_message = g_strdup (prompt_message);
g_mutex_unlock (&session->priv->property_lock);
g_object_notify (G_OBJECT (session), "prompt-message");
}
/**
* e_authentication_session_get_prompt_description:
* @session: an #EAuthenticationSession
*
* Returns the text used for the password prompt description should prompting
* be necessary. See #GcrPrompt for more details about password prompts.
*
* Returns: the password prompt description
*
* Since: 3.6
**/
const gchar *
e_authentication_session_get_prompt_description (EAuthenticationSession *session)
{
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
return session->priv->prompt_description;
}
/**
* e_authentication_session_dup_prompt_description:
* @session: an #EAuthenticationSession
*
* Thread-safe variation of e_authentication_session_get_prompt_description().
* Use this function when accessing @session from multiple threads.
*
* The returned string should be freed with g_free() when no longer needed.
*
* Returns: a newly-allocated copy of
* #EAuthenticationSession:prompt-description
*
* Since: 3.6
**/
gchar *
e_authentication_session_dup_prompt_description (EAuthenticationSession *session)
{
const gchar *protected;
gchar *duplicate;
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
g_mutex_lock (&session->priv->property_lock);
protected = e_authentication_session_get_prompt_description (session);
duplicate = g_strdup (protected);
g_mutex_unlock (&session->priv->property_lock);
return duplicate;
}
/**
* e_authentication_session_set_prompt_description:
* @session: an #EAuthenticationSession
* @prompt_description: the password prompt description
*
* Sets the text used for the password prompt description should prompting
* be necessary. See #GcrPrompt for more details about password prompts.
*
* Since: 3.6
**/
void
e_authentication_session_set_prompt_description (EAuthenticationSession *session,
const gchar *prompt_description)
{
g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
g_mutex_lock (&session->priv->property_lock);
if (g_strcmp0 (session->priv->prompt_description, prompt_description) == 0) {
g_mutex_unlock (&session->priv->property_lock);
return;
}
g_free (session->priv->prompt_description);
session->priv->prompt_description = g_strdup (prompt_description);
g_mutex_unlock (&session->priv->property_lock);
g_object_notify (G_OBJECT (session), "prompt-description");
}
/**
* e_authentication_session_execute_sync:
* @session: an #EAuthenticationSession
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Executes an authentication session.
*
* First the secret service is queried for a stored password. If found,
* an authentication attempt is made without disturbing the user. If no
* stored password is found, or if the stored password is rejected, the
* user is shown a system-modal dialog requesting a password. Further
* authentication attempts are repeated with user-provided passwords
* until authentication is verified or the user dismisses the prompt.
* The returned #EAuthenticationSessionResult indicates the outcome.
*
* If an error occurs while interacting with the secret service, or while
* prompting the user for a password, or while attempting authentication,
* the function sets @error and returns #E_AUTHENTICATION_SESSION_ERROR.
*
* Returns: the result of the authentication session
*
* Since: 3.6
**/
EAuthenticationSessionResult
e_authentication_session_execute_sync (EAuthenticationSession *session,
GCancellable *cancellable,
GError **error)
{
EAuthenticationSessionClass *class;
g_return_val_if_fail (
E_IS_AUTHENTICATION_SESSION (session),
E_AUTHENTICATION_SESSION_DISMISSED);
class = E_AUTHENTICATION_SESSION_GET_CLASS (session);
g_return_val_if_fail (
class->execute_sync != NULL,
E_AUTHENTICATION_SESSION_DISMISSED);
return class->execute_sync (session, cancellable, error);
}
/**
* e_authentication_session_execute:
* @session: an #EAuthenticationSession
* @io_priority: the I/O priority of the request
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* See e_authentication_session_execute_sync() for details.
*
* When the operation is finished, @callback will be called. You can then
* call e_authentication_session_execute_finish() to get the result of the
* operation.
*
* Since: 3.6
**/
void
e_authentication_session_execute (EAuthenticationSession *session,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
EAuthenticationSessionClass *class;
g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
class = E_AUTHENTICATION_SESSION_GET_CLASS (session);
g_return_if_fail (class->execute != NULL);
return class->execute (
session, io_priority, cancellable, callback, user_data);
}
/**
* e_authentication_session_execute_finish:
* @session: an #EAuthenticationSession
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finishes the operation started with e_authentication_session_execute().
*
* If an error occurs while interacting with the secret service, or while
* prompting the user for a password, or while attempting authentication,
* the function sets @error and returns #E_AUTHENTICATION_SESSION_ERROR.
*
* Returns: the result of the authentication session
*
* Since: 3.6
**/
EAuthenticationSessionResult
e_authentication_session_execute_finish (EAuthenticationSession *session,
GAsyncResult *result,
GError **error)
{
EAuthenticationSessionClass *class;
g_return_val_if_fail (
E_IS_AUTHENTICATION_SESSION (session),
E_AUTHENTICATION_SESSION_DISMISSED);
g_return_val_if_fail (
G_IS_ASYNC_RESULT (result),
E_AUTHENTICATION_SESSION_DISMISSED);
class = E_AUTHENTICATION_SESSION_GET_CLASS (session);
g_return_val_if_fail (
class->execute_finish != NULL,
E_AUTHENTICATION_SESSION_DISMISSED);
return class->execute_finish (session, result, error);
}
/* Helper for e_authentication_session_store_password() */
static void
authentication_session_store_password_thread (GSimpleAsyncResult *simple,
GObject *object,
GCancellable *cancellable)
{
AsyncContext *async_context;
GError *error = NULL;
async_context = g_simple_async_result_get_op_res_gpointer (simple);
e_authentication_session_store_password_sync (
E_AUTHENTICATION_SESSION (object),
async_context->password,
async_context->permanently,
cancellable, &error);
if (error != NULL)
g_simple_async_result_take_error (simple, error);
}
/**
* e_authentication_session_store_password_sync:
* @session: an #EAuthenticationSession
* @password: the password to store
* @permanently: store permanently or just for the session
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Store a password for the data source that @session is representing.
* If @permanently is %TRUE, the password is stored in the default keyring.
* Otherwise the password is stored in the memory-only session keyring. If
* an error occurs, the function sets @error and returns %FALSE.
*
* Returns: %TRUE on success, %FALSE on error
*
* Since: 3.6
**/
gboolean
e_authentication_session_store_password_sync (EAuthenticationSession *session,
const gchar *password,
gboolean permanently,
GCancellable *cancellable,
GError **error)
{
gboolean result;
const gchar *collection;
const gchar *uid;
gchar *display_name;
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), FALSE);
g_return_val_if_fail (password != NULL, FALSE);
if (permanently)
collection = SECRET_COLLECTION_DEFAULT;
else
collection = SECRET_COLLECTION_SESSION;
uid = e_authentication_session_get_source_uid (session);
display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid);
result = secret_password_store_sync (
&schema, collection, display_name,
password, cancellable, error,
KEYRING_ITEM_ATTRIBUTE_NAME, uid,
NULL);
g_free (display_name);
return result;
}
/**
* e_authentication_session_store_password:
* @session: an #EAuthenticationSession
* @password: the password to store
* @permanently: store permanently or just for the session
* @io_priority: the I/O priority of the request
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* Asynchronously stores a password for the data source that @session
* is representing. If @permanently is %TRUE, the password is stored in the
* default keyring. Otherwise the password is stored in the memory-only
* session keyring.
*
* When the operation is finished, @callback will be called. You can then
* call e_authentication_session_store_password_finish() to get the result
* of the operation.
*
* Since: 3.6
**/
void
e_authentication_session_store_password (EAuthenticationSession *session,
const gchar *password,
gboolean permanently,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
g_return_if_fail (password != NULL);
async_context = g_slice_new0 (AsyncContext);
async_context->password = g_strdup (password);
async_context->permanently = permanently;
simple = g_simple_async_result_new (
G_OBJECT (session), callback, user_data,
e_authentication_session_store_password);
g_simple_async_result_set_check_cancellable (simple, cancellable);
g_simple_async_result_set_op_res_gpointer (
simple, async_context, (GDestroyNotify) async_context_free);
g_simple_async_result_run_in_thread (
simple, authentication_session_store_password_thread,
io_priority, cancellable);
g_object_unref (simple);
}
/**
* e_authentication_session_store_password_finish:
* @session: an #EAuthenticationSession
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finished the operation started with
* e_authentication_session_store_password().
*
* Returns: %TRUE on success, %FALSE on error
*
* Since: 3.6
**/
gboolean
e_authentication_session_store_password_finish (EAuthenticationSession *session,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
g_return_val_if_fail (
g_simple_async_result_is_valid (
result, G_OBJECT (session),
e_authentication_session_store_password), FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
/* Assume success unless a GError is set. */
return !g_simple_async_result_propagate_error (simple, error);
}
/* Helper for e_authentication_session_store_password() */
static void
authentication_session_lookup_password_thread (GSimpleAsyncResult *simple,
GObject *object,
GCancellable *cancellable)
{
AsyncContext *async_context;
GError *error = NULL;
async_context = g_simple_async_result_get_op_res_gpointer (simple);
e_authentication_session_lookup_password_sync (
E_AUTHENTICATION_SESSION (object), cancellable,
&async_context->password, &error);
if (error != NULL)
g_simple_async_result_take_error (simple, error);
}
/**
* e_authentication_session_lookup_password_sync:
* @session: an #EAuthenticationSession
* @cancellable: optional #GCancellable object, or %NULL
* @password: return location for the password, or %NULL
* @error: return location for a #GError, or %NULL
*
* Looks up a password for the data source that @session is
* representing. Both the default and session keyrings are queried.
*
* Note the boolean return value indicates whether the lookup operation
* itself completed successfully, not whether a password was found. If
* no password was found, the function will set @password to %NULL but
* still return %TRUE. If an error occurs, the function sets @error
* and returns %FALSE.
*
* Returns: %TRUE on success, %FALSE on error
*
* Since: 3.6
**/
gboolean
e_authentication_session_lookup_password_sync (EAuthenticationSession *session,
GCancellable *cancellable,
gchar **password,
GError **error)
{
const gchar *uid;
gchar *temp = NULL;
gboolean success = TRUE;
GError *local_error = NULL;
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), FALSE);
uid = e_authentication_session_get_source_uid (session);
temp = secret_password_lookup_sync (
&schema, cancellable, &local_error,
KEYRING_ITEM_ATTRIBUTE_NAME, uid, NULL);
if (local_error != NULL) {
g_warn_if_fail (temp == NULL);
g_propagate_error (error, local_error);
success = FALSE;
} else if (password != NULL) {
*password = temp; /* takes ownership */
} else {
secret_password_free (temp);
}
return success;
}
/**
* e_authentication_session_lookup_password:
* @session: an #EAuthenticationSession
* @io_priority: the I/O priority of the request
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* Asynchronously looks up a password for the data source that @session
* is representing. Both the default and session keyrings are queried.
*
* When the operation is finished, @callback will be called. You can then
* call e_authentication_session_lookup_password_finish() to get the
* result of the operation.
*
* Since: 3.6
**/
void
e_authentication_session_lookup_password (EAuthenticationSession *session,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
async_context = g_slice_new0 (AsyncContext);
simple = g_simple_async_result_new (
G_OBJECT (session), callback, user_data,
e_authentication_session_lookup_password);
g_simple_async_result_set_check_cancellable (simple, cancellable);
g_simple_async_result_set_op_res_gpointer (
simple, async_context, (GDestroyNotify) async_context_free);
g_simple_async_result_run_in_thread (
simple, authentication_session_lookup_password_thread,
io_priority, cancellable);
g_object_unref (simple);
}
/**
* e_authentication_session_lookup_password_finish:
* @session: an #EAuthenticationSession
* @result: a #GAsyncResult
* @password: return location for the password, or %NULL
* @error: return location for a #GError, or %NULL
*
* Finishes the operation started with
* e_authentication_session_lookup_password().
*
* Note the boolean return value indicates whether the lookup operation
* itself completed successfully, not whether a password was found. If
* no password was found, the function will set @password to %NULL but
* still return %TRUE. If an error occurs, the function sets @error
* and returns %FALSE.
*
* Returns: %TRUE on success, %FALSE on error
*
* Since: 3.6
**/
gboolean
e_authentication_session_lookup_password_finish (EAuthenticationSession *session,
GAsyncResult *result,
gchar **password,
GError **error)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
g_return_val_if_fail (
g_simple_async_result_is_valid (
result, G_OBJECT (session),
e_authentication_session_lookup_password), FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
async_context = g_simple_async_result_get_op_res_gpointer (simple);
if (g_simple_async_result_propagate_error (simple, error))
return FALSE;
if (password != NULL) {
*password = async_context->password;
async_context->password = NULL;
}
return TRUE;
}
/* Helper for e_authentication_session_delete_password() */
static void
authentication_session_delete_password_thread (GSimpleAsyncResult *simple,
GObject *object,
GCancellable *cancellable)
{
GError *error = NULL;
e_authentication_session_delete_password_sync (
E_AUTHENTICATION_SESSION (object), cancellable, &error);
if (error != NULL)
g_simple_async_result_take_error (simple, error);
}
/**
* e_authentication_session_delete_password_sync:
* @session: an #EAuthenticationSession
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Deletes the password for the data source that @session is
* representing from either the default keyring or session keyring.
*
* Note the boolean return value indicates whether the delete operation
* itself completed successfully, not whether a password was found and
* deleted. If no password was found, the function will still return
* %TRUE. If an error occurs, the function sets @error and returns %FALSE.
*
* Returns: %TRUE on success, %FALSE on error
*
* Since: 3.6
**/
gboolean
e_authentication_session_delete_password_sync (EAuthenticationSession *session,
GCancellable *cancellable,
GError **error)
{
const gchar *uid;
gboolean success = TRUE;
GError *local_error = NULL;
g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), FALSE);
uid = e_authentication_session_get_source_uid (session);
/* The return value indicates whether any passwords were removed,
* not whether the operation completed successfully. So we have
* check the GError directly. */
secret_password_clear_sync (
&schema, cancellable, &local_error,
KEYRING_ITEM_ATTRIBUTE_NAME, uid, NULL);
if (local_error != NULL) {
g_propagate_error (error, local_error);
success = FALSE;
}
return success;
}
/**
* e_authentication_session_delete_password:
* @session: an #EAuthenticationSession
* @io_priority: the I/O priority of the request
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* Asyncronously deletes the password for the data source that @session
* is representing from either the default keyring or session keyring.
*
* When the operation is finished, @callback will be called. You can then
* call e_authentication_session_delete_password_finish() to get the result
* of the operation.
*
* Since: 3.6
**/
void
e_authentication_session_delete_password (EAuthenticationSession *session,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
simple = g_simple_async_result_new (
G_OBJECT (session), callback, user_data,
e_authentication_session_delete_password);
g_simple_async_result_set_check_cancellable (simple, cancellable);
g_simple_async_result_run_in_thread (
simple, authentication_session_delete_password_thread,
io_priority, cancellable);
g_object_unref (simple);
}
/**
* e_authentication_session_delete_password_finish:
* @session: an #EAuthenticationSession
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finishes the operation started with
* e_authentication_session_delete_password().
*
* Note the boolean return value indicates whether the delete operation
* itself completed successfully, not whether a password was found and
* deleted. If no password was found, the function will still return
* %TRUE. If an error occurs, the function sets @error and returns %FALSE.
*
* Returns: %TRUE on success, %FALSE on error
*
* Since: 3.6
**/
gboolean
e_authentication_session_delete_password_finish (EAuthenticationSession *session,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
g_return_val_if_fail (
g_simple_async_result_is_valid (
result, G_OBJECT (session),
e_authentication_session_delete_password), FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
/* Assume success unless a GError is set. */
return !g_simple_async_result_propagate_error (simple, error);
}