diff options
| author | Jörg Frings-Fürst <debian@jff.email> | 2024-03-24 09:24:40 +0100 | 
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff.email> | 2024-03-24 09:24:40 +0100 | 
| commit | 734fc45fc296a4b6cfa303329023c24e026f35df (patch) | |
| tree | 30038fff46b2fdf3fde9f20a538993cf2ed23fc8 /plugins | |
| parent | 14bc7db2e07c5d1ccfb4d723c9dba395e6c93171 (diff) | |
| parent | dde66becd94817998e320c7ace72729af7455345 (diff) | |
Merge branch 'release/debian/0.36.6-1'debian/0.36.6-1
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala | 111 | ||||
| -rw-r--r-- | plugins/authenticator/shotwell/GoogleAuthenticator.vala | 122 | ||||
| -rw-r--r-- | plugins/authenticator/shotwell/meson.build | 2 | ||||
| -rw-r--r-- | plugins/common/WebAuthenticationPane.vala | 129 | ||||
| -rw-r--r-- | plugins/meson.build | 2 | ||||
| -rw-r--r-- | plugins/shotwell-publishing/meson.build | 2 | ||||
| -rw-r--r-- | plugins/shotwell-transitions/meson.build | 2 | 
7 files changed, 96 insertions, 274 deletions
| diff --git a/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala b/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala index 23de183..e381ae9 100644 --- a/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala +++ b/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala @@ -18,86 +18,26 @@ namespace Publishing.Authenticator.Shotwell.Flickr {      internal const string SERVICE_DISCLAIMER = "<b>This product uses the Flickr API but is not endorsed or certified by SmugMug, Inc.</b>";      internal class AuthenticationRequestTransaction : Publishing.RESTSupport.OAuth1.Transaction { -        public AuthenticationRequestTransaction(Publishing.RESTSupport.OAuth1.Session session) { +        public AuthenticationRequestTransaction(Publishing.RESTSupport.OAuth1.Session session, string cookie) {              base.with_uri(session, "https://www.flickr.com/services/oauth/request_token",                      Publishing.RESTSupport.HttpMethod.GET); -            add_argument("oauth_callback", "shotwell-auth://local-callback"); +            add_argument("oauth_callback", "shotwell-oauth2://localhost?sw_auth_cookie=%s".printf(cookie));          }      }      internal class AccessTokenFetchTransaction : Publishing.RESTSupport.OAuth1.Transaction { -        public AccessTokenFetchTransaction(Publishing.RESTSupport.OAuth1.Session session, string user_verifier) { +        public AccessTokenFetchTransaction(Publishing.RESTSupport.OAuth1.Session session, string user_verifier, string cookie) {              base.with_uri(session, "https://www.flickr.com/services/oauth/access_token",                      Publishing.RESTSupport.HttpMethod.GET);              add_argument("oauth_verifier", user_verifier);              add_argument("oauth_token", session.get_request_phase_token()); -            add_argument("oauth_callback", "shotwell-auth://local-callback"); -        } -    } - -    internal class WebAuthenticationPane : Common.WebAuthenticationPane { -        private string? auth_code = null; -        private const string LOGIN_URI = "https://www.flickr.com/services/oauth/authorize?oauth_token=%s&perms=write"; - -        public signal void authorized(string auth_code); -        public signal void error(); - -        public WebAuthenticationPane(string token) { -            Object(login_uri : LOGIN_URI.printf(token)); -        } - -        public override void constructed() { -            base.constructed(); - -            var ctx = WebKit.WebContext.get_default(); -            ctx.register_uri_scheme("shotwell-auth", this.on_shotwell_auth_request_cb); - -            var mgr = ctx.get_security_manager(); -            mgr.register_uri_scheme_as_secure("shotwell-auth"); -            mgr.register_uri_scheme_as_cors_enabled("shotwell-auth"); -        } - -        public override void on_page_load() { -            if (this.load_error != null) { -                this.error(); - -                return; -            } - -            try { -                var uri = GLib.Uri.parse(get_view().get_uri(), GLib.UriFlags.NONE); -                if (uri.get_scheme() == "shotwell-auth" && this.auth_code == null) { -                    var form_data = Soup.Form.decode (uri.get_query()); -                    this.auth_code = form_data.lookup("oauth_verifier"); -                } -            } catch (Error err) { -                this.error(); - -                return; -            } - -            if (this.auth_code != null) { -                this.authorized(this.auth_code); -            } -        } - -        private void on_shotwell_auth_request_cb(WebKit.URISchemeRequest request) { -            try { -                var uri = GLib.Uri.parse(request.get_uri(), GLib.UriFlags.NONE); -                var form_data = Soup.Form.decode (uri.get_query()); -                this.auth_code = form_data.lookup("oauth_verifier"); -            } catch (Error err) { -                debug ("Failed to parse URI %s: %s", request.get_uri(), err.message); -            } - -            var response = ""; -            var mins = new MemoryInputStream.from_data(response.data); -            request.finish(mins, -1, "text/plain"); +            add_argument("oauth_callback", "shotwell-oauth2://localhost?sw_auth_cookie=%s".printf(cookie));          }      }      internal class Flickr : Publishing.Authenticator.Shotwell.OAuth1.Authenticator { -        private WebAuthenticationPane pane; +        private Common.ExternalWebPane pane; +        private string auth_cookie = Uuid.string_random();          public Flickr(Spit.Publishing.PluginHost host) {              base("Flickr", API_KEY, API_SECRET, host); @@ -147,7 +87,7 @@ namespace Publishing.Authenticator.Shotwell.Flickr {              host.set_service_locked(true);              host.install_static_message_pane(_("Preparing for login…")); -            AuthenticationRequestTransaction txn = new AuthenticationRequestTransaction(session); +            AuthenticationRequestTransaction txn = new AuthenticationRequestTransaction(session, auth_cookie);              try {                  yield txn.execute_async();                  debug("EVENT: OAuth authentication request transaction completed; response = '%s'", @@ -185,22 +125,33 @@ namespace Publishing.Authenticator.Shotwell.Flickr {              session.set_request_phase_credentials(token, token_secret); -            do_web_authentication(token); +            do_web_authentication.begin(token);          } -        private void do_web_authentication(string token) { -            pane = new WebAuthenticationPane(token); -            host.install_dialog_pane(pane); -            pane.authorized.connect((pin) => { this.do_verify_pin.begin(pin); }); -            pane.error.connect(this.on_web_login_error); -        } +        private class AuthCallback : Spit.Publishing.AuthenticatedCallback, Object { +            public signal void auth(GLib.HashTable<string, string> params); -        private void on_web_login_error() { -            if (pane.load_error != null) { -                host.post_error(pane.load_error); -                return; +            public void authenticated(GLib.HashTable<string, string> params) { +                auth(params);              } -            host.post_error(new Spit.Publishing.PublishingError.PROTOCOL_ERROR(_("Flickr authorization failed"))); +        } + +        private async void do_web_authentication(string token) { +            var uri = "https://www.flickr.com/services/oauth/authorize?oauth_token=%s&perms=write".printf(token); +            pane = new Common.ExternalWebPane(uri); +            host.install_dialog_pane(pane); +            var auth_callback = new AuthCallback(); +            string? web_auth_code = null; +            auth_callback.auth.connect((prm) => { +                if ("oauth_verifier" in prm) { +                    web_auth_code = prm["oauth_verifier"]; +                } +                do_web_authentication.callback(); +            }); +            host.register_auth_callback(auth_cookie, auth_callback); +            yield; +            host.unregister_auth_callback(auth_cookie); +            yield do_verify_pin(web_auth_code);          }          private async void do_verify_pin(string pin) { @@ -209,7 +160,7 @@ namespace Publishing.Authenticator.Shotwell.Flickr {              host.set_service_locked(true);              host.install_static_message_pane(_("Verifying authorization…")); -            AccessTokenFetchTransaction txn = new AccessTokenFetchTransaction(session, pin); +            AccessTokenFetchTransaction txn = new AccessTokenFetchTransaction(session, pin, auth_cookie);              try {                  yield txn.execute_async(); diff --git a/plugins/authenticator/shotwell/GoogleAuthenticator.vala b/plugins/authenticator/shotwell/GoogleAuthenticator.vala index 9fc5b27..5a0d934 100644 --- a/plugins/authenticator/shotwell/GoogleAuthenticator.vala +++ b/plugins/authenticator/shotwell/GoogleAuthenticator.vala @@ -5,73 +5,11 @@ namespace Publishing.Authenticator.Shotwell.Google {      private const string OAUTH_CLIENT_ID = "534227538559-hvj2e8bj0vfv2f49r7gvjoq6jibfav67.apps.googleusercontent.com";      private const string REVERSE_CLIENT_ID = "com.googleusercontent.apps.534227538559-hvj2e8bj0vfv2f49r7gvjoq6jibfav67";      private const string OAUTH_CLIENT_SECRET = "pwpzZ7W1TCcD5uIfYCu8sM7x"; -    private const string OAUTH_CALLBACK_URI = REVERSE_CLIENT_ID + ":/auth-callback"; +    private const string OAUTH_CALLBACK_URI = REVERSE_CLIENT_ID + ":/localhost";      private const string SCHEMA_KEY_PROFILE_ID = "shotwell-profile-id";      private const string SCHEMA_KEY_ACCOUNTNAME = "accountname"; -    private class WebAuthenticationPane : Common.WebAuthenticationPane { -        public static bool cache_dirty = false; -        private string? auth_code = null; - -        public signal void error(); - -        public override void constructed() { -            base.constructed(); - -            var ctx = WebKit.WebContext.get_default(); -            ctx.register_uri_scheme(REVERSE_CLIENT_ID, this.on_shotwell_auth_request_cb); -        } - -        public override void on_page_load() { -            if (this.load_error != null) { -                this.error (); - -                return; -            } - -            try { -                var uri = GLib.Uri.parse(get_view().get_uri(), UriFlags.NONE); -                if (uri.get_scheme() == REVERSE_CLIENT_ID && this.auth_code == null) { -                    var form_data = Soup.Form.decode (uri.get_query()); -                    this.auth_code = form_data.lookup("code"); -                } -            } catch (Error err) { -                debug ("Failed to parse auth code from URI %s: %s", get_view().get_uri(), -                    err.message); -            } - -            if (this.auth_code != null) { -                this.authorized(this.auth_code); -            } -        } - -        private void on_shotwell_auth_request_cb(WebKit.URISchemeRequest request) { -            try { -                var uri = GLib.Uri.parse(request.get_uri(), GLib.UriFlags.NONE); -                debug("URI: %s", request.get_uri()); -                var form_data = Soup.Form.decode (uri.get_query()); -                this.auth_code = form_data.lookup("code"); -            } catch (Error err) { -                debug("Failed to parse request URI: %s", err.message); -            } - -            var response = ""; -            var mins = new MemoryInputStream.from_data(response.data); -            request.finish(mins, -1, "text/plain"); -        } - -        public signal void authorized(string auth_code); - -        public WebAuthenticationPane(string auth_sequence_start_url) { -            Object (login_uri : auth_sequence_start_url); -        } - -        public static bool is_cache_dirty() { -            return cache_dirty; -        } -    } -      private class Session : Publishing.RESTSupport.Session {          public string access_token = null;          public string refresh_token = null; @@ -132,7 +70,6 @@ namespace Publishing.Authenticator.Shotwell.Google {          private string accountname = "default";          private Spit.Publishing.PluginHost host = null;          private GLib.HashTable<string, Variant> params = null; -        private WebAuthenticationPane web_auth_pane = null;          private Session session = null;          private string welcome_message = null;          private Secret.Schema? schema = null; @@ -166,14 +103,7 @@ namespace Publishing.Authenticator.Shotwell.Google {                  return;              } -            // FIXME: Find a way for a proper logout -            if (WebAuthenticationPane.is_cache_dirty()) { -                host.set_service_locked(false); - -                host.install_static_message_pane(_("You have already logged in and out of a Google service during this Shotwell session.\n\nTo continue publishing to Google services, quit and restart Shotwell, then try publishing again.")); -            } else { -                this.do_show_service_welcome_pane(); -            } +            this.do_show_service_welcome_pane();          }          public bool can_logout() { @@ -202,8 +132,15 @@ namespace Publishing.Authenticator.Shotwell.Google {          public void set_accountname(string accountname) {              this.accountname = accountname;          } +        private class AuthCallback : Spit.Publishing.AuthenticatedCallback, Object { +            public signal void auth(GLib.HashTable<string, string> params); + +            public void authenticated(GLib.HashTable<string, string> params) { +                auth(params); +            } +        } -        private void do_hosted_web_authentication() { +        private async void do_hosted_web_authentication() {              debug("ACTION: running OAuth authentication flow in hosted web pane.");              string user_authorization_url = "https://accounts.google.com/o/oauth2/auth?" + @@ -216,23 +153,26 @@ namespace Publishing.Authenticator.Shotwell.Google {                  "access_type=offline&" +                  "approval_prompt=force"; -            web_auth_pane = new WebAuthenticationPane(user_authorization_url); -            web_auth_pane.authorized.connect(on_web_auth_pane_authorized); -            web_auth_pane.error.connect(on_web_auth_pane_error); - -            host.install_dialog_pane(web_auth_pane); -        } - -        private void on_web_auth_pane_authorized(string auth_code) { -            web_auth_pane.authorized.disconnect(on_web_auth_pane_authorized); - -            debug("EVENT: user authorized scope %s with auth_code %s", scope, auth_code); - -            do_get_access_tokens.begin(auth_code); -        } +            var auth_callback = new AuthCallback(); +            string? web_auth_code = null; +            auth_callback.auth.connect((prm) => { +                if ("code" in prm) { +                    web_auth_code = prm["code"]; +                } +                do_hosted_web_authentication.callback(); +            }); +            host.register_auth_callback(REVERSE_CLIENT_ID, auth_callback); +            try { +                AppInfo.launch_default_for_uri(user_authorization_url, null); +                host.install_login_wait_pane(); +                yield; -        private void on_web_auth_pane_error() { -            host.post_error(web_auth_pane.load_error); +                yield do_get_access_tokens(web_auth_code); +            } catch (Error err) { +                host.post_error(err); +            } finally { +                host.unregister_auth_callback(REVERSE_CLIENT_ID); +            }          }          private async void do_get_access_tokens(string auth_code) { @@ -384,7 +324,6 @@ namespace Publishing.Authenticator.Shotwell.Google {              }              this.authenticated(); -            web_auth_pane.clear();          }          private async void do_exchange_refresh_token_for_access_token() { @@ -421,7 +360,6 @@ namespace Publishing.Authenticator.Shotwell.Google {                      Idle.add (() => { this.authenticate(); return false; });                  } -                web_auth_pane.clear();                  host.post_error(err);              }          } @@ -435,7 +373,7 @@ namespace Publishing.Authenticator.Shotwell.Google {          private void on_service_welcome_login() {              debug("EVENT: user clicked 'Login' in welcome pane."); -            this.do_hosted_web_authentication(); +            this.do_hosted_web_authentication.begin();          }      }  } diff --git a/plugins/authenticator/shotwell/meson.build b/plugins/authenticator/shotwell/meson.build index 037ec3b..a6475e0 100644 --- a/plugins/authenticator/shotwell/meson.build +++ b/plugins/authenticator/shotwell/meson.build @@ -11,7 +11,7 @@ authenticator_shotwell_resources = gnome.compile_resources('authenticator-resour          source_dir : meson.project_source_root())  authenticator_shotwell_deps = [gee, gtk, gio, soup, json_glib, sw_plugin, -                               sw_plugin_common_dep, json_glib, xml, webkit, secret] +                               sw_plugin_common_dep, json_glib, xml, secret]  authenticator = library('shotwell-authenticator',                          authenticator_shotwell_sources + authenticator_shotwell_resources, diff --git a/plugins/common/WebAuthenticationPane.vala b/plugins/common/WebAuthenticationPane.vala index b9f7280..f745252 100644 --- a/plugins/common/WebAuthenticationPane.vala +++ b/plugins/common/WebAuthenticationPane.vala @@ -6,109 +6,44 @@  using Spit.Publishing;  namespace Shotwell.Plugins.Common { -    public abstract class WebAuthenticationPane : Spit.Publishing.DialogPane, Object { +    public class ExternalWebPane : Spit.Publishing.DialogPane, Object {          public DialogPane.GeometryOptions preferred_geometry {              get; construct; default = DialogPane.GeometryOptions.COLOSSAL_SIZE;          } -          public string login_uri { owned get; construct; } -        public Error load_error { get; private set; default = null; } - -        private WebKit.WebView webview; -        private Gtk.Widget widget; -        private Gtk.Entry entry; +        public Gtk.Widget widget; -        public void clear() { -            debug("Clearing the data of WebKit..."); -            this.webview.get_website_data_manager().clear.begin(WebKit.WebsiteDataTypes.ALL, (GLib.TimeSpan)0); +        public ExternalWebPane(string uri) { +            Object(login_uri: uri);          } +        public signal void browser_toggled(); +          public override void constructed () {              base.constructed (); -            var ctx = WebKit.WebContext.get_default(); -            if (!ctx.get_sandbox_enabled()) { -                ctx.set_sandbox_enabled(true); -            } - -            var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 4); -            this.widget = box; -            this.entry = new Gtk.Entry(); -            this.entry.editable = false; -            this.entry.get_style_context().add_class("flat"); -            this.entry.get_style_context().add_class("read-only"); -            box.pack_start (entry, false, false, 6); - -            this.webview = new WebKit.WebView (); - -            this.webview.load_changed.connect (this.on_page_load_changed); -            this.webview.load_failed.connect (this.on_page_load_failed); -            this.webview.context_menu.connect ( () => { return false; }); -            this.webview.decide_policy.connect (this.on_decide_policy); -            this.webview.bind_property("uri", this.entry, "text", GLib.BindingFlags.DEFAULT); -            box.pack_end (this.webview); -        } - -        private bool on_decide_policy(WebKit.PolicyDecision decision, WebKit.PolicyDecisionType type) { -            switch (type) { -                case WebKit.PolicyDecisionType.NEW_WINDOW_ACTION: { -                    var navigation = (WebKit.NavigationPolicyDecision) decision; -                    var action = navigation.get_navigation_action(); -                    var uri = action.get_request().uri; -                    decision.ignore(); -                    AppInfo.launch_default_for_uri_async.begin(uri, null); -                    return true; -                } -                default: -                    break; -            } - -            return false; -        } - -        public abstract void on_page_load (); - -        protected void set_cursor (Gdk.CursorType type) { -            var window = webview.get_window (); -            if (window == null) -                return; - -            var display = window.get_display (); -            if (display == null) -                return; - -            var cursor = new Gdk.Cursor.for_display (display, type); -            window.set_cursor (cursor); -        } - -        private bool on_page_load_failed (WebKit.LoadEvent load_event, string uri, Error error) { -            // OAuth call-back scheme. Produces a load error because it is not HTTP(S) -            // Do not set the load_error, but continue the error handling -            if (uri.has_prefix ("shotwell-auth://")) -                return false; - -            critical ("Failed to load uri %s: %s", uri, error.message); -            this.load_error = error; - -            return false; -        } - -        private void on_page_load_changed (WebKit.LoadEvent load_event) { -            switch (load_event) { -                case WebKit.LoadEvent.STARTED: -                case WebKit.LoadEvent.REDIRECTED: -                    this.set_cursor (Gdk.CursorType.WATCH); -                    break; -                case WebKit.LoadEvent.FINISHED: -                    this.set_cursor (Gdk.CursorType.LEFT_PTR); -                    this.on_page_load (); -                    break; -                default: -                    break; -            } -        } - -        public WebKit.WebView get_view () { -            return this.webview; +            var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 18); +            box.set_halign(Gtk.Align.CENTER); +            box.hexpand = true; +            box.set_valign(Gtk.Align.CENTER); +            box.vexpand = true; +            var image = new Gtk.Image.from_icon_name ("web-browser-symbolic", Gtk.IconSize.DIALOG); +            image.get_style_context().add_class("dim-label"); +            image.set_pixel_size(128); +            box.add(image); + +            var label = new Gtk.Label(_("Sign in with your browser to setup an account")); +            label.get_style_context().add_class("heading"); +            box.add(label); +            var button = new Gtk.Button.with_label (_("Continue")); +            button.set_halign(Gtk.Align.CENTER); +            button.get_style_context().add_class ("suggested-action"); +            button.clicked.connect(() => { +                AppInfo.launch_default_for_uri_async.begin(login_uri, null); +                browser_toggled(); +            }); +            box.pack_end(button); + +            widget = box;          }          public DialogPane.GeometryOptions get_preferred_geometry() { @@ -120,11 +55,9 @@ namespace Shotwell.Plugins.Common {          }          public void on_pane_installed () { -            this.get_view ().load_uri (this.login_uri);          }          public void on_pane_uninstalled() { -            this.clear(); -        } -   } +        }         +    }  } diff --git a/plugins/meson.build b/plugins/meson.build index e9c0e49..bd4ac9e 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -7,7 +7,7 @@ sw_plugin_common = library('shotwell-plugin-common',                             'common/BuilderPane.vala',                             'common/OAuth1Support.vala'],                            version: meson.project_version(), -                          dependencies : [gtk, gee, webkit, soup, xml, sw_plugin], +                          dependencies : [gtk, gee, soup, xml, sw_plugin],                            vala_header : 'shotwell-plugin-common.h',                            vala_vapi : 'shotwell-plugin-common.vapi',                            include_directories : config_incdir, diff --git a/plugins/shotwell-publishing/meson.build b/plugins/shotwell-publishing/meson.build index a93726b..962195c 100644 --- a/plugins/shotwell-publishing/meson.build +++ b/plugins/shotwell-publishing/meson.build @@ -18,7 +18,7 @@ shotwell_publishing_resources = gnome.compile_resources('publishing-resource',  shared_module('shotwell-publishing',                shotwell_publishing_sources + shotwell_publishing_resources,                dependencies : [gtk, soup, gexiv2, gee, sw_plugin, json_glib, -                              webkit, sw_plugin_common_dep, xml, gcr, +                              sw_plugin_common_dep, xml, gcr,                                gcr_ui, authenticator_dep, secret],                c_args : ['-DPLUGIN_RESOURCE_PATH="/org/gnome/Shotwell/Publishing"',                          '-DGCR_API_SUBJECT_TO_CHANGE'], diff --git a/plugins/shotwell-transitions/meson.build b/plugins/shotwell-transitions/meson.build index 8134292..f809ade 100644 --- a/plugins/shotwell-transitions/meson.build +++ b/plugins/shotwell-transitions/meson.build @@ -20,7 +20,7 @@ libm = cc.find_library('m', required : false)  shared_module('shotwell-transitions',                shotwell_transitions_sources + shotwell_transitions_resources,                dependencies : [gio, gdk_pixbuf, cairo, gtk, gdk, xml, sw_plugin, -                              sw_plugin_common_dep, gee, soup, webkit, libm], +                              sw_plugin_common_dep, gee, soup, libm],                vala_args : [                    '--gresources', 'org.gnome.Shotwell.Transitions.gresource.xml',                    ], | 
