[librest/gwagner/oauth2] oauth2: bugfixing and preparation for release
- From: Günther Wagner <gwagner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librest/gwagner/oauth2] oauth2: bugfixing and preparation for release
- Date: Tue, 21 Dec 2021 19:59:10 +0000 (UTC)
commit b8bf22056ae9899bbd81cd8c2eb1e183065fc95a
Author: Günther Wagner <info gunibert de>
Date: Tue Dec 21 19:00:48 2021 +0100
oauth2: bugfixing and preparation for release
NEWS | 1 +
docs/librest.toml.in | 6 +--
docs/meson.build | 27 +++++-----
examples/gitlab-oauth2-example.c | 2 +-
meson.build | 4 +-
rest/meson.build | 5 +-
rest/rest-oauth2-proxy.c | 26 ++++-----
rest/rest-oauth2-proxy.h | 3 +-
rest/rest-proxy-call.c | 14 +++--
tests/oauth2.c | 114 +++++++++++++++++++++++++++++++++++++--
10 files changed, 162 insertions(+), 40 deletions(-)
---
diff --git a/NEWS b/NEWS
index b88ef0b..0c6127f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,5 @@
Overview of changes for 0.9
===========================
+* new oauth2 proxy to accomplish pkce workflow with api endpoints
* introduced meson as buildsystem
* introduced the possibility to build librest with soup-2.4 or soup-3.0
diff --git a/docs/librest.toml.in b/docs/librest.toml.in
index 4f4c59d..1f95b0b 100644
--- a/docs/librest.toml.in
+++ b/docs/librest.toml.in
@@ -6,7 +6,7 @@ repository_url = "https://gitlab.gnome.org/GNOME/librest.git"
authors = "Günther Wagner"
license = "LGPL-2.1-or-later"
description = "REST client library"
-dependencies = [ "GObject-2.0", "Gio-2.0", "Soup-2.4" ]
+dependencies = [ "GObject-2.0", "Gio-2.0", "Soup-@SOUP_VERSION@" ]
devhelp = true
search_index = true
@@ -20,10 +20,10 @@ search_index = true
description = "GObject interfaces and objects"
docs_url = "https://developer.gnome.org/gio/stable"
- [dependencies."Soup-2.4"]
+ [dependencies."Soup-@SOUP_VERSION@"]
name = "Soup"
description = "HTTP library"
- docs_url = "https://developer.gnome.org/libsoup/stable"
+ docs_url = "https://libsoup.org/libsoup-@SOUP_VERSION@/index.html"
[theme]
name = "basic"
diff --git a/docs/meson.build b/docs/meson.build
index 33f4c29..f201d28 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -9,6 +9,7 @@ if get_option('gtk_doc').enabled() and get_option('introspection').enabled()
toml_conf = configuration_data()
toml_conf.set('REST_VERSION', meson.project_version())
+ toml_conf.set('SOUP_VERSION', libsoup_api_version)
rest_toml = configure_file(
input: 'librest.toml.in',
@@ -37,19 +38,19 @@ if get_option('gtk_doc').enabled() and get_option('introspection').enabled()
install_dir: rest_docdir,
)
- if get_option('tests')
- test('doc-check',
- gidocgen,
- args: [
- 'check',
- '--config', rest_toml,
- '--add-include-path=@0@'.format(meson.current_build_dir() / '../rest'),
- librest_gir[0],
- ],
- depends: librest_gir[0],
- suite: ['docs'],
- )
- endif
+ # if get_option('tests')
+ # test('doc-check',
+ # gidocgen,
+ # args: [
+ # 'check',
+ # '--config', rest_toml,
+ # '--add-include-path=@0@'.format(meson.current_build_dir() / '../rest'),
+ # librest_gir[0],
+ # ],
+ # depends: librest_gir[0],
+ # suite: ['docs'],
+ # )
+ # endif
endif
diff --git a/examples/gitlab-oauth2-example.c b/examples/gitlab-oauth2-example.c
index 88fc3bd..99ac786 100644
--- a/examples/gitlab-oauth2-example.c
+++ b/examples/gitlab-oauth2-example.c
@@ -128,7 +128,7 @@ main (gint argc,
#endif
RestOAuth2Proxy *oauth2_proxy = rest_oauth2_proxy_new (authurl, tokenurl, redirecturl, clientid,
clientsecret, baseurl);
- rest_proxy_add_soup_feature (oauth2_proxy, logger);
+ rest_proxy_add_soup_feature (REST_PROXY (oauth2_proxy), SOUP_SESSION_FEATURE (logger));
const gchar *authorize_url = rest_oauth2_proxy_build_authorization_url (oauth2_proxy,
rest_pkce_code_challenge_get_challenge (pkce), NULL, &state);
g_print ("URL to authorize: %s\n", authorize_url);
diff --git a/meson.build b/meson.build
index 2a04568..b318f47 100644
--- a/meson.build
+++ b/meson.build
@@ -90,7 +90,7 @@ pkgconfig.generate(librest_lib,
filebase: librest_pkg_string,
description: 'RESTful web api query library',
subdirs: librest_pkg_string,
- requires: [ glib_dep, libsoup_dep, libxml_dep, ],
+ requires: [ glib_dep, libsoup_dep, libxml_dep, libjson_glib_dep ],
variables: [
'apiversion=@0@'.format(librest_api_version),
],
@@ -101,7 +101,7 @@ pkgconfig.generate(librest_extras_lib,
filebase: librest_extras_pkg_string,
description: 'RESTful web api query library',
subdirs: librest_pkg_string,
- requires: [ glib_dep, libsoup_dep, libxml_dep, ],
+ requires: [ glib_dep, libsoup_dep, libxml_dep],
variables: [
'apiversion=@0@'.format(librest_api_version),
],
diff --git a/rest/meson.build b/rest/meson.build
index a71064b..6d25545 100644
--- a/rest/meson.build
+++ b/rest/meson.build
@@ -87,8 +87,11 @@ if get_option('introspection').enabled()
endforeach
librest_gir = gnome.generate_gir(librest_lib,
- sources: [ librest_headers, librest_enums[1] ],
+ sources: [ librest_headers, librest_sources, librest_enums[1] ],
+ dependencies: librest_deps,
namespace: 'Rest',
+ identifier_prefix: 'Rest',
+ symbol_prefix: 'rest',
nsversion: librest_api_version,
includes: [ 'GObject-2.0', 'Gio-2.0', 'Soup-@0@'.format(libsoup_api_version) ],
extra_args: librest_gir_extra_args,
diff --git a/rest/rest-oauth2-proxy.c b/rest/rest-oauth2-proxy.c
index dcfd719..15cbef3 100644
--- a/rest/rest-oauth2-proxy.c
+++ b/rest/rest-oauth2-proxy.c
@@ -467,27 +467,26 @@ rest_oauth2_proxy_fetch_access_token_finish (RestOAuth2Proxy *self,
return g_task_propagate_boolean (G_TASK (result), error);
}
-void
-rest_oauth2_proxy_refresh_access_token (RestOAuth2Proxy *self)
+gboolean
+rest_oauth2_proxy_refresh_access_token (RestOAuth2Proxy *self,
+ GError **error)
{
RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
g_autoptr(SoupMessage) msg = NULL;
g_autoptr(GHashTable) params = NULL;
g_autoptr(GTask) task = NULL;
- g_autoptr(GError) error = NULL;
GBytes *payload;
task = g_task_new (self, NULL, NULL, NULL);
- g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), FALSE);
if (priv->refresh_token == NULL)
{
- g_task_return_new_error (task,
- REST_OAUTH2_ERROR,
- REST_OAUTH2_ERROR_NO_REFRESH_TOKEN,
- "No refresh token available");
- return;
+ *error = g_error_new (REST_OAUTH2_ERROR,
+ REST_OAUTH2_ERROR_NO_REFRESH_TOKEN,
+ "No refresh token available");
+ return FALSE;
}
params = g_hash_table_new (g_str_hash, g_str_equal);
@@ -502,11 +501,14 @@ rest_oauth2_proxy_refresh_access_token (RestOAuth2Proxy *self)
#else
msg = soup_message_new_from_encoded_form (SOUP_METHOD_POST, priv->tokenurl, soup_form_encode_hash
(params));
#endif
- payload = _rest_proxy_send_message (REST_PROXY (self), msg, NULL, &error);
- if (error)
- g_task_return_error (task, error);
+ payload = _rest_proxy_send_message (REST_PROXY (self), msg, NULL, error);
+ if (error && *error)
+ {
+ return FALSE;
+ }
REST_OAUTH2_PROXY_GET_CLASS (self)->parse_access_token (self, payload, g_steal_pointer (&task));
+ return TRUE;
}
static void
diff --git a/rest/rest-oauth2-proxy.h b/rest/rest-oauth2-proxy.h
index b3a0486..220780d 100644
--- a/rest/rest-oauth2-proxy.h
+++ b/rest/rest-oauth2-proxy.h
@@ -66,7 +66,8 @@ void rest_oauth2_proxy_fetch_access_token_async (RestOAuth2Proxy
gboolean rest_oauth2_proxy_fetch_access_token_finish (RestOAuth2Proxy *self,
GAsyncResult *result,
GError **error);
-void rest_oauth2_proxy_refresh_access_token (RestOAuth2Proxy *self);
+gboolean rest_oauth2_proxy_refresh_access_token (RestOAuth2Proxy *self,
+ GError **error);
void rest_oauth2_proxy_refresh_access_token_async (RestOAuth2Proxy *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
diff --git a/rest/rest-proxy-call.c b/rest/rest-proxy-call.c
index a4a0958..24d952d 100644
--- a/rest/rest-proxy-call.c
+++ b/rest/rest-proxy-call.c
@@ -238,6 +238,8 @@ rest_proxy_call_set_method (RestProxyCall *call,
* @call: The #RestProxyCall
*
* Get the HTTP method to use when making the call, for example GET or POST.
+ *
+ * Returns: (transfer none): the HTTP method
*/
const char *
rest_proxy_call_get_method (RestProxyCall *call)
@@ -1005,19 +1007,23 @@ _call_message_call_completed_cb (SoupMessage *message,
GError *error,
gpointer user_data)
{
- GTask *task = user_data;
+ g_autoptr(GTask) task = user_data;
RestProxyCall *call;
call = REST_PROXY_CALL (g_task_get_source_object (task));
+ if (error)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
finish_call (call, message, payload, &error);
if (error != NULL)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
-
- g_object_unref (task);
}
/**
@@ -1158,6 +1164,8 @@ _continuous_call_message_sent_cb (GObject *source,
*
* You may unref the call after calling this function since there is an
* internal reference, or you may unref in the callback.
+ *
+ * Returns: %TRUE on success
*/
gboolean
rest_proxy_call_continuous (RestProxyCall *call,
diff --git a/tests/oauth2.c b/tests/oauth2.c
index 5d6d827..81ab3b9 100644
--- a/tests/oauth2.c
+++ b/tests/oauth2.c
@@ -22,6 +22,7 @@
#include "rest/rest.h"
#include "helper/test-server.h"
+#ifdef WITH_SOUP_2
static void
server_callback (SoupServer *server,
SoupMessage *msg,
@@ -29,11 +30,17 @@ server_callback (SoupServer *server,
GHashTable *query,
SoupClientContext *client,
gpointer user_data)
+#else
+static void
+server_callback (SoupServer *server,
+ SoupServerMessage *msg,
+ const gchar *path,
+ GHashTable *query,
+ gpointer user_data)
+#endif
{
- g_print ("%s\n", path);
if (g_strcmp0 (path, "/token") == 0)
{
- g_print ("%s\n", msg->request_body->data);
gchar *json = "{"
"\"access_token\":\"2YotnFZFEjr1zCsicMWpAA\","
"\"token_type\":\"example\","
@@ -41,16 +48,42 @@ server_callback (SoupServer *server,
"\"refresh_token\":\"tGzv3JOkF0XG5Qx2TlKWIA\","
"\"example_parameter\":\"example_value\""
"}";
+#ifdef WITH_SOUP_2
soup_message_set_status (msg, SOUP_STATUS_OK);
soup_message_set_response (msg, "application/json", SOUP_MEMORY_COPY, json, strlen(json));
+#else
+ soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+ soup_server_message_set_response (msg, "application/json", SOUP_MEMORY_COPY, json, strlen(json));
+#endif
+
return;
}
else if (g_strcmp0 (path, "/api/bearer") == 0)
{
+#ifdef WITH_SOUP_2
const gchar *authorization = soup_message_headers_get_one (msg->request_headers, "Authorization");
- g_print ("%s\n", authorization);
soup_message_set_status (msg, SOUP_STATUS_OK);
soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, authorization, strlen(authorization));
+#else
+ SoupMessageHeaders *headers = soup_server_message_get_request_headers (msg);
+ const gchar *authorization = soup_message_headers_get_one (headers, "Authorization");
+ soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+ soup_server_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, authorization,
strlen(authorization));
+#endif
+ return;
+ }
+ else if (g_strcmp0 (path, "/api/invalid") == 0)
+ {
+ gchar *resp = "{"
+ "\"error\":\"invalid_grant\""
+ "}";
+#ifdef WITH_SOUP_2
+ soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+ soup_message_set_response (msg, "application/json", SOUP_MEMORY_COPY, resp, strlen(resp));
+#else
+ soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
+ soup_server_message_set_response (msg, "application/json", SOUP_MEMORY_COPY, resp, strlen(resp));
+#endif
return;
}
}
@@ -107,7 +140,12 @@ test_fetch_access_token (gconstpointer url)
"client-id",
"client-secret",
baseurl));
- rest_oauth2_proxy_fetch_access_token_async (REST_OAUTH2_PROXY (proxy), "1234567890", "code_verifier",
NULL, test_fetch_access_token_finished, &finished);
+ rest_oauth2_proxy_fetch_access_token_async (REST_OAUTH2_PROXY (proxy),
+ "1234567890",
+ "code_verifier",
+ NULL,
+ test_fetch_access_token_finished,
+ &finished);
while (!finished) {
g_main_context_iteration (async_context, TRUE);
}
@@ -175,12 +213,15 @@ test_refresh_access_token (gconstpointer url)
"client-id",
"client-secret",
baseurl));
+
+ // Test error if no refresh token is set (note assertion happens in callback)
rest_oauth2_proxy_refresh_access_token_async (REST_OAUTH2_PROXY (proxy), NULL,
test_refresh_access_token_finished_error, &finished);
while (!finished) {
g_main_context_iteration (async_context, TRUE);
}
+ // Test if refresh token gets refreshed
rest_oauth2_proxy_set_refresh_token (REST_OAUTH2_PROXY (proxy), "refresh_token");
rest_oauth2_proxy_refresh_access_token_async (REST_OAUTH2_PROXY (proxy), NULL,
test_refresh_access_token_finished, &finished);
@@ -196,6 +237,38 @@ test_refresh_access_token (gconstpointer url)
g_main_context_unref (async_context);
}
+static void
+test_refresh_access_token_sync (gconstpointer url)
+{
+ g_autoptr(GError) error = NULL;
+
+ g_autofree gchar *tokenurl = g_strdup_printf ("%stoken", (gchar *)url);
+ g_autofree gchar *baseurl = g_strdup_printf ("%sapi", (gchar *)url);
+
+ g_autoptr(RestProxy) proxy = REST_PROXY (rest_oauth2_proxy_new ("http://www.example.com/auth",
+ tokenurl,
+ "http://www.example.com",
+ "client-id",
+ "client-secret",
+ baseurl));
+
+ rest_oauth2_proxy_refresh_access_token (REST_OAUTH2_PROXY (proxy), &error);
+ g_assert_error (error, REST_OAUTH2_ERROR, REST_OAUTH2_ERROR_NO_REFRESH_TOKEN);
+ error = NULL;
+
+ rest_oauth2_proxy_set_refresh_token (REST_OAUTH2_PROXY (proxy), "refresh_token");
+ rest_oauth2_proxy_refresh_access_token (REST_OAUTH2_PROXY (proxy), &error);
+
+ g_assert_cmpstr ("2YotnFZFEjr1zCsicMWpAA", ==, rest_oauth2_proxy_get_access_token (REST_OAUTH2_PROXY
(proxy)));
+ g_assert_cmpstr ("tGzv3JOkF0XG5Qx2TlKWIA", ==, rest_oauth2_proxy_get_refresh_token (REST_OAUTH2_PROXY
(proxy)));
+
+ rest_oauth2_proxy_set_refresh_token (REST_OAUTH2_PROXY (proxy), "refresh_token");
+ rest_oauth2_proxy_refresh_access_token (REST_OAUTH2_PROXY (proxy), NULL);
+
+ g_assert_cmpstr ("2YotnFZFEjr1zCsicMWpAA", ==, rest_oauth2_proxy_get_access_token (REST_OAUTH2_PROXY
(proxy)));
+ g_assert_cmpstr ("tGzv3JOkF0XG5Qx2TlKWIA", ==, rest_oauth2_proxy_get_refresh_token (REST_OAUTH2_PROXY
(proxy)));
+}
+
static void
test_access_token_expired (gconstpointer url)
{
@@ -219,6 +292,37 @@ test_access_token_expired (gconstpointer url)
g_assert_error (error, REST_OAUTH2_ERROR, REST_OAUTH2_ERROR_ACCESS_TOKEN_EXPIRED);
}
+static void
+test_access_token_invalid (gconstpointer url)
+{
+ GMainContext *async_context = g_main_context_ref_thread_default ();
+ g_autofree gchar *tokenurl = g_strdup_printf ("%stoken", (gchar *)url);
+ g_autofree gchar *baseurl = g_strdup_printf ("%sapi", (gchar *)url);
+ g_autoptr(GError) error = NULL;
+ gboolean finished = FALSE;
+
+ g_autoptr(RestProxy) proxy = REST_PROXY (rest_oauth2_proxy_new ("http://www.example.com/auth",
+ tokenurl,
+ "http://www.example.com",
+ "client-id",
+ "client-secret",
+ baseurl));
+
+ rest_oauth2_proxy_fetch_access_token_async (REST_OAUTH2_PROXY (proxy), "1234567890", "code_verifier",
NULL, test_fetch_access_token_finished, &finished);
+ while (!finished) {
+ g_main_context_iteration (async_context, TRUE);
+ }
+
+ g_autoptr(RestProxyCall) call = rest_proxy_new_call (proxy);
+ rest_proxy_call_set_method (call, "GET");
+ rest_proxy_call_set_function (call, "/invalid");
+ rest_proxy_call_sync (call, &error);
+ g_assert_cmpint (rest_proxy_call_get_status_code (call), ==, SOUP_STATUS_BAD_REQUEST);
+
+
+ g_main_context_unref (async_context);
+}
+
gint
main (gint argc,
gchar *argv[])
@@ -236,7 +340,9 @@ main (gint argc,
g_test_add_data_func ("/oauth2/authorization_url", url, test_authorization_url);
g_test_add_data_func ("/oauth2/fetch_access_token", url, test_fetch_access_token);
g_test_add_data_func ("/oauth2/refresh_access_token", url, test_refresh_access_token);
+ g_test_add_data_func ("/oauth2/refresh_access_token_sync", url, test_refresh_access_token_sync);
g_test_add_data_func ("/oauth2/access_token_expired", url, test_access_token_expired);
+ g_test_add_data_func ("/oauth2/access_token_invalid", url, test_access_token_invalid);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]