[shotwell/wip/phako/libsecret] wip: Store online service secrets with libsecret



commit 7fa25acba80c00baf9563ef31ab5fd8dfaea951d
Author: Jens Georg <mail jensge org>
Date:   Sat Jun 9 02:13:15 2018 +0200

    wip: Store online service secrets with libsecret

 meson.build                                        |  1 +
 .../shotwell/FacebookPublishingAuthenticator.vala  | 24 ++++++++--
 .../shotwell/GoogleAuthenticator.vala              | 34 +++++++++++--
 plugins/authenticator/shotwell/meson.build         |  2 +-
 plugins/shotwell-publishing/PiwigoPublishing.vala  | 56 +++++++++++++++++-----
 plugins/shotwell-publishing/meson.build            |  2 +-
 6 files changed, 98 insertions(+), 21 deletions(-)
---
diff --git a/meson.build b/meson.build
index 508f45d9..c9609b0e 100644
--- a/meson.build
+++ b/meson.build
@@ -56,6 +56,7 @@ gexiv2 = dependency('gexiv2', version: '>= 0.10.4')
 libraw = dependency('libraw', version : '>= 0.13.2')
 libexif = dependency('libexif', version : '>= 0.6.16')
 unity = dependency('unity', required : false)
+secret = dependency('libsecret-1', required: true)
 
 unity_available = false
 if unity.found() and get_option('unity-support')
diff --git a/plugins/authenticator/shotwell/FacebookPublishingAuthenticator.vala 
b/plugins/authenticator/shotwell/FacebookPublishingAuthenticator.vala
index 26a2363e..c798c3e9 100644
--- a/plugins/authenticator/shotwell/FacebookPublishingAuthenticator.vala
+++ b/plugins/authenticator/shotwell/FacebookPublishingAuthenticator.vala
@@ -158,9 +158,12 @@ namespace Publishing.Authenticator.Shotwell.Facebook {
     }
 
     internal class Facebook : Spit.Publishing.Authenticator, GLib.Object {
+        private const string PASSWORD_SCHEME = "org.gnome.Shotwell.Facebook";
+
         private Spit.Publishing.PluginHost host;
         private Publishing.Authenticator.Shotwell.Facebook.WebAuthenticationPane web_auth_pane = null;
         private GLib.HashTable<string, Variant> params;
+        private Secret.Schema? schema = null;
 
         private const string SERVICE_WELCOME_MESSAGE =
     _("You are not currently logged into Facebook.\n\nIf you don’t yet have a Facebook account, you can 
create one during the login process. During login, Shotwell Connect may ask you for permission to upload 
photos and publish to your feed. These permissions are required for Shotwell Connect to function.");
@@ -171,6 +174,7 @@ namespace Publishing.Authenticator.Shotwell.Facebook {
         public Facebook(Spit.Publishing.PluginHost host) {
             this.host = host;
             this.params = new GLib.HashTable<string, Variant>(str_hash, str_equal);
+            this.schema = new Secret.Schema (PASSWORD_SCHEME, Secret.SchemaFlags.NONE);
         }
 
         public void authenticate() {
@@ -204,7 +208,11 @@ namespace Publishing.Authenticator.Shotwell.Facebook {
 
         public void invalidate_persistent_session() {
             debug("invalidating saved Facebook session.");
-            set_persistent_access_token("");
+            try {
+                Secret.password_clear_sync(this.schema, null);
+            } catch (Error err) {
+                critical("Failed to remove password for Facebook %s", err.message);
+            }
         }
 
         public void logout() {
@@ -229,11 +237,21 @@ namespace Publishing.Authenticator.Shotwell.Facebook {
         }
 
         private string? get_persistent_access_token() {
-            return host.get_config_string("access_token", null);
+            string? token = null;
+            try {
+                return Secret.password_lookup_sync(this.schema, null);
+            } catch (Error err) {
+                critical("Failed to lookup Facebook token from password store: %s", err.message);
+            }
         }
 
         private void set_persistent_access_token(string access_token) {
-            host.set_config_string("access_token", access_token);
+            try {
+                Secret.password_store_sync(this.schema, Secret.COLLECTION_DEFAULT,
+                    "Shotwell publishig (Facebook connector)", access_token, null);
+            } catch (Error err) {
+                critical("Failed to look up password for scope %s: %s", this.scope, err.message);
+            }
         }
 
         private void do_show_service_welcome_pane() {
diff --git a/plugins/authenticator/shotwell/GoogleAuthenticator.vala 
b/plugins/authenticator/shotwell/GoogleAuthenticator.vala
index f561197c..28dcde14 100644
--- a/plugins/authenticator/shotwell/GoogleAuthenticator.vala
+++ b/plugins/authenticator/shotwell/GoogleAuthenticator.vala
@@ -105,12 +105,15 @@ namespace Publishing.Authenticator.Shotwell.Google {
     }
 
     internal class Google : Spit.Publishing.Authenticator, Object {
+        private const string PASSWORD_SCHEME = "org.gnome.Shotwell.Google";
+
         private string scope = null;
         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;
 
         public Google(string scope,
                       string welcome_message,
@@ -120,10 +123,17 @@ namespace Publishing.Authenticator.Shotwell.Google {
             this.scope = scope;
             this.session = new Session();
             this.welcome_message = welcome_message;
+            this.schema = new Secret.Schema (PASSWORD_SCHEME, Secret.SchemaFlags.NONE,
+                                             "scope", Secret.SchemaAttributeType.STRING);
         }
 
         public void authenticate() {
-            var refresh_token = host.get_config_string("refresh_token", null);
+            string? refresh_token = null;
+            try {
+                refresh_token = Secret.password_lookup_sync(this.schema, null, "scope", this.scope);
+            } catch (Error err) {
+                critical("Failed to lookup refresh_token from password store: %s", err.message);
+            }
             if (refresh_token != null && refresh_token != "") {
                 on_refresh_token_available(refresh_token);
                 do_exchange_refresh_token_for_access_token();
@@ -150,7 +160,11 @@ namespace Publishing.Authenticator.Shotwell.Google {
 
         public void logout() {
             session.deauthenticate();
-            host.set_config_string("refresh_token", "");
+            try {
+                Secret.password_clear_sync(this.schema, null, "scope", this.scope);
+            } catch (Error err) {
+                critical("Failed to remove password for scope %s: %s", this.scope, err.message);
+            }
         }
 
         public void refresh() {
@@ -356,12 +370,17 @@ namespace Publishing.Authenticator.Shotwell.Google {
             // by the time we get a username, the session should be authenticated, or else something
             // really tragic has happened
             assert(session.is_authenticated());
-            host.set_config_string("refresh_token", session.refresh_token);
+            try {
+                Secret.password_store_sync(this.schema, Secret.COLLECTION_DEFAULT,
+                    "Shotwell publishing (Google account scope %s)".printf(this.scope),
+                    session.refresh_token, null, "scope", this.scope);
+            } catch (Error err) {
+                critical("Failed to look up password for scope %s: %s", this.scope, err.message);
+            }
 
             this.authenticated();
         }
 
-
         private void do_exchange_refresh_token_for_access_token() {
             debug("ACTION: exchanging OAuth refresh token for OAuth access token.");
 
@@ -404,7 +423,12 @@ namespace Publishing.Authenticator.Shotwell.Google {
             if (txn.get_status_code() == Soup.Status.BAD_REQUEST ||
                 txn.get_status_code() == Soup.Status.UNAUTHORIZED) {
                 // Refresh token invalid, starting over
-                host.set_config_string("refresh_token", "");
+                try {
+                    Secret.password_clear_sync(this.schema, null, "scope", this.scope);
+                } catch (Error err) {
+                    critical("Failed to remove password for scope %s: %s", this.scope, err.message);
+                }
+
                 Idle.add (() => { this.authenticate(); return false; });
             }
 
diff --git a/plugins/authenticator/shotwell/meson.build b/plugins/authenticator/shotwell/meson.build
index 44042426..7d67f138 100644
--- a/plugins/authenticator/shotwell/meson.build
+++ b/plugins/authenticator/shotwell/meson.build
@@ -12,7 +12,7 @@ authenticator_shotwell_resources = gnome.compile_resources('authenticator-resour
         source_dir : meson.source_root())
 
 authenticator_shotwell_deps = [gee, gtk, gio, soup, json_glib, sw_plugin,
-                               sw_plugin_common_dep, json_glib, xml, webkit]
+                               sw_plugin_common_dep, json_glib, xml, webkit, secret]
 
 authenticator = library('shotwell-authenticator',
                         authenticator_shotwell_sources + authenticator_shotwell_resources,
diff --git a/plugins/shotwell-publishing/PiwigoPublishing.vala 
b/plugins/shotwell-publishing/PiwigoPublishing.vala
index fcd0aeee..7f5cb8e1 100644
--- a/plugins/shotwell-publishing/PiwigoPublishing.vala
+++ b/plugins/shotwell-publishing/PiwigoPublishing.vala
@@ -123,6 +123,8 @@ internal class PublishingParameters {
 }
 
 public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
+    private const string PASSWORD_SCHEME = "org.gnome.Shotwell.Piwigo";
+
     private Spit.Publishing.Service service;
     private Spit.Publishing.PluginHost host;
     private bool running = false;
@@ -131,6 +133,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
     private Category[] categories = null;
     private PublishingParameters parameters = null;
     private Spit.Publishing.ProgressCallback progress_reporter = null;
+    private Secret.Schema? schema = null;
 
     public PiwigoPublisher(Spit.Publishing.Service service,
         Spit.Publishing.PluginHost host) {
@@ -138,6 +141,9 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
         this.service = service;
         this.host = host;
         session = new Session();
+        this.schema = new Secret.Schema (PASSWORD_SCHEME, Secret.SchemaFlags.NONE,
+                                         "url", Secret.SchemaAttributeType.STRING,
+                                         "user", Secret.SchemaAttributeType.STRING);
     }
 
     // Publisher interface implementation
@@ -169,7 +175,9 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
             debug("PiwigoPublisher: session is not authenticated.");
             string? persistent_url = get_persistent_url();
             string? persistent_username = get_persistent_username();
-            string? persistent_password = get_persistent_password();
+            string? persistent_password = get_persistent_password(persistent_url, persistent_username);
+
+            // This will only be null if either of the other two was null or the password did not exist
             if (persistent_url != null && persistent_username != null && persistent_password != null)
                 do_network_login(persistent_url, persistent_username,
                     persistent_password, get_remember_password());
@@ -200,12 +208,37 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
         host.set_config_string("username", username);
     }
     
-    public string? get_persistent_password() {
-        return host.get_config_string("password", null);
+    public string? get_persistent_password(string? url, string? user) {
+        if (url != null && user != null) {
+            try {
+                var pw = Secret.password_lookup_sync(this.schema, null, "url", url, "user", user);
+
+                return pw;
+            } catch (Error err) {
+                critical("Failed to lookup the password for url %s and user %s: %s", url, user, err.message);
+
+                return null;
+            }
+        }
+
+        return null;
     }
     
-    private void set_persistent_password(string? password) {
-        host.set_config_string("password", password);
+    private void set_persistent_password(string? url, string? user, string? password) {
+        try {
+            if (password == null) {
+                // remove
+                Secret.password_clear_sync(this.schema, null, "url", url, "user", user);
+            } else {
+                Secret.password_store_sync(this.schema, Secret.COLLECTION_DEFAULT,
+                        "Shotwell publishing (Piwigo account %s@%s)".printf(user, url),
+                        password,
+                        null,
+                        "url", url, "user", user);
+            }
+        } catch (Error err) {
+            critical("Failed to store password for %s@%s: %s", user, url, err.message);
+        }
     }
     
     public bool get_remember_password() {
@@ -300,7 +333,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
 
             string? persistent_url = get_persistent_url();
             string? persistent_username = get_persistent_username();
-            string? persistent_password = get_persistent_password();
+            string? persistent_password = get_persistent_password(persistent_url, persistent_username);
             if (persistent_url != null && persistent_username != null && persistent_password != null)
                 do_network_login(persistent_url, persistent_username,
                     persistent_password, get_remember_password());
@@ -350,10 +383,11 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
         host.install_login_wait_pane();
         
         set_remember_password(remember_password);
-        if (remember_password)
-            set_persistent_password(password);
-        else
-            set_persistent_password(null);
+        if (remember_password) {
+            set_persistent_password(url, username, password);
+        } else {
+            set_persistent_password(url, username, null);
+        }
 
         SessionLoginTransaction login_trans = new SessionLoginTransaction(
             session, normalise_url(url), username, password);
@@ -1129,7 +1163,7 @@ internal class AuthenticationPane : Shotwell.Plugins.Common.BuilderPane {
             username_entry.set_text(persistent_username);
         }
         password_entry = builder.get_object ("password_entry") as Gtk.Entry;
-        string? persistent_password = publisher.get_persistent_password();
+        string? persistent_password = publisher.get_persistent_password(persistent_url, persistent_username);
         if (persistent_password != null) {
             password_entry.set_text(persistent_password);
         }
diff --git a/plugins/shotwell-publishing/meson.build b/plugins/shotwell-publishing/meson.build
index 45058b85..3ef3ff1c 100644
--- a/plugins/shotwell-publishing/meson.build
+++ b/plugins/shotwell-publishing/meson.build
@@ -15,7 +15,7 @@ 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, gdata, gcr,
-                              gcr_ui, authenticator_dep],
+                              gcr_ui, authenticator_dep, secret],
               vala_args : [
                   '--gresources', 'org.gnome.Shotwell.Publishing.gresource.xml'
                   ],


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]