diff --git a/configure.ac b/configure.ac index ddf98f1..f997d36 100644 --- a/configure.ac +++ b/configure.ac @@ -124,10 +124,6 @@ if test "$enable_backend" != "no"; then AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_LIBS) - PKG_CHECK_MODULES(WEBKIT_GTK, [webkit2gtk-4.0 >= 2.12.0]) - AC_SUBST(WEBKIT_GTK_CFLAGS) - AC_SUBST(WEBKIT_GTK_LIBS) - PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.42]) AC_SUBST(LIBSOUP_CFLAGS) AC_SUBST(LIBSOUP_LIBS) @@ -176,14 +172,6 @@ if test "$enable_backend" != "no"; then fi fi -AC_ARG_ENABLE([inspector], - [AS_HELP_STRING([--enable-inspector], [Enable a WebKitWebInspector for the embedded web view])], - [], - [enable_inspector=no]) -if test "$enable_inspector" != "no"; then - AC_DEFINE(GOA_INSPECTOR_ENABLED, 1, [Enable a WebKitWebInspector for the embedded web view]) -fi - AC_ARG_WITH(template-file, [AS_HELP_STRING([--with-template-file], [Path to the template file])], [], @@ -271,7 +259,11 @@ AC_DEFINE_UNQUOTED(GOA_GOOGLE_CLIENT_ID, ["$with_google_client_id"], [Google OAu AC_DEFINE_UNQUOTED(GOA_GOOGLE_CLIENT_SECRET, ["$with_google_client_secret"], [Google OAuth 2.0 client secret]) if test "$enable_google" != "no"; then AC_DEFINE(GOA_GOOGLE_ENABLED, 1, [Enable Google data provider]) + if test "$with_google_client_id" != "44438659992-7kgjeitenc16ssihbtdjbgguch7ju55s.apps.googleusercontent.com"; then + AC_MSG_ERROR([Unexpected Google OAuth2 Client ID, correct it here and in data/Makefile.am in oauth2_schemes, to be reverse-DNS of the new Client ID]) + fi fi +AM_CONDITIONAL(GOOGLE_ENABLED, [test x$enable_google != xno]) # IMAP/SMTP AC_DEFINE(GOA_IMAP_SMTP_NAME, ["imap_smtp"], [ProviderType and extension point name]) diff --git a/data/Makefile.am b/data/Makefile.am index 286dcd0..61b69e1 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -20,16 +20,30 @@ service_DATA = $(service_in_files:.service.in=.service) @sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ endif +desktopdir = $(datadir)/applications +desktop_in_files = org.gnome.OnlineAccounts.OAuth2.desktop.in +desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) +if GOOGLE_ENABLED + oauth2_schemes=x-scheme-handler/com.googleusercontent.apps.44438659992-7kgjeitenc16ssihbtdjbgguch7ju55s; +else + oauth2_schemes= +endif + +%.desktop: %.desktop.in Makefile + @sed -e "s|\@libexecdir\@|$(libexecdir)|" -e "s|\@oauth2_schemes\@|$(oauth2_schemes)|" $< > $@ + EXTRA_DIST = \ $(gsettings_SCHEMAS) \ dbus-interfaces.xml \ org.gnome.Identity.service.in \ org.gnome.OnlineAccounts.service.in \ + org.gnome.OnlineAccounts.OAuth2.desktop.in \ $(NULL) CLEANFILES = \ org.gnome.OnlineAccounts.service \ org.gnome.Identity.service \ + org.gnome.OnlineAccounts.OAuth2.desktop \ $(NULL) clean-local : diff --git a/data/org.gnome.OnlineAccounts.OAuth2.desktop.in b/data/org.gnome.OnlineAccounts.OAuth2.desktop.in new file mode 100644 index 0000000..d0478aa --- /dev/null +++ b/data/org.gnome.OnlineAccounts.OAuth2.desktop.in @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=GNOME OAuth2 Handler +Exec=@libexecdir@/goa-oauth2-handler %u +Type=Application +MimeType=x-scheme-handler/goa-oauth2;@oauth2_schemes@ +NoDisplay=true diff --git a/doc/goa-docs.xml b/doc/goa-docs.xml index 0abb53a..a9d45e1 100644 --- a/doc/goa-docs.xml +++ b/doc/goa-docs.xml @@ -171,7 +171,6 @@ Core - diff --git a/doc/goa-sections.txt b/doc/goa-sections.txt index 306846e..a27d942 100644 --- a/doc/goa-sections.txt +++ b/doc/goa-sections.txt @@ -498,36 +498,6 @@ GoaOAuth2ProviderPrivate goa_oauth2_provider_get_type -
-goaoauthprovider -GoaOAuthProvider -GoaOAuthProviderClass -goa_oauth_provider_get_request_uri -goa_oauth_provider_get_request_uri_params -goa_oauth_provider_get_authorization_uri -goa_oauth_provider_get_token_uri -goa_oauth_provider_get_callback_uri -goa_oauth_provider_get_consumer_key -goa_oauth_provider_get_consumer_secret -goa_oauth_provider_build_authorization_uri -goa_oauth_provider_get_use_mobile_browser -goa_oauth_provider_is_deny_node -goa_oauth_provider_is_identity_node -goa_oauth_provider_is_password_node -goa_oauth_provider_add_account_key_values -goa_oauth_provider_get_identity_sync -goa_oauth_provider_get_access_token_sync -goa_oauth_provider_parse_request_token_error - -GOA_OAUTH_PROVIDER -GOA_OAUTH_PROVIDER_CLASS -GOA_OAUTH_PROVIDER_GET_CLASS -GOA_IS_OAUTH_PROVIDER -GOA_IS_OAUTH_PROVIDER_CLASS -GOA_TYPE_OAUTH_PROVIDER -goa_oauth_provider_get_type -
-
GoaMail GoaMail diff --git a/doc/goa.types b/doc/goa.types index 56ba3c4..d8d7325 100644 --- a/doc/goa.types +++ b/doc/goa.types @@ -62,5 +62,4 @@ goa_printers_proxy_get_type goa_printers_skeleton_get_type goa_provider_get_type -goa_oauth_provider_get_type goa_oauth2_provider_get_type diff --git a/po/POTFILES.in b/po/POTFILES.in index b65650c..8ee89ae 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -22,7 +22,6 @@ src/goabackend/goasmtpauth.c src/goabackend/goatelepathyprovider.c src/goabackend/goatodoistprovider.c src/goabackend/goautils.c -src/goabackend/goawebview.c src/goabackend/goawindowsliveprovider.c src/goaidentity/goaidentityservice.c src/goaidentity/goakerberosidentity.c diff --git a/src/goabackend/Makefile.am b/src/goabackend/Makefile.am index c254594..7f15518 100644 --- a/src/goabackend/Makefile.am +++ b/src/goabackend/Makefile.am @@ -19,7 +19,6 @@ AM_CPPFLAGS = \ -DPACKAGE_LOCALSTATE_DIR=\""$(localstatedir)"\" \ -DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \ -DPACKAGE_LIB_DIR=\""$(libdir)"\" \ - -DPACKAGE_WEB_EXTENSIONS_DIR=\""$(libdir)/goa-1.0/web-extensions"\" \ -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT \ $(WARN_CFLAGS) \ $(NULL) @@ -81,17 +80,13 @@ libgoa_backend_1_0_la_SOURCES = \ goasouplogger.h goasouplogger.c \ goamailclient.h goamailclient.c \ goaexchangeprovider.h goaexchangeprovider.c \ - goaoauthprovider.h goaoauthprovider.c \ goaoauth2provider.h goaoauth2provider-priv.h \ - goaoauth2provider-web-extension.h \ - goaoauth2provider-web-view.h \ goaoauth2provider.c \ goagoogleprovider.h goagoogleprovider.c \ goafacebookprovider.h goafacebookprovider.c \ goaimapsmtpprovider.h goaimapsmtpprovider.c \ goamediaserverprovider.h goamediaserverprovider.c \ goaowncloudprovider.h goaowncloudprovider.c \ - goaflickrprovider.h goaflickrprovider.c \ goafoursquareprovider.h goafoursquareprovider.c \ goawindowsliveprovider.h goawindowsliveprovider.c \ goapocketprovider.h goapocketprovider.c \ @@ -99,7 +94,6 @@ libgoa_backend_1_0_la_SOURCES = \ goatodoistprovider.h goatodoistprovider.c \ goaobjectskeletonutils.h goaobjectskeletonutils.c \ goautils.h goautils.c \ - goawebview.h goawebview.c \ nautilus-floating-bar.h nautilus-floating-bar.c \ $(top_builddir)/src/goaidentity/org.gnome.Identity.c \ $(top_srcdir)/src/goaidentity/goaidentitymanagererror.c \ @@ -119,7 +113,6 @@ libgoa_backend_1_0_la_SOURCES += \ endif libgoa_backend_1_0_la_CFLAGS = \ - $(WEBKIT_GTK_CFLAGS) \ $(JSON_GLIB_CFLAGS) \ $(GCR_CFLAGS) \ $(GLIB_CFLAGS) \ @@ -134,7 +127,6 @@ libgoa_backend_1_0_la_CFLAGS = \ libgoa_backend_1_0_la_LIBADD = \ $(top_builddir)/src/goa/libgoa-1.0.la \ - $(WEBKIT_GTK_LIBS) \ $(JSON_GLIB_LIBS) \ $(GCR_LIBS) \ $(GLIB_LIBS) \ @@ -154,39 +146,29 @@ libgoa_backend_1_0_la_LDFLAGS = \ # ---------------------------------------------------------------------------------------------------- -webextension_LTLIBRARIES = libgoawebextension.la +libexec_PROGRAMS = goa-oauth2-handler -webextensiondir = $(libdir)/goa-1.0/web-extensions - -libgoawebextension_la_SOURCES = \ - goawebextension.h goawebextension.c \ - goawebextensionmain.c \ +goa_oauth2_handler_SOURCES = \ + goaoauth2handler.c \ $(NULL) -libgoawebextension_la_CFLAGS = \ - $(REST_CFLAGS) \ - $(WEBKIT_GTK_CFLAGS) \ +goa_oauth2_handler_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ + $(SECRET_CFLAGS) \ + -DG_LOG_DOMAIN=\"goa-oauth2-handler\" \ $(NULL) -libgoawebextension_la_LIBADD = \ - libgoa-backend-1.0.la \ - $(REST_LIBS) \ - $(WEBKIT_GTK_LIBS) \ +goa_oauth2_handler_LDADD = \ + $(GLIB_LIBS) \ + $(LIBSOUP_LIBS) \ + $(SECRET_LIBS) \ $(NULL) -libgoawebextension_la_LDFLAGS = \ - -avoid-version \ - -module \ - -no-undefined \ +goa_oauth2_handler_LDFLAGS = \ + $(WARN_LDFLAGS) \ $(NULL) -# Force installation order: libgoa-backend-1.0 must be installed first, othwerwise -# libtool will incorrectly relink libgoawebextension.la under parallel make install. -# Requires ugly automake syntax - see http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 - -installwebextensionLTLIBRARIES = install-webextensionLTLIBRARIES -$(installwebextensionLTLIBRARIES): install-libLTLIBRARIES - # ---------------------------------------------------------------------------------------------------- BUILT_SOURCES = \ diff --git a/src/goabackend/goafacebookprovider.c b/src/goabackend/goafacebookprovider.c index c6033fb..c1d35d0 100644 --- a/src/goabackend/goafacebookprovider.c +++ b/src/goabackend/goafacebookprovider.c @@ -243,31 +243,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) -{ - gboolean ret = FALSE; - gchar *element_type = NULL; - gchar *name = NULL; - - g_object_get (element, "type", &element_type, NULL); - if (g_strcmp0 (element_type, "text") != 0) - goto out; - - name = webkit_dom_html_input_element_get_name (element); - if (g_strcmp0 (name, "email") != 0) - goto out; - - ret = TRUE; - - out: - g_free (element_type); - g_free (name); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - static gboolean build_object (GoaProvider *provider, GoaObjectSkeleton *object, @@ -367,6 +342,5 @@ goa_facebook_provider_class_init (GoaFacebookProviderClass *klass) oauth2_class->get_client_id = get_client_id; oauth2_class->get_client_secret = get_client_secret; oauth2_class->get_identity_sync = get_identity_sync; - oauth2_class->is_identity_node = is_identity_node; oauth2_class->add_account_key_values = add_account_key_values; } diff --git a/src/goabackend/goaflickrprovider.c b/src/goabackend/goaflickrprovider.c deleted file mode 100644 index 702ed1e..0000000 --- a/src/goabackend/goaflickrprovider.c +++ /dev/null @@ -1,364 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright (C) 2011 Willem van Engen - * Copyright © 2012 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#include "config.h" -#include - -#include -#include - -#include "goaprovider.h" -#include "goaprovider-priv.h" -#include "goaflickrprovider.h" -#include "goaobjectskeletonutils.h" -#include "goasouplogger.h" - -struct _GoaFlickrProvider -{ - GoaOAuthProvider parent_instance; -}; - -G_DEFINE_TYPE_WITH_CODE (GoaFlickrProvider, goa_flickr_provider, GOA_TYPE_OAUTH_PROVIDER, - goa_provider_ensure_extension_points_registered (); - g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, - g_define_type_id, - GOA_FLICKR_NAME, - 0)); - -/* ---------------------------------------------------------------------------------------------------- */ - -static const gchar * -get_provider_type (GoaProvider *provider) -{ - return GOA_FLICKR_NAME; -} - -static gchar * -get_provider_name (GoaProvider *provider, - GoaObject *object) -{ - return g_strdup (_("Flickr")); -} - -static GoaProviderGroup -get_provider_group (GoaProvider *provider) -{ - return GOA_PROVIDER_GROUP_BRANDED; -} - -static GoaProviderFeatures -get_provider_features (GoaProvider *provider) -{ - return GOA_PROVIDER_FEATURE_BRANDED | GOA_PROVIDER_FEATURE_PHOTOS; -} - -static const gchar * -get_consumer_key (GoaOAuthProvider *oauth_provider) -{ - return GOA_FLICKR_CONSUMER_KEY; -} - -static const gchar * -get_consumer_secret (GoaOAuthProvider *oauth_provider) -{ - return GOA_FLICKR_CONSUMER_SECRET; -} - -static const gchar * -get_request_uri (GoaOAuthProvider *oauth_provider) -{ - return "https://www.flickr.com/services/oauth/request_token"; -} - -static const gchar * -get_authorization_uri (GoaOAuthProvider *oauth_provider) -{ - return "https://www.flickr.com/services/oauth/authorize"; -} - -static const gchar * -get_token_uri (GoaOAuthProvider *oauth_provider) -{ - return "https://www.flickr.com/services/oauth/access_token"; -} - -static const gchar * -get_callback_uri (GoaOAuthProvider *oauth_provider) -{ - /* Should match the URI specified in the Flickr App - * Garden in order to detect when the user denied access via - * the OAuth1 web page. - */ - return "https://www.gnome.org/"; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gchar * -get_identity_sync (GoaOAuthProvider *oauth_provider, - const gchar *access_token, - const gchar *access_token_secret, - gchar **out_presentation_identity, - GCancellable *cancellable, - GError **error) -{ - GError *identity_error = NULL; - RestProxy *proxy = NULL; - RestProxyCall *call = NULL; - JsonParser *parser = NULL; - JsonObject *json_object; - SoupLogger *logger = NULL; - gchar *ret = NULL; - gchar *id = NULL; - gchar *presentation_identity = NULL; - - /* TODO: cancellable */ - - proxy = oauth_proxy_new_with_token (goa_oauth_provider_get_consumer_key (oauth_provider), - goa_oauth_provider_get_consumer_secret (oauth_provider), - access_token, - access_token_secret, - "https://api.flickr.com/services/rest", - FALSE); - logger = goa_soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); - rest_proxy_add_soup_feature (proxy, SOUP_SESSION_FEATURE (logger)); - - call = rest_proxy_new_call (proxy); - rest_proxy_call_add_param (call, "method", "flickr.test.login"); - rest_proxy_call_add_param (call, "format", "json"); - rest_proxy_call_add_param (call, "nojsoncallback", "1"); - rest_proxy_call_set_method (call, "GET"); - - if (!rest_proxy_call_sync (call, error)) - goto out; - if (rest_proxy_call_get_status_code (call) != 200) - { - g_set_error (error, - GOA_ERROR, - GOA_ERROR_FAILED, - _("Expected status 200 when requesting your identity, instead got status %d (%s)"), - rest_proxy_call_get_status_code (call), - rest_proxy_call_get_status_message (call)); - goto out; - } - - parser = json_parser_new (); - if (!json_parser_load_from_data (parser, - rest_proxy_call_get_payload (call), - rest_proxy_call_get_payload_length (call), - &identity_error)) - { - g_warning ("json_parser_load_from_data() failed: %s (%s, %d)", - identity_error->message, - g_quark_to_string (identity_error->domain), - identity_error->code); - g_set_error (error, - GOA_ERROR, - GOA_ERROR_FAILED, - _("Could not parse response")); - goto out; - } - - json_object = json_node_get_object (json_parser_get_root (parser)); - if (!json_object_has_member (json_object, "user")) - { - g_warning ("Did not find user in JSON data"); - g_set_error (error, - GOA_ERROR, - GOA_ERROR_FAILED, - _("Could not parse response")); - goto out; - } - - json_object = json_object_get_object_member (json_object, "user"); - if (!json_object_has_member (json_object, "id")) - { - g_warning ("Did not find user.id in JSON data"); - g_set_error (error, - GOA_ERROR, - GOA_ERROR_FAILED, - _("Could not parse response")); - goto out; - } - if (!json_object_has_member (json_object, "username")) - { - g_warning ("Did not find user.username in JSON data"); - g_set_error (error, - GOA_ERROR, - GOA_ERROR_FAILED, - _("Could not parse response")); - goto out; - } - - id = g_strdup (json_object_get_string_member (json_object, "id")); - - json_object = json_object_get_object_member (json_object, "username"); - if (!json_object_has_member (json_object, "_content")) - { - g_warning ("Did not find user.username._content in JSON data"); - g_set_error (error, - GOA_ERROR, - GOA_ERROR_FAILED, - _("Could not parse response")); - goto out; - } - - presentation_identity = g_strdup (json_object_get_string_member (json_object, "_content")); - - ret = id; - id = NULL; - if (out_presentation_identity != NULL) - { - *out_presentation_identity = presentation_identity; - presentation_identity = NULL; - } - - out: - g_clear_object (&parser); - g_clear_error (&identity_error); - g_clear_object (&call); - g_clear_object (&proxy); - g_clear_object (&logger); - g_free (id); - g_free (presentation_identity); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -is_identity_node (GoaOAuthProvider *oauth_provider, WebKitDOMHTMLInputElement *element) -{ - /* Flickr does not provide a way to query the string used by the - * user to log in via the web interface. The user id and username - * returned by flickr.test.login [1] are not what we are looking - * for. - * - * [1] http://www.flickr.com/services/api/flickr.test.login.html - */ - return FALSE; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gchar * -parse_request_token_error (GoaOAuthProvider *oauth_provider, RestProxyCall *call) -{ - const gchar *payload; - gchar *msg = NULL; - guint status; - - payload = rest_proxy_call_get_payload (call); - status = rest_proxy_call_get_status_code (call); - - if (status == 401 && g_strcmp0 (payload, "oauth_problem=timestamp_refused") == 0) - msg = g_strdup (_("Your system time is invalid. Check your date and time settings.")); - - return msg; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -build_object (GoaProvider *provider, - GoaObjectSkeleton *object, - GKeyFile *key_file, - const gchar *group, - GDBusConnection *connection, - gboolean just_added, - GError **error) -{ - GoaAccount *account = NULL; - gboolean photos_enabled; - gboolean ret = FALSE; - - /* Chain up */ - if (!GOA_PROVIDER_CLASS (goa_flickr_provider_parent_class)->build_object (provider, - object, - key_file, - group, - connection, - just_added, - error)) - goto out; - - account = goa_object_get_account (GOA_OBJECT (object)); - - /* Photos */ - photos_enabled = g_key_file_get_boolean (key_file, group, "PhotosEnabled", NULL); - goa_object_skeleton_attach_photos (object, photos_enabled); - - if (just_added) - { - goa_account_set_photos_disabled (account, !photos_enabled); - - g_signal_connect (account, - "notify::photos-disabled", - G_CALLBACK (goa_util_account_notify_property_cb), - (gpointer) "PhotosEnabled"); - } - - ret = TRUE; - - out: - g_clear_object (&account); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -add_account_key_values (GoaOAuthProvider *oauth_provider, - GVariantBuilder *builder) -{ - g_variant_builder_add (builder, "{ss}", "PhotosEnabled", "true"); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -goa_flickr_provider_init (GoaFlickrProvider *self) -{ -} - -static void -goa_flickr_provider_class_init (GoaFlickrProviderClass *klass) -{ - GoaProviderClass *provider_class; - GoaOAuthProviderClass *oauth_class; - - provider_class = GOA_PROVIDER_CLASS (klass); - provider_class->get_provider_type = get_provider_type; - provider_class->get_provider_name = get_provider_name; - provider_class->get_provider_group = get_provider_group; - provider_class->get_provider_features = get_provider_features; - provider_class->build_object = build_object; - - oauth_class = GOA_OAUTH_PROVIDER_CLASS (klass); - oauth_class->get_identity_sync = get_identity_sync; - oauth_class->is_identity_node = is_identity_node; - oauth_class->get_consumer_key = get_consumer_key; - oauth_class->get_consumer_secret = get_consumer_secret; - oauth_class->get_request_uri = get_request_uri; - oauth_class->get_authorization_uri = get_authorization_uri; - oauth_class->get_token_uri = get_token_uri; - oauth_class->get_callback_uri = get_callback_uri; - oauth_class->parse_request_token_error = parse_request_token_error; - oauth_class->add_account_key_values = add_account_key_values; -} diff --git a/src/goabackend/goaflickrprovider.h b/src/goabackend/goaflickrprovider.h deleted file mode 100644 index f08a8a6..0000000 --- a/src/goabackend/goaflickrprovider.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright © 2012 Willem van Engen - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __GOA_FLICKR_PROVIDER_H__ -#define __GOA_FLICKR_PROVIDER_H__ - -#include - -#include "goaoauthprovider.h" - -G_BEGIN_DECLS - -#define GOA_TYPE_FLICKR_PROVIDER (goa_flickr_provider_get_type ()) -G_DECLARE_FINAL_TYPE (GoaFlickrProvider, goa_flickr_provider, GOA, FLICKR_PROVIDER, GoaOAuthProvider); - -G_END_DECLS - -#endif /* __GOA_FLICKR_PROVIDER_H__ */ diff --git a/src/goabackend/goafoursquareprovider.c b/src/goabackend/goafoursquareprovider.c index c1e4146..def21cb 100644 --- a/src/goabackend/goafoursquareprovider.c +++ b/src/goabackend/goafoursquareprovider.c @@ -251,31 +251,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) -{ - gboolean ret = FALSE; - gchar *element_type = NULL; - gchar *name = NULL; - - g_object_get (element, "type", &element_type, NULL); - if (g_strcmp0 (element_type, "email") != 0) - goto out; - - name = webkit_dom_html_input_element_get_name (element); - if (g_strcmp0 (name, "emailOrPhone") != 0) - goto out; - - ret = TRUE; - - out: - g_free (element_type); - g_free (name); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - static gboolean build_object (GoaProvider *provider, GoaObjectSkeleton *object, @@ -366,6 +341,5 @@ goa_foursquare_provider_class_init (GoaFoursquareProviderClass *klass) oauth2_class->get_client_secret = get_client_secret; oauth2_class->get_use_mobile_browser = get_use_mobile_browser; oauth2_class->get_identity_sync = get_identity_sync; - oauth2_class->is_identity_node = is_identity_node; oauth2_class->add_account_key_values = add_account_key_values; } diff --git a/src/goabackend/goagoogleprovider.c b/src/goabackend/goagoogleprovider.c index 6e4ace2..6f951e5 100644 --- a/src/goabackend/goagoogleprovider.c +++ b/src/goabackend/goagoogleprovider.c @@ -32,6 +32,7 @@ struct _GoaGoogleProvider { GoaOAuth2Provider parent_instance; + gchar *redirect_uri; }; G_DEFINE_TYPE_WITH_CODE (GoaGoogleProvider, goa_google_provider, GOA_TYPE_OAUTH2_PROVIDER, @@ -81,19 +82,50 @@ get_provider_features (GoaProvider *provider) static const gchar * get_authorization_uri (GoaOAuth2Provider *oauth2_provider) { - return "https://accounts.google.com/o/oauth2/auth"; + return "https://accounts.google.com/o/oauth2/v2/auth"; } static const gchar * get_token_uri (GoaOAuth2Provider *oauth2_provider) { - return "https://accounts.google.com/o/oauth2/token"; + return "https://oauth2.googleapis.com/token"; } static const gchar * get_redirect_uri (GoaOAuth2Provider *oauth2_provider) { - return "http://localhost"; + G_LOCK_DEFINE_STATIC (redirect_uri); + GoaGoogleProvider *self = GOA_GOOGLE_PROVIDER (oauth2_provider); + + G_LOCK (redirect_uri); + + if (!self->redirect_uri) { + GPtrArray *array; + gchar **strv; + gchar *joinstr; + guint ii; + + strv = g_strsplit (GOA_GOOGLE_CLIENT_ID, ".", -1); + array = g_ptr_array_new (); + + for (ii = 0; strv[ii]; ii++) { + g_ptr_array_insert (array, 0, strv[ii]); + } + + g_ptr_array_add (array, NULL); + + joinstr = g_strjoinv (".", (gchar **) array->pdata); + /* Use reverse-DNS of the client ID with the below path */ + self->redirect_uri = g_strconcat (joinstr, ":/oauth2redirect", NULL); + + g_ptr_array_free (array, TRUE); + g_strfreev (strv); + g_free (joinstr); + } + + G_UNLOCK (redirect_uri); + + return self->redirect_uri; } static const gchar * @@ -241,37 +273,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) -{ - gboolean ret = FALSE; - gchar *element_type = NULL; - gchar *id = NULL; - gchar *name = NULL; - - g_object_get (element, "type", &element_type, NULL); - if (g_strcmp0 (element_type, "email") != 0) - goto out; - - id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (element)); - if (g_strcmp0 (id, "identifierId") != 0) - goto out; - - name = webkit_dom_html_input_element_get_name (element); - if (g_strcmp0 (name, "identifier") != 0) - goto out; - - ret = TRUE; - - out: - g_free (element_type); - g_free (id); - g_free (name); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - static gboolean build_object (GoaProvider *provider, GoaObjectSkeleton *object, @@ -446,6 +447,16 @@ add_account_key_values (GoaOAuth2Provider *oauth2_provider, /* ---------------------------------------------------------------------------------------------------- */ +static void +goa_google_finalize (GObject *object) +{ + GoaGoogleProvider *self = GOA_GOOGLE_PROVIDER (object); + + g_free (self->redirect_uri); + + G_OBJECT_CLASS (goa_google_provider_parent_class)->finalize (object); +} + static void goa_google_provider_init (GoaGoogleProvider *self) { @@ -456,6 +467,10 @@ goa_google_provider_class_init (GoaGoogleProviderClass *klass) { GoaProviderClass *provider_class; GoaOAuth2ProviderClass *oauth2_class; + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = goa_google_finalize; provider_class = GOA_PROVIDER_CLASS (klass); provider_class->get_provider_type = get_provider_type; @@ -472,7 +487,6 @@ goa_google_provider_class_init (GoaGoogleProviderClass *klass) oauth2_class->get_identity_sync = get_identity_sync; oauth2_class->get_redirect_uri = get_redirect_uri; oauth2_class->get_scope = get_scope; - oauth2_class->is_identity_node = is_identity_node; oauth2_class->get_token_uri = get_token_uri; oauth2_class->add_account_key_values = add_account_key_values; } diff --git a/src/goabackend/goaoauth2handler.c b/src/goabackend/goaoauth2handler.c new file mode 100644 index 0000000..c5a86cb --- /dev/null +++ b/src/goabackend/goaoauth2handler.c @@ -0,0 +1,173 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright © 2023 GNOME Foundation Inc. + * Contributor: Andy Holmes + * + * 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; either + * version 2 of the License, or (at your option) any later version. + * + * 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 . + */ + +#include "config.h" + +#include +#include +#include + + +static const SecretSchema oauth2_schema = +{ + .name = "org.gnome.OnlineAccounts.OAuth2", + .flags = SECRET_SCHEMA_NONE, + .attributes = { + { + .name = "goa-oauth2-client", + .type = SECRET_SCHEMA_ATTRIBUTE_STRING, + }, + { + .name = "goa-oauth2-provider", + .type = SECRET_SCHEMA_ATTRIBUTE_STRING, + }, + { "NULL", 0 } + } +}; + +static struct +{ + const char *client_id; + const char *provider; +} +oauth2_providers[] = +{ +#ifdef GOA_GOOGLE_ENABLED + { + .client_id = GOA_GOOGLE_CLIENT_ID, + .provider = GOA_GOOGLE_NAME, + }, +#endif +#ifdef GOA_WINDOWS_LIVE_ENABLED + { + .client_id = GOA_WINDOWS_LIVE_CLIENT_ID, + .provider = GOA_WINDOWS_LIVE_NAME, + }, +#endif + { NULL, NULL }, +}; + +static gboolean +get_oauth2_provider (const char *needle, + const char **client_out, + const char **provider_out) +{ + g_return_val_if_fail (needle != NULL, FALSE); + + for (unsigned int i = 0; oauth2_providers[i].client_id != NULL; i++) + { + if (g_str_equal (needle, oauth2_providers[i].client_id)) + { + if (client_out) + *client_out = oauth2_providers[i].client_id; + + if (provider_out) + *provider_out = oauth2_providers[i].provider; + + return TRUE; + } + } + + return FALSE; +} + +int +main (int argc, + char **argv) +{ + SoupURI *uri = NULL; + const char *scheme = NULL; + const char *path = NULL; + const char *client_id = NULL; + const char *provider_type = NULL; + GError *error = NULL; + + if (argc < 2) + { + g_printerr ("%s: Missing URI\n", argv[0]); + return EXIT_FAILURE; + } + + uri = soup_uri_new (argv[1]); + if (uri == NULL) + { + g_printerr ("%s: Invalid URI: %s\n", argv[0], argv[1]); + return EXIT_FAILURE; + } + + /* Google apps may use a reverse-DNS form of the client ID as the URI scheme + * See: https://developers.google.com/identity/protocols/oauth2/native-app + */ + scheme = soup_uri_get_scheme (uri); + if (scheme != NULL) + { + g_auto (GStrv) strv = g_strsplit (scheme, ".", -1); + g_autoptr (GString) tmp = g_string_new (""); + + for (unsigned int i = 0; strv[i] != NULL; i++) + { + if (i > 0) + g_string_prepend_c (tmp, '.'); + g_string_prepend (tmp, strv[i]); + } + + get_oauth2_provider (tmp->str, &client_id, &provider_type); + } + + /* Windows Live uses goa-oauth2:// with the client ID as the first path segment + */ + if (client_id == NULL) + { + path = soup_uri_get_path (uri); + if (path != NULL && *path != '\0') + { + g_auto (GStrv) strv = NULL; + + strv = g_strsplit (*path == '/' ? path +1 : path, "/", 1); + get_oauth2_provider (strv[0], &client_id, &provider_type); + } + } + + if (client_id == NULL) + { + g_printerr ("%s: Unknown provider\n", argv[0]); + soup_uri_free (uri); + return EXIT_FAILURE; + } + + if (!secret_password_store_sync (&oauth2_schema, + SECRET_COLLECTION_SESSION, + "GNOME Online Accounts OAuth2 URI", + argv[1], /* Secret */ + NULL, + &error, + "goa-oauth2-client", client_id, + "goa-oauth2-provider", provider_type, + NULL)) + { + if (error != NULL) + g_printerr ("%s: Failed to store OAuth2 URI: %s\n", argv[0], error->message); + + soup_uri_free (uri); + g_clear_error (&error); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/goabackend/goaoauth2provider-priv.h b/src/goabackend/goaoauth2provider-priv.h index de1b808..4b00a24 100644 --- a/src/goabackend/goaoauth2provider-priv.h +++ b/src/goabackend/goaoauth2provider-priv.h @@ -26,8 +26,6 @@ #include #include #include -#include -#include G_BEGIN_DECLS @@ -51,11 +49,7 @@ G_BEGIN_DECLS * @build_authorization_uri: Virtual function for goa_oauth2_provider_build_authorization_uri(). * @get_use_mobile_browser: Virtual function for goa_oauth2_provider_get_use_mobile_browser(). * @add_account_key_values: Virtual function for goa_oauth2_provider_add_account_key_values(). - * @decide_navigation_policy: Virtual function for goa_oauth2_provider_decide_navigation_policy(). * @process_redirect_url: Virtual function for goa_oauth2_provider_process_redirect_url(). - * @is_deny_node: Virtual function for goa_oauth2_provider_is_deny_node(). - * @is_identity_node: Virtual function for goa_oauth2_provider_is_identity_node(). - * @is_password_node: Virtual function for goa_oauth2_provider_is_password_node(). * * Class structure for #GoaOAuth2Provider. */ @@ -86,18 +80,7 @@ struct _GoaOAuth2ProviderClass void (*add_account_key_values) (GoaOAuth2Provider *provider, GVariantBuilder *builder); - /* pure virtual */ - gboolean (*is_identity_node) (GoaOAuth2Provider *provider, - WebKitDOMHTMLInputElement *element); - /* virtual but with default implementation */ - gboolean (*is_deny_node) (GoaOAuth2Provider *provider, - WebKitDOMNode *node); - gboolean (*is_password_node) (GoaOAuth2Provider *provider, - WebKitDOMHTMLInputElement *element); - gboolean (*decide_navigation_policy) (GoaOAuth2Provider *provider, - WebKitWebView *web_view, - WebKitNavigationPolicyDecision *decision); gboolean (*process_redirect_url) (GoaOAuth2Provider *provider, const gchar *redirect_url, gchar **access_token, diff --git a/src/goabackend/goaoauth2provider-web-extension.h b/src/goabackend/goaoauth2provider-web-extension.h deleted file mode 100644 index baac005..0000000 --- a/src/goabackend/goaoauth2provider-web-extension.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright © 2016 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __GOA_OAUTH2_PROVIDER_WEB_EXTENSION_H__ -#define __GOA_OAUTH2_PROVIDER_WEB_EXTENSION_H__ - -#include -#include - -G_BEGIN_DECLS - -gboolean goa_oauth2_provider_is_deny_node (GoaOAuth2Provider *provider, - WebKitDOMNode *node); -gboolean goa_oauth2_provider_is_identity_node (GoaOAuth2Provider *provider, - WebKitDOMHTMLInputElement *element); -gboolean goa_oauth2_provider_is_password_node (GoaOAuth2Provider *provider, - WebKitDOMHTMLInputElement *element); - -G_END_DECLS - -#endif /* __GOA_OAUTH2_PROVIDER_WEB_EXTENSION_H__ */ diff --git a/src/goabackend/goaoauth2provider-web-view.h b/src/goabackend/goaoauth2provider-web-view.h deleted file mode 100644 index f2dae5e..0000000 --- a/src/goabackend/goaoauth2provider-web-view.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright © 2016 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __GOA_OAUTH2_PROVIDER_WEB_VIEW_H__ -#define __GOA_OAUTH2_PROVIDER_WEB_VIEW_H__ - -#include -#include - -G_BEGIN_DECLS - -gboolean goa_oauth2_provider_decide_navigation_policy (GoaOAuth2Provider *provider, - WebKitWebView *web_view, - WebKitNavigationPolicyDecision *decision); - -G_END_DECLS - -#endif /* __GOA_OAUTH2_PROVIDER_WEB_VIEW_H__ */ diff --git a/src/goabackend/goaoauth2provider.c b/src/goabackend/goaoauth2provider.c index 9092605..1ceacb5 100644 --- a/src/goabackend/goaoauth2provider.c +++ b/src/goabackend/goaoauth2provider.c @@ -22,16 +22,13 @@ #include #include +#include #include -#include #include "goaprovider.h" #include "goautils.h" -#include "goawebview.h" #include "goaoauth2provider.h" #include "goaoauth2provider-priv.h" -#include "goaoauth2provider-web-extension.h" -#include "goaoauth2provider-web-view.h" #include "goarestproxy.h" /** @@ -81,6 +78,8 @@ struct _GoaOAuth2ProviderPrivate gchar *identity; gchar *presentation_identity; gchar *password; + gchar *request_uri; + SecretCollection *session; }; G_LOCK_DEFINE_STATIC (provider_lock); @@ -134,70 +133,6 @@ goa_oauth2_provider_get_use_mobile_browser (GoaOAuth2Provider *self) /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -goa_oauth2_provider_is_deny_node_default (GoaOAuth2Provider *self, WebKitDOMNode *node) -{ - return FALSE; -} - -/** - * goa_oauth2_provider_is_deny_node: - * @self: A #GoaOAuth2Provider. - * @node: A WebKitDOMNode. - * - * Checks whether @node is the HTML UI element that the user can use - * to deny permission to access his account. Usually they are either a - * WebKitDOMHTMLButtonElement or a WebKitDOMHTMLInputElement. - * - * Please note that providers may have multiple such elements in their - * UI and this method should catch all of them. - * - * This is a virtual method where the default implementation returns - * %FALSE. - * - * Returns: %TRUE if the @node can be used to deny permission. - */ -gboolean -goa_oauth2_provider_is_deny_node (GoaOAuth2Provider *self, WebKitDOMNode *node) -{ - g_return_val_if_fail (GOA_IS_OAUTH2_PROVIDER (self), FALSE); - return GOA_OAUTH2_PROVIDER_GET_CLASS (self)->is_deny_node (self, node); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -goa_oauth2_provider_is_password_node_default (GoaOAuth2Provider *self, WebKitDOMHTMLInputElement *element) -{ - return FALSE; -} - -/** - * goa_oauth2_provider_is_password_node: - * @self: A #GoaOAuth2Provider. - * @element: A WebKitDOMHTMLInputElement - * - * Checks whether @element is the HTML UI element that the user can - * use to enter her password. This can be used to offer a - * #GoaPasswordBased interface by saving the user's - * password. Providers usually frown upon doing this, so this is not - * recommended. - * - * This is a virtual method where the default implementation returns - * %FALSE. - * - * Returns: %TRUE if @element can be used to enter the password. - */ -gboolean -goa_oauth2_provider_is_password_node (GoaOAuth2Provider *self, WebKitDOMHTMLInputElement *element) -{ - g_return_val_if_fail (GOA_IS_OAUTH2_PROVIDER (self), FALSE); - g_return_val_if_fail (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element), FALSE); - return GOA_OAUTH2_PROVIDER_GET_CLASS (self)->is_password_node (self, element); -} - -/* ---------------------------------------------------------------------------------------------------- */ - static void goa_oauth2_provider_add_account_key_values_default (GoaOAuth2Provider *self, GVariantBuilder *builder) @@ -287,45 +222,6 @@ goa_oauth2_provider_build_authorization_uri (GoaOAuth2Provider *self, /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -goa_oauth2_provider_decide_navigation_policy_default (GoaOAuth2Provider *self, - WebKitWebView *web_view, - WebKitNavigationPolicyDecision *decision) -{ - return FALSE; -} - -/* - * goa_oauth2_provider_decide_navigation_policy_default: - * @self: A #GoaOAuth2Provider. - * @decision: A #WebKitNavigationPolicyDecision - * - * Certain OAuth2-like, but not exactly OAuth2, - * providers may not send us to the redirect URI, as expected. They - * might need some special handling for that. This is a provider - * specific hook to accommodate them. - * - * This is a virtual method where the default implementation returns - * %FALSE. - * - * Returns: %TRUE if @provider decided what to do with @decision, - * %FALSE otherwise. - */ -gboolean -goa_oauth2_provider_decide_navigation_policy (GoaOAuth2Provider *self, - WebKitWebView *web_view, - WebKitNavigationPolicyDecision *decision) -{ - g_return_val_if_fail (GOA_IS_OAUTH2_PROVIDER (self), FALSE); - g_return_val_if_fail (WEBKIT_IS_WEB_VIEW (web_view), FALSE); - g_return_val_if_fail (WEBKIT_IS_NAVIGATION_POLICY_DECISION (decision), FALSE); - - return GOA_OAUTH2_PROVIDER_GET_CLASS (self)->decide_navigation_policy (self, web_view, decision); -} - -/* ---------------------------------------------------------------------------------------------------- */ - /** * goa_oauth2_provider_process_redirect_url: * @self: A #GoaOAuth2Provider. @@ -551,26 +447,6 @@ goa_oauth2_provider_get_identity_sync (GoaOAuth2Provider *self, error); } -/** - * goa_oauth2_provider_is_identity_node: - * @self: A #GoaOAuth2Provider. - * @element: A WebKitDOMHTMLInputElement. - * - * Checks whether @element is the HTML UI element that the user can - * use to identify herself at the provider. - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: %TRUE if the @element can be used to deny permission. - */ -gboolean -goa_oauth2_provider_is_identity_node (GoaOAuth2Provider *self, WebKitDOMHTMLInputElement *element) -{ - g_return_val_if_fail (GOA_IS_OAUTH2_PROVIDER (self), FALSE); - return GOA_OAUTH2_PROVIDER_GET_CLASS (self)->is_identity_node (self, element); -} - /* ---------------------------------------------------------------------------------------------------- */ static gchar * @@ -730,72 +606,42 @@ get_tokens_sync (GoaOAuth2Provider *self, /* ---------------------------------------------------------------------------------------------------- */ -static void -on_web_view_deny_click (GoaWebView *web_view, gpointer user_data) -{ - GoaOAuth2Provider *self = GOA_OAUTH2_PROVIDER (user_data); - GoaOAuth2ProviderPrivate *priv; - - priv = goa_oauth2_provider_get_instance_private (self); - gtk_dialog_response (priv->dialog, GTK_RESPONSE_CANCEL); -} - -static void -on_web_view_password_submit (GoaWebView *web_view, const gchar *password, gpointer user_data) -{ - GoaOAuth2Provider *self = GOA_OAUTH2_PROVIDER (user_data); - GoaOAuth2ProviderPrivate *priv; - - priv = goa_oauth2_provider_get_instance_private (self); - - g_free (priv->password); - priv->password = g_strdup (password); -} - static gboolean -on_web_view_decide_policy (WebKitWebView *web_view, - WebKitPolicyDecision *decision, - WebKitPolicyDecisionType decision_type, - gpointer user_data) +parse_requested_uri (GoaOAuth2Provider *self, + const char *requested_uri) { - GoaOAuth2Provider *self = GOA_OAUTH2_PROVIDER (user_data); - GoaOAuth2ProviderPrivate *priv; + GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); GHashTable *key_value_pairs; - WebKitNavigationAction *action; - WebKitURIRequest *request; SoupURI *uri; const gchar *fragment; const gchar *oauth2_error; const gchar *query; const gchar *redirect_uri; - const gchar *requested_uri; - gint response_id = GTK_RESPONSE_NONE; - - priv = goa_oauth2_provider_get_instance_private (self); - - if (decision_type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION) - goto default_behaviour; - - if (goa_oauth2_provider_decide_navigation_policy (self, - web_view, - WEBKIT_NAVIGATION_POLICY_DECISION (decision))) - { - response_id = 0; - goto ignore_request; - } /* TODO: use oauth2_proxy_extract_access_token() */ + g_assert (priv->error == NULL); - action = webkit_navigation_policy_decision_get_navigation_action (WEBKIT_NAVIGATION_POLICY_DECISION (decision)); - request = webkit_navigation_action_get_request (action); - requested_uri = webkit_uri_request_get_uri (request); redirect_uri = goa_oauth2_provider_get_redirect_uri (self); if (!g_str_has_prefix (requested_uri, redirect_uri)) - goto default_behaviour; + { + g_set_error (&priv->error, + GOA_ERROR, + GOA_ERROR_FAILED, + "Invalid URI: %s", + requested_uri); + return FALSE; + } uri = soup_uri_new (requested_uri); - fragment = soup_uri_get_fragment (uri); - query = soup_uri_get_query (uri); + if (uri == NULL) + { + g_set_error (&priv->error, + GOA_ERROR, + GOA_ERROR_FAILED, + "Invalid URI: %s", + requested_uri); + return FALSE; + } /* Three cases: * 1) we can either have the backend handle the URI for us, or @@ -806,23 +652,23 @@ on_web_view_decide_policy (WebKitWebView *web_view, */ if (GOA_OAUTH2_PROVIDER_GET_CLASS (self)->process_redirect_url) { - gchar *url; + g_autofree char *url = NULL; url = soup_uri_to_string (uri, FALSE); + soup_uri_free (uri); if (!goa_oauth2_provider_process_redirect_url (self, url, &priv->access_token, &priv->error)) { g_prefix_error (&priv->error, _("Authorization response: ")); priv->error->domain = GOA_ERROR; priv->error->code = GOA_ERROR_NOT_AUTHORIZED; - response_id = GTK_RESPONSE_CLOSE; + + return FALSE; } - else - response_id = GTK_RESPONSE_OK; - g_free (url); - goto ignore_request; + return TRUE; } + fragment = soup_uri_get_fragment (uri); if (fragment != NULL) { /* fragment is encoded into a key/value pairs for the token and @@ -846,57 +692,175 @@ on_web_view_decide_policy (WebKitWebView *web_view, priv->access_token_expires_in = atoi (expires_in_str); priv->refresh_token = g_strdup (g_hash_table_lookup (key_value_pairs, "refresh_token")); - - response_id = GTK_RESPONSE_OK; } g_hash_table_unref (key_value_pairs); - } - if (priv->access_token != NULL) - goto ignore_request; + if (priv->access_token != NULL) + { + soup_uri_free (uri); + return TRUE; + } + } + query = soup_uri_get_query (uri); if (query != NULL) { key_value_pairs = soup_form_decode (query); priv->authorization_code = g_strdup (g_hash_table_lookup (key_value_pairs, "code")); - if (priv->authorization_code != NULL) - response_id = GTK_RESPONSE_OK; - g_hash_table_unref (key_value_pairs); + if (priv->authorization_code != NULL) + { + soup_uri_free (uri); + return TRUE; + } } - if (priv->authorization_code != NULL) - goto ignore_request; - /* In case we don't find the access_token or auth code, then look * for the error in the query part of the URI. */ key_value_pairs = soup_form_decode (query); oauth2_error = (const gchar *) g_hash_table_lookup (key_value_pairs, "error"); if (g_strcmp0 (oauth2_error, GOA_OAUTH2_ACCESS_DENIED) == 0) - response_id = GTK_RESPONSE_CANCEL; - else { g_set_error (&priv->error, GOA_ERROR, GOA_ERROR_NOT_AUTHORIZED, _("Authorization response: %s"), oauth2_error); - response_id = GTK_RESPONSE_CLOSE; + } + else + { + g_set_error_literal (&priv->error, + GOA_ERROR, + GOA_ERROR_FAILED, + _("Failed to authenticate")); } g_hash_table_unref (key_value_pairs); - goto ignore_request; + soup_uri_free (uri); + return FALSE; +} - ignore_request: - g_assert (response_id != GTK_RESPONSE_NONE); - if (response_id < 0) - gtk_dialog_response (priv->dialog, response_id); - webkit_policy_decision_ignore (decision); - return TRUE; +/* ---------------------------------------------------------------------------------------------------- */ - default_behaviour: - return FALSE; +static const SecretSchema oauth2_schema = +{ + .name = "org.gnome.OnlineAccounts.OAuth2", + .flags = SECRET_SCHEMA_NONE, + .attributes = { + { + .name = "goa-oauth2-client", + .type = SECRET_SCHEMA_ATTRIBUTE_STRING, + }, + { + .name = "goa-oauth2-provider", + .type = SECRET_SCHEMA_ATTRIBUTE_STRING, + }, + { "NULL", 0 } + } +}; + +static void +on_secrets_changed (SecretCollection *collection, + GParamSpec *pspec, + GoaOAuth2Provider *self) +{ + GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); + const char *client_id = NULL; + const char *provider_type = NULL; + g_autofree char *requested_uri = NULL; + GtkResponseType response_id = GTK_RESPONSE_NONE; + + client_id = goa_oauth2_provider_get_client_id (self); + provider_type = goa_provider_get_provider_type (GOA_PROVIDER (self)); + requested_uri = secret_password_lookup_sync (&oauth2_schema, NULL, NULL, + "goa-oauth2-client", client_id, + "goa-oauth2-provider", provider_type, + NULL); + + if (requested_uri != NULL) + { + if (parse_requested_uri (self, requested_uri)) + response_id = GTK_RESPONSE_OK; + else + response_id = GTK_RESPONSE_CANCEL; + } + + if (response_id != GTK_RESPONSE_NONE) + { + g_signal_handlers_disconnect_by_func (collection, on_secrets_changed, self); + gtk_dialog_response (priv->dialog, response_id); + } +} + +static void +secret_service_get_cb (GObject *object, + GAsyncResult *result, + GoaOAuth2Provider *self) +{ + GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); + SecretService *service = NULL; + GList *collections = NULL; + + service = secret_service_get_finish (result, &priv->error); + if (service == NULL) + goto out; + + collections = secret_service_get_collections (service); + for (const GList *iter = collections; iter != NULL; iter = iter->next) + { + g_autofree char *label = secret_collection_get_label (iter->data); + + /* The session collection is an empty string (?) */ + if (g_strcmp0 (label, "") == 0) + { + const char *client_id = NULL; + const char *provider_type = NULL; + + /* Ensure there's no dangling entry */ + client_id = goa_oauth2_provider_get_client_id (self); + provider_type = goa_provider_get_provider_type (GOA_PROVIDER (self)); + secret_password_clear_sync (&oauth2_schema, NULL, NULL, + "goa-oauth2-client", client_id, + "goa-oauth2-provider", provider_type, + NULL); + + /* Watch the session collection for the requested URI */ + priv->session = g_object_ref (iter->data); + g_signal_connect_object (priv->session, + "notify::items", + G_CALLBACK (on_secrets_changed), + self, + 0); + goto out; + } + } + + if (priv->session == NULL && priv->error == NULL) + { + g_set_error (&priv->error, + GOA_ERROR, + GOA_ERROR_FAILED, + "Failed to connect to session keyring"); + goto out; + } + +out: + g_clear_object (&service); + g_list_free_full (collections, g_object_unref); + g_main_loop_quit (priv->loop); +} + +static void +on_continue_in_browser (GtkButton *button, + GoaOAuth2Provider *self) +{ + GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); + + if (!g_app_info_launch_default_for_uri (priv->request_uri, NULL, &priv->error)) + gtk_dialog_response (priv->dialog, GTK_RESPONSE_CANCEL); + else + gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE); } static gboolean @@ -906,12 +870,13 @@ get_tokens_and_identity (GoaOAuth2Provider *self, GtkDialog *dialog, GtkBox *vbox) { - GoaOAuth2ProviderPrivate *priv; + GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); gboolean ret = FALSE; - gchar *url; - GtkWidget *embed; + int response_id = GTK_RESPONSE_NONE; GtkWidget *grid; - GtkWidget *web_view; + GtkWidget *image; + GtkWidget *label; + GtkWidget *button; const gchar *scope; gchar *escaped_redirect_uri = NULL; gchar *escaped_client_id = NULL; @@ -923,7 +888,6 @@ get_tokens_and_identity (GoaOAuth2Provider *self, g_return_val_if_fail (GTK_IS_DIALOG (dialog), FALSE); g_return_val_if_fail (GTK_IS_BOX (vbox), FALSE); - priv = goa_oauth2_provider_get_instance_private (self); g_return_val_if_fail (priv->error == NULL, FALSE); /* TODO: check with NM whether we're online, if not - return error */ @@ -937,6 +901,8 @@ get_tokens_and_identity (GoaOAuth2Provider *self, g_clear_pointer (&priv->authorization_code, g_free); g_clear_pointer (&priv->access_token, g_free); g_clear_pointer (&priv->refresh_token, g_free); + g_clear_pointer (&priv->request_uri, g_free); + g_clear_object (&priv->session); /* TODO: use oauth2_proxy_build_login_url_full() */ escaped_redirect_uri = g_uri_escape_string (goa_oauth2_provider_get_redirect_uri (self), NULL, TRUE); @@ -946,40 +912,71 @@ get_tokens_and_identity (GoaOAuth2Provider *self, escaped_scope = g_uri_escape_string (goa_oauth2_provider_get_scope (self), NULL, TRUE); else escaped_scope = NULL; - url = goa_oauth2_provider_build_authorization_uri (self, - goa_oauth2_provider_get_authorization_uri (self), - escaped_redirect_uri, - escaped_client_id, - escaped_scope); + priv->request_uri = goa_oauth2_provider_build_authorization_uri (self, + goa_oauth2_provider_get_authorization_uri (self), + escaped_redirect_uri, + escaped_client_id, + escaped_scope); goa_utils_set_dialog_title (GOA_PROVIDER (self), dialog, add_account); - grid = gtk_grid_new (); - gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing (GTK_GRID (grid), 12); + grid = g_object_new (GTK_TYPE_BOX, + "orientation", GTK_ORIENTATION_VERTICAL, + "spacing", 18, + "halign", GTK_ALIGN_CENTER, + "hexpand", TRUE, + "valign", GTK_ALIGN_CENTER, + "vexpand", TRUE, + "margin", 12, + NULL); gtk_container_add (GTK_CONTAINER (vbox), grid); - web_view = goa_web_view_new (GOA_PROVIDER (self), existing_identity); - gtk_widget_set_hexpand (web_view, TRUE); - gtk_widget_set_vexpand (web_view, TRUE); - embed = goa_web_view_get_view (GOA_WEB_VIEW (web_view)); - - if (goa_oauth2_provider_get_use_mobile_browser (self)) - goa_web_view_fake_mobile (GOA_WEB_VIEW (web_view)); - - webkit_web_view_load_uri (WEBKIT_WEB_VIEW (embed), url); - g_signal_connect (embed, - "decide-policy", - G_CALLBACK (on_web_view_decide_policy), - self); - g_signal_connect (web_view, "deny-click", G_CALLBACK (on_web_view_deny_click), self); - g_signal_connect (web_view, "password-submit", G_CALLBACK (on_web_view_password_submit), self); + image = g_object_new (GTK_TYPE_IMAGE, + "icon-name", "web-browser-symbolic", + "pixel-size", 128, + NULL); + gtk_style_context_add_class (gtk_widget_get_style_context (image), "dim-label"); + gtk_container_add (GTK_CONTAINER (grid), image); + + label = gtk_label_new (_("Sign in with your browser to setup an account.")); + gtk_style_context_add_class (gtk_widget_get_style_context (label), "heading"); + gtk_container_add (GTK_CONTAINER (grid), label); + + button = gtk_button_new_with_label (_("Continue")); + gtk_widget_set_halign (button, GTK_ALIGN_CENTER); + gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action"); + g_signal_connect_object (button, + "clicked", + G_CALLBACK (on_continue_in_browser), + self, 0); + gtk_container_add (GTK_CONTAINER (grid), button); + gtk_dialog_add_button (priv->dialog, _("_Cancel"), GTK_RESPONSE_CANCEL); - gtk_container_add (GTK_CONTAINER (grid), web_view); + gtk_widget_show_all (GTK_WIDGET (vbox)); gtk_window_set_default_size (GTK_WINDOW (dialog), -1, -1); - gtk_widget_show_all (GTK_WIDGET (vbox)); - gtk_dialog_run (GTK_DIALOG (dialog)); + /* Watch the session secret collection for the OAuth2 URI */ + secret_service_get (SECRET_SERVICE_LOAD_COLLECTIONS | SECRET_SERVICE_OPEN_SESSION, + NULL, + (GAsyncReadyCallback) secret_service_get_cb, + self); + g_main_loop_run (priv->loop); + if (priv->error != NULL) + goto out; + + /* Inform the user authentication should be completed in the browser */ + response_id = gtk_dialog_run (GTK_DIALOG (dialog)); + if (response_id != GTK_RESPONSE_OK) + { + if (priv->error == NULL) + { + g_set_error (&priv->error, + GOA_ERROR, + GOA_ERROR_DIALOG_DISMISSED, + _("Dialog was dismissed")); + } + goto out; + } /* We can have either the auth code, with which we'll obtain the token, or * the token directly if we are using a client side flow, since we don't @@ -1038,12 +1035,14 @@ get_tokens_and_identity (GoaOAuth2Provider *self, } ret = TRUE; + gtk_dialog_response (dialog, GTK_RESPONSE_OK); out: - g_free (url); g_free (escaped_redirect_uri); g_free (escaped_client_id); g_free (escaped_scope); + g_clear_pointer (&priv->request_uri, g_free); + g_clear_object (&priv->session); return ret; } @@ -1131,6 +1130,7 @@ goa_oauth2_provider_add_account (GoaProvider *provider, g_return_val_if_fail (error == NULL || *error == NULL, NULL); priv = goa_oauth2_provider_get_instance_private (self); + priv->loop = g_main_loop_new (NULL, FALSE); if (!get_tokens_and_identity (self, TRUE, NULL, dialog, vbox)) goto out; @@ -1165,7 +1165,6 @@ goa_oauth2_provider_add_account (GoaProvider *provider, NULL, /* GCancellable* */ (GAsyncReadyCallback) add_account_cb, self); - priv->loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (priv->loop); if (priv->error != NULL) goto out; @@ -1215,6 +1214,7 @@ goa_oauth2_provider_refresh_account (GoaProvider *provider, g_return_val_if_fail (error == NULL || *error == NULL, FALSE); priv = goa_oauth2_provider_get_instance_private (self); + priv->loop = g_main_loop_new (NULL, FALSE); dialog = gtk_dialog_new_with_buttons (NULL, parent, @@ -1625,6 +1625,8 @@ goa_oauth2_provider_finalize (GObject *object) g_free (priv->authorization_code); g_free (priv->access_token); g_free (priv->refresh_token); + g_clear_pointer (&priv->request_uri, g_free); + g_clear_object (&priv->session); G_OBJECT_CLASS (goa_oauth2_provider_parent_class)->finalize (object); } @@ -1650,12 +1652,9 @@ goa_oauth2_provider_class_init (GoaOAuth2ProviderClass *klass) provider_class->ensure_credentials_sync = goa_oauth2_provider_ensure_credentials_sync; klass->build_authorization_uri = goa_oauth2_provider_build_authorization_uri_default; - klass->decide_navigation_policy = goa_oauth2_provider_decide_navigation_policy_default; klass->get_token_uri = goa_oauth2_provider_get_token_uri_default; klass->get_scope = goa_oauth2_provider_get_scope_default; klass->get_use_mobile_browser = goa_oauth2_provider_get_use_mobile_browser_default; - klass->is_deny_node = goa_oauth2_provider_is_deny_node_default; - klass->is_password_node = goa_oauth2_provider_is_password_node_default; klass->add_account_key_values = goa_oauth2_provider_add_account_key_values_default; } diff --git a/src/goabackend/goaoauthprovider.c b/src/goabackend/goaoauthprovider.c deleted file mode 100644 index 71bcad6..0000000 --- a/src/goabackend/goaoauthprovider.c +++ /dev/null @@ -1,1662 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright © 2011 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#include "config.h" -#include -#include - -#include -#include -#include -#include - -#include "goaprovider.h" -#include "goautils.h" -#include "goawebview.h" -#include "goaoauthprovider.h" -#include "goasouplogger.h" - -/** - * SECTION:goaoauthprovider - * @title: GoaOAuthProvider - * @short_description: Abstract base class for OAuth 1.0a providers - * - * #GoaOAuthProvider is an abstract base class for OAuth 1.0a - * compliant implementations as defined by RFC - * 5849. Additionally, the code works with providers - * implementing OAuth - * Session 1.0 Draft 1 for refreshing access tokens. - * - * Subclasses must implement - * #GoaOAuthProviderClass.get_consumer_key, - * #GoaOAuthProviderClass.get_consumer_secret, - * #GoaOAuthProviderClass.get_request_uri, - * #GoaOAuthProviderClass.get_authorization_uri, - * #GoaOAuthProviderClass.get_token_uri, - * #GoaOAuthProviderClass.get_callback_uri and - * #GoaOAuthProviderClass.get_identity_sync methods. - * - * Additionally, the - * #GoaProviderClass.get_provider_type, - * #GoaProviderClass.get_provider_name, - * #GoaProviderClass.build_object (this should chain up to its - * parent class) methods must be implemented. - * - * Note that the #GoaProviderClass.add_account, - * #GoaProviderClass.refresh_account and - * #GoaProviderClass.ensure_credentials_sync methods do not - * need to be implemented - this type implements these methods. - */ - -G_LOCK_DEFINE_STATIC (provider_lock); - -G_DEFINE_ABSTRACT_TYPE (GoaOAuthProvider, goa_oauth_provider, GOA_TYPE_PROVIDER); - -static gboolean -is_authorization_error (GError *error) -{ - gboolean ret; - - g_return_val_if_fail (error != NULL, FALSE); - - ret = FALSE; - if (error->domain == REST_PROXY_ERROR || error->domain == SOUP_HTTP_ERROR) - { - if (SOUP_STATUS_IS_CLIENT_ERROR (error->code)) - ret = TRUE; - } - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -goa_oauth_provider_get_use_mobile_browser_default (GoaOAuthProvider *provider) -{ - return FALSE; -} - -/** - * goa_oauth_provider_get_use_mobile_browser: - * @provider: A #GoaOAuthProvider. - * - * Returns whether there is a need for the embedded browser to identify - * itself as running on a mobile phone in order to get a more compact - * version of the approval page. - * - * This is a virtual method where the default implementation returns - * %FALSE. - * - * Returns: %TRUE if the embedded browser should identify itself as - * running on a mobile platform, %FALSE otherwise. - */ -gboolean -goa_oauth_provider_get_use_mobile_browser (GoaOAuthProvider *provider) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_use_mobile_browser (provider); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -goa_oauth_provider_is_deny_node_default (GoaOAuthProvider *provider, WebKitDOMNode *node) -{ - return FALSE; -} - -/** - * goa_oauth_provider_is_deny_node: - * @provider: A #GoaOAuthProvider. - * @node: A WebKitDOMNode. - * - * Checks whether @node is the HTML UI element that the user can use - * to deny permission to access his account. Usually they are either a - * WebKitDOMHTMLButtonElement or a WebKitDOMHTMLInputElement. - * - * Please note that providers may have multiple such elements in their - * UI and this method should catch all of them. - * - * This is a virtual method where the default implementation returns - * %FALSE. - * - * Returns: %TRUE if the @node can be used to deny permission. - */ -gboolean -goa_oauth_provider_is_deny_node (GoaOAuthProvider *provider, WebKitDOMNode *node) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->is_deny_node (provider, node); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -goa_oauth_provider_is_password_node_default (GoaOAuthProvider *provider, WebKitDOMHTMLInputElement *element) -{ - return FALSE; -} - -/** - * goa_oauth_provider_is_password_node: - * @provider: A #GoaOAuthProvider. - * @element: A WebKitDOMHTMLInputElement - * - * Checks whether @element is the HTML UI element that the user can - * use to enter her password. This can be used to offer a - * #GoaPasswordBased interface by saving the user's - * password. Providers usually frown upon doing this, so this is not - * recommended. - * - * This is a virtual method where the default implementation returns - * %FALSE. - * - * Returns: %TRUE if @element can be used to enter the password. - */ -gboolean -goa_oauth_provider_is_password_node (GoaOAuthProvider *provider, WebKitDOMHTMLInputElement *element) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); - g_return_val_if_fail (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element), FALSE); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->is_password_node (provider, element); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -goa_oauth_provider_add_account_key_values_default (GoaOAuthProvider *provider, - GVariantBuilder *builder) -{ - /* do nothing */ -} - -/** - * goa_oauth_provider_add_account_key_values: - * @provider: A #GoaProvider. - * @builder: A #GVariantBuilder for a a{ss} variant. - * - * Hook for implementations to add key/value pairs to the key-file - * when creating an account. - * - * This is a virtual method where the default implementation does nothing. - */ -void -goa_oauth_provider_add_account_key_values (GoaOAuthProvider *provider, - GVariantBuilder *builder) -{ - g_return_if_fail (GOA_IS_OAUTH_PROVIDER (provider)); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->add_account_key_values (provider, builder); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gchar * -goa_oauth_provider_build_authorization_uri_default (GoaOAuthProvider *provider, - const gchar *authorization_uri, - const gchar *escaped_oauth_token) -{ - return g_strdup_printf ("%s" - "?oauth_token=%s", - authorization_uri, - escaped_oauth_token); -} - -/** - * goa_oauth_provider_build_authorization_uri: - * @provider: A #GoaOAuthProvider. - * @authorization_uri: An authorization URI. - * @escaped_oauth_token: An escaped oauth token. - * - * Builds the URI that can be opened in a web browser (or embedded web - * browser widget) to start authenticating an user. - * - * The default implementation just returns the expected URI - * (e.g. http://example.com/dialog/oauth?auth_token=1234567890) - * - override (and chain up) if you e.g. need to to pass additional - * parameters. - * - * The @authorization_uri parameter originate from the result of the - * the goa_oauth_provider_get_authorization_uri() method. The - * @escaped_oauth_token parameter is the temporary credentials identifier - * escaped using g_uri_escape_string(). - * - * Returns: (transfer full): An authorization URI that must be freed with g_free(). - */ -gchar * -goa_oauth_provider_build_authorization_uri (GoaOAuthProvider *provider, - const gchar *authorization_uri, - const gchar *escaped_oauth_token) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - g_return_val_if_fail (authorization_uri != NULL, NULL); - g_return_val_if_fail (escaped_oauth_token != NULL, NULL); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->build_authorization_uri (provider, - authorization_uri, - escaped_oauth_token); -} - -/** - * goa_oauth_provider_get_consumer_key: - * @provider: A #GoaOAuthProvider. - * - * Gets the consumer key identifying the client. - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: (transfer none): A string owned by @provider - do not free. - */ -const gchar * -goa_oauth_provider_get_consumer_key (GoaOAuthProvider *provider) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_consumer_key (provider); -} - -/** - * goa_oauth_provider_get_consumer_secret: - * @provider: A #GoaOAuthProvider. - * - * Gets the consumer secret identifying the client. - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: (transfer none): A string owned by @provider - do not free. - */ -const gchar * -goa_oauth_provider_get_consumer_secret (GoaOAuthProvider *provider) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_consumer_secret (provider); -} - -/** - * goa_oauth_provider_get_request_uri: - * @provider: A #GoaOAuthProvider. - * - * Gets the request uri. - * - * http://tools.ietf.org/html/rfc5849#section-2.1 - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: (transfer none): A string owned by @provider - do not free. - */ -const gchar * -goa_oauth_provider_get_request_uri (GoaOAuthProvider *provider) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_request_uri (provider); -} - -/** - * goa_oauth_provider_get_request_uri_params: - * @provider: A #GoaOAuthProvider. - * - * Gets additional parameters for the request URI. - * - * http://tools.ietf.org/html/rfc5849#section-2.1 - * - * This is a virtual method where the default implementation returns - * %NULL. - * - * Returns: (transfer full): %NULL (for no parameters) or a - * %NULL-terminated array of (key, value) pairs that will be added to - * the URI. The caller will free the returned value with g_strfreev(). - */ -gchar ** -goa_oauth_provider_get_request_uri_params (GoaOAuthProvider *provider) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_request_uri_params (provider); -} - -static gchar ** -goa_oauth_provider_get_request_uri_params_default (GoaOAuthProvider *provider) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - return NULL; -} - -/** - * goa_oauth_provider_get_authorization_uri: - * @provider: A #GoaOAuthProvider. - * - * Gets the authorization uri. - * - * http://tools.ietf.org/html/rfc5849#section-2.2 - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: (transfer none): A string owned by @provider - do not free. - */ -const gchar * -goa_oauth_provider_get_authorization_uri (GoaOAuthProvider *provider) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_authorization_uri (provider); -} - -/** - * goa_oauth_provider_get_token_uri: - * @provider: A #GoaOAuthProvider. - * - * Gets the token uri. - * - * http://tools.ietf.org/html/rfc5849#section-2.3 - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: (transfer none): A string owned by @provider - do not free. - */ -const gchar * -goa_oauth_provider_get_token_uri (GoaOAuthProvider *provider) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_token_uri (provider); -} - -/** - * goa_oauth_provider_get_callback_uri: - * @provider: A #GoaOAuthProvider. - * - * Gets the callback uri. - * - * http://tools.ietf.org/html/rfc5849#section-2.1 - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: (transfer none): A string owned by @provider - do not free. - */ -const gchar * -goa_oauth_provider_get_callback_uri (GoaOAuthProvider *provider) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_callback_uri (provider); -} - -/** - * goa_oauth_provider_get_identity_sync: - * @provider: A #GoaOAuthProvider. - * @access_token: A valid OAuth 1.0 access token. - * @access_token_secret: The valid secret for @access_token. - * @out_presentation_identity: (out): Return location for presentation identity or %NULL. - * @cancellable: (allow-none): A #GCancellable or %NULL. - * @error: Return location for error or %NULL. - * - * Method that returns the identity corresponding to @access_token and - * @access_token_secret. - * - * The identity is needed because all authentication happens out of - * band. In addition to the identity, an implementation also returns a - * presentation identity that is more suitable - * for presentation (the identity could be a GUID for example). - * - * The calling thread is blocked while the identity is obtained. - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: The identity or %NULL if error is set. The returned string - * must be freed with g_free(). - */ -gchar * -goa_oauth_provider_get_identity_sync (GoaOAuthProvider *provider, - const gchar *access_token, - const gchar *access_token_secret, - gchar **out_presentation_identity, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - g_return_val_if_fail (access_token != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_identity_sync (provider, - access_token, - access_token_secret, - out_presentation_identity, - cancellable, - error); -} - -/** - * goa_oauth_provider_is_identity_node: - * @provider: A #GoaOAuthProvider. - * @element: A WebKitDOMHTMLInputElement. - * - * Checks whether @element is the HTML UI element that the user can - * use to identify herself at the provider. - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: %TRUE if the @element can be used to deny permission. - */ -gboolean -goa_oauth_provider_is_identity_node (GoaOAuthProvider *provider, WebKitDOMHTMLInputElement *element) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->is_identity_node (provider, element); -} - -/** - * goa_oauth_provider_parse_request_token_error: - * @provider: A #GoaOAuthProvider. - * @call: The #RestProxyCall that was used to fetch the request token. - * - * Tries to parse the headers and payload within @call to provide a - * human readable error message in case the request token could not - * be fetched. - * - * This is a pure virtual method - a subclass must provide an - * implementation. - * - * Returns: A human readable error message or %NULL if the cause of the - * error could not be determined. The returned string must be freed with - * g_free(). - */ -gchar * -goa_oauth_provider_parse_request_token_error (GoaOAuthProvider *provider, RestProxyCall *call) -{ - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->parse_request_token_error (provider, call); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gchar * -get_tokens_sync (GoaOAuthProvider *provider, - const gchar *token, - const gchar *token_secret, - const gchar *session_handle, /* may be NULL */ - const gchar *verifier, /* may be NULL */ - gchar **out_access_token_secret, - gint *out_access_token_expires_in, - gchar **out_session_handle, - gint *out_session_handle_expires_in, - GCancellable *cancellable, - GError **error) -{ - RestProxy *proxy; - RestProxyCall *call; - SoupLogger *logger = NULL; - gchar *ret = NULL; - guint status_code; - GHashTable *f; - const gchar *expires_in_str; - gchar *ret_access_token = NULL; - gchar *ret_access_token_secret = NULL; - gint ret_access_token_expires_in = 0; - gchar *ret_session_handle = NULL; - gint ret_session_handle_expires_in = 0; - - proxy = oauth_proxy_new (goa_oauth_provider_get_consumer_key (provider), - goa_oauth_provider_get_consumer_secret (provider), - goa_oauth_provider_get_token_uri (provider), - FALSE); - logger = goa_soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); - rest_proxy_add_soup_feature (proxy, SOUP_SESSION_FEATURE (logger)); - oauth_proxy_set_token (OAUTH_PROXY (proxy), token); - oauth_proxy_set_token_secret (OAUTH_PROXY (proxy), token_secret); - call = rest_proxy_new_call (proxy); - rest_proxy_call_set_method (call, "POST"); - if (verifier != NULL) - rest_proxy_call_add_param (call, "oauth_verifier", verifier); - if (session_handle != NULL) - rest_proxy_call_add_param (call, "oauth_session_handle", session_handle); - /* TODO: cancellable support? */ - if (!rest_proxy_call_sync (call, error)) - goto out; - - status_code = rest_proxy_call_get_status_code (call); - if (status_code != 200) - { - g_set_error (error, - GOA_ERROR, - GOA_ERROR_FAILED, - /* Translators: the %d is a HTTP status code and the %s is a textual description of it */ - _("Expected status 200 when requesting access token, instead got status %d (%s)"), - status_code, - rest_proxy_call_get_status_message (call)); - goto out; - } - - f = soup_form_decode (rest_proxy_call_get_payload (call)); - ret_access_token = g_strdup (g_hash_table_lookup (f, "oauth_token")); - ret_access_token_secret = g_strdup (g_hash_table_lookup (f, "oauth_token_secret")); - ret_session_handle = g_strdup (g_hash_table_lookup (f, "oauth_session_handle")); - expires_in_str = g_hash_table_lookup (f, "oauth_expires_in"); - if (expires_in_str != NULL) - ret_access_token_expires_in = atoi (expires_in_str); - expires_in_str = g_hash_table_lookup (f, "oauth_authorization_expires_in"); - if (expires_in_str != NULL) - ret_session_handle_expires_in = atoi (expires_in_str); - g_hash_table_unref (f); - - if (ret_access_token == NULL || ret_access_token_secret == NULL) - { - g_set_error (error, - GOA_ERROR, - GOA_ERROR_FAILED, - _("Missing access_token or access_token_secret headers in response")); - goto out; - } - - ret = ret_access_token; ret_access_token = NULL; - if (out_access_token_secret != NULL) - { - *out_access_token_secret = ret_access_token_secret; - ret_access_token_secret = NULL; - } - if (out_access_token_expires_in != NULL) - *out_access_token_expires_in = ret_access_token_expires_in; - if (out_session_handle != NULL) - { - *out_session_handle = ret_session_handle; - ret_session_handle = NULL; - } - if (out_session_handle_expires_in != NULL) - *out_session_handle_expires_in = ret_session_handle_expires_in; - - out: - g_free (ret_access_token); - g_free (ret_access_token_secret); - g_free (ret_session_handle); - g_clear_object (&call); - g_clear_object (&proxy); - g_clear_object (&logger); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - GoaOAuthProvider *provider; - GtkDialog *dialog; - GError *error; - GMainLoop *loop; - - gchar *password; - - gchar *oauth_verifier; - - const gchar *existing_identity; - - gchar *identity; - gchar *presentation_identity; - - gchar *request_token; - gchar *request_token_secret; - gchar *access_token; - gchar *access_token_secret; - gint access_token_expires_in; - gchar *session_handle; - gint session_handle_expires_in; -} IdentifyData; - -static void -on_web_view_deny_click (GoaWebView *web_view, gpointer user_data) -{ - IdentifyData *data = user_data; - gtk_dialog_response (data->dialog, GTK_RESPONSE_CANCEL); -} - -static void -on_web_view_password_submit (GoaWebView *web_view, const gchar *password, gpointer user_data) -{ - IdentifyData *data = user_data; - - g_free (data->password); - data->password = g_strdup (password); -} - -static gboolean -on_web_view_decide_policy (WebKitWebView *web_view, - WebKitPolicyDecision *decision, - WebKitPolicyDecisionType decision_type, - gpointer user_data) -{ - GHashTable *key_value_pairs; - IdentifyData *data = user_data; - SoupURI *uri; - WebKitNavigationAction *action; - WebKitURIRequest *request; - const gchar *query; - const gchar *redirect_uri; - const gchar *requested_uri; - gint response_id = GTK_RESPONSE_NONE; - - if (decision_type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION) - return FALSE; - - /* TODO: use oauth_proxy_extract_access_token() */ - - action = webkit_navigation_policy_decision_get_navigation_action (WEBKIT_NAVIGATION_POLICY_DECISION (decision)); - request = webkit_navigation_action_get_request (action); - requested_uri = webkit_uri_request_get_uri (request); - redirect_uri = goa_oauth_provider_get_callback_uri (data->provider); - - if (!g_str_has_prefix (requested_uri, redirect_uri)) - goto default_behaviour; - - uri = soup_uri_new (requested_uri); - query = soup_uri_get_query (uri); - - if (query != NULL) - { - key_value_pairs = soup_form_decode (query); - - data->oauth_verifier = g_strdup (g_hash_table_lookup (key_value_pairs, "oauth_verifier")); - if (data->oauth_verifier != NULL) - response_id = GTK_RESPONSE_OK; - - g_hash_table_unref (key_value_pairs); - } - - if (data->oauth_verifier != NULL) - goto ignore_request; - - /* TODO: The only OAuth1 provider is Flickr. It doesn't send any - * error code and only redirects to the URI specified in the Flickr - * App Garden. Re-evaluate when the situation changes. - */ - response_id = GTK_RESPONSE_CANCEL; - goto ignore_request; - - ignore_request: - g_assert (response_id != GTK_RESPONSE_NONE); - gtk_dialog_response (data->dialog, response_id); - webkit_policy_decision_ignore (decision); - return TRUE; - - default_behaviour: - return FALSE; -} - -static void -rest_proxy_call_cb (RestProxyCall *call, const GError *error, GObject *weak_object, gpointer user_data) -{ - IdentifyData *data = user_data; - g_main_loop_quit (data->loop); -} - -static gboolean -get_tokens_and_identity (GoaOAuthProvider *provider, - gboolean add_account, - const gchar *existing_identity, - GtkDialog *dialog, - GtkBox *vbox, - gchar **out_access_token, - gchar **out_access_token_secret, - gint *out_access_token_expires_in, - gchar **out_session_handle, - gint *out_session_handle_expires_in, - gchar **out_identity, - gchar **out_presentation_identity, - gchar **out_password, - GError **error) -{ - gboolean ret = FALSE; - gchar *url = NULL; - IdentifyData data; - gchar *escaped_request_token = NULL; - RestProxy *proxy = NULL; - RestProxyCall *call = NULL; - SoupLogger *logger = NULL; - GHashTable *f; - GtkWidget *embed; - GtkWidget *grid; - GtkWidget *spinner; - GtkWidget *web_view; - gchar **request_params = NULL; - guint n; - - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); - g_return_val_if_fail ((!add_account && existing_identity != NULL && existing_identity[0] != '\0') - || (add_account && existing_identity == NULL), FALSE); - g_return_val_if_fail (GTK_IS_DIALOG (dialog), FALSE); - g_return_val_if_fail (GTK_IS_BOX (vbox), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* TODO: check with NM whether we're online, if not - return error */ - - memset (&data, '\0', sizeof (IdentifyData)); - data.provider = provider; - data.dialog = dialog; - data.loop = g_main_loop_new (NULL, FALSE); - data.existing_identity = existing_identity; - - proxy = oauth_proxy_new (goa_oauth_provider_get_consumer_key (provider), - goa_oauth_provider_get_consumer_secret (provider), - goa_oauth_provider_get_request_uri (provider), FALSE); - logger = goa_soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); - rest_proxy_add_soup_feature (proxy, SOUP_SESSION_FEATURE (logger)); - - call = rest_proxy_new_call (proxy); - rest_proxy_call_set_method (call, "POST"); - rest_proxy_call_add_param (call, "oauth_callback", goa_oauth_provider_get_callback_uri (provider)); - - request_params = goa_oauth_provider_get_request_uri_params (provider); - if (request_params != NULL) - { - g_assert (g_strv_length (request_params) % 2 == 0); - for (n = 0; request_params[n] != NULL; n += 2) - rest_proxy_call_add_param (call, request_params[n], request_params[n+1]); - } - if (!rest_proxy_call_async (call, rest_proxy_call_cb, NULL, &data, &data.error)) - { - g_prefix_error (&data.error, _("Error getting a Request Token: ")); - goto out; - } - - goa_utils_set_dialog_title (GOA_PROVIDER (provider), dialog, add_account); - - grid = gtk_grid_new (); - gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing (GTK_GRID (grid), 12); - gtk_container_add (GTK_CONTAINER (vbox), grid); - - spinner = gtk_spinner_new (); - gtk_widget_set_hexpand (spinner, TRUE); - gtk_widget_set_halign (spinner, GTK_ALIGN_CENTER); - gtk_widget_set_vexpand (spinner, TRUE); - gtk_widget_set_valign (spinner, GTK_ALIGN_CENTER); - gtk_widget_set_size_request (GTK_WIDGET (spinner), 24, 24); - gtk_spinner_start (GTK_SPINNER (spinner)); - gtk_container_add (GTK_CONTAINER (grid), spinner); - gtk_widget_show_all (GTK_WIDGET (vbox)); - - g_main_loop_run (data.loop); - gtk_container_remove (GTK_CONTAINER (grid), spinner); - - if (rest_proxy_call_get_status_code (call) != 200) - { - gchar *msg; - - msg = goa_oauth_provider_parse_request_token_error (provider, call); - if (msg == NULL) - /* Translators: the %d is a HTTP status code and the %s is a textual description of it */ - msg = g_strdup_printf (_("Expected status 200 for getting a Request Token, instead got status %d (%s)"), - rest_proxy_call_get_status_code (call), - rest_proxy_call_get_status_message (call)); - - g_set_error_literal (&data.error, GOA_ERROR, GOA_ERROR_FAILED, msg); - g_free (msg); - goto out; - } - f = soup_form_decode (rest_proxy_call_get_payload (call)); - data.request_token = g_strdup (g_hash_table_lookup (f, "oauth_token")); - data.request_token_secret = g_strdup (g_hash_table_lookup (f, "oauth_token_secret")); - g_hash_table_unref (f); - if (data.request_token == NULL || data.request_token_secret == NULL) - { - g_set_error (&data.error, - GOA_ERROR, - GOA_ERROR_FAILED, - _("Missing request_token or request_token_secret headers in response")); - goto out; - } - - escaped_request_token = g_uri_escape_string (data.request_token, NULL, TRUE); - url = goa_oauth_provider_build_authorization_uri (provider, - goa_oauth_provider_get_authorization_uri (provider), - escaped_request_token); - - web_view = goa_web_view_new (GOA_PROVIDER (provider), existing_identity); - gtk_widget_set_hexpand (web_view, TRUE); - gtk_widget_set_vexpand (web_view, TRUE); - embed = goa_web_view_get_view (GOA_WEB_VIEW (web_view)); - - if (goa_oauth_provider_get_use_mobile_browser (provider)) - goa_web_view_fake_mobile (GOA_WEB_VIEW (web_view)); - - webkit_web_view_load_uri (WEBKIT_WEB_VIEW (embed), url); - g_signal_connect (embed, - "decide-policy", - G_CALLBACK (on_web_view_decide_policy), - &data); - g_signal_connect (web_view, "deny-click", G_CALLBACK (on_web_view_deny_click), &data); - g_signal_connect (web_view, "password-submit", G_CALLBACK (on_web_view_password_submit), &data); - - gtk_container_add (GTK_CONTAINER (grid), web_view); - gtk_window_set_default_size (GTK_WINDOW (dialog), -1, -1); - - gtk_widget_show_all (GTK_WIDGET (vbox)); - gtk_dialog_run (GTK_DIALOG (dialog)); - - if (data.oauth_verifier == NULL) - { - if (data.error == NULL) - { - g_set_error (&data.error, - GOA_ERROR, - GOA_ERROR_DIALOG_DISMISSED, - _("Dialog was dismissed")); - } - goto out; - } - g_assert (data.error == NULL); - - /* OK, we are done interacting with the user ... but before we can - * make up our mind, there are two more RPC calls to make and these - * call may take some time. So hide the dialog immediately. - */ - gtk_widget_hide (GTK_WIDGET (dialog)); - - /* OK, we now have the request token... we can exchange that for a - * (short-lived) access token and session_handle (used to refresh the - * access token).. - */ - - /* TODO: run in worker thread */ - data.access_token = get_tokens_sync (provider, - data.request_token, - data.request_token_secret, - NULL, /* session_handle */ - data.oauth_verifier, - &data.access_token_secret, - &data.access_token_expires_in, - &data.session_handle, - &data.session_handle_expires_in, - NULL, /* GCancellable */ - &data.error); - if (data.access_token == NULL) - { - g_prefix_error (&data.error, _("Error getting an Access Token: ")); - goto out; - } - - /* TODO: run in worker thread */ - data.identity = goa_oauth_provider_get_identity_sync (provider, - data.access_token, - data.access_token_secret, - &data.presentation_identity, - NULL, /* TODO: GCancellable */ - &data.error); - if (data.identity == NULL) - { - g_prefix_error (&data.error, _("Error getting identity: ")); - goto out; - } - - ret = TRUE; - - out: - g_clear_object (&call); - - if (ret) - { - g_warn_if_fail (data.error == NULL); - if (out_access_token != NULL) - *out_access_token = g_strdup (data.access_token); - if (out_access_token_secret != NULL) - *out_access_token_secret = g_strdup (data.access_token_secret); - if (out_access_token_expires_in != NULL) - *out_access_token_expires_in = data.access_token_expires_in; - if (out_session_handle != NULL) - *out_session_handle = g_strdup (data.session_handle); - if (out_session_handle_expires_in != NULL) - *out_session_handle_expires_in = data.session_handle_expires_in; - if (out_identity != NULL) - *out_identity = g_strdup (data.identity); - if (out_presentation_identity != NULL) - *out_presentation_identity = g_strdup (data.presentation_identity); - if (out_password != NULL) - *out_password = g_strdup (data.password); - } - else - { - g_warn_if_fail (data.error != NULL); - g_propagate_error (error, data.error); - } - - g_free (data.password); - g_free (data.presentation_identity); - g_free (data.identity); - g_free (url); - - g_free (data.oauth_verifier); - g_clear_pointer (&data.loop, (GDestroyNotify) g_main_loop_unref); - g_free (data.access_token); - g_free (data.access_token_secret); - g_free (escaped_request_token); - - g_free (data.request_token); - g_free (data.request_token_secret); - - g_strfreev (request_params); - g_clear_object (&proxy); - g_clear_object (&logger); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - GError *error; - GMainLoop *loop; - gchar *account_object_path; -} AddData; - -static void -add_account_cb (GoaManager *manager, - GAsyncResult *res, - gpointer user_data) -{ - AddData *data = user_data; - goa_manager_call_add_account_finish (manager, - &data->account_object_path, - res, - &data->error); - g_main_loop_quit (data->loop); -} - -static gint64 -duration_to_abs_usec (gint duration_sec) -{ - gint64 ret; - GTimeVal now; - - g_get_current_time (&now); - ret = ((gint64) now.tv_sec) * 1000L * 1000L + ((gint64) now.tv_usec); - ret += ((gint64) duration_sec) * 1000L * 1000L; - return ret; -} - -static gint -abs_usec_to_duration (gint64 abs_usec) -{ - gint64 ret; - GTimeVal now; - - g_get_current_time (&now); - ret = abs_usec - (((gint64) now.tv_sec) * 1000L * 1000L + ((gint64) now.tv_usec)); - ret /= 1000L * 1000L; - return ret; -} - -static GoaObject * -goa_oauth_provider_add_account (GoaProvider *_provider, - GoaClient *client, - GtkDialog *dialog, - GtkBox *vbox, - GError **error) -{ - GoaOAuthProvider *provider = GOA_OAUTH_PROVIDER (_provider); - GoaObject *ret = NULL; - gchar *access_token = NULL; - gchar *access_token_secret = NULL; - gint access_token_expires_in; - gchar *session_handle = NULL; - gint session_handle_expires_in; - gchar *identity = NULL; - gchar *presentation_identity = NULL; - gchar *password = NULL; - AddData data; - GVariantBuilder credentials; - GVariantBuilder details; - - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - g_return_val_if_fail (GOA_IS_CLIENT (client), NULL); - g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); - g_return_val_if_fail (GTK_IS_BOX (vbox), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - memset (&data, '\0', sizeof (AddData)); - data.loop = g_main_loop_new (NULL, FALSE); - - if (!get_tokens_and_identity (provider, - TRUE, - NULL, - dialog, - vbox, - &access_token, - &access_token_secret, - &access_token_expires_in, - &session_handle, - &session_handle_expires_in, - &identity, - &presentation_identity, - &password, - &data.error)) - goto out; - - /* OK, got the identity... see if there's already an account - * of this type with the given identity - */ - if (!goa_utils_check_duplicate (client, - identity, - presentation_identity, - goa_provider_get_provider_type (GOA_PROVIDER (provider)), - (GoaPeekInterfaceFunc) goa_object_peek_oauth_based, - &data.error)) - goto out; - - g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&credentials, "{sv}", "access_token", g_variant_new_string (access_token)); - g_variant_builder_add (&credentials, "{sv}", "access_token_secret", g_variant_new_string (access_token_secret)); - if (access_token_expires_in > 0) - g_variant_builder_add (&credentials, "{sv}", "access_token_expires_at", - g_variant_new_int64 (duration_to_abs_usec (access_token_expires_in))); - if (session_handle != NULL) - g_variant_builder_add (&credentials, "{sv}", "session_handle", g_variant_new_string (session_handle)); - if (session_handle_expires_in > 0) - g_variant_builder_add (&credentials, "{sv}", "session_handle_expires_at", - g_variant_new_int64 (duration_to_abs_usec (session_handle_expires_in))); - if (password != NULL) - g_variant_builder_add (&credentials, "{sv}", "password", g_variant_new_string (password)); - - g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); - goa_oauth_provider_add_account_key_values (provider, &details); - - /* we want the GoaClient to update before this method returns (so it - * can create a proxy for the new object) so run the mainloop while - * waiting for this to complete - */ - goa_manager_call_add_account (goa_client_get_manager (client), - goa_provider_get_provider_type (GOA_PROVIDER (provider)), - identity, - presentation_identity, - g_variant_builder_end (&credentials), - g_variant_builder_end (&details), - NULL, /* GCancellable* */ - (GAsyncReadyCallback) add_account_cb, - &data); - g_main_loop_run (data.loop); - if (data.error != NULL) - goto out; - - ret = GOA_OBJECT (g_dbus_object_manager_get_object (goa_client_get_object_manager (client), - data.account_object_path)); - - out: - /* We might have an object even when data.error is set. - * eg., if we failed to store the credentials in the keyring. - */ - if (data.error != NULL) - g_propagate_error (error, data.error); - else - g_assert (ret != NULL); - - g_free (identity); - g_free (presentation_identity); - g_free (password); - g_free (access_token); - g_free (access_token_secret); - g_free (session_handle); - g_free (data.account_object_path); - g_clear_pointer (&data.loop, (GDestroyNotify) g_main_loop_unref); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -goa_oauth_provider_refresh_account (GoaProvider *_provider, - GoaClient *client, - GoaObject *object, - GtkWindow *parent, - GError **error) -{ - GoaOAuthProvider *provider = GOA_OAUTH_PROVIDER (_provider); - GoaAccount *account; - GtkWidget *dialog; - gchar *access_token = NULL; - gchar *access_token_secret = NULL; - gchar *password = NULL; - gint access_token_expires_in; - gchar *session_handle = NULL; - gint session_handle_expires_in; - gchar *identity = NULL; - const gchar *existing_identity; - const gchar *existing_presentation_identity; - GVariantBuilder builder; - gboolean ret = FALSE; - - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); - g_return_val_if_fail (GOA_IS_CLIENT (client), FALSE); - g_return_val_if_fail (GOA_IS_OBJECT (object), FALSE); - g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - dialog = gtk_dialog_new_with_buttons (NULL, - parent, - GTK_DIALOG_MODAL - | GTK_DIALOG_DESTROY_WITH_PARENT - | GTK_DIALOG_USE_HEADER_BAR, - NULL, - NULL); - gtk_container_set_border_width (GTK_CONTAINER (dialog), 12); - gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); - gtk_widget_show_all (dialog); - - account = goa_object_peek_account (object); - - /* We abuse presentation identity here because for some providers - * identity can be a machine readable ID, which can not be used to - * log in via the provider's web interface. - */ - existing_presentation_identity = goa_account_get_presentation_identity (account); - if (!get_tokens_and_identity (provider, - FALSE, - existing_presentation_identity, - GTK_DIALOG (dialog), - GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), - &access_token, - &access_token_secret, - &access_token_expires_in, - &session_handle, - &session_handle_expires_in, - &identity, - NULL, /* out_presentation_identity */ - &password, - error)) - goto out; - - /* Changes made to the web interface by the providers can break our - * DOM parsing. So we should still query and check the identity - * afterwards. - */ - existing_identity = goa_account_get_identity (account); - if (g_strcmp0 (identity, existing_identity) != 0) - { - g_set_error (error, - GOA_ERROR, - GOA_ERROR_FAILED, - _("Was asked to log in as %s, but logged in as %s"), - existing_identity, - identity); - goto out; - } - - g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&builder, "{sv}", "access_token", g_variant_new_string (access_token)); - g_variant_builder_add (&builder, "{sv}", "access_token_secret", g_variant_new_string (access_token_secret)); - if (access_token_expires_in > 0) - g_variant_builder_add (&builder, "{sv}", "access_token_expires_at", - g_variant_new_int64 (duration_to_abs_usec (access_token_expires_in))); - if (session_handle != NULL) - g_variant_builder_add (&builder, "{sv}", "session_handle", g_variant_new_string (session_handle)); - if (session_handle_expires_in > 0) - g_variant_builder_add (&builder, "{sv}", "session_handle_expires_at", - g_variant_new_int64 (duration_to_abs_usec (session_handle_expires_in))); - if (password != NULL) - g_variant_builder_add (&builder, "{sv}", "password", g_variant_new_string (password)); - /* TODO: run in worker thread */ - if (!goa_utils_store_credentials_for_object_sync (GOA_PROVIDER (provider), - object, - g_variant_builder_end (&builder), - NULL, /* GCancellable */ - error)) - goto out; - - goa_account_call_ensure_credentials (goa_object_peek_account (object), - NULL, /* GCancellable */ - NULL, NULL); /* callback, user_data */ - - ret = TRUE; - - out: - gtk_widget_destroy (dialog); - - g_free (identity); - g_free (access_token); - g_free (access_token_secret); - g_free (password); - g_free (session_handle); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -free_mutex (GMutex *mutex) -{ - g_mutex_clear (mutex); - g_slice_free (GMutex, mutex); -} - -/** - * goa_oauth_provider_get_access_token_sync: - * @provider: A #GoaOAuthProvider. - * @object: A #GoaObject. - * @force_refresh: If set to %TRUE, forces a refresh of the access token, if possible. - * @out_access_token_secret: (out): The secret for the return access token. - * @out_access_token_expires_in: (out): Return location for how many seconds the returned token is valid for (0 if unknown) or %NULL. - * @cancellable: (allow-none): A #GCancellable or %NULL. - * @error: Return location for error or %NULL. - * - * Synchronously gets an access token for @object. The calling thread - * is blocked while the operation is pending. - * - * The resulting token is typically read from the local cache so most - * of the time only a local roundtrip to the storage for the token - * cache (e.g. gnome-keyring-daemon) is - * needed. However, the operation may involve refreshing the token - * with the service provider so a full network round-trip may be - * needed. - * - * Note that multiple calls are serialized to avoid multiple - * outstanding requests to the service provider. - * - * This operation may fail if e.g. unable to refresh the credentials - * or if network connectivity is not available. Note that even if a - * token is returned, the returned token isn't guaranteed to work - - * use goa_provider_ensure_credentials_sync() if you need - * stronger guarantees. - * - * Returns: The access token or %NULL if error is set. The returned - * string must be freed with g_free(). - */ -gchar * -goa_oauth_provider_get_access_token_sync (GoaOAuthProvider *provider, - GoaObject *object, - gboolean force_refresh, - gchar **out_access_token_secret, - gint *out_access_token_expires_in, - GCancellable *cancellable, - GError **error) -{ - GVariant *credentials = NULL; - GVariantIter iter; - const gchar *key; - GVariant *value; - gchar *access_token = NULL; - gchar *access_token_secret = NULL; - gchar *session_handle = NULL; - gchar *access_token_for_refresh = NULL; - gchar *access_token_secret_for_refresh = NULL; - gchar *session_handle_for_refresh = NULL; - gchar *password = NULL; - gint access_token_expires_in = 0; - gint session_handle_expires_in = 0; - gboolean success = FALSE; - GVariantBuilder builder; - gchar *ret = NULL; - GMutex *lock; - - g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); - g_return_val_if_fail (GOA_IS_OBJECT (object), NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* provider_lock is too coarse, use a per-object lock instead */ - G_LOCK (provider_lock); - lock = g_object_get_data (G_OBJECT (object), "-goa-oauth-provider-get-access-token-lock"); - if (lock == NULL) - { - lock = g_slice_new0 (GMutex); - g_mutex_init (lock); - g_object_set_data_full (G_OBJECT (object), - "-goa-oauth-provider-get-access-token-lock", - lock, - (GDestroyNotify) free_mutex); - } - G_UNLOCK (provider_lock); - - g_mutex_lock (lock); - - /* First, get the credentials from the keyring */ - credentials = goa_utils_lookup_credentials_sync (GOA_PROVIDER (provider), - object, - cancellable, - error); - if (credentials == NULL) - { - if (error != NULL) - { - (*error)->domain = GOA_ERROR; - (*error)->code = GOA_ERROR_NOT_AUTHORIZED; - } - goto out; - } - - g_variant_iter_init (&iter, credentials); - while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) - { - if (g_strcmp0 (key, "access_token") == 0) - access_token = g_variant_dup_string (value, NULL); - else if (g_strcmp0 (key, "access_token_secret") == 0) - access_token_secret = g_variant_dup_string (value, NULL); - else if (g_strcmp0 (key, "access_token_expires_at") == 0) - access_token_expires_in = abs_usec_to_duration (g_variant_get_int64 (value)); - else if (g_strcmp0 (key, "session_handle") == 0) - session_handle = g_variant_dup_string (value, NULL); - else if (g_strcmp0 (key, "session_handle_expires_at") == 0) - session_handle_expires_in = abs_usec_to_duration (g_variant_get_int64 (value)); - else if (g_strcmp0 (key, "password") == 0) - password = g_variant_dup_string (value, NULL); - g_variant_unref (value); - } - - if (access_token == NULL || access_token_secret == NULL) - { - g_set_error (error, - GOA_ERROR, - GOA_ERROR_NOT_AUTHORIZED, - _("Credentials do not contain access_token or access_token_secret")); - goto out; - } - - /* if we can't refresh the token, just return it no matter what */ - if (session_handle == NULL) - { - g_debug ("Returning locally cached credentials that cannot be refreshed"); - success = TRUE; - goto out; - } - - /* If access_token is still "fresh enough" (e.g. more than ten - * minutes of life left in it), just return it unless we've been - * asked to forcibly refresh it - */ - if (!force_refresh && access_token_expires_in > 10*60) - { - g_debug ("Returning locally cached credentials (expires in %d seconds)", access_token_expires_in); - success = TRUE; - goto out; - } - - g_debug ("Refreshing locally cached credentials (expires in %d seconds, force_refresh=%d)", access_token_expires_in, force_refresh); - - /* Otherwise, refresh it */ - access_token_for_refresh = access_token; access_token = NULL; - access_token_secret_for_refresh = access_token_secret; access_token_secret = NULL; - session_handle_for_refresh = session_handle; session_handle = NULL; - access_token = get_tokens_sync (provider, - access_token_for_refresh, - access_token_secret_for_refresh, - session_handle_for_refresh, - NULL, /* verifier */ - &access_token_secret, - &access_token_expires_in, - &session_handle, - &session_handle_expires_in, - cancellable, - error); - if (access_token == NULL) - { - if (error != NULL) - { - g_prefix_error (error, _("Failed to refresh access token (%s, %d): "), - g_quark_to_string ((*error)->domain), (*error)->code); - (*error)->code = is_authorization_error (*error) ? GOA_ERROR_NOT_AUTHORIZED : GOA_ERROR_FAILED; - (*error)->domain = GOA_ERROR; - } - goto out; - } - - /* Good. Now update the keyring with the refreshed credentials */ - g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&builder, "{sv}", "access_token", g_variant_new_string (access_token)); - g_variant_builder_add (&builder, "{sv}", "access_token_secret", g_variant_new_string (access_token_secret)); - if (access_token_expires_in > 0) - g_variant_builder_add (&builder, "{sv}", "access_token_expires_at", - g_variant_new_int64 (duration_to_abs_usec (access_token_expires_in))); - if (session_handle != NULL) - g_variant_builder_add (&builder, "{sv}", "session_handle", g_variant_new_string (session_handle)); - if (session_handle_expires_in > 0) - g_variant_builder_add (&builder, "{sv}", "session_handle_expires_at", - g_variant_new_int64 (duration_to_abs_usec (session_handle_expires_in))); - if (password != NULL) - g_variant_builder_add (&builder, "{sv}", "password", g_variant_new_string (password)); - - /* TODO: run in worker thread */ - if (!goa_utils_store_credentials_for_object_sync (GOA_PROVIDER (provider), - object, - g_variant_builder_end (&builder), - cancellable, - error)) - { - if (error != NULL) - { - (*error)->domain = GOA_ERROR; - (*error)->code = GOA_ERROR_NOT_AUTHORIZED; - } - goto out; - } - - success = TRUE; - - out: - if (success) - { - ret = access_token; access_token = NULL; - g_assert (ret != NULL); - if (out_access_token_secret != NULL) - { - *out_access_token_secret = access_token_secret; access_token_secret = NULL; - } - if (out_access_token_expires_in != NULL) - *out_access_token_expires_in = access_token_expires_in; - } - g_free (access_token); - g_free (access_token_secret); - g_free (session_handle); - g_free (access_token_for_refresh); - g_free (access_token_secret_for_refresh); - g_free (session_handle_for_refresh); - g_free (password); - g_clear_pointer (&credentials, (GDestroyNotify) g_variant_unref); - - g_mutex_unlock (lock); - - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean on_handle_get_access_token (GoaOAuthBased *object, - GDBusMethodInvocation *invocation, - gpointer user_data); - -static gboolean -goa_oauth_provider_build_object (GoaProvider *provider, - GoaObjectSkeleton *object, - GKeyFile *key_file, - const gchar *group, - GDBusConnection *connection, - gboolean just_added, - GError **error) -{ - GoaOAuthBased *oauth_based; - gchar *identity; - - identity = NULL; - - oauth_based = goa_object_get_oauth_based (GOA_OBJECT (object)); - if (oauth_based != NULL) - goto out; - - oauth_based = goa_oauth_based_skeleton_new (); - goa_oauth_based_set_consumer_key (oauth_based, - goa_oauth_provider_get_consumer_key (GOA_OAUTH_PROVIDER (provider))); - goa_oauth_based_set_consumer_secret (oauth_based, - goa_oauth_provider_get_consumer_secret (GOA_OAUTH_PROVIDER (provider))); - /* Ensure D-Bus method invocations run in their own thread */ - g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (oauth_based), - G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); - goa_object_skeleton_set_oauth_based (object, oauth_based); - g_signal_connect (oauth_based, - "handle-get-access-token", - G_CALLBACK (on_handle_get_access_token), - NULL); - - out: - g_object_unref (oauth_based); - g_free (identity); - return TRUE; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -goa_oauth_provider_ensure_credentials_sync (GoaProvider *_provider, - GoaObject *object, - gint *out_expires_in, - GCancellable *cancellable, - GError **error) -{ - GoaOAuthProvider *provider = GOA_OAUTH_PROVIDER (_provider); - gboolean ret = FALSE; - gchar *access_token = NULL; - gchar *access_token_secret = NULL; - gint access_token_expires_in; - gchar *identity = NULL; - gboolean force_refresh = FALSE; - - again: - access_token = goa_oauth_provider_get_access_token_sync (provider, - object, - force_refresh, - &access_token_secret, - &access_token_expires_in, - cancellable, - error); - if (access_token == NULL) - goto out; - - identity = goa_oauth_provider_get_identity_sync (provider, - access_token, - access_token_secret, - NULL, /* out_presentation_identity */ - cancellable, - error); - if (identity == NULL) - { - /* OK, try again, with forcing the locally cached credentials to be refreshed */ - if (!force_refresh) - { - force_refresh = TRUE; - g_free (access_token); access_token = NULL; - g_free (access_token_secret); access_token_secret = NULL; - g_clear_error (error); - goto again; - } - else - { - goto out; - } - } - - /* TODO: maybe check with the identity we have */ - ret = TRUE; - if (out_expires_in != NULL) - *out_expires_in = access_token_expires_in; - - out: - g_free (identity); - g_free (access_token); - g_free (access_token_secret); - return ret; -} - - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -goa_oauth_provider_init (GoaOAuthProvider *client) -{ -} - -static void -goa_oauth_provider_class_init (GoaOAuthProviderClass *klass) -{ - GoaProviderClass *provider_class; - - provider_class = GOA_PROVIDER_CLASS (klass); - provider_class->add_account = goa_oauth_provider_add_account; - provider_class->refresh_account = goa_oauth_provider_refresh_account; - provider_class->build_object = goa_oauth_provider_build_object; - provider_class->ensure_credentials_sync = goa_oauth_provider_ensure_credentials_sync; - - klass->build_authorization_uri = goa_oauth_provider_build_authorization_uri_default; - klass->get_use_mobile_browser = goa_oauth_provider_get_use_mobile_browser_default; - klass->is_deny_node = goa_oauth_provider_is_deny_node_default; - klass->is_password_node = goa_oauth_provider_is_password_node_default; - klass->get_request_uri_params = goa_oauth_provider_get_request_uri_params_default; - klass->add_account_key_values = goa_oauth_provider_add_account_key_values_default; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/* runs in a thread dedicated to handling @invocation */ -static gboolean -on_handle_get_access_token (GoaOAuthBased *interface, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - GoaObject *object; - GoaAccount *account; - GoaProvider *provider; - GError *error; - const gchar *id; - const gchar *method_name; - const gchar *provider_type; - gchar *access_token = NULL; - gchar *access_token_secret = NULL; - gint access_token_expires_in; - - /* TODO: maybe log what app is requesting access */ - - object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (interface))); - account = goa_object_peek_account (object); - - id = goa_account_get_id (account); - provider_type = goa_account_get_provider_type (account); - method_name = g_dbus_method_invocation_get_method_name (invocation); - g_debug ("Handling %s for account (%s, %s)", method_name, provider_type, id); - - provider = goa_provider_get_for_provider_type (provider_type); - - error = NULL; - access_token = goa_oauth_provider_get_access_token_sync (GOA_OAUTH_PROVIDER (provider), - object, - FALSE, /* force_refresh */ - &access_token_secret, - &access_token_expires_in, - NULL, /* GCancellable* */ - &error); - if (access_token == NULL) - { - g_dbus_method_invocation_take_error (invocation, error); - } - else - { - goa_oauth_based_complete_get_access_token (interface, - invocation, - access_token, - access_token_secret, - access_token_expires_in); - } - g_free (access_token); - g_free (access_token_secret); - g_object_unref (provider); - return TRUE; /* invocation was handled */ -} diff --git a/src/goabackend/goaoauthprovider.h b/src/goabackend/goaoauthprovider.h deleted file mode 100644 index d4ffa3c..0000000 --- a/src/goabackend/goaoauthprovider.h +++ /dev/null @@ -1,143 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright © 2011 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __GOA_OAUTH_PROVIDER_H__ -#define __GOA_OAUTH_PROVIDER_H__ - -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define GOA_TYPE_OAUTH_PROVIDER (goa_oauth_provider_get_type ()) -G_DECLARE_DERIVABLE_TYPE (GoaOAuthProvider, goa_oauth_provider, GOA, OAUTH_PROVIDER, GoaProvider); - -/** - * GoaOAuthProvider: - * - * The #GoaOAuthProvider structure contains only private data and should - * only be accessed using the provided API. - */ - -/** - * GoaOAuthProviderClass: - * @parent_class: The parent class. - * @get_consumer_key: Virtual function for goa_oauth_provider_get_consumer_key(). - * @get_consumer_secret: Virtual function for goa_oauth_provider_get_consumer_secret(). - * @get_request_uri: Virtual function for goa_oauth_provider_get_request_uri(). - * @get_authorization_uri: Virtual function for goa_oauth_provider_get_authorization_uri(). - * @get_token_uri: Virtual function for goa_oauth_provider_get_token_uri(). - * @get_callback_uri: Virtual function for goa_oauth_provider_get_callback_uri(). - * @get_identity_sync: Virtual function for goa_oauth_provider_get_identity_sync(). - * @parse_request_token_error: Virtual function for goa_oauth_provider_parse_request_token_error(). - * @build_authorization_uri: Virtual function for goa_oauth_provider_build_authorization_uri(). - * @get_use_mobile_browser: Virtual function for goa_oauth_provider_get_use_mobile_browser(). - * @get_request_uri_params: Virtual function for goa_oauth_provider_get_request_uri_params(). - * @add_account_key_values: Virtual function for goa_oauth_provider_add_account_key_values(). - * @is_deny_node: Virtual function for goa_oauth_provider_is_deny_node(). - * @is_identity_node: Virtual function for goa_oauth_provider_is_identity_node(). - * @is_password_node: Virtual function for goa_oauth_provider_is_password_node(). - * - * Class structure for #GoaOAuthProvider. - */ -struct _GoaOAuthProviderClass -{ - GoaProviderClass parent_class; - - /* pure virtual */ - const gchar *(*get_consumer_key) (GoaOAuthProvider *provider); - const gchar *(*get_consumer_secret) (GoaOAuthProvider *provider); - const gchar *(*get_request_uri) (GoaOAuthProvider *provider); - const gchar *(*get_authorization_uri) (GoaOAuthProvider *provider); - const gchar *(*get_token_uri) (GoaOAuthProvider *provider); - const gchar *(*get_callback_uri) (GoaOAuthProvider *provider); - - gchar *(*get_identity_sync) (GoaOAuthProvider *provider, - const gchar *access_token, - const gchar *access_token_secret, - gchar **out_presentation_identity, - GCancellable *cancellable, - GError **error); - - gchar *(*parse_request_token_error) (GoaOAuthProvider *provider, - RestProxyCall *call); - - /* virtual but with default implementation */ - gchar *(*build_authorization_uri) (GoaOAuthProvider *provider, - const gchar *authorization_uri, - const gchar *escaped_oauth_token); - gboolean (*get_use_mobile_browser) (GoaOAuthProvider *provider); - gchar **(*get_request_uri_params) (GoaOAuthProvider *provider); - void (*add_account_key_values) (GoaOAuthProvider *provider, - GVariantBuilder *builder); - - /* pure virtual */ - gboolean (*is_identity_node) (GoaOAuthProvider *provider, - WebKitDOMHTMLInputElement *element); - - /* virtual but with default implementation */ - gboolean (*is_deny_node) (GoaOAuthProvider *provider, - WebKitDOMNode *node); - gboolean (*is_password_node) (GoaOAuthProvider *provider, - WebKitDOMHTMLInputElement *element); -}; - -const gchar *goa_oauth_provider_get_consumer_key (GoaOAuthProvider *provider); -const gchar *goa_oauth_provider_get_consumer_secret (GoaOAuthProvider *provider); -const gchar *goa_oauth_provider_get_request_uri (GoaOAuthProvider *provider); -gchar **goa_oauth_provider_get_request_uri_params (GoaOAuthProvider *provider); -const gchar *goa_oauth_provider_get_authorization_uri (GoaOAuthProvider *provider); -const gchar *goa_oauth_provider_get_token_uri (GoaOAuthProvider *provider); -const gchar *goa_oauth_provider_get_callback_uri (GoaOAuthProvider *provider); -gchar *goa_oauth_provider_get_identity_sync (GoaOAuthProvider *provider, - const gchar *access_token, - const gchar *access_token_secret, - gchar **out_presentation_identity, - GCancellable *cancellable, - GError **error); -gboolean goa_oauth_provider_is_deny_node (GoaOAuthProvider *provider, - WebKitDOMNode *node); -gboolean goa_oauth_provider_is_identity_node (GoaOAuthProvider *provider, - WebKitDOMHTMLInputElement *element); -gboolean goa_oauth_provider_is_password_node (GoaOAuthProvider *provider, - WebKitDOMHTMLInputElement *element); -gchar *goa_oauth_provider_parse_request_token_error (GoaOAuthProvider *provider, - RestProxyCall *call); -gchar *goa_oauth_provider_get_access_token_sync (GoaOAuthProvider *provider, - GoaObject *object, - gboolean force_refresh, - gchar **out_access_token_secret, - gint *out_access_token_expires_in, - GCancellable *cancellable, - GError **error); -gchar *goa_oauth_provider_build_authorization_uri (GoaOAuthProvider *provider, - const gchar *authorization_uri, - const gchar *escaped_oauth_token); -gboolean goa_oauth_provider_get_use_mobile_browser (GoaOAuthProvider *provider); -void goa_oauth_provider_add_account_key_values (GoaOAuthProvider *provider, - GVariantBuilder *builder); - -G_END_DECLS - -#endif /* __GOA_OAUTH_PROVIDER_H__ */ diff --git a/src/goabackend/goapocketprovider.c b/src/goabackend/goapocketprovider.c index 38f9863..0d1a8ce 100644 --- a/src/goabackend/goapocketprovider.c +++ b/src/goabackend/goapocketprovider.c @@ -185,32 +185,6 @@ build_authorization_uri (GoaOAuth2Provider *oauth2_provider, return url; } -static gboolean -decide_navigation_policy (GoaOAuth2Provider *oauth2_provider, - WebKitWebView *web_view, - WebKitNavigationPolicyDecision *decision) -{ - GoaPocketProvider *self = GOA_POCKET_PROVIDER (oauth2_provider); - WebKitNavigationAction *action; - WebKitURIRequest *request; - gboolean ret = FALSE; - const gchar *uri; - - action = webkit_navigation_policy_decision_get_navigation_action (decision); - request = webkit_navigation_action_get_request (action); - uri = webkit_uri_request_get_uri (request); - if (!g_str_has_prefix (uri, "https://getpocket.com/a/")) - goto out; - - webkit_uri_request_set_uri (request, self->authorization_uri); - webkit_web_view_load_request (web_view, request); - - ret = TRUE; - - out: - return ret; -} - static gboolean process_redirect_url (GoaOAuth2Provider *oauth2_provider, const gchar *redirect_url, @@ -279,68 +253,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -is_deny_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMNode *node) -{ - WebKitDOMElement *element; - gboolean ret = FALSE; - gchar *id = NULL; - gchar *class = NULL; - gchar *text = NULL; - - if (!WEBKIT_DOM_IS_ELEMENT (node)) - goto out; - - element = WEBKIT_DOM_ELEMENT (node); - - /* Desktop version */ - id = webkit_dom_element_get_id (element); - if (g_strcmp0 (id, "denyButton") == 0) - { - ret = TRUE; - goto out; - } - - /* Mobile version */ - class = webkit_dom_element_get_class_name (element); - if (g_strcmp0 (class, "toolbarButton") != 0) - goto out; - - /* FIXME: This only seems to work if we don't click on the "Sign Up" - * button, does the check need to be done again? */ - text = webkit_dom_node_get_text_content (node); - if (g_strcmp0 (text, "Cancel") != 0) - goto out; - - ret = TRUE; - - out: - g_free (id); - g_free (class); - g_free (text); - return ret; -} - -static gboolean -is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) -{ - gboolean ret = FALSE; - gchar *name; - - name = webkit_dom_html_input_element_get_name (element); - if (g_strcmp0 (name, "feed_id") != 0) - goto out; - - ret = TRUE; - -out: - g_free (name); - return ret; - -} - -/* ---------------------------------------------------------------------------------------------------- */ - static gboolean build_object (GoaProvider *provider, GoaObjectSkeleton *object, @@ -431,15 +343,12 @@ goa_pocket_provider_class_init (GoaPocketProviderClass *klass) provider_class->build_object = build_object; oauth2_class->build_authorization_uri = build_authorization_uri; - oauth2_class->decide_navigation_policy = decide_navigation_policy; oauth2_class->get_authorization_uri = get_authorization_uri; oauth2_class->get_token_uri = get_token_uri; oauth2_class->get_redirect_uri = get_redirect_uri; oauth2_class->get_client_id = get_client_id; oauth2_class->get_client_secret = get_client_secret; oauth2_class->get_identity_sync = get_identity_sync; - oauth2_class->is_deny_node = is_deny_node; - oauth2_class->is_identity_node = is_identity_node; oauth2_class->add_account_key_values = add_account_key_values; oauth2_class->process_redirect_url = process_redirect_url; } diff --git a/src/goabackend/goaprovider.c b/src/goabackend/goaprovider.c index c8f7f0d..fe8ce22 100644 --- a/src/goabackend/goaprovider.c +++ b/src/goabackend/goaprovider.c @@ -28,7 +28,6 @@ #include "goafacebookprovider.h" #include "goaimapsmtpprovider.h" #include "goaowncloudprovider.h" -#include "goaflickrprovider.h" #include "goafoursquareprovider.h" #include "goawindowsliveprovider.h" #include "goatelepathyfactory.h" @@ -968,9 +967,6 @@ static struct #ifdef GOA_WINDOWS_LIVE_ENABLED { GOA_WINDOWS_LIVE_NAME, goa_windows_live_provider_get_type }, #endif -#ifdef GOA_FLICKR_ENABLED - { GOA_FLICKR_NAME, goa_flickr_provider_get_type }, -#endif #ifdef GOA_POCKET_ENABLED { GOA_POCKET_NAME, goa_pocket_provider_get_type }, #endif diff --git a/src/goabackend/goatodoistprovider.c b/src/goabackend/goatodoistprovider.c index d97c33c..7f17a59 100644 --- a/src/goabackend/goatodoistprovider.c +++ b/src/goabackend/goatodoistprovider.c @@ -133,33 +133,6 @@ build_authorization_uri (GoaOAuth2Provider *oauth2_provider, /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) -{ - gboolean ret = FALSE; - gchar *element_type = NULL; - gchar *id = NULL; - gchar *name = NULL; - - g_object_get (element, "type", &element_type, NULL); - if (g_strcmp0 (element_type, "email") != 0) - goto out; - - id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (element)); - if (g_strcmp0 (id, "email") != 0) - goto out; - - ret = TRUE; - - out: - g_free (element_type); - g_free (id); - g_free (name); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - static gchar * get_identity_sync (GoaOAuth2Provider *oauth2_provider, const gchar *access_token, @@ -337,6 +310,5 @@ goa_todoist_provider_class_init (GoaTodoistProviderClass *klass) oauth2_class->get_client_secret = get_client_secret; oauth2_class->get_scope = get_scope; oauth2_class->get_identity_sync = get_identity_sync; - oauth2_class->is_identity_node = is_identity_node; oauth2_class->add_account_key_values = add_account_key_values; } diff --git a/src/goabackend/goawebextension.c b/src/goabackend/goawebextension.c deleted file mode 100644 index 6a25ab9..0000000 --- a/src/goabackend/goawebextension.c +++ /dev/null @@ -1,273 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright © 2015 Damián Nohales - * Copyright © 2015 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#include "config.h" - -#include - -#include "goaoauthprovider.h" -#include "goaoauth2provider.h" -#include "goaoauth2provider-web-extension.h" -#include "goaprovider.h" -#include "goawebextension.h" - -struct _GoaWebExtension -{ - GObject parent; - GoaProvider *provider; - WebKitWebExtension *wk_extension; - gchar *existing_identity; - gchar *provider_type; -}; - -struct _GoaWebExtensionClass -{ - GObjectClass parent; -}; - -enum -{ - PROP_0, - PROP_EXISTING_IDENTITY, - PROP_PROVIDER_TYPE, - PROP_WK_EXTENSION -}; - -G_DEFINE_TYPE (GoaWebExtension, goa_web_extension, G_TYPE_OBJECT) - -static void -web_extension_dom_node_deny_click_cb (WebKitDOMNode *element, WebKitDOMEvent *event, gpointer user_data) -{ - WebKitDOMDOMWindow *dom_window = WEBKIT_DOM_DOM_WINDOW (user_data); - webkit_dom_dom_window_webkit_message_handlers_post_message (dom_window, "deny-click", ""); -} - -static void -web_extension_dom_node_password_submit_cb (WebKitDOMNode *element, WebKitDOMEvent *event, gpointer user_data) -{ - WebKitDOMDOMWindow *dom_window = WEBKIT_DOM_DOM_WINDOW (user_data); - WebKitDOMHTMLInputElement *password_node; - gchar *password; - - password_node = WEBKIT_DOM_HTML_INPUT_ELEMENT (g_object_get_data (G_OBJECT (dom_window), "goa-password-node")); - password = webkit_dom_html_input_element_get_value (password_node); - webkit_dom_dom_window_webkit_message_handlers_post_message (dom_window, "password-submit", password); - g_free (password); -} - -static void -web_extension_document_loaded_cb (WebKitWebPage *web_page, gpointer user_data) -{ - GoaWebExtension *self = GOA_WEB_EXTENSION (user_data); - WebKitDOMDocument *document; - WebKitDOMDOMWindow *dom_window; - WebKitDOMHTMLCollection *elements = NULL; - gulong element_count; - gulong i; - - document = webkit_web_page_get_dom_document (web_page); - elements = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "*"); - element_count = webkit_dom_html_collection_get_length (elements); - - dom_window = webkit_dom_document_get_default_view (document); - - for (i = 0; i < element_count; i++) - { - WebKitDOMNode *element = webkit_dom_html_collection_item (elements, i); - - if ((GOA_IS_OAUTH_PROVIDER (self->provider) - && goa_oauth_provider_is_deny_node (GOA_OAUTH_PROVIDER (self->provider), element)) - || (GOA_IS_OAUTH2_PROVIDER (self->provider) - && goa_oauth2_provider_is_deny_node (GOA_OAUTH2_PROVIDER (self->provider), element))) - { - webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (element), - "click", - G_CALLBACK (web_extension_dom_node_deny_click_cb), - FALSE, - dom_window); - } - else if (self->existing_identity != NULL - && self->existing_identity[0] != '\0' - && WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element) - && ((GOA_IS_OAUTH_PROVIDER (self->provider) - && goa_oauth_provider_is_identity_node (GOA_OAUTH_PROVIDER (self->provider), - WEBKIT_DOM_HTML_INPUT_ELEMENT (element))) - || (GOA_IS_OAUTH2_PROVIDER (self->provider) - && goa_oauth2_provider_is_identity_node (GOA_OAUTH2_PROVIDER (self->provider), - WEBKIT_DOM_HTML_INPUT_ELEMENT (element))))) - { - webkit_dom_html_input_element_set_value (WEBKIT_DOM_HTML_INPUT_ELEMENT (element), - self->existing_identity); - webkit_dom_html_input_element_set_read_only (WEBKIT_DOM_HTML_INPUT_ELEMENT (element), TRUE); - } - else if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element) - && ((GOA_IS_OAUTH_PROVIDER (self->provider) - && goa_oauth_provider_is_password_node (GOA_OAUTH_PROVIDER (self->provider), - WEBKIT_DOM_HTML_INPUT_ELEMENT (element))) - || (GOA_IS_OAUTH2_PROVIDER (self->provider) - && goa_oauth2_provider_is_password_node (GOA_OAUTH2_PROVIDER (self->provider), - WEBKIT_DOM_HTML_INPUT_ELEMENT (element))))) - { - WebKitDOMHTMLFormElement *form; - - form = webkit_dom_html_input_element_get_form (WEBKIT_DOM_HTML_INPUT_ELEMENT (element)); - if (form != NULL) - { - g_object_set_data_full (G_OBJECT (dom_window), - "goa-password-node", - g_object_ref (element), - g_object_unref); - webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (form), - "submit", - G_CALLBACK (web_extension_dom_node_password_submit_cb), - FALSE, - dom_window); - } - } - } - - g_clear_object (&elements); -} - -static void -web_extension_page_created_cb (GoaWebExtension *self, WebKitWebPage *web_page) -{ - g_signal_connect_object (web_page, "document-loaded", G_CALLBACK (web_extension_document_loaded_cb), self, 0); -} - -static void -goa_web_extension_constructed (GObject *object) -{ - GoaWebExtension *self = GOA_WEB_EXTENSION (object); - - G_OBJECT_CLASS (goa_web_extension_parent_class)->constructed (object); - - self->provider = goa_provider_get_for_provider_type (self->provider_type); - - g_signal_connect_object (self->wk_extension, - "page-created", - G_CALLBACK (web_extension_page_created_cb), - self, - G_CONNECT_SWAPPED); -} - -static void -goa_web_extension_dispose (GObject *object) -{ - GoaWebExtension *self = GOA_WEB_EXTENSION (object); - - g_clear_object (&self->provider); - g_clear_object (&self->wk_extension); - - G_OBJECT_CLASS (goa_web_extension_parent_class)->dispose (object); -} - -static void -goa_web_extension_finalize (GObject *object) -{ - GoaWebExtension *self = GOA_WEB_EXTENSION (object); - - g_free (self->existing_identity); - g_free (self->provider_type); - - G_OBJECT_CLASS (goa_web_extension_parent_class)->finalize (object); -} - -static void -goa_web_extension_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - GoaWebExtension *self = GOA_WEB_EXTENSION (object); - - switch (prop_id) - { - case PROP_EXISTING_IDENTITY: - self->existing_identity = g_value_dup_string (value); - break; - - case PROP_PROVIDER_TYPE: - self->provider_type = g_value_dup_string (value); - break; - - case PROP_WK_EXTENSION: - self->wk_extension = WEBKIT_WEB_EXTENSION (g_value_dup_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -goa_web_extension_class_init (GoaWebExtensionClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = goa_web_extension_constructed; - object_class->dispose = goa_web_extension_dispose; - object_class->finalize = goa_web_extension_finalize; - object_class->set_property = goa_web_extension_set_property; - - g_object_class_install_property (object_class, - PROP_EXISTING_IDENTITY, - g_param_spec_string ("existing-identity", - "A GoaAccount identity", - "The user name with which we want to prefill the form", - NULL, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_PROVIDER_TYPE, - g_param_spec_string ("provider-type", - "A GoaProvider type", - "The provider type that is represented by this view", - NULL, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_WK_EXTENSION, - g_param_spec_object ("wk-extension", - "A WebKitWebExtension", - "The associated WebKitWebExtension", - WEBKIT_TYPE_WEB_EXTENSION, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); -} - -static void -goa_web_extension_init (GoaWebExtension *self) -{ -} - -GoaWebExtension * -goa_web_extension_new (WebKitWebExtension *wk_extension, - const gchar *provider_type, - const gchar *existing_identity) -{ - return g_object_new (GOA_TYPE_WEB_EXTENSION, - "existing-identity", existing_identity, - "provider-type", provider_type, - "wk-extension", wk_extension, - NULL); -} diff --git a/src/goabackend/goawebextension.h b/src/goabackend/goawebextension.h deleted file mode 100644 index 4a8cf8f..0000000 --- a/src/goabackend/goawebextension.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright © 2015 Damián Nohales - * Copyright © 2015 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#ifndef __GOA_WEB_EXTENSION_H__ -#define __GOA_WEB_EXTENSION_H__ - -#include -#include - -G_BEGIN_DECLS - -#define GOA_TYPE_WEB_EXTENSION (goa_web_extension_get_type()) -#define GOA_WEB_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOA_TYPE_WEB_EXTENSION, GoaWebExtension)) -#define GOA_IS_WEB_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOA_TYPE_WEB_EXTENSION)) - -typedef struct _GoaWebExtension GoaWebExtension; -typedef struct _GoaWebExtensionClass GoaWebExtensionClass; - -GType goa_web_extension_get_type (void); -GoaWebExtension *goa_web_extension_new (WebKitWebExtension *wk_extension, - const gchar *provider_type, - const gchar *existing_identity); - -G_END_DECLS - -#endif /* __GOA_WEB_EXTENSION_H__ */ diff --git a/src/goabackend/goawebextensionmain.c b/src/goabackend/goawebextensionmain.c deleted file mode 100644 index 4fc91e5..0000000 --- a/src/goabackend/goawebextensionmain.c +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * Copyright © 2015 Damián Nohales - * Copyright © 2015 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#include "config.h" - -#include -#include - -#include "goawebextension.h" - -static GoaWebExtension *the_extension; - -/* Silence -Wmissing-prototypes */ -void webkit_web_extension_initialize (WebKitWebExtension *wk_extension); -void webkit_web_extension_initialize_with_user_data (WebKitWebExtension *wk_extension, GVariant *user_data); - -G_MODULE_EXPORT void -webkit_web_extension_initialize (WebKitWebExtension *wk_extension) -{ - g_warning ("Error initializing web extension: user data not set"); -} - -G_MODULE_EXPORT void -webkit_web_extension_initialize_with_user_data (WebKitWebExtension *wk_extension, GVariant *user_data) -{ - const gchar *existing_identity; - const gchar *provider_type; - - g_variant_get (user_data, "(&s&s)", &provider_type, &existing_identity); - the_extension = goa_web_extension_new (wk_extension, provider_type, existing_identity); -} - -static void __attribute__((destructor)) -goa_web_extension_shutdown (void) -{ - g_clear_object (&the_extension); -} diff --git a/src/goabackend/goawebview.c b/src/goabackend/goawebview.c deleted file mode 100644 index b7af122..0000000 --- a/src/goabackend/goawebview.c +++ /dev/null @@ -1,501 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright © 2015 Damián Nohales - * Copyright © 2012 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -/* Based on code by the Epiphany team. - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "goawebview.h" -#include "nautilus-floating-bar.h" - -struct _GoaWebView -{ - GtkOverlay parent_instance; - GoaProvider *provider; - GtkWidget *floating_bar; - GtkWidget *progress_bar; - GtkWidget *web_view; - WebKitUserContentManager *user_content_manager; - WebKitWebContext *context; - gchar *existing_identity; - gulong clear_notify_progress_id; - gulong notify_load_status_id; - gulong notify_progress_id; -}; - -struct _GoaWebViewClass -{ - GtkOverlayClass parent_class; -}; - -enum -{ - PROP_0, - PROP_EXISTING_IDENTITY, - PROP_PROVIDER -}; - -enum -{ - DENY_CLICK, - PASSWORD_SUBMIT, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE (GoaWebView, goa_web_view, GTK_TYPE_OVERLAY) - -static gboolean -web_view_clear_notify_progress_cb (gpointer user_data) -{ - GoaWebView *self = GOA_WEB_VIEW (user_data); - - gtk_widget_hide (self->progress_bar); - self->clear_notify_progress_id = 0; - return FALSE; -} - -static char * -web_view_create_loading_title (const gchar *url) -{ - SoupURI *uri; - const gchar *hostname; - gchar *title; - - g_return_val_if_fail (url != NULL && url[0] != '\0', NULL); - - uri = soup_uri_new (url); - hostname = soup_uri_get_host (uri); - /* translators: %s here is the address of the web page */ - title = g_strdup_printf (_("Loading “%s”…"), hostname); - soup_uri_free (uri); - - return title; -} - -static void -web_view_floating_bar_update (GoaWebView *self, const gchar *text) -{ - nautilus_floating_bar_set_label (NAUTILUS_FLOATING_BAR (self->floating_bar), text); - - if (text == NULL || text[0] == '\0') - { - gtk_widget_hide (self->floating_bar); - gtk_widget_set_halign (self->floating_bar, GTK_ALIGN_START); - } - else - gtk_widget_show (self->floating_bar); -} - -static void -web_view_initialize_web_extensions_cb (GoaWebView *self) -{ - GVariant *data; - const gchar *existing_identity; - const gchar *provider_type; - - webkit_web_context_set_web_extensions_directory (self->context, PACKAGE_WEB_EXTENSIONS_DIR); - - if (self->provider == NULL) - return; - - provider_type = goa_provider_get_provider_type (self->provider); - existing_identity = (self->existing_identity == NULL) ? "" : self->existing_identity; - data = g_variant_new ("(ss)", provider_type, existing_identity); - webkit_web_context_set_web_extensions_initialization_user_data (self->context, data); -} - -#ifdef GOA_INSPECTOR_ENABLED -static void -web_view_inspector_closed_cb (WebKitWebInspector *inspector) -{ - GtkWidget *window; - WebKitWebViewBase *inspector_web_view; - - inspector_web_view = webkit_web_inspector_get_web_view (inspector); - window = gtk_widget_get_toplevel (GTK_WIDGET (inspector_web_view)); - if (gtk_widget_is_toplevel (window)) - gtk_widget_destroy (window); -} - -static gboolean -web_view_inspector_open_window_cb (WebKitWebInspector *inspector) -{ - GtkWidget *window; - GtkWindowGroup *group; - WebKitWebViewBase *inspector_web_view; - - group = gtk_window_group_new (); - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_resize (GTK_WINDOW (window), 800, 600); - gtk_window_group_add_window (group, GTK_WINDOW (window)); - g_object_unref (group); - - inspector_web_view = webkit_web_inspector_get_web_view (inspector); - gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (inspector_web_view)); - - gtk_widget_show_all (window); - gtk_window_present (GTK_WINDOW (window)); - - return GDK_EVENT_STOP; -} -#endif /* GOA_INSPECTOR_ENABLED */ - -static void -web_view_load_changed_cb (WebKitWebView *web_view, - WebKitLoadEvent load_event, - gpointer user_data) -{ - GoaWebView *self = GOA_WEB_VIEW (user_data); - - switch (load_event) - { - case WEBKIT_LOAD_STARTED: - case WEBKIT_LOAD_COMMITTED: - { - const gchar *uri; - gchar *title; - - uri = webkit_web_view_get_uri (web_view); - title = web_view_create_loading_title (uri); - - web_view_floating_bar_update (self, title); - g_free (title); - break; - } - - case WEBKIT_LOAD_REDIRECTED: - /* TODO: Update the loading uri */ - break; - - case WEBKIT_LOAD_FINISHED: - web_view_floating_bar_update (self, NULL); - break; - - default: - break; - } -} - -static void -web_view_notify_estimated_load_progress_cb (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - GoaWebView *self = GOA_WEB_VIEW (user_data); - WebKitWebView *web_view = WEBKIT_WEB_VIEW (object); - gboolean loading; - const gchar *uri; - gdouble progress; - - if (self->clear_notify_progress_id != 0) - { - g_source_remove (self->clear_notify_progress_id); - self->clear_notify_progress_id = 0; - } - - uri = webkit_web_view_get_uri (web_view); - if (!uri || g_str_equal (uri, "about:blank")) - return; - - progress = webkit_web_view_get_estimated_load_progress (web_view); - loading = webkit_web_view_is_loading (web_view); - - if (progress == 1.0 || !loading) - self->clear_notify_progress_id = g_timeout_add (500, web_view_clear_notify_progress_cb, self); - else - gtk_widget_show (self->progress_bar); - - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->progress_bar), - (loading || progress == 1.0) ? progress : 0.0); -} - -static void -web_view_script_message_received_deny_click_cb (GoaWebView *self) -{ - g_signal_emit (self, signals[DENY_CLICK], 0); -} - -static void -web_view_script_message_received_password_submit_cb (GoaWebView *self, WebKitJavascriptResult *js_result) -{ - JSGlobalContextRef js_context; - JSStringRef js_string; - JSValueRef js_value; - gsize max_size; - - js_value = webkit_javascript_result_get_value (js_result); - js_context = webkit_javascript_result_get_global_context (js_result); - js_string = JSValueToStringCopy (js_context, js_value, NULL); - max_size = JSStringGetMaximumUTF8CStringSize (js_string); - if (max_size > 0) - { - gchar *password; - - password = g_malloc0 (max_size); - JSStringGetUTF8CString (js_string, password, max_size); - g_signal_emit (self, signals[PASSWORD_SUBMIT], 0, password); - g_free (password); - } - - JSStringRelease (js_string); -} - -static void -goa_web_view_constructed (GObject *object) -{ - GoaWebView *self = GOA_WEB_VIEW (object); - const gchar *const *language_names; - - G_OBJECT_CLASS (goa_web_view_parent_class)->constructed (object); - - self->context = webkit_web_context_new (); - language_names = g_get_language_names (); - webkit_web_context_set_preferred_languages (self->context, language_names); - g_signal_connect_swapped (self->context, - "initialize-web-extensions", - G_CALLBACK (web_view_initialize_web_extensions_cb), - self); - - self->user_content_manager = webkit_user_content_manager_new (); - g_signal_connect_swapped (self->user_content_manager, - "script-message-received::deny-click", - G_CALLBACK (web_view_script_message_received_deny_click_cb), - self); - g_signal_connect_swapped (self->user_content_manager, - "script-message-received::password-submit", - G_CALLBACK (web_view_script_message_received_password_submit_cb), - self); - webkit_user_content_manager_register_script_message_handler (self->user_content_manager, "deny-click"); - webkit_user_content_manager_register_script_message_handler (self->user_content_manager, "password-submit"); - - self->web_view = GTK_WIDGET (g_object_new (WEBKIT_TYPE_WEB_VIEW, - "user-content-manager", self->user_content_manager, - "web-context", self->context, - NULL)); - gtk_widget_set_size_request (self->web_view, 500, 400); - gtk_container_add (GTK_CONTAINER (self), self->web_view); - -#ifdef GOA_INSPECTOR_ENABLED - { - WebKitSettings *settings; - WebKitWebInspector *inspector; - - /* Setup the inspector */ - settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (self->web_view)); - g_object_set (settings, "enable-developer-extras", TRUE, NULL); - - inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (self->web_view)); - g_signal_connect (inspector, "closed", G_CALLBACK (web_view_inspector_closed_cb), NULL); - g_signal_connect (inspector, "open-window", G_CALLBACK (web_view_inspector_open_window_cb), NULL); - } -#endif /* GOA_INSPECTOR_ENABLED */ - - /* statusbar is hidden by default */ - self->floating_bar = nautilus_floating_bar_new (NULL, FALSE); - gtk_widget_set_halign (self->floating_bar, GTK_ALIGN_START); - gtk_widget_set_valign (self->floating_bar, GTK_ALIGN_END); - gtk_widget_set_no_show_all (self->floating_bar, TRUE); - gtk_overlay_add_overlay (GTK_OVERLAY (self), self->floating_bar); - - self->progress_bar = gtk_progress_bar_new (); - gtk_style_context_add_class (gtk_widget_get_style_context (self->progress_bar), - GTK_STYLE_CLASS_OSD); - gtk_widget_set_halign (self->progress_bar, GTK_ALIGN_FILL); - gtk_widget_set_valign (self->progress_bar, GTK_ALIGN_START); - gtk_overlay_add_overlay (GTK_OVERLAY (self), self->progress_bar); - - self->notify_progress_id = g_signal_connect (self->web_view, - "notify::estimated-load-progress", - G_CALLBACK (web_view_notify_estimated_load_progress_cb), - self); - self->notify_load_status_id = g_signal_connect (self->web_view, - "load_changed", - G_CALLBACK (web_view_load_changed_cb), - self); -} - -static void -goa_web_view_dispose (GObject *object) -{ - GoaWebView *self = GOA_WEB_VIEW (object); - - g_clear_object (&self->user_content_manager); - g_clear_object (&self->context); - - if (self->clear_notify_progress_id != 0) - { - g_source_remove (self->clear_notify_progress_id); - self->clear_notify_progress_id = 0; - } - - if (self->notify_load_status_id != 0) - { - g_signal_handler_disconnect (self->web_view, self->notify_load_status_id); - self->notify_load_status_id = 0; - } - - if (self->notify_progress_id != 0) - { - g_signal_handler_disconnect (self->web_view, self->notify_progress_id); - self->notify_progress_id = 0; - } - - G_OBJECT_CLASS (goa_web_view_parent_class)->dispose (object); -} - -static void -goa_web_view_finalize (GObject *object) -{ - GoaWebView *self = GOA_WEB_VIEW (object); - - g_free (self->existing_identity); - - if (self->provider != NULL) - g_object_remove_weak_pointer (G_OBJECT (self->provider), (gpointer *) &self->provider); - - G_OBJECT_CLASS (goa_web_view_parent_class)->finalize (object); -} - -static void -goa_web_view_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - GoaWebView *self = GOA_WEB_VIEW (object); - - switch (prop_id) - { - case PROP_EXISTING_IDENTITY: - self->existing_identity = g_value_dup_string (value); - break; - - case PROP_PROVIDER: - self->provider = GOA_PROVIDER (g_value_get_object (value)); - if (self->provider != NULL) - g_object_add_weak_pointer (G_OBJECT (self->provider), (gpointer *) &self->provider); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -goa_web_view_init (GoaWebView *self) -{ -} - -static void -goa_web_view_class_init (GoaWebViewClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = goa_web_view_constructed; - object_class->dispose = goa_web_view_dispose; - object_class->finalize = goa_web_view_finalize; - object_class->set_property = goa_web_view_set_property; - - g_object_class_install_property (object_class, - PROP_EXISTING_IDENTITY, - g_param_spec_string ("existing-identity", - "A GoaAccount identity", - "The user name with which we want to prefill the form", - NULL, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_PROVIDER, - g_param_spec_object ("provider", - "A GoaProvider", - "The provider that is represented by this view", - GOA_TYPE_PROVIDER, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - signals[DENY_CLICK] = g_signal_new ("deny-click", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - signals[PASSWORD_SUBMIT] = g_signal_new ("password-submit", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); -} - -GtkWidget * -goa_web_view_new (GoaProvider *provider, const gchar *existing_identity) -{ - return g_object_new (GOA_TYPE_WEB_VIEW, "provider", provider, "existing-identity", existing_identity, NULL); -} - -GtkWidget * -goa_web_view_get_view (GoaWebView *self) -{ - return self->web_view; -} - -void -goa_web_view_fake_mobile (GoaWebView *self) -{ - WebKitSettings *settings; - - settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (self->web_view)); - - /* This is based on the HTC Wildfire's user agent. Some - * providers, like Google, refuse to provide the mobile - * version of their authentication pages otherwise. eg., - * in Google's case, passing btmpl=mobile does not help. - * - * The actual user agent used by a HTC Wildfire is: - * Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; HTC Wildfire - * Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 - * Mobile Safari/533.1 - * - * Also note that the user agents of some mobile browsers may - * not work. eg., Nokia N9. - */ - webkit_settings_set_user_agent (settings, - "Mozilla/5.0 (GNOME; not Android) " - "AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile"); -} diff --git a/src/goabackend/goawebview.h b/src/goabackend/goawebview.h deleted file mode 100644 index 4b415cd..0000000 --- a/src/goabackend/goawebview.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright © 2012 – 2017 Red Hat, Inc. - * - * 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; either - * version 2 of the License, or (at your option) any later version. - * - * 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 . - */ - -#ifndef __GOA_WEB_VIEW_H__ -#define __GOA_WEB_VIEW_H__ - -#include - -#include "goaprovider.h" - -G_BEGIN_DECLS - -#define GOA_TYPE_WEB_VIEW (goa_web_view_get_type ()) -#define GOA_WEB_VIEW(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GOA_TYPE_WEB_VIEW, GoaWebView)) -#define GOA_IS_WEB_VIEW(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GOA_TYPE_WEB_VIEW)) - -typedef struct _GoaWebView GoaWebView; -typedef struct _GoaWebViewClass GoaWebViewClass; - -GType goa_web_view_get_type (void) G_GNUC_CONST; -GtkWidget *goa_web_view_new (GoaProvider *provider, - const gchar *existing_identity); -GtkWidget *goa_web_view_get_view (GoaWebView *self); -void goa_web_view_fake_mobile (GoaWebView *self); - -G_END_DECLS - -#endif /* __GOA_WEB_VIEW_H__ */ diff --git a/src/goabackend/goawindowsliveprovider.c b/src/goabackend/goawindowsliveprovider.c index 10c2dcf..2135fbd 100644 --- a/src/goabackend/goawindowsliveprovider.c +++ b/src/goabackend/goawindowsliveprovider.c @@ -94,7 +94,8 @@ get_token_uri (GoaOAuth2Provider *oauth2_provider) static const gchar * get_redirect_uri (GoaOAuth2Provider *oauth2_provider) { - return "https://login.live.com/oauth20_desktop.srf"; + /* See: https://learn.microsoft.com/en-us/entra/identity-platform/reply-url */ + return "goa-oauth2://localhost/"GOA_WINDOWS_LIVE_CLIENT_ID; } static const gchar * @@ -234,36 +235,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) -{ - gboolean ret = FALSE; - gchar *element_type = NULL; - gchar *name = NULL; - - /* FIXME: This does not show up in - * webkit_dom_document_get_elements_by_tag_name, but can be - * seen in the inspector. Needs further investigation. - */ - - g_object_get (element, "type", &element_type, NULL); - if (g_strcmp0 (element_type, "email") != 0) - goto out; - - name = webkit_dom_html_input_element_get_name (element); - if (g_strcmp0 (name, "login") != 0) - goto out; - - ret = TRUE; - - out: - g_free (element_type); - g_free (name); - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - static gboolean build_object (GoaProvider *provider, GoaObjectSkeleton *object, @@ -390,6 +361,5 @@ goa_windows_live_provider_class_init (GoaWindowsLiveProviderClass *klass) oauth2_class->get_client_id = get_client_id; oauth2_class->get_client_secret = get_client_secret; oauth2_class->get_identity_sync = get_identity_sync; - oauth2_class->is_identity_node = is_identity_node; oauth2_class->add_account_key_values = add_account_key_values; }