diff options
Diffstat (limited to 'plugins/common/RESTSupport.vala')
-rw-r--r-- | plugins/common/RESTSupport.vala | 371 |
1 files changed, 21 insertions, 350 deletions
diff --git a/plugins/common/RESTSupport.vala b/plugins/common/RESTSupport.vala index 1a9052b..f06473c 100644 --- a/plugins/common/RESTSupport.vala +++ b/plugins/common/RESTSupport.vala @@ -746,7 +746,6 @@ public string asciify_string(string s) { public abstract class GoogleSession : Session { public abstract string get_user_name(); public abstract string get_access_token(); - public abstract string get_refresh_token(); public abstract void deauthenticate(); } @@ -779,11 +778,6 @@ public abstract class GooglePublisher : Object, Spit.Publishing.Publisher { return access_token; } - public override string get_refresh_token() { - assert(refresh_token != null); - return refresh_token; - } - public override void deauthenticate() { access_token = null; user_name = null; @@ -791,63 +785,6 @@ public abstract class GooglePublisher : Object, Spit.Publishing.Publisher { } } - private class WebAuthenticationPane : Shotwell.Plugins.Common.WebAuthenticationPane { - public static bool cache_dirty = false; - - 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; - } - - public override void on_page_load() { - string page_title = get_view ().get_title(); - if (page_title.index_of("state=connect") > 0) { - int auth_code_field_start = page_title.index_of("code="); - if (auth_code_field_start < 0) - return; - - string auth_code = - page_title.substring(auth_code_field_start + 5); // 5 = "code=".length - - cache_dirty = true; - - authorized(auth_code); - } - } - } - - private class GetAccessTokensTransaction : Publishing.RESTSupport.Transaction { - private const string ENDPOINT_URL = "https://accounts.google.com/o/oauth2/token"; - - public GetAccessTokensTransaction(Session session, string auth_code) { - base.with_endpoint_url(session, ENDPOINT_URL); - - add_argument("code", auth_code); - add_argument("client_id", OAUTH_CLIENT_ID); - add_argument("client_secret", OAUTH_CLIENT_SECRET); - add_argument("redirect_uri", "urn:ietf:wg:oauth:2.0:oob"); - add_argument("grant_type", "authorization_code"); - } - } - - private class RefreshAccessTokenTransaction : Publishing.RESTSupport.Transaction { - private const string ENDPOINT_URL = "https://accounts.google.com/o/oauth2/token"; - - public RefreshAccessTokenTransaction(Session session) { - base.with_endpoint_url(session, ENDPOINT_URL); - - add_argument("client_id", OAUTH_CLIENT_ID); - add_argument("client_secret", OAUTH_CLIENT_SECRET); - add_argument("refresh_token", ((GoogleSession) session).get_refresh_token()); - add_argument("grant_type", "refresh_token"); - } - } - public class AuthenticatedTransaction : Publishing.RESTSupport.Transaction { private AuthenticatedTransaction.with_endpoint_url(GoogleSession session, string endpoint_url, Publishing.RESTSupport.HttpMethod method) { @@ -863,19 +800,11 @@ public abstract class GooglePublisher : Object, Spit.Publishing.Publisher { } } - private class UsernameFetchTransaction : AuthenticatedTransaction { - private const string ENDPOINT_URL = "https://www.googleapis.com/oauth2/v1/userinfo"; - - public UsernameFetchTransaction(GoogleSession session) { - base(session, ENDPOINT_URL, Publishing.RESTSupport.HttpMethod.GET); - } - } - private string scope; private GoogleSessionImpl session; - private WebAuthenticationPane? web_auth_pane; private weak Spit.Publishing.PluginHost host; private weak Spit.Publishing.Service service; + private Spit.Publishing.Authenticator authenticator; protected GooglePublisher(Spit.Publishing.Service service, Spit.Publishing.PluginHost host, string scope) { @@ -883,272 +812,11 @@ public abstract class GooglePublisher : Object, Spit.Publishing.Publisher { this.session = new GoogleSessionImpl(); this.service = service; this.host = host; - this.web_auth_pane = null; - } - - 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); - - if (!is_running()) - return; - - do_get_access_tokens(auth_code); - } - - private void on_get_access_tokens_complete(Publishing.RESTSupport.Transaction txn) { - txn.completed.disconnect(on_get_access_tokens_complete); - txn.network_error.disconnect(on_get_access_tokens_error); - - debug("EVENT: network transaction to exchange authorization code for access tokens " + - "completed successfully."); - - if (!is_running()) - return; - - do_extract_tokens(txn.get_response()); + this.authenticator = this.get_authenticator(); + this.authenticator.authenticated.connect(on_authenticator_authenticated); } - private void on_get_access_tokens_error(Publishing.RESTSupport.Transaction txn, - Spit.Publishing.PublishingError err) { - txn.completed.disconnect(on_get_access_tokens_complete); - txn.network_error.disconnect(on_get_access_tokens_error); - - debug("EVENT: network transaction to exchange authorization code for access tokens " + - "failed; response = '%s'", txn.get_response()); - - if (!is_running()) - return; - - host.post_error(err); - } - - private void on_refresh_access_token_transaction_completed(Publishing.RESTSupport.Transaction - txn) { - txn.completed.disconnect(on_refresh_access_token_transaction_completed); - txn.network_error.disconnect(on_refresh_access_token_transaction_error); - - debug("EVENT: refresh access token transaction completed successfully."); - - if (!is_running()) - return; - - if (session.is_authenticated()) // ignore these events if the session is already auth'd - return; - - do_extract_tokens(txn.get_response()); - } - - private void on_refresh_access_token_transaction_error(Publishing.RESTSupport.Transaction txn, - Spit.Publishing.PublishingError err) { - txn.completed.disconnect(on_refresh_access_token_transaction_completed); - txn.network_error.disconnect(on_refresh_access_token_transaction_error); - - debug("EVENT: refresh access token transaction caused a network error."); - - if (!is_running()) - return; - - if (session.is_authenticated()) // ignore these events if the session is already auth'd - return; - - // 400 errors indicate that the OAuth client ID and secret have become invalid. In most - // cases, this can be fixed by logging the user out - if (txn.get_status_code() == 400) { - do_logout(); - return; - } - - host.post_error(err); - } - - private void on_refresh_token_available(string token) { - debug("EVENT: an OAuth refresh token has become available; token = '%s'.", token); - - if (!is_running()) - return; - - session.refresh_token = token; - } - - private void on_access_token_available(string token) { - debug("EVENT: an OAuth access token has become available; token = '%s'.", token); - - if (!is_running()) - return; - - session.access_token = token; - - do_fetch_username(); - } - - private void on_fetch_username_transaction_completed(Publishing.RESTSupport.Transaction txn) { - txn.completed.disconnect(on_fetch_username_transaction_completed); - txn.network_error.disconnect(on_fetch_username_transaction_error); - - debug("EVENT: username fetch transaction completed successfully."); - - if (!is_running()) - return; - - do_extract_username(txn.get_response()); - } - - private void on_fetch_username_transaction_error(Publishing.RESTSupport.Transaction txn, - Spit.Publishing.PublishingError err) { - txn.completed.disconnect(on_fetch_username_transaction_completed); - txn.network_error.disconnect(on_fetch_username_transaction_error); - - debug("EVENT: username fetch transaction caused a network error"); - - if (!is_running()) - return; - - host.post_error(err); - } - - private void do_get_access_tokens(string auth_code) { - debug("ACTION: exchanging authorization code for access & refresh tokens"); - - host.install_login_wait_pane(); - - GetAccessTokensTransaction tokens_txn = new GetAccessTokensTransaction(session, auth_code); - tokens_txn.completed.connect(on_get_access_tokens_complete); - tokens_txn.network_error.connect(on_get_access_tokens_error); - - try { - tokens_txn.execute(); - } catch (Spit.Publishing.PublishingError err) { - host.post_error(err); - } - } - - private 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?" + - "response_type=code&" + - "client_id=" + OAUTH_CLIENT_ID + "&" + - "redirect_uri=" + Soup.URI.encode("urn:ietf:wg:oauth:2.0:oob", null) + "&" + - "scope=" + Soup.URI.encode(scope, null) + "+" + - Soup.URI.encode("https://www.googleapis.com/auth/userinfo.profile", null) + "&" + - "state=connect&" + - "access_type=offline&" + - "approval_prompt=force"; - - web_auth_pane = new WebAuthenticationPane(user_authorization_url); - web_auth_pane.authorized.connect(on_web_auth_pane_authorized); - - host.install_dialog_pane(web_auth_pane); - - } - - private void do_exchange_refresh_token_for_access_token() { - debug("ACTION: exchanging OAuth refresh token for OAuth access token."); - - host.install_login_wait_pane(); - - RefreshAccessTokenTransaction txn = new RefreshAccessTokenTransaction(session); - - txn.completed.connect(on_refresh_access_token_transaction_completed); - txn.network_error.connect(on_refresh_access_token_transaction_error); - - try { - txn.execute(); - } catch (Spit.Publishing.PublishingError err) { - host.post_error(err); - } - } - - private void do_extract_tokens(string response_body) { - debug("ACTION: extracting OAuth tokens from body of server response"); - - Json.Parser parser = new Json.Parser(); - - try { - parser.load_from_data(response_body); - } catch (Error err) { - host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE( - "Couldn't parse JSON response: " + err.message)); - return; - } - - Json.Object response_obj = parser.get_root().get_object(); - - if ((!response_obj.has_member("access_token")) && (!response_obj.has_member("refresh_token"))) { - host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE( - "neither access_token nor refresh_token not present in server response")); - return; - } - - if (response_obj.has_member("refresh_token")) { - string refresh_token = response_obj.get_string_member("refresh_token"); - - if (refresh_token != "") - on_refresh_token_available(refresh_token); - } - - if (response_obj.has_member("access_token")) { - string access_token = response_obj.get_string_member("access_token"); - - if (access_token != "") - on_access_token_available(access_token); - } - } - - private void do_fetch_username() { - debug("ACTION: running network transaction to fetch username."); - - host.install_login_wait_pane(); - host.set_service_locked(true); - - UsernameFetchTransaction txn = new UsernameFetchTransaction(session); - txn.completed.connect(on_fetch_username_transaction_completed); - txn.network_error.connect(on_fetch_username_transaction_error); - - try { - txn.execute(); - } catch (Error err) { - host.post_error(err); - } - } - - private void do_extract_username(string response_body) { - debug("ACTION: extracting username from body of server response"); - - Json.Parser parser = new Json.Parser(); - - try { - parser.load_from_data(response_body); - } catch (Error err) { - host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE( - "Couldn't parse JSON response: " + err.message)); - return; - } - - Json.Object response_obj = parser.get_root().get_object(); - - if (response_obj.has_member("name")) { - string username = response_obj.get_string_member("name"); - - if (username != "") - session.user_name = username; - } - - if (response_obj.has_member("access_token")) { - string access_token = response_obj.get_string_member("access_token"); - - if (access_token != "") - session.access_token = access_token; - } - - // by the time we get a username, the session should be authenticated, or else something - // really tragic has happened - assert(session.is_authenticated()); - - on_login_flow_complete(); - } + protected abstract Spit.Publishing.Authenticator get_authenticator(); protected unowned Spit.Publishing.PluginHost get_host() { return host; @@ -1158,20 +826,6 @@ public abstract class GooglePublisher : Object, Spit.Publishing.Publisher { return session; } - protected void start_oauth_flow(string? refresh_token = null) { - if (refresh_token != null && refresh_token != "") { - session.refresh_token = refresh_token; - do_exchange_refresh_token_for_access_token(); - } else { - if (WebAuthenticationPane.is_cache_dirty()) { - 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.")); - return; - } - - do_hosted_web_authentication(); - } - } - protected abstract void on_login_flow_complete(); protected abstract void do_logout(); @@ -1185,6 +839,23 @@ public abstract class GooglePublisher : Object, Spit.Publishing.Publisher { public Spit.Publishing.Service get_service() { return service; } + + private void on_authenticator_authenticated() { + var params = this.authenticator.get_authentication_parameter(); + Variant refresh_token = null; + Variant access_token = null; + Variant user_name = null; + + params.lookup_extended("RefreshToken", null, out refresh_token); + params.lookup_extended("AccessToken", null, out access_token); + params.lookup_extended("UserName", null, out user_name); + + this.session.refresh_token = refresh_token.get_string(); + this.session.access_token = access_token.get_string(); + this.session.user_name = user_name.get_string(); + + this.on_login_flow_complete(); + } } } |