From fa0d18fcf2d8084d2a41f24f50f689eed8e3e241 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Tue, 09 Jul 2013 18:23:04 +0000 Subject: Add ESoupAuthBearer. SoupAuth subclass for use with OAuth 2.0 HTTP authentication. See http://tools.ietf.org/html/rfc6750 EBackends should use e_source_get_oauth2_access_token() to obtain the access token and token expiry for an ESource, then pass them to e_soup_auth_bearer_set_access_token(). --- diff --git a/docs/reference/libebackend/libebackend-docs.xml b/docs/reference/libebackend/libebackend-docs.xml index fa9ab92..f54d3d6 100644 --- a/docs/reference/libebackend/libebackend-docs.xml +++ b/docs/reference/libebackend/libebackend-docs.xml @@ -34,6 +34,7 @@ Miscellaneous Utilities + diff --git a/docs/reference/libebackend/libebackend-sections.txt b/docs/reference/libebackend/libebackend-sections.txt index 6c604bf..189f002 100644 --- a/docs/reference/libebackend/libebackend-sections.txt +++ b/docs/reference/libebackend/libebackend-sections.txt @@ -397,6 +397,24 @@ EServerSideSourcePrivate
+e-soup-auth-bearer +ESoupAuthBearer +ESoupAuthBearer +e_soup_auth_bearer_set_access_token + +E_SOUP_AUTH_BEARER +E_IS_SOUP_AUTH_BEARER +E_TYPE_SOUP_AUTH_BEARER +E_SOUP_AUTH_BEARER_CLASS +E_IS_SOUP_AUTH_BEARER_CLASS +E_SOUP_AUTH_BEARER_GET_CLASS +ESoupAuthBearerClass +e_soup_auth_bearer_get_type + +ESoupAuthBearerPrivate +
+ +
e-source-registry-server ESourceRegistryServer E_SOURCE_REGISTRY_SERVER_OBJECT_PATH diff --git a/docs/reference/libebackend/libebackend.types b/docs/reference/libebackend/libebackend.types index aeb38dc..1d7fe96 100644 --- a/docs/reference/libebackend/libebackend.types +++ b/docs/reference/libebackend/libebackend.types @@ -15,6 +15,7 @@ e_module_get_type e_oauth2_support_get_type e_offline_listener_get_type e_server_side_source_get_type +e_soup_auth_bearer_get_type e_source_registry_server_get_type e_user_prompter_get_type e_user_prompter_server_get_type diff --git a/libebackend/Makefile.am b/libebackend/Makefile.am index c77b470..783732e 100644 --- a/libebackend/Makefile.am +++ b/libebackend/Makefile.am @@ -35,6 +35,7 @@ libebackend_1_2_la_CPPFLAGS = \ $(E_BACKEND_CFLAGS) \ $(GCR_BASE_CFLAGS) \ $(GIO_UNIX_CFLAGS) \ + $(SOUP_CFLAGS) \ $(CODE_COVERAGE_CFLAGS) \ $(NULL) @@ -56,6 +57,7 @@ libebackend_1_2_la_SOURCES = \ e-db3-utils.c \ e-module.c \ e-server-side-source.c \ + e-soup-auth-bearer.c \ e-source-registry-server.c \ e-sqlite3-vfs.c \ e-user-prompter.c \ @@ -71,6 +73,7 @@ libebackend_1_2_la_LIBADD = \ $(SQLITE3_LIBS) \ $(GCR_BASE_LIBS) \ $(GIO_UNIX_LIBS) \ + $(SOUP_LIBS) \ $(DB_LIBS) libebackend_1_2_la_LDFLAGS = \ @@ -100,6 +103,7 @@ libebackendinclude_HEADERS = \ e-dbhash.h \ e-module.h \ e-server-side-source.h \ + e-soup-auth-bearer.h \ e-source-registry-server.h \ e-sqlite3-vfs.h \ e-user-prompter.h \ diff --git a/libebackend/e-soup-auth-bearer.c b/libebackend/e-soup-auth-bearer.c new file mode 100644 index 0000000..1d5f804 --- /dev/null +++ b/libebackend/e-soup-auth-bearer.c @@ -0,0 +1,196 @@ +/* + * e-soup-auth-bearer.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-soup-auth-bearer + * @include: libebackend/libebackend.h + * @short_description: OAuth 2.0 support for libsoup + * + * #ESoupAuthBearer adds libsoup support for the use of bearer tokens in + * HTTP requests to access OAuth 2.0 protected resources, as defined in + * RFC 6750. + * + * An #EBackend should integrate #ESoupAuthBearer first by adding it as a + * feature to a #SoupSession's #SoupAuthManager, then from a #SoupSession + * #SoupSession::authenticate handler call e_source_get_oauth2_access_token() + * and pass the results to e_soup_auth_bearer_set_access_token(). + **/ + +#include "e-soup-auth-bearer.h" + +#include + +#define E_SOUP_AUTH_BEARER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearerPrivate)) + +#define AUTH_STRENGTH 1 + +#define EXPIRY_INVALID ((time_t) -1) + +struct _ESoupAuthBearerPrivate { + gchar *access_token; + time_t expiry; +}; + +G_DEFINE_TYPE ( + ESoupAuthBearer, + e_soup_auth_bearer, + SOUP_TYPE_AUTH) + +static gboolean +e_soup_auth_bearer_is_expired (ESoupAuthBearer *bearer) +{ + gboolean expired = FALSE; + + if (bearer->priv->expiry != EXPIRY_INVALID) + expired = (bearer->priv->expiry < time (NULL)); + + return expired; +} + +static void +e_soup_auth_bearer_finalize (GObject *object) +{ + ESoupAuthBearerPrivate *priv; + + priv = E_SOUP_AUTH_BEARER_GET_PRIVATE (object); + + g_free (priv->access_token); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_soup_auth_bearer_parent_class)->finalize (object); +} + +static gboolean +e_soup_auth_bearer_update (SoupAuth *auth, + SoupMessage *message, + GHashTable *auth_header) +{ + /* XXX Not sure what to do here. Discard the access token? */ + + return TRUE; +} + +static GSList * +e_soup_auth_bearer_get_protection_space (SoupAuth *auth, + SoupURI *source_uri) +{ + /* XXX Not sure what to do here. Need to return something. */ + + return g_slist_prepend (NULL, g_strdup ("")); +} + +static gboolean +e_soup_auth_bearer_is_authenticated (SoupAuth *auth) +{ + ESoupAuthBearer *bearer; + gboolean authenticated = FALSE; + + bearer = E_SOUP_AUTH_BEARER (auth); + + if (!e_soup_auth_bearer_is_expired (bearer)) + authenticated = (bearer->priv->access_token != NULL); + + return authenticated; +} + +static gchar * +e_soup_auth_bearer_get_authorization (SoupAuth *auth, + SoupMessage *message) +{ + ESoupAuthBearer *bearer; + + bearer = E_SOUP_AUTH_BEARER (auth); + + return g_strdup_printf ("Bearer %s", bearer->priv->access_token); +} + +static void +e_soup_auth_bearer_class_init (ESoupAuthBearerClass *class) +{ + GObjectClass *object_class; + SoupAuthClass *auth_class; + + g_type_class_add_private (class, sizeof (ESoupAuthBearerPrivate)); + + /* Keep the "e" prefix on private methods + * so we don't step on libsoup's namespace. */ + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = e_soup_auth_bearer_finalize; + + auth_class = SOUP_AUTH_CLASS (class); + auth_class->scheme_name = "Bearer"; + auth_class->strength = AUTH_STRENGTH; + auth_class->update = e_soup_auth_bearer_update; + auth_class->get_protection_space = e_soup_auth_bearer_get_protection_space; + auth_class->is_authenticated = e_soup_auth_bearer_is_authenticated; + auth_class->get_authorization = e_soup_auth_bearer_get_authorization; +} + +static void +e_soup_auth_bearer_init (ESoupAuthBearer *bearer) +{ + bearer->priv = E_SOUP_AUTH_BEARER_GET_PRIVATE (bearer); + bearer->priv->expiry = EXPIRY_INVALID; +} + +/** + * e_soup_auth_bearer_set_access_token: + * @bearer: an #ESoupAuthBearer + * @access_token: an OAuth 2.0 access token + * @expires_in_seconds: expiry for @access_token, or 0 if unknown + * + * This function is analogous to soup_auth_authenticate() for "Basic" HTTP + * authentication, except it takes an OAuth 2.0 access token instead of a + * username and password. + * + * If @expires_in_seconds is greater than zero, soup_auth_is_authenticated() + * will return %FALSE after the given number of seconds have elapsed. + * + * Since: 3.10 + **/ +void +e_soup_auth_bearer_set_access_token (ESoupAuthBearer *bearer, + const gchar *access_token, + gint expires_in_seconds) +{ + gboolean was_authenticated; + gboolean now_authenticated; + + g_return_if_fail (E_IS_SOUP_AUTH_BEARER (bearer)); + + was_authenticated = soup_auth_is_authenticated (SOUP_AUTH (bearer)); + + g_free (bearer->priv->access_token); + bearer->priv->access_token = g_strdup (access_token); + + if (expires_in_seconds > 0) + bearer->priv->expiry = time (NULL) + expires_in_seconds; + else + bearer->priv->expiry = EXPIRY_INVALID; + + now_authenticated = soup_auth_is_authenticated (SOUP_AUTH (bearer)); + + if (was_authenticated != now_authenticated) + g_object_notify ( + G_OBJECT (bearer), + SOUP_AUTH_IS_AUTHENTICATED); +} + diff --git a/libebackend/e-soup-auth-bearer.h b/libebackend/e-soup-auth-bearer.h new file mode 100644 index 0000000..83746a4 --- /dev/null +++ b/libebackend/e-soup-auth-bearer.h @@ -0,0 +1,79 @@ +/* + * e-soup-auth-bearer.h + * + * 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 + * + */ + +#if !defined (__LIBEBACKEND_H_INSIDE__) && !defined (LIBEBACKEND_COMPILATION) +#error "Only should be included directly." +#endif + +#ifndef E_SOUP_AUTH_BEARER_H +#define E_SOUP_AUTH_BEARER_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOUP_AUTH_BEARER \ + (e_soup_auth_bearer_get_type ()) +#define E_SOUP_AUTH_BEARER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearer)) +#define E_SOUP_AUTH_BEARER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearerClass)) +#define E_IS_SOUP_AUTH_BEARER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOUP_AUTH_BEARER)) +#define E_IS_SOUP_AUTH_BEARER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOUP_AUTH_BEARER)) +#define E_SOUP_AUTH_BEARER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearerClass)) + +G_BEGIN_DECLS + +typedef struct _ESoupAuthBearer ESoupAuthBearer; +typedef struct _ESoupAuthBearerClass ESoupAuthBearerClass; +typedef struct _ESoupAuthBearerPrivate ESoupAuthBearerPrivate; + +/** + * ESoupAuthBearer: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.10 + **/ +struct _ESoupAuthBearer { + SoupAuth parent; + ESoupAuthBearerPrivate *priv; +}; + +struct _ESoupAuthBearerClass { + SoupAuthClass parent_class; +}; + +GType e_soup_auth_bearer_get_type (void) G_GNUC_CONST; +void e_soup_auth_bearer_set_access_token + (ESoupAuthBearer *bearer, + const gchar *access_token, + gint expires_in_seconds); + +G_END_DECLS + +#endif /* E_SOUP_AUTH_BEARER_H */ + diff --git a/libebackend/libebackend.h b/libebackend/libebackend.h index f7f0157..0aac5d4 100644 --- a/libebackend/libebackend.h +++ b/libebackend/libebackend.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include