[evolution-data-server] Miscellaneous changes related to built-in OAuth2 services
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Miscellaneous changes related to built-in OAuth2 services
- Date: Tue, 23 Jan 2018 12:52:44 +0000 (UTC)
commit c48a46403443174ece8535aed173b753c62a53ea
Author: Milan Crha <mcrha redhat com>
Date: Tue Jan 23 13:53:16 2018 +0100
Miscellaneous changes related to built-in OAuth2 services
.../evolution-data-server-docs.sgml.in | 2 +-
src/camel/providers/imapx/camel-imapx-store.c | 23 ++-
src/libedataserver/e-oauth2-service-google.c | 59 +++---
src/libedataserver/e-oauth2-service.c | 200 +++++++++++++++++---
src/libedataserver/e-oauth2-service.h | 51 +++++
.../e-credentials-prompter-impl-oauth2.c | 160 ++++++++++++++--
6 files changed, 405 insertions(+), 90 deletions(-)
---
diff --git a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
index c0c94b7..2ce2b79 100644
--- a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
+++ b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
@@ -248,7 +248,7 @@
</chapter>
<chapter>
- <title>Built-in OAuth2 authentication</title>
+ <title>Built-in OAuth 2.0 authentication</title>
<xi:include href="xml/e-oauth2-service.xml"/>
<xi:include href="xml/e-oauth2-services.xml"/>
<xi:include href="xml/e-oauth2-service-base.xml"/>
diff --git a/src/camel/providers/imapx/camel-imapx-store.c b/src/camel/providers/imapx/camel-imapx-store.c
index 721be3c..9d4e4d6 100644
--- a/src/camel/providers/imapx/camel-imapx-store.c
+++ b/src/camel/providers/imapx/camel-imapx-store.c
@@ -1079,10 +1079,8 @@ imapx_query_auth_types_sync (CamelService *service,
GCancellable *cancellable,
GError **error)
{
- CamelServiceAuthType *authtype;
CamelIMAPXStore *imapx_store;
- GList *sasl_types = NULL;
- GList *t, *next;
+ GList *sasl_types;
CamelIMAPXServer *server;
const struct _capability_info *cinfo;
@@ -1098,14 +1096,19 @@ imapx_query_auth_types_sync (CamelService *service,
cinfo = camel_imapx_server_get_capability_info (server);
- sasl_types = camel_sasl_authtype_list (FALSE);
- for (t = sasl_types; t; t = next) {
- authtype = t->data;
- next = t->next;
+ sasl_types = NULL;
- if (!cinfo || !g_hash_table_lookup (cinfo->auth_types, authtype->authproto)) {
- sasl_types = g_list_remove_link (sasl_types, t);
- g_list_free_1 (t);
+ if (cinfo && cinfo->auth_types) {
+ GHashTableIter iter;
+ gpointer key;
+
+ g_hash_table_iter_init (&iter, cinfo->auth_types);
+ while (g_hash_table_iter_next (&iter, &key, NULL)) {
+ CamelServiceAuthType *auth_type;
+
+ auth_type = camel_sasl_authtype (key);
+ if (auth_type)
+ sasl_types = g_list_prepend (sasl_types, auth_type);
}
}
diff --git a/src/libedataserver/e-oauth2-service-google.c b/src/libedataserver/e-oauth2-service-google.c
index 34f7dee..5951060 100644
--- a/src/libedataserver/e-oauth2-service-google.c
+++ b/src/libedataserver/e-oauth2-service-google.c
@@ -32,9 +32,9 @@ G_DEFINE_TYPE_WITH_CODE (EOAuth2ServiceGoogle, e_oauth2_service_google, E_TYPE_O
G_IMPLEMENT_INTERFACE (E_TYPE_OAUTH2_SERVICE, e_oauth2_service_google_oauth2_service_init))
static gboolean
-e_oauth2_service_google_guess_can_process (EOAuth2Service *service,
- const gchar *protocol,
- const gchar *hostname)
+eos_google_guess_can_process (EOAuth2Service *service,
+ const gchar *protocol,
+ const gchar *hostname)
{
return hostname && (
e_util_utf8_strstrcase (hostname, ".google.com") ||
@@ -44,46 +44,46 @@ e_oauth2_service_google_guess_can_process (EOAuth2Service *service,
}
static const gchar *
-e_oauth2_service_google_get_name (EOAuth2Service *service)
+eos_google_get_name (EOAuth2Service *service)
{
return "Google";
}
static const gchar *
-e_oauth2_service_google_get_display_name (EOAuth2Service *service)
+eos_google_get_display_name (EOAuth2Service *service)
{
/* Translators: This is a user-visible string, display name of an OAuth2 service. */
return C_("OAuth2Service", "Google");
}
static const gchar *
-e_oauth2_service_google_get_client_id (EOAuth2Service *service)
+eos_google_get_client_id (EOAuth2Service *service)
{
return GOOGLE_CLIENT_ID;
}
static const gchar *
-e_oauth2_service_google_get_client_secret (EOAuth2Service *service)
+eos_google_get_client_secret (EOAuth2Service *service)
{
return GOOGLE_CLIENT_SECRET;
}
static const gchar *
-e_oauth2_service_google_get_authentication_uri (EOAuth2Service *service)
+eos_google_get_authentication_uri (EOAuth2Service *service)
{
return "https://accounts.google.com/o/oauth2/auth";
}
static const gchar *
-e_oauth2_service_google_get_refresh_uri (EOAuth2Service *service)
+eos_google_get_refresh_uri (EOAuth2Service *service)
{
return "https://www.googleapis.com/oauth2/v3/token";
}
static void
-e_oauth2_service_google_prepare_authentication_uri_query (EOAuth2Service *service,
- ESource *source,
- GHashTable *uri_query)
+eos_google_prepare_authentication_uri_query (EOAuth2Service *service,
+ ESource *source,
+ GHashTable *uri_query)
{
const gchar *GOOGLE_SCOPE =
/* GMail IMAP and SMTP access */
@@ -99,19 +99,16 @@ e_oauth2_service_google_prepare_authentication_uri_query (EOAuth2Service *servic
g_return_if_fail (uri_query != NULL);
- #define add_to_form(name, value) g_hash_table_insert (uri_query, g_strdup (name), g_strdup (value))
-
- add_to_form ("scope", GOOGLE_SCOPE);
- add_to_form ("include_granted_scopes", "false");
-
- #undef add_to_form
+ e_oauth2_service_util_set_to_form (uri_query, "scope", GOOGLE_SCOPE);
+ e_oauth2_service_util_set_to_form (uri_query, "include_granted_scopes", "false");
}
static gboolean
-e_oauth2_service_google_extract_authorization_code (EOAuth2Service *service,
- const gchar *page_title,
- const gchar *page_uri,
- gchar **out_authorization_code)
+eos_google_extract_authorization_code (EOAuth2Service *service,
+ const gchar *page_title,
+ const gchar *page_uri,
+ const gchar *page_content,
+ gchar **out_authorization_code)
{
g_return_val_if_fail (out_authorization_code != NULL, FALSE);
@@ -135,15 +132,15 @@ e_oauth2_service_google_extract_authorization_code (EOAuth2Service *service,
static void
e_oauth2_service_google_oauth2_service_init (EOAuth2ServiceInterface *iface)
{
- iface->guess_can_process = e_oauth2_service_google_guess_can_process;
- iface->get_name = e_oauth2_service_google_get_name;
- iface->get_display_name = e_oauth2_service_google_get_display_name;
- iface->get_client_id = e_oauth2_service_google_get_client_id;
- iface->get_client_secret = e_oauth2_service_google_get_client_secret;
- iface->get_authentication_uri = e_oauth2_service_google_get_authentication_uri;
- iface->get_refresh_uri = e_oauth2_service_google_get_refresh_uri;
- iface->prepare_authentication_uri_query = e_oauth2_service_google_prepare_authentication_uri_query;
- iface->extract_authorization_code = e_oauth2_service_google_extract_authorization_code;
+ iface->guess_can_process = eos_google_guess_can_process;
+ iface->get_name = eos_google_get_name;
+ iface->get_display_name = eos_google_get_display_name;
+ iface->get_client_id = eos_google_get_client_id;
+ iface->get_client_secret = eos_google_get_client_secret;
+ iface->get_authentication_uri = eos_google_get_authentication_uri;
+ iface->get_refresh_uri = eos_google_get_refresh_uri;
+ iface->prepare_authentication_uri_query = eos_google_prepare_authentication_uri_query;
+ iface->extract_authorization_code = eos_google_extract_authorization_code;
}
static void
diff --git a/src/libedataserver/e-oauth2-service.c b/src/libedataserver/e-oauth2-service.c
index 5c602ed..794fef7 100644
--- a/src/libedataserver/e-oauth2-service.c
+++ b/src/libedataserver/e-oauth2-service.c
@@ -139,16 +139,20 @@ eos_default_guess_can_process (EOAuth2Service *service,
return can;
}
+static guint32
+eos_default_get_flags (EOAuth2Service *service)
+{
+ return E_OAUTH2_SERVICE_FLAG_NONE;
+}
+
static void
eos_default_prepare_authentication_uri_query (EOAuth2Service *service,
ESource *source,
GHashTable *uri_query)
{
- #define add_to_form(name, value) g_hash_table_insert (uri_query, g_strdup (name), value)
-
- add_to_form ("response_type", g_strdup ("code"));
- add_to_form ("client_id", g_strdup (e_oauth2_service_get_client_id (service)));
- add_to_form ("redirect_uri", g_strdup (DEFAULT_REDIRECT_URI));
+ e_oauth2_service_util_set_to_form (uri_query, "response_type", "code");
+ e_oauth2_service_util_set_to_form (uri_query, "client_id", e_oauth2_service_get_client_id (service));
+ e_oauth2_service_util_set_to_form (uri_query, "redirect_uri", DEFAULT_REDIRECT_URI);
if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
ESourceAuthentication *auth_extension;
@@ -158,12 +162,10 @@ eos_default_prepare_authentication_uri_query (EOAuth2Service *service,
user = e_source_authentication_dup_user (auth_extension);
if (user && *user)
- add_to_form ("login_hint", user);
+ e_oauth2_service_util_take_to_form (uri_query, "login_hint", user);
else
g_free (user);
}
-
- #undef add_to_form
}
static void
@@ -171,15 +173,11 @@ eos_default_prepare_get_token_form (EOAuth2Service *service,
const gchar *authorization_code,
GHashTable *form)
{
- #define add_to_form(name, value) g_hash_table_insert (form, g_strdup (name), g_strdup (value))
-
- add_to_form ("code", authorization_code);
- add_to_form ("client_id", e_oauth2_service_get_client_id (service));
- add_to_form ("client_secret", e_oauth2_service_get_client_secret (service));
- add_to_form ("redirect_uri", DEFAULT_REDIRECT_URI);
- add_to_form ("grant_type", "authorization_code");
-
- #undef add_to_form
+ e_oauth2_service_util_set_to_form (form, "code", authorization_code);
+ e_oauth2_service_util_set_to_form (form, "client_id", e_oauth2_service_get_client_id (service));
+ e_oauth2_service_util_set_to_form (form, "client_secret", e_oauth2_service_get_client_secret
(service));
+ e_oauth2_service_util_set_to_form (form, "redirect_uri", DEFAULT_REDIRECT_URI);
+ e_oauth2_service_util_set_to_form (form, "grant_type", "authorization_code");
}
static void
@@ -193,14 +191,10 @@ eos_default_prepare_refresh_token_form (EOAuth2Service *service,
const gchar *refresh_token,
GHashTable *form)
{
- #define add_to_form(name, value) g_hash_table_insert (form, g_strdup (name), g_strdup (value))
-
- add_to_form ("refresh_token", refresh_token);
- add_to_form ("client_id", e_oauth2_service_get_client_id (service));
- add_to_form ("client_secret", e_oauth2_service_get_client_secret (service));
- add_to_form ("grant_type", "refresh_token");
-
- #undef add_to_form
+ e_oauth2_service_util_set_to_form (form, "refresh_token", refresh_token);
+ e_oauth2_service_util_set_to_form (form, "client_id", e_oauth2_service_get_client_id (service));
+ e_oauth2_service_util_set_to_form (form, "client_secret", e_oauth2_service_get_client_secret
(service));
+ e_oauth2_service_util_set_to_form (form, "grant_type", "refresh_token");
}
static void
@@ -214,6 +208,7 @@ e_oauth2_service_default_init (EOAuth2ServiceInterface *iface)
{
iface->can_process = eos_default_can_process;
iface->guess_can_process = eos_default_guess_can_process;
+ iface->get_flags = eos_default_get_flags;
iface->prepare_authentication_uri_query = eos_default_prepare_authentication_uri_query;
iface->prepare_get_token_form = eos_default_prepare_get_token_form;
iface->prepare_get_token_message = eos_default_prepare_get_token_message;
@@ -307,6 +302,29 @@ e_oauth2_service_guess_can_process (EOAuth2Service *service,
}
/**
+ * e_oauth2_service_get_flags:
+ * @service: an #EOAuth2Service
+ *
+ * Returns: bit-or of #EOAuth2ServiceFlags for the @service. The default
+ * implementation returns %E_OAUTH2_SERVICE_FLAG_NONE.
+ *
+ * Since: 3.28
+ **/
+guint32
+e_oauth2_service_get_flags (EOAuth2Service *service)
+{
+ EOAuth2ServiceInterface *iface;
+
+ g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), E_OAUTH2_SERVICE_FLAG_NONE);
+
+ iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
+ g_return_val_if_fail (iface != NULL, E_OAUTH2_SERVICE_FLAG_NONE);
+ g_return_val_if_fail (iface->get_flags != NULL, E_OAUTH2_SERVICE_FLAG_NONE);
+
+ return iface->get_flags (service);
+}
+
+/**
* e_oauth2_service_get_name:
* @service: an #EOAuth2Service
*
@@ -487,23 +505,62 @@ e_oauth2_service_prepare_authentication_uri_query (EOAuth2Service *service,
}
/**
+ * e_oauth2_service_get_authentication_policy:
+ * @service: an #EOAuth2Service
+ * @uri: a URI of the navigation resource
+ *
+ * Used to decide what to do when the server redirects to the next page.
+ * The default implementation always returns %E_OAUTH2_SERVICE_NAVIGATION_POLICY_ALLOW.
+ *
+ * This method is called before e_oauth2_service_extract_authorization_code() and
+ * can be used to block certain resources or to abort the authentication when
+ * the server redirects to an unexpected page (like when user denies authorization
+ * in the page).
+ *
+ * Returns: one of #EOAuth2ServiceNavigationPolicy
+ *
+ * Since: 3.28
+ **/
+EOAuth2ServiceNavigationPolicy
+e_oauth2_service_get_authentication_policy (EOAuth2Service *service,
+ const gchar *uri)
+{
+ EOAuth2ServiceInterface *iface;
+
+ g_return_val_if_fail (E_IS_OAUTH2_SERVICE (service), E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT);
+ g_return_val_if_fail (uri != NULL, E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT);
+
+ iface = E_OAUTH2_SERVICE_GET_INTERFACE (service);
+ g_return_val_if_fail (iface != NULL, E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT);
+ g_return_val_if_fail (iface->get_authentication_policy != NULL,
E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT);
+
+ return iface->get_authentication_policy (service, uri);
+}
+
+/**
* e_oauth2_service_extract_authorization_code:
* @service: an #EOAuth2Service
* @page_title: a web page title
* @page_uri: a web page URI
+ * @page_content: (nullable): a web page content
* @out_authorization_code: (out) (transfer full): the extracted authorization code
*
* Tries to extract an authorization code from a web page provided by the server.
* The function can be called multiple times, whenever the page load is finished.
*
* There can happen three states: 1) either the @service cannot determine
- * the authentication code from the @page_title nor @page_uri, then the %FALSE is
+ * the authentication code from the page information, then the %FALSE is
* returned and the @out_authorization_code is left untouched; or 2) the server
* reported a failure, in which case the function returns %TRUE and lefts
* the @out_authorization_code untouched; or 3) the @service could extract
* the authentication code from the given arguments, then the function
* returns %TRUE and sets the received authorization code to @out_authorization_code.
*
+ * The @page_content is %NULL, unless flags returned by e_oauth2_service_get_flags()
+ * contain also %E_OAUTH2_SERVICE_FLAG_EXTRACT_REQUIRES_PAGE_CONTENT.
+ *
+ * This method is always called after e_oauth2_service_get_authentication_policy().
+ *
* Returns: whether could recognized successful or failed server response.
* The @out_authorization_code is populated on success too.
*
@@ -513,6 +570,7 @@ gboolean
e_oauth2_service_extract_authorization_code (EOAuth2Service *service,
const gchar *page_title,
const gchar *page_uri,
+ const gchar *page_content,
gchar **out_authorization_code)
{
EOAuth2ServiceInterface *iface;
@@ -523,7 +581,7 @@ e_oauth2_service_extract_authorization_code (EOAuth2Service *service,
g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (iface->extract_authorization_code != NULL, FALSE);
- return iface->extract_authorization_code (service, page_title, page_uri, out_authorization_code);
+ return iface->extract_authorization_code (service, page_title, page_uri, page_content,
out_authorization_code);
}
/**
@@ -664,11 +722,15 @@ eos_create_soup_session (EOAuth2ServiceRefSourceFunc ref_source,
gpointer ref_source_user_data,
ESource *source)
{
+ static gint oauth2_debug = -1;
ESourceAuthentication *auth_extension;
ESource *proxy_source = NULL;
SoupSession *session;
gchar *uid;
+ if (oauth2_debug == -1)
+ oauth2_debug = g_strcmp0 (g_getenv ("OAUTH2_DEBUG"), "1") == 0 ? 1 : 0;
+
session = soup_session_new ();
g_object_set (
session,
@@ -678,6 +740,14 @@ eos_create_soup_session (EOAuth2ServiceRefSourceFunc ref_source,
SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
NULL);
+ if (oauth2_debug) {
+ SoupLogger *logger;
+
+ logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
+ g_object_unref (logger);
+ }
+
if (!e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION))
return session;
@@ -776,7 +846,18 @@ eos_send_message (SoupSession *session,
status_code = SOUP_STATUS_MALFORMED;
}
} else if (status_code != SOUP_STATUS_CANCELLED) {
- g_set_error_literal (error, SOUP_HTTP_ERROR, message->status_code, message->reason_phrase);
+ GString *error_msg;
+
+ error_msg = g_string_new (message->reason_phrase);
+ if (message->response_body && message->response_body->length) {
+ g_string_append (error_msg, " (");
+ g_string_append_len (error_msg, message->response_body->data,
message->response_body->length);
+ g_string_append (error_msg, ")");
+ }
+
+ g_set_error_literal (error, SOUP_HTTP_ERROR, message->status_code, error_msg->str);
+
+ g_string_free (error_msg, TRUE);
}
return success;
@@ -1046,7 +1127,7 @@ eos_lookup_token_sync (EOAuth2Service *service,
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
/* Translators: The first %s is a display name of the source, the second is its UID
and
the third is the name of the OAuth service. */
- _("Source “%s” (%s) is not a valid for “%s” OAuth2 service"),
+ _("Source “%s” (%s) is not valid for “%s” OAuth2 service"),
e_source_get_display_name (source),
e_source_get_uid (source),
e_oauth2_service_get_name (service));
@@ -1377,3 +1458,64 @@ e_oauth2_service_get_access_token_sync (EOAuth2Service *service,
return success;
}
+
+/**
+ * e_oauth2_service_util_set_to_form:
+ * @form: a #GHashTable
+ * @name: a property name
+ * @value: (nullable): a property value
+ *
+ * Sets @value for @name to @form. The @form should be
+ * the one used in e_oauth2_service_prepare_authentication_uri_query(),
+ * e_oauth2_service_prepare_get_token_form() or
+ * e_oauth2_service_prepare_refresh_token_form().
+ *
+ * If the @value is %NULL, then the property named @name is removed
+ * from the @form instead.
+ *
+ * Since: 3.28
+ **/
+void
+e_oauth2_service_util_set_to_form (GHashTable *form,
+ const gchar *name,
+ const gchar *value)
+{
+ g_return_if_fail (form != NULL);
+ g_return_if_fail (name != NULL);
+
+ if (value)
+ g_hash_table_insert (form, g_strdup (name), g_strdup (value));
+ else
+ g_hash_table_remove (form, name);
+}
+
+/**
+ * e_oauth2_service_util_take_to_form:
+ * @form: a #GHashTable
+ * @name: a property name
+ * @value: (transfer full) (nullable): a property value
+ *
+ * Takes ownership of @value and sets it for @name to @form. The @value
+ * will be freed with g_free(), when no longer needed. The @form should be
+ * the one used in e_oauth2_service_prepare_authentication_uri_query(),
+ * e_oauth2_service_prepare_get_token_form() or
+ * e_oauth2_service_prepare_refresh_token_form().
+ *
+ * If the @value is %NULL, then the property named @name is removed
+ * from the @form instead.
+ *
+ * Since: 3.28
+ **/
+void
+e_oauth2_service_util_take_to_form (GHashTable *form,
+ const gchar *name,
+ gchar *value)
+{
+ g_return_if_fail (form != NULL);
+ g_return_if_fail (name != NULL);
+
+ if (value)
+ g_hash_table_insert (form, g_strdup (name), value);
+ else
+ g_hash_table_remove (form, name);
+}
diff --git a/src/libedataserver/e-oauth2-service.h b/src/libedataserver/e-oauth2-service.h
index 92b5ccb..f2d48e3 100644
--- a/src/libedataserver/e-oauth2-service.h
+++ b/src/libedataserver/e-oauth2-service.h
@@ -48,6 +48,39 @@
G_BEGIN_DECLS
/**
+ * EOAuth2ServiceFlags:
+ * @E_OAUTH2_SERVICE_FLAG_NONE: No flag set
+ * @E_OAUTH2_SERVICE_FLAG_EXTRACT_REQUIRES_PAGE_CONTENT: the service requires also page
+ * content to be passed to e_oauth2_service_extract_authorization_code()
+ *
+ * Flags of the OAuth2 service.
+ *
+ * Since: 3.28
+ **/
+typedef enum {
+ E_OAUTH2_SERVICE_FLAG_NONE = 0,
+ E_OAUTH2_SERVICE_FLAG_EXTRACT_REQUIRES_PAGE_CONTENT = (1 << 1)
+} EOAuth2ServiceFlags;
+
+/**
+ * EOAuth2ServiceNavigationPolicy:
+ * @E_OAUTH2_SERVICE_NAVIGATION_POLICY_DENY: Deny navigation to the given web resource
+ * @E_OAUTH2_SERVICE_NAVIGATION_POLICY_ALLOW: Allow navigation to the given web resource
+ * @E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT: Abort authentication processing
+ *
+ * A value used during querying authentication URI, to decide whether certain
+ * resource can be used or not. The @E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT
+ * can be used to abort the authentication query, like when user cancelled it.
+ *
+ * Since: 3.28
+ **/
+typedef enum {
+ E_OAUTH2_SERVICE_NAVIGATION_POLICY_DENY,
+ E_OAUTH2_SERVICE_NAVIGATION_POLICY_ALLOW,
+ E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT
+} EOAuth2ServiceNavigationPolicy;
+
+/**
* EOAuth2ServiceRefSourceFunc:
* @user_data: user data, as passed to e_oauth2_service_get_token_sync()
* or e_oauth2_service_refresh_token_sync()
@@ -80,6 +113,7 @@ struct _EOAuth2ServiceInterface {
gboolean (* guess_can_process) (EOAuth2Service *service,
const gchar *protocol,
const gchar *hostname);
+ guint32 (* get_flags) (EOAuth2Service *service);
const gchar * (* get_name) (EOAuth2Service *service);
const gchar * (* get_display_name) (EOAuth2Service *service);
const gchar * (* get_client_id) (EOAuth2Service *service);
@@ -90,9 +124,13 @@ struct _EOAuth2ServiceInterface {
(EOAuth2Service *service,
ESource *source,
GHashTable *uri_query);
+ EOAuth2ServiceNavigationPolicy
+ (* get_authentication_policy) (EOAuth2Service *service,
+ const gchar *uri);
gboolean (* extract_authorization_code) (EOAuth2Service *service,
const gchar *page_title,
const gchar *page_uri,
+ const gchar *page_content,
gchar **out_authorization_code);
void (* prepare_get_token_form) (EOAuth2Service *service,
const gchar *authorization_code,
@@ -116,6 +154,7 @@ gboolean e_oauth2_service_can_process (EOAuth2Service *service,
gboolean e_oauth2_service_guess_can_process (EOAuth2Service *service,
const gchar *protocol,
const gchar *hostname);
+guint32 e_oauth2_service_get_flags (EOAuth2Service *service);
const gchar * e_oauth2_service_get_name (EOAuth2Service *service);
const gchar * e_oauth2_service_get_display_name (EOAuth2Service *service);
const gchar * e_oauth2_service_get_client_id (EOAuth2Service *service);
@@ -126,10 +165,15 @@ void e_oauth2_service_prepare_authentication_uri_query
(EOAuth2Service *service,
ESource *source,
GHashTable *uri_query);
+EOAuth2ServiceNavigationPolicy
+ e_oauth2_service_get_authentication_policy
+ (EOAuth2Service *service,
+ const gchar *uri);
gboolean e_oauth2_service_extract_authorization_code
(EOAuth2Service *service,
const gchar *page_title,
const gchar *page_uri,
+ const gchar *page_content,
gchar **out_authorization_code);
void e_oauth2_service_prepare_get_token_form (EOAuth2Service *service,
const gchar *authorization_code,
@@ -174,6 +218,13 @@ gboolean e_oauth2_service_get_access_token_sync (EOAuth2Service *service,
GCancellable *cancellable,
GError **error);
+void e_oauth2_service_util_set_to_form (GHashTable *form,
+ const gchar *name,
+ const gchar *value);
+void e_oauth2_service_util_take_to_form (GHashTable *form,
+ const gchar *name,
+ gchar *value);
+
G_END_DECLS
#endif /* E_OAUTH2_SERVICE_H */
diff --git a/src/libedataserverui/e-credentials-prompter-impl-oauth2.c
b/src/libedataserverui/e-credentials-prompter-impl-oauth2.c
index 9d92f29..5cf02d0 100644
--- a/src/libedataserverui/e-credentials-prompter-impl-oauth2.c
+++ b/src/libedataserverui/e-credentials-prompter-impl-oauth2.c
@@ -209,29 +209,20 @@ cpi_oauth2_get_access_token_thread (gpointer user_data)
}
static void
-cpi_oauth2_document_load_changed_cb (WebKitWebView *web_view,
- WebKitLoadEvent load_event,
- ECredentialsPrompterImplOAuth2 *prompter_oauth2)
+cpi_oauth2_extract_authentication_code (ECredentialsPrompterImplOAuth2 *prompter_oauth2,
+ const gchar *page_title,
+ const gchar *page_uri,
+ const gchar *page_content)
{
- const gchar *title, *uri;
gchar *authorization_code = NULL;
- g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_OAUTH2 (prompter_oauth2));
-
- if (load_event != WEBKIT_LOAD_FINISHED)
- return;
-
- title = webkit_web_view_get_title (web_view);
- uri = webkit_web_view_get_uri (web_view);
- if (!title || !uri)
- return;
-
g_return_if_fail (prompter_oauth2->priv->service != NULL);
if (!e_oauth2_service_extract_authorization_code (prompter_oauth2->priv->service,
- title, uri, &authorization_code))
+ page_title, page_uri, page_content, &authorization_code)) {
return;
+ }
if (authorization_code) {
ECredentialsPrompter *prompter;
@@ -239,10 +230,10 @@ cpi_oauth2_document_load_changed_cb (WebKitWebView *web_view,
AccessTokenThreadData *td;
GThread *thread;
- e_credentials_prompter_impl_oauth2_show_html (web_view,
+ e_credentials_prompter_impl_oauth2_show_html (prompter_oauth2->priv->web_view,
"Checking returned code", _("Requesting access token, please wait..."));
- gtk_widget_set_sensitive (GTK_WIDGET (web_view), FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (prompter_oauth2->priv->web_view), FALSE);
e_named_parameters_set (prompter_oauth2->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD,
NULL);
@@ -266,6 +257,119 @@ cpi_oauth2_document_load_changed_cb (WebKitWebView *web_view,
}
static void
+cpi_oauth2_web_view_resource_get_data_done_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECredentialsPrompterImplOAuth2 *prompter_oauth2 = user_data;
+ GByteArray *page_content = NULL;
+ const gchar *title, *uri;
+ guchar *data;
+ gsize len = 0;
+ GError *local_error = NULL;
+
+ g_return_if_fail (WEBKIT_IS_WEB_RESOURCE (source_object));
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_OAUTH2 (prompter_oauth2));
+
+ data = webkit_web_resource_get_data_finish (WEBKIT_WEB_RESOURCE (source_object), result, &len,
&local_error);
+ if (data) {
+ page_content = g_byte_array_new_take ((guint8 *) data, len);
+
+ /* NULL-terminate the array, to be able to use it as a string */
+ g_byte_array_append (page_content, (const guint8 *) "", 1);
+ } else if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_clear_error (&local_error);
+ return;
+ }
+
+ g_clear_error (&local_error);
+
+ title = webkit_web_view_get_title (prompter_oauth2->priv->web_view);
+ uri = webkit_web_view_get_uri (prompter_oauth2->priv->web_view);
+
+ cpi_oauth2_extract_authentication_code (prompter_oauth2, title, uri, page_content ? (const gchar *)
page_content->data : NULL);
+
+ if (page_content)
+ g_byte_array_free (page_content, TRUE);
+}
+
+static gboolean
+cpi_oauth2_decide_policy_cb (WebKitWebView *web_view,
+ WebKitPolicyDecision *decision,
+ WebKitPolicyDecisionType decision_type,
+ ECredentialsPrompterImplOAuth2 *prompter_oauth2)
+{
+ WebKitNavigationAction *navigation_action;
+ WebKitURIRequest *request;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_OAUTH2 (prompter_oauth2), FALSE);
+ g_return_val_if_fail (WEBKIT_IS_POLICY_DECISION (decision), FALSE);
+
+ if (decision_type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION)
+ return FALSE;
+
+ navigation_action = webkit_navigation_policy_decision_get_navigation_action
(WEBKIT_NAVIGATION_POLICY_DECISION (decision));
+ if (!navigation_action)
+ return FALSE;
+
+ request = webkit_navigation_action_get_request (navigation_action);
+ if (!request || !webkit_uri_request_get_uri (request))
+ return FALSE;
+
+ g_return_val_if_fail (prompter_oauth2->priv->service != NULL, FALSE);
+
+ switch (e_oauth2_service_get_authentication_policy (prompter_oauth2->priv->service,
webkit_uri_request_get_uri (request))) {
+ case E_OAUTH2_SERVICE_NAVIGATION_POLICY_DENY:
+ webkit_policy_decision_ignore (decision);
+ break;
+ case E_OAUTH2_SERVICE_NAVIGATION_POLICY_ALLOW:
+ webkit_policy_decision_use (decision);
+ break;
+ case E_OAUTH2_SERVICE_NAVIGATION_POLICY_ABORT:
+ g_cancellable_cancel (prompter_oauth2->priv->cancellable);
+ gtk_dialog_response (prompter_oauth2->priv->dialog, GTK_RESPONSE_CANCEL);
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+cpi_oauth2_document_load_changed_cb (WebKitWebView *web_view,
+ WebKitLoadEvent load_event,
+ ECredentialsPrompterImplOAuth2 *prompter_oauth2)
+{
+ const gchar *title, *uri;
+
+ g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_OAUTH2 (prompter_oauth2));
+
+ if (load_event != WEBKIT_LOAD_FINISHED)
+ return;
+
+ title = webkit_web_view_get_title (web_view);
+ uri = webkit_web_view_get_uri (web_view);
+ if (!title || !uri)
+ return;
+
+ g_return_if_fail (prompter_oauth2->priv->service != NULL);
+
+ if ((e_oauth2_service_get_flags (prompter_oauth2->priv->service) &
E_OAUTH2_SERVICE_FLAG_EXTRACT_REQUIRES_PAGE_CONTENT) != 0) {
+ WebKitWebResource *main_resource;
+
+ main_resource = webkit_web_view_get_main_resource (web_view);
+ if (main_resource) {
+ webkit_web_resource_get_data (main_resource, prompter_oauth2->priv->cancellable,
+ cpi_oauth2_web_view_resource_get_data_done_cb, prompter_oauth2);
+ }
+ } else {
+ cpi_oauth2_extract_authentication_code (prompter_oauth2, title, uri, NULL);
+ }
+}
+
+static void
cpi_oauth2_notify_estimated_load_progress_cb (WebKitWebView *web_view,
GParamSpec *param,
GtkProgressBar *progress_bar)
@@ -449,6 +553,7 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
GtkScrolledWindow *scrolled_window;
GtkWindow *dialog_parent;
ECredentialsPrompter *prompter;
+ WebKitSettings *webkit_settings;
gchar *title, *uri;
GString *info_markup;
gint row = 0;
@@ -544,7 +649,19 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
scrolled_window = GTK_SCROLLED_WINDOW (widget);
- widget = webkit_web_view_new ();
+ webkit_settings = webkit_settings_new_with_settings (
+ "auto-load-images", TRUE,
+ "default-charset", "utf-8",
+ "enable-html5-database", FALSE,
+ "enable-dns-prefetching", FALSE,
+ "enable-html5-local-storage", FALSE,
+ "enable-offline-web-application-cache", FALSE,
+ "enable-page-cache", FALSE,
+ "enable-plugins", FALSE,
+ "media-playback-allows-inline", FALSE,
+ NULL);
+
+ widget = webkit_web_view_new_with_settings (webkit_settings);
g_object_set (
G_OBJECT (widget),
"hexpand", TRUE,
@@ -553,6 +670,7 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
"valign", GTK_ALIGN_FILL,
NULL);
gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
+ g_object_unref (webkit_settings);
prompter_oauth2->priv->web_view = WEBKIT_WEB_VIEW (widget);
@@ -578,8 +696,10 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
success = FALSE;
} else {
WebKitWebView *web_view = prompter_oauth2->priv->web_view;
- gulong load_finished_handler_id, progress_handler_id;
+ gulong decide_policy_handler_id, load_finished_handler_id, progress_handler_id;
+ decide_policy_handler_id = g_signal_connect (web_view, "decide-policy",
+ G_CALLBACK (cpi_oauth2_decide_policy_cb), prompter_oauth2);
load_finished_handler_id = g_signal_connect (web_view, "load-changed",
G_CALLBACK (cpi_oauth2_document_load_changed_cb), prompter_oauth2);
progress_handler_id = g_signal_connect (web_view, "notify::estimated-load-progress",
@@ -589,6 +709,8 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
success = gtk_dialog_run (prompter_oauth2->priv->dialog) == GTK_RESPONSE_OK;
+ if (decide_policy_handler_id)
+ g_signal_handler_disconnect (web_view, decide_policy_handler_id);
if (load_finished_handler_id)
g_signal_handler_disconnect (web_view, load_finished_handler_id);
if (progress_handler_id)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]