[librest/gwagner/oauth2] oauth2: bugfixing and preparation for release




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]