[shotwell] Flickr: Split out OAuth1 stuff into common class



commit 78d3090eb507835abf52df86217ba0724fc28d09
Author: Jens Georg <mail jensge org>
Date:   Sun Oct 22 14:01:33 2017 +0200

    Flickr: Split out OAuth1 stuff into common class

 authenticator.am                                   |    3 +-
 .../shotwell/FlickrPublishingAuthenticator.vala    |  263 +------------------
 .../shotwell/OAuth1Authenticator.vala              |  269 ++++++++++++++++++++
 plugins/authenticator/shotwell/meson.build         |    3 +-
 4 files changed, 285 insertions(+), 253 deletions(-)
---
diff --git a/authenticator.am b/authenticator.am
index 0fd4b81..0ba2898 100644
--- a/authenticator.am
+++ b/authenticator.am
@@ -54,5 +54,6 @@ plugins_authenticator_libshotwell_authenticator_la_SOURCES += \
        plugins/authenticator/shotwell/ShotwellAuthenticatorFactory.vala \
        plugins/authenticator/shotwell/FacebookPublishingAuthenticator.vala \
        plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala \
-       plugins/authenticator/shotwell/GoogleAuthenticator.vala
+       plugins/authenticator/shotwell/GoogleAuthenticator.vala \
+       plugins/authenticator/shotwell/OAuth1Authenticator.vala
 endif
diff --git a/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala 
b/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala
index e389908..111c772 100644
--- a/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala
+++ b/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala
@@ -7,198 +7,28 @@
 namespace Publishing.Authenticator.Shotwell.Flickr {
     internal const string ENDPOINT_URL = "https://api.flickr.com/services/rest";;
     internal const string EXPIRED_SESSION_ERROR_CODE = "98";
-    internal const string ENCODE_RFC_3986_EXTRA = "!*'();:@&=+$,/?%#[] \\";
 
-    internal class Transaction : Publishing.RESTSupport.Transaction {
-        public Transaction(Session session, Publishing.RESTSupport.HttpMethod method =
-                Publishing.RESTSupport.HttpMethod.POST) {
-            base(session, method);
-            setup_arguments();
-        }
-
-        public Transaction.with_uri(Session session, string uri,
-                Publishing.RESTSupport.HttpMethod method = Publishing.RESTSupport.HttpMethod.POST) {
-            base.with_endpoint_url(session, uri, method);
-            setup_arguments();
-        }
-
-        private void setup_arguments() {
-            var session = (Session) get_parent_session();
-
-            add_argument("oauth_nonce", session.get_oauth_nonce());
-            add_argument("oauth_signature_method", "HMAC-SHA1");
-            add_argument("oauth_version", "1.0");
-            add_argument("oauth_callback", "oob");
-            add_argument("oauth_timestamp", session.get_oauth_timestamp());
-            add_argument("oauth_consumer_key", session.get_consumer_key());
-        }
-
-
-        public override void execute() throws Spit.Publishing.PublishingError {
-            ((Session) get_parent_session()).sign_transaction(this);
-
-            base.execute();
-        }
-    }
-
-    internal class Session : Publishing.RESTSupport.Session {
-        private string? request_phase_token = null;
-        private string? request_phase_token_secret = null;
-        private string? access_phase_token = null;
-        private string? access_phase_token_secret = null;
-        private string? username = null;
-        private string? consumer_key = null;
-        private string? consumer_secret = null;
-
-        public Session() {
-            base();
-        }
-
-        public override bool is_authenticated() {
-            return (access_phase_token != null && access_phase_token_secret != null &&
-                    username != null);
-        }
-
-        public void authenticate_from_persistent_credentials(string token, string secret,
-                string username) {
-            this.access_phase_token = token;
-            this.access_phase_token_secret = secret;
-            this.username = username;
-
-            this.authenticated();
-        }
-
-        public void deauthenticate() {
-            access_phase_token = null;
-            access_phase_token_secret = null;
-            username = null;
-        }
-
-        public void set_api_credentials(string consumer_key, string consumer_secret) {
-            this.consumer_key = consumer_key;
-            this.consumer_secret = consumer_secret;
-        }
-
-        public void sign_transaction(Publishing.RESTSupport.Transaction txn) {
-            string http_method = txn.get_method().to_string();
-
-            debug("signing transaction with parameters:");
-            debug("HTTP method = " + http_method);
-
-            Publishing.RESTSupport.Argument[] base_string_arguments = txn.get_arguments();
-
-            Publishing.RESTSupport.Argument[] sorted_args =
-                Publishing.RESTSupport.Argument.sort(base_string_arguments);
-
-            string arguments_string = "";
-            for (int i = 0; i < sorted_args.length; i++) {
-                arguments_string += (sorted_args[i].key + "=" + sorted_args[i].value);
-                if (i < sorted_args.length - 1)
-                    arguments_string += "&";
-            }
-
-            string? signing_key = null;
-            if (access_phase_token_secret != null) {
-                debug("access phase token secret available; using it as signing key");
-
-                signing_key = consumer_secret + "&" + access_phase_token_secret;
-            } else if (request_phase_token_secret != null) {
-                debug("request phase token secret available; using it as signing key");
-
-                signing_key = consumer_secret + "&" + request_phase_token_secret;
-            } else {
-                debug("neither access phase nor request phase token secrets available; using API " +
-                        "key as signing key");
-
-                signing_key = consumer_secret + "&";
-            }
-
-            string signature_base_string = http_method + "&" + Soup.URI.encode(
-                    txn.get_endpoint_url(), ENCODE_RFC_3986_EXTRA) + "&" +
-                Soup.URI.encode(arguments_string, ENCODE_RFC_3986_EXTRA);
-
-            debug("signature base string = '%s'", signature_base_string);
-
-            debug("signing key = '%s'", signing_key);
-
-            // compute the signature
-            string signature = RESTSupport.hmac_sha1(signing_key, signature_base_string);
-            signature = Soup.URI.encode(signature, ENCODE_RFC_3986_EXTRA);
-
-            debug("signature = '%s'", signature);
-
-            txn.add_argument("oauth_signature", signature);
-        }
-
-        public void set_request_phase_credentials(string token, string secret) {
-            this.request_phase_token = token;
-            this.request_phase_token_secret = secret;
-        }
-
-        public void set_access_phase_credentials(string token, string secret, string username) {
-            this.access_phase_token = token;
-            this.access_phase_token_secret = secret;
-            this.username = username;
-
-            authenticated();
-        }
-
-        public string get_oauth_nonce() {
-            TimeVal currtime = TimeVal();
-            currtime.get_current_time();
-
-            return Checksum.compute_for_string(ChecksumType.MD5, currtime.tv_sec.to_string() +
-                    currtime.tv_usec.to_string());
-        }
-
-        public string get_oauth_timestamp() {
-            return GLib.get_real_time().to_string().substring(0, 10);
-        }
-
-        public string get_consumer_key() {
-            assert(consumer_key != null);
-            return consumer_key;
-        }
-
-        public string get_request_phase_token() {
-            assert(request_phase_token != null);
-            return request_phase_token;
-        }
-
-        public string get_access_phase_token() {
-            assert(access_phase_token != null);
-            return access_phase_token;
-        }
-
-        public string get_access_phase_token_secret() {
-            assert(access_phase_token_secret != null);
-            return access_phase_token_secret;
-        }
-
-        public string get_username() {
-            assert(is_authenticated());
-            return username;
-        }
-    }
     internal const string API_KEY = "60dd96d4a2ad04888b09c9e18d82c26f";
     internal const string API_SECRET = "d0960565e03547c1";
 
     internal const string SERVICE_WELCOME_MESSAGE =
         _("You are not currently logged into Flickr.\n\nClick Log in to log into Flickr in your Web browser. 
You will have to authorize Shotwell Connect to link to your Flickr account.");
 
-    internal class AuthenticationRequestTransaction : Transaction {
-        public AuthenticationRequestTransaction(Session session) {
+    internal class AuthenticationRequestTransaction : OAuth1.Transaction {
+        public AuthenticationRequestTransaction(OAuth1.Session session) {
             base.with_uri(session, "https://www.flickr.com/services/oauth/request_token";,
                     Publishing.RESTSupport.HttpMethod.GET);
+            add_argument("oauth_callback", "oob");
         }
     }
 
-    internal class AccessTokenFetchTransaction : Transaction {
-        public AccessTokenFetchTransaction(Session session, string user_verifier) {
+    internal class AccessTokenFetchTransaction : OAuth1.Transaction {
+        public AccessTokenFetchTransaction(OAuth1.Session session, string user_verifier) {
             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", "oob");
         }
     }
 
@@ -257,65 +87,12 @@ namespace Publishing.Authenticator.Shotwell.Flickr {
     }
 
 
-    internal class Flickr : GLib.Object, Spit.Publishing.Authenticator {
-        private GLib.HashTable<string, Variant> params;
-        private Session session;
-        private Spit.Publishing.PluginHost host;
-
+    internal class Flickr : Publishing.Authenticator.Shotwell.OAuth1.Authenticator {
         public Flickr(Spit.Publishing.PluginHost host) {
-            base();
-
-            this.host = host;
-            params = new GLib.HashTable<string, Variant>(str_hash, str_equal);
-            params.insert("ConsumerKey", API_KEY);
-            params.insert("ConsumerSecret", API_SECRET);
-
-            session = new Session();
-            session.set_api_credentials(API_KEY, API_SECRET);
-            session.authenticated.connect(on_session_authenticated);
-        }
-
-        ~Flickr() {
-            session.authenticated.disconnect(on_session_authenticated);
-        }
-
-        public void invalidate_persistent_session() {
-            set_persistent_access_phase_token("");
-            set_persistent_access_phase_token_secret("");
-            set_persistent_access_phase_username("");
-        }
-
-        private bool is_persistent_session_valid() {
-            return (get_persistent_access_phase_username() != null &&
-                    get_persistent_access_phase_token() != null &&
-                    get_persistent_access_phase_token_secret() != null);
-        }
-
-        private string? get_persistent_access_phase_username() {
-            return host.get_config_string("access_phase_username", null);
-        }
-
-        private void set_persistent_access_phase_username(string username) {
-            host.set_config_string("access_phase_username", username);
+            base(API_KEY, API_SECRET, host);
         }
 
-        private string? get_persistent_access_phase_token() {
-            return host.get_config_string("access_phase_token", null);
-        }
-
-        private void set_persistent_access_phase_token(string token) {
-            host.set_config_string("access_phase_token", token);
-        }
-
-        private string? get_persistent_access_phase_token_secret() {
-            return host.get_config_string("access_phase_token_secret", null);
-        }
-
-        private void set_persistent_access_phase_token_secret(string secret) {
-            host.set_config_string("access_phase_token_secret", secret);
-        }
-
-        public void authenticate() {
+        public override void authenticate() {
             if (is_persistent_session_valid()) {
                 debug("attempt start: a persistent session is available; using it");
 
@@ -327,20 +104,16 @@ namespace Publishing.Authenticator.Shotwell.Flickr {
             }
         }
 
-        public bool can_logout() {
+        public override bool can_logout() {
             return true;
         }
 
-        public GLib.HashTable<string, Variant> get_authentication_parameter() {
-            return this.params;
-        }
-
-        public void logout () {
+        public override void logout () {
             session.deauthenticate();
             invalidate_persistent_session();
         }
 
-        public void refresh() {
+        public override void refresh() {
             // No-Op with flickr
         }
 
@@ -536,17 +309,5 @@ namespace Publishing.Authenticator.Shotwell.Flickr {
             }
         }
 
-        private void on_session_authenticated() {
-            params.insert("AuthToken", session.get_access_phase_token());
-            params.insert("AuthTokenSecret", session.get_access_phase_token_secret());
-            params.insert("Username", session.get_username());
-
-            set_persistent_access_phase_token(session.get_access_phase_token());
-            set_persistent_access_phase_token_secret(session.get_access_phase_token_secret());
-            set_persistent_access_phase_username(session.get_username());
-
-
-            this.authenticated();
-        }
     }
 }
diff --git a/plugins/authenticator/shotwell/OAuth1Authenticator.vala 
b/plugins/authenticator/shotwell/OAuth1Authenticator.vala
new file mode 100644
index 0000000..62988ec
--- /dev/null
+++ b/plugins/authenticator/shotwell/OAuth1Authenticator.vala
@@ -0,0 +1,269 @@
+/* Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2017 Jens Georg <mail jensge org>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+namespace Publishing.Authenticator.Shotwell.OAuth1 {
+    internal const string ENCODE_RFC_3986_EXTRA = "!*'();:@&=+$,/?%#[] \\";
+
+    internal class Session : Publishing.RESTSupport.Session {
+        private string? request_phase_token = null;
+        private string? request_phase_token_secret = null;
+        private string? access_phase_token = null;
+        private string? access_phase_token_secret = null;
+        private string? username = null;
+        private string? consumer_key = null;
+        private string? consumer_secret = null;
+
+        public Session() {
+            base();
+        }
+
+        public override bool is_authenticated() {
+            return (access_phase_token != null && access_phase_token_secret != null &&
+                    username != null);
+        }
+
+        public void authenticate_from_persistent_credentials(string token, string secret,
+                string username) {
+            this.access_phase_token = token;
+            this.access_phase_token_secret = secret;
+            this.username = username;
+
+            this.authenticated();
+        }
+
+        public void deauthenticate() {
+            access_phase_token = null;
+            access_phase_token_secret = null;
+            username = null;
+        }
+
+        public void set_api_credentials(string consumer_key, string consumer_secret) {
+            this.consumer_key = consumer_key;
+            this.consumer_secret = consumer_secret;
+        }
+
+        public void sign_transaction(Publishing.RESTSupport.Transaction txn) {
+            string http_method = txn.get_method().to_string();
+
+            debug("signing transaction with parameters:");
+            debug("HTTP method = " + http_method);
+
+            Publishing.RESTSupport.Argument[] base_string_arguments = txn.get_arguments();
+
+            Publishing.RESTSupport.Argument[] sorted_args =
+                Publishing.RESTSupport.Argument.sort(base_string_arguments);
+
+            string arguments_string = "";
+            for (int i = 0; i < sorted_args.length; i++) {
+                arguments_string += (sorted_args[i].key + "=" + sorted_args[i].value);
+                if (i < sorted_args.length - 1)
+                    arguments_string += "&";
+            }
+
+            string? signing_key = null;
+            if (access_phase_token_secret != null) {
+                debug("access phase token secret available; using it as signing key");
+
+                signing_key = consumer_secret + "&" + access_phase_token_secret;
+            } else if (request_phase_token_secret != null) {
+                debug("request phase token secret available; using it as signing key");
+
+                signing_key = consumer_secret + "&" + request_phase_token_secret;
+            } else {
+                debug("neither access phase nor request phase token secrets available; using API " +
+                        "key as signing key");
+
+                signing_key = consumer_secret + "&";
+            }
+
+            string signature_base_string = http_method + "&" + Soup.URI.encode(
+                    txn.get_endpoint_url(), ENCODE_RFC_3986_EXTRA) + "&" +
+                Soup.URI.encode(arguments_string, ENCODE_RFC_3986_EXTRA);
+
+            debug("signature base string = '%s'", signature_base_string);
+
+            debug("signing key = '%s'", signing_key);
+
+            // compute the signature
+            string signature = RESTSupport.hmac_sha1(signing_key, signature_base_string);
+            signature = Soup.URI.encode(signature, ENCODE_RFC_3986_EXTRA);
+
+            debug("signature = '%s'", signature);
+
+            txn.add_argument("oauth_signature", signature);
+        }
+
+        public void set_request_phase_credentials(string token, string secret) {
+            this.request_phase_token = token;
+            this.request_phase_token_secret = secret;
+        }
+
+        public void set_access_phase_credentials(string token, string secret, string username) {
+            this.access_phase_token = token;
+            this.access_phase_token_secret = secret;
+            this.username = username;
+
+            authenticated();
+        }
+
+        public string get_oauth_nonce() {
+            TimeVal currtime = TimeVal();
+            currtime.get_current_time();
+
+            return Checksum.compute_for_string(ChecksumType.MD5, currtime.tv_sec.to_string() +
+                    currtime.tv_usec.to_string());
+        }
+
+        public string get_oauth_timestamp() {
+            return GLib.get_real_time().to_string().substring(0, 10);
+        }
+
+        public string get_consumer_key() {
+            assert(consumer_key != null);
+            return consumer_key;
+        }
+
+        public string get_request_phase_token() {
+            assert(request_phase_token != null);
+            return request_phase_token;
+        }
+
+        public string get_access_phase_token() {
+            assert(access_phase_token != null);
+            return access_phase_token;
+        }
+
+        public string get_access_phase_token_secret() {
+            assert(access_phase_token_secret != null);
+            return access_phase_token_secret;
+        }
+
+        public string get_username() {
+            assert(is_authenticated());
+            return username;
+        }
+    }
+
+    internal class Transaction : Publishing.RESTSupport.Transaction {
+        public Transaction(Session session, Publishing.RESTSupport.HttpMethod method =
+                Publishing.RESTSupport.HttpMethod.POST) {
+            base(session, method);
+            setup_arguments();
+        }
+
+        public Transaction.with_uri(Session session, string uri,
+                Publishing.RESTSupport.HttpMethod method = Publishing.RESTSupport.HttpMethod.POST) {
+            base.with_endpoint_url(session, uri, method);
+            setup_arguments();
+        }
+
+        private void setup_arguments() {
+            var session = (Session) get_parent_session();
+
+            add_argument("oauth_nonce", session.get_oauth_nonce());
+            add_argument("oauth_signature_method", "HMAC-SHA1");
+            add_argument("oauth_version", "1.0");
+            add_argument("oauth_timestamp", session.get_oauth_timestamp());
+            add_argument("oauth_consumer_key", session.get_consumer_key());
+        }
+
+
+        public override void execute() throws Spit.Publishing.PublishingError {
+            ((Session) get_parent_session()).sign_transaction(this);
+
+            base.execute();
+        }
+    }
+
+    internal abstract class Authenticator : GLib.Object, Spit.Publishing.Authenticator {
+        protected GLib.HashTable<string, Variant> params;
+        protected Session session;
+        protected Spit.Publishing.PluginHost host;
+
+        public Authenticator(string api_key, string api_secret, Spit.Publishing.PluginHost host) {
+            base();
+            this.host = host;
+
+            params = new GLib.HashTable<string, Variant>(str_hash, str_equal);
+            params.insert("ConsumerKey", api_key);
+            params.insert("ConsumerSecret", api_secret);
+
+            session = new Session();
+            session.set_api_credentials(api_key, api_secret);
+            session.authenticated.connect(on_session_authenticated);
+        }
+
+        ~Authenticator() {
+            session.authenticated.disconnect(on_session_authenticated);
+        }
+
+        // Methods from Authenticator interface
+        public abstract void authenticate();
+
+        public abstract bool can_logout();
+
+        public GLib.HashTable<string, Variant> get_authentication_parameter() {
+            return this.params;
+        }
+
+        public abstract void logout ();
+
+        public abstract void refresh();
+
+        public void invalidate_persistent_session() {
+            set_persistent_access_phase_token("");
+            set_persistent_access_phase_token_secret("");
+            set_persistent_access_phase_username("");
+        }
+
+        protected bool is_persistent_session_valid() {
+            return (get_persistent_access_phase_username() != null &&
+                    get_persistent_access_phase_token() != null &&
+                    get_persistent_access_phase_token_secret() != null);
+        }
+
+        protected string? get_persistent_access_phase_username() {
+            return host.get_config_string("access_phase_username", null);
+        }
+
+        protected void set_persistent_access_phase_username(string username) {
+            host.set_config_string("access_phase_username", username);
+        }
+
+        protected string? get_persistent_access_phase_token() {
+            return host.get_config_string("access_phase_token", null);
+        }
+
+        protected void set_persistent_access_phase_token(string token) {
+            host.set_config_string("access_phase_token", token);
+        }
+
+        protected string? get_persistent_access_phase_token_secret() {
+            return host.get_config_string("access_phase_token_secret", null);
+        }
+
+        protected void set_persistent_access_phase_token_secret(string secret) {
+            host.set_config_string("access_phase_token_secret", secret);
+        }
+
+
+        protected void on_session_authenticated() {
+            params.insert("AuthToken", session.get_access_phase_token());
+            params.insert("AuthTokenSecret", session.get_access_phase_token_secret());
+            params.insert("Username", session.get_username());
+
+            set_persistent_access_phase_token(session.get_access_phase_token());
+            set_persistent_access_phase_token_secret(session.get_access_phase_token_secret());
+            set_persistent_access_phase_username(session.get_username());
+
+
+            this.authenticated();
+        }
+
+    }
+
+}
diff --git a/plugins/authenticator/shotwell/meson.build b/plugins/authenticator/shotwell/meson.build
index 9fe27fc..37f8a3b 100644
--- a/plugins/authenticator/shotwell/meson.build
+++ b/plugins/authenticator/shotwell/meson.build
@@ -2,7 +2,8 @@ authenticator_shotwell_sources = [
         'ShotwellAuthenticatorFactory.vala',
         'FacebookPublishingAuthenticator.vala',
         'FlickrPublishingAuthenticator.vala',
-        'GoogleAuthenticator.vala'
+        'GoogleAuthenticator.vala',
+        'OAuth1Authenticator.vala'
         ]
 
 authenticator_shotwell_resources = gnome.compile_resources('authenticator-resource',


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