[shotwell] Flickr: Split out OAuth1 stuff into common class
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [shotwell] Flickr: Split out OAuth1 stuff into common class
- Date: Thu, 9 Nov 2017 13:02:31 +0000 (UTC)
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]