diff options
| author | Milan Crha <mcrha@redhat.com> | 2018-01-22 13:32:29 +0100 |
|---|---|---|
| committer | Milan Crha <mcrha@redhat.com> | 2018-01-22 13:32:29 +0100 |
| commit | 9a4c9d6a58e6dd5d4bbf3f8dc5d79040219dfbca (patch) | |
| tree | df078253f22bcd8c33802c8156b3892b32ba78cf /src/modules | |
| parent | 01857f50c25d411f5ed83bff2d4554c65e3947bd (diff) | |
| download | evolution-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.txt | 4 | ||||
| -rw-r--r-- | src/modules/google-backend/module-google-backend.c | 51 | ||||
| -rw-r--r-- | src/modules/oauth2-services/CMakeLists.txt | 17 | ||||
| -rw-r--r-- | src/modules/oauth2-services/module-oauth2-services.c | 296 |
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) +{ +} |
