summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2018-01-22 13:32:29 +0100
committerMilan Crha <mcrha@redhat.com>2018-01-22 13:32:29 +0100
commit9a4c9d6a58e6dd5d4bbf3f8dc5d79040219dfbca (patch)
treedf078253f22bcd8c33802c8156b3892b32ba78cf /src/modules
parent01857f50c25d411f5ed83bff2d4554c65e3947bd (diff)
downloadevolution-data-server-9a4c9d6a58e6dd5d4bbf3f8dc5d79040219dfbca.tar.gz
Change how built-in OAuth2 authentication works
This change allows easier extending of built-in OAuth2 authentications with minimal code "duplication". A CMake option ENABLE_GOOGLE_AUTH had been renamed to ENABLE_OAUTH2 to reflect this extensibility as well.
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/CMakeLists.txt4
-rw-r--r--src/modules/google-backend/module-google-backend.c51
-rw-r--r--src/modules/oauth2-services/CMakeLists.txt17
-rw-r--r--src/modules/oauth2-services/module-oauth2-services.c296
4 files changed, 350 insertions, 18 deletions
diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt
index 9bd3a1611..7fb343877 100644
--- a/src/modules/CMakeLists.txt
+++ b/src/modules/CMakeLists.txt
@@ -68,6 +68,10 @@ add_subdirectory(outlook-backend)
add_subdirectory(webdav-backend)
add_subdirectory(yahoo-backend)
+if(ENABLE_OAUTH2)
+ add_subdirectory(oauth2-services)
+endif(ENABLE_OAUTH2)
+
if(HAVE_GTK)
add_subdirectory(trust-prompt)
endif(HAVE_GTK)
diff --git a/src/modules/google-backend/module-google-backend.c b/src/modules/google-backend/module-google-backend.c
index 4efa245ea..5b6f47f14 100644
--- a/src/modules/google-backend/module-google-backend.c
+++ b/src/modules/google-backend/module-google-backend.c
@@ -36,6 +36,8 @@
/* Just for readability... */
#define METHOD(x) (CAMEL_NETWORK_SECURITY_METHOD_##x)
+#define GOOGLE_OAUTH2_MEHTOD "Google"
+
/* IMAP Configuration Details */
#define GOOGLE_IMAP_BACKEND_NAME "imapx"
#define GOOGLE_IMAP_HOST "imap.googlemail.com"
@@ -104,10 +106,10 @@ google_backend_can_use_google_auth (ESource *source)
g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
- if (!e_source_credentials_google_is_supported ())
+ registry = e_server_side_source_get_server (E_SERVER_SIDE_SOURCE (source));
+ if (!e_oauth2_services_is_oauth2_alias (e_source_registry_server_get_oauth2_services (registry), GOOGLE_OAUTH2_MEHTOD))
return FALSE;
- registry = e_server_side_source_get_server (E_SERVER_SIDE_SOURCE (source));
g_object_ref (source);
while (source && e_source_get_parent (source)) {
@@ -176,6 +178,7 @@ google_backend_mail_update_auth_method (ESource *child_source,
ESourceAuthentication *auth_extension;
EOAuth2Support *oauth2_support;
const gchar *method;
+ gboolean can_use_google_auth;
auth_extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION);
@@ -186,10 +189,14 @@ google_backend_mail_update_auth_method (ESource *child_source,
if (!oauth2_support && master_source)
oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (master_source));
- if (oauth2_support != NULL) {
+ can_use_google_auth = google_backend_can_use_google_auth (child_source);
+ if (!can_use_google_auth && master_source)
+ can_use_google_auth = google_backend_can_use_google_auth (master_source);
+
+ if (oauth2_support && !can_use_google_auth) {
method = "XOAUTH2";
- } else if (google_backend_can_use_google_auth (child_source)) {
- method = "Google";
+ } else if (can_use_google_auth) {
+ method = GOOGLE_OAUTH2_MEHTOD;
} else {
method = NULL;
}
@@ -215,6 +222,7 @@ google_backend_calendar_update_auth_method (ESource *child_source,
EOAuth2Support *oauth2_support;
ESourceAuthentication *auth_extension;
const gchar *method;
+ gboolean can_use_google_auth;
auth_extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION);
@@ -225,10 +233,14 @@ google_backend_calendar_update_auth_method (ESource *child_source,
if (!oauth2_support && master_source)
oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (master_source));
- if (oauth2_support != NULL) {
+ can_use_google_auth = google_backend_can_use_google_auth (child_source);
+ if (!can_use_google_auth && master_source)
+ can_use_google_auth = google_backend_can_use_google_auth (master_source);
+
+ if (oauth2_support && !can_use_google_auth) {
method = "OAuth2";
- } else if (google_backend_can_use_google_auth (child_source)) {
- method = "Google";
+ } else if (can_use_google_auth) {
+ method = GOOGLE_OAUTH2_MEHTOD;
} else {
method = "plain/password";
}
@@ -252,11 +264,10 @@ google_backend_contacts_update_auth_method (ESource *child_source,
{
EOAuth2Support *oauth2_support;
ESourceAuthentication *extension;
- const gchar *extension_name;
const gchar *method;
+ gboolean can_use_google_auth;
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- extension = e_source_get_extension (child_source, extension_name);
+ extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION);
if (!google_backend_is_google_host (extension))
return;
@@ -265,12 +276,16 @@ google_backend_contacts_update_auth_method (ESource *child_source,
if (!oauth2_support && master_source)
oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (master_source));
- if (oauth2_support != NULL)
+ can_use_google_auth = google_backend_can_use_google_auth (child_source);
+ if (!can_use_google_auth && master_source)
+ can_use_google_auth = google_backend_can_use_google_auth (master_source);
+
+ if (oauth2_support && !can_use_google_auth)
method = "OAuth2";
- else /* if (google_backend_can_use_google_auth (source)) */
- method = "Google";
+ else /* if (can_use_google_auth) */
+ method = GOOGLE_OAUTH2_MEHTOD;
/* "ClientLogin" for Contacts is not supported anymore, thus
- force "Google" method regardless the evolution-data-server
+ force GOOGLE_OAUTH2_MEHTOD method regardless the evolution-data-server
was compiled with it or not. */
e_source_authentication_set_method (extension, method);
@@ -386,7 +401,7 @@ google_add_task_list (ECollectionBackend *collection,
e_source_authentication_set_host (E_SOURCE_AUTHENTICATION (extension), "www.google.com");
if (google_backend_can_use_google_auth (collection_source))
- e_source_authentication_set_method (E_SOURCE_AUTHENTICATION (extension), "Google");
+ e_source_authentication_set_method (E_SOURCE_AUTHENTICATION (extension), GOOGLE_OAUTH2_MEHTOD);
else
e_source_authentication_set_method (E_SOURCE_AUTHENTICATION (extension), "OAuth2");
@@ -470,7 +485,7 @@ google_backend_authenticate_sync (EBackend *backend,
gchar *method;
method = e_source_authentication_dup_method (auth_extension);
- if (g_strcmp0 (method, "Google") == 0)
+ if (g_strcmp0 (method, GOOGLE_OAUTH2_MEHTOD) == 0)
calendar_url = "https://apidata.googleusercontent.com/caldav/v2/";
g_free (method);
@@ -487,7 +502,7 @@ google_backend_authenticate_sync (EBackend *backend,
#ifdef HAVE_LIBGDATA
if (result == E_SOURCE_AUTHENTICATION_ACCEPTED &&
e_source_collection_get_calendar_enabled (collection_extension) &&
- (goa_extension || e_source_credentials_google_is_supported ())) {
+ (goa_extension || e_oauth2_services_is_supported ())) {
EGDataOAuth2Authorizer *authorizer;
GDataTasksService *tasks_service;
GError *local_error = NULL;
diff --git a/src/modules/oauth2-services/CMakeLists.txt b/src/modules/oauth2-services/CMakeLists.txt
new file mode 100644
index 000000000..be92c4469
--- /dev/null
+++ b/src/modules/oauth2-services/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(extra_deps)
+set(sources
+ module-oauth2-services.c
+)
+set(extra_defines)
+set(extra_cflags)
+set(extra_incdirs)
+set(extra_ldflags)
+
+add_source_registry_module(module-oauth2-services
+ sources
+ extra_deps
+ extra_defines
+ extra_cflags
+ extra_incdirs
+ extra_ldflags
+)
diff --git a/src/modules/oauth2-services/module-oauth2-services.c b/src/modules/oauth2-services/module-oauth2-services.c
new file mode 100644
index 000000000..31540f78d
--- /dev/null
+++ b/src/modules/oauth2-services/module-oauth2-services.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-data-server-config.h"
+
+#include <gmodule.h>
+
+#include <libedataserver/libedataserver.h>
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_OAUTH2_SOURCE_MONITOR \
+ (e_oauth2_source_monitor_get_type ())
+#define E_OAUTH2_SOURCE_MONITOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_OAUTH2_SOURCE_MONITOR, EOAuth2SourceMonitor))
+#define E_IS_OAUTH2_SOURCE_MONITOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_OAUTH2_SOURCE_MONITOR))
+
+typedef struct _EOAuth2SourceMonitor EOAuth2SourceMonitor;
+typedef struct _EOAuth2SourceMonitorClass EOAuth2SourceMonitorClass;
+
+struct _EOAuth2SourceMonitor {
+ EExtension parent;
+
+ EOAuth2Services *oauth2_services;
+};
+
+struct _EOAuth2SourceMonitorClass {
+ EExtensionClass parent_class;
+};
+
+/* Forward Declarations */
+GType e_oauth2_source_monitor_get_type (void);
+static void e_oauth2_source_monitor_oauth2_support_init (EOAuth2SupportInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (EOAuth2SourceMonitor, e_oauth2_source_monitor, E_TYPE_EXTENSION, 0,
+ G_IMPLEMENT_INTERFACE_DYNAMIC (E_TYPE_OAUTH2_SUPPORT, e_oauth2_source_monitor_oauth2_support_init))
+
+static ESourceRegistryServer *
+oauth2_source_monitor_get_registry_server (EOAuth2SourceMonitor *extension)
+{
+ return E_SOURCE_REGISTRY_SERVER (e_extension_get_extensible (E_EXTENSION (extension)));
+}
+
+static gboolean
+oauth2_source_monitor_get_access_token_sync (EOAuth2Support *support,
+ ESource *source,
+ GCancellable *cancellable,
+ gchar **out_access_token,
+ gint *out_expires_in,
+ GError **error)
+{
+ EOAuth2ServiceRefSourceFunc ref_source;
+ ESourceRegistryServer *registry_server;
+ EOAuth2SourceMonitor *extension;
+ EOAuth2Service *service;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_OAUTH2_SOURCE_MONITOR (support), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ extension = E_OAUTH2_SOURCE_MONITOR (support);
+ service = e_oauth2_services_find (extension->oauth2_services, source);
+ g_return_val_if_fail (service != NULL, FALSE);
+
+ ref_source = (EOAuth2ServiceRefSourceFunc) e_source_registry_server_ref_source;
+ registry_server = oauth2_source_monitor_get_registry_server (extension);
+
+ success = e_oauth2_service_get_access_token_sync (service, source, ref_source, registry_server,
+ out_access_token, out_expires_in, cancellable, error);
+
+ g_clear_object (&service);
+
+ return success;
+}
+
+static void
+oauth2_source_monitor_update_source (EOAuth2SourceMonitor *extension,
+ ESource *source,
+ gboolean is_new_source);
+
+static void
+oauth2_source_monitor_method_changed_cb (ESourceExtension *auth_extension,
+ GParamSpec *param,
+ EOAuth2SourceMonitor *extension)
+{
+ ESource *source;
+
+ g_return_if_fail (E_IS_SOURCE_EXTENSION (auth_extension));
+ g_return_if_fail (E_IS_OAUTH2_SOURCE_MONITOR (extension));
+
+ source = e_source_extension_ref_source (auth_extension);
+ if (source) {
+ oauth2_source_monitor_update_source (extension, source, FALSE);
+ g_clear_object (&source);
+ }
+}
+
+static void
+oauth2_source_monitor_update_source (EOAuth2SourceMonitor *extension,
+ ESource *source,
+ gboolean is_new_source)
+{
+ ESourceAuthentication *authentication_extension;
+ EServerSideSource *server_source;
+ gchar *auth_method;
+
+ g_return_if_fail (E_IS_OAUTH2_SOURCE_MONITOR (extension));
+ g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
+
+ if (!extension->oauth2_services ||
+ !e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION) ||
+ e_source_has_extension (source, E_SOURCE_EXTENSION_GOA) ||
+ e_source_has_extension (source, E_SOURCE_EXTENSION_UOA))
+ return;
+
+ server_source = E_SERVER_SIDE_SOURCE (source);
+ authentication_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+
+ auth_method = e_source_authentication_dup_method (authentication_extension);
+
+ if (e_oauth2_services_is_oauth2_alias (extension->oauth2_services, auth_method)) {
+ e_server_side_source_set_oauth2_support (server_source, E_OAUTH2_SUPPORT (extension));
+ } else {
+ EOAuth2Support *existing;
+
+ existing = e_server_side_source_ref_oauth2_support (server_source);
+ if (existing == E_OAUTH2_SUPPORT (extension))
+ e_server_side_source_set_oauth2_support (server_source, NULL);
+
+ g_clear_object (&existing);
+ }
+
+ g_free (auth_method);
+
+ if (is_new_source) {
+ g_signal_connect (authentication_extension, "notify::method",
+ G_CALLBACK (oauth2_source_monitor_method_changed_cb), extension);
+ }
+}
+
+static void
+oauth2_source_monitor_source_added_cb (ESourceRegistryServer *server,
+ ESource *source,
+ EOAuth2SourceMonitor *extension)
+{
+ g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
+ g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
+ g_return_if_fail (E_IS_OAUTH2_SOURCE_MONITOR (extension));
+
+ oauth2_source_monitor_update_source (extension, source, TRUE);
+}
+
+static void
+oauth2_source_monitor_bus_acquired_cb (EDBusServer *dbus_server,
+ GDBusConnection *connection,
+ EOAuth2SourceMonitor *extension)
+{
+ ESourceRegistryServer *server;
+ GList *sources, *link;
+
+ g_return_if_fail (E_IS_OAUTH2_SOURCE_MONITOR (extension));
+
+ server = oauth2_source_monitor_get_registry_server (extension);
+
+ if (!server || !extension->oauth2_services)
+ return;
+
+ sources = e_source_registry_server_list_sources (server, NULL);
+ for (link = sources; link; link = g_list_next (link)) {
+ ESource *source = link->data;
+
+ oauth2_source_monitor_source_added_cb (server, source, extension);
+ }
+
+ g_list_free_full (sources, g_object_unref);
+
+ g_signal_connect (server, "source-added",
+ G_CALLBACK (oauth2_source_monitor_source_added_cb), extension);
+}
+
+static void
+oauth2_source_monitor_dispose (GObject *object)
+{
+ EOAuth2SourceMonitor *extension;
+ ESourceRegistryServer *server;
+
+ extension = E_OAUTH2_SOURCE_MONITOR (object);
+
+ server = oauth2_source_monitor_get_registry_server (extension);
+ if (server) {
+ GList *sources, *link;
+
+ sources = e_source_registry_server_list_sources (server, NULL);
+ for (link = sources; link; link = g_list_next (link)) {
+ ESource *source = link->data;
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *auth_extension;
+
+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ g_signal_handlers_disconnect_by_func (auth_extension,
+ G_CALLBACK (oauth2_source_monitor_method_changed_cb), extension);
+ }
+ }
+
+ g_list_free_full (sources, g_object_unref);
+ }
+
+ g_clear_object (&extension->oauth2_services);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_oauth2_source_monitor_parent_class)->dispose (object);
+}
+
+static void
+oauth2_source_monitor_constructed (GObject *object)
+{
+ EExtension *extension;
+ EExtensible *extensible;
+
+ extension = E_EXTENSION (object);
+ extensible = e_extension_get_extensible (extension);
+
+ /* Wait for the registry service to acquire its well-known
+ * bus name so we don't do anything destructive beforehand. */
+
+ g_signal_connect (
+ extensible, "bus-acquired",
+ G_CALLBACK (oauth2_source_monitor_bus_acquired_cb),
+ extension);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_oauth2_source_monitor_parent_class)->constructed (object);
+}
+
+static void
+e_oauth2_source_monitor_class_init (EOAuth2SourceMonitorClass *class)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = oauth2_source_monitor_dispose;
+ object_class->constructed = oauth2_source_monitor_constructed;
+
+ extension_class = E_EXTENSION_CLASS (class);
+ extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
+}
+
+static void
+e_oauth2_source_monitor_oauth2_support_init (EOAuth2SupportInterface *iface)
+{
+ iface->get_access_token_sync = oauth2_source_monitor_get_access_token_sync;
+}
+
+static void
+e_oauth2_source_monitor_class_finalize (EOAuth2SourceMonitorClass *class)
+{
+}
+
+static void
+e_oauth2_source_monitor_init (EOAuth2SourceMonitor *extension)
+{
+ extension->oauth2_services = e_oauth2_services_new ();
+}
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ e_oauth2_source_monitor_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}