Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP2:Update
deja-dup
deja-dup-GoogleAuthChange.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File deja-dup-GoogleAuthChange.patch of Package deja-dup
diff --git a/data/meson.build b/data/meson.build index 41db2e04..11571c9e 100644 --- a/data/meson.build +++ b/data/meson.build @@ -3,6 +3,13 @@ # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: Michael Terry +google_client_id_parts = get_option('google_client_id').split('.') +google_client_id_parts_reversed = [] +foreach part : google_client_id_parts + google_client_id_parts_reversed = [part] + google_client_id_parts_reversed +endforeach +google_reversedns = '.'.join(google_client_id_parts_reversed) + conf_data = configuration_data() conf_data.set('appid', application_id) conf_data.set('bindir', bindir) @@ -11,6 +18,7 @@ conf_data.set('gsettingspath', profile == '' ? 'deja-dup' : 'deja-dup-' + profil conf_data.set('icon', application_id) conf_data.set('pkglibexecdir', pkglibexecdir) conf_data.set('profile', profile) +conf_data.set('scheme_google', google_reversedns) conf_data.set('version', meson.project_version()) install_data(join_paths('icons', '@0@.svg'.format(application_id)), diff --git a/data/org.gnome.DejaDup.desktop.in b/data/org.gnome.DejaDup.desktop.in index afa30e4a..5784ccef 100644 --- a/data/org.gnome.DejaDup.desktop.in +++ b/data/org.gnome.DejaDup.desktop.in @@ -7,7 +7,7 @@ Comment=Change your backup settings Icon=@icon@ -Exec=deja-dup +Exec=deja-dup %u StartupNotify=true DBusActivatable=true @@ -15,6 +15,9 @@ DBusActivatable=true Type=Application Categories=Utility;Archiving;GNOME;GTK;X-GNOME-Utilities; +# Used for oauth flows (server redirects to this custom scheme and we catch it) +MimeType=x-scheme-handler/@scheme_google@; + # Translators: Add whatever keywords you want in your language, separated by semicolons # These keywords are used when searching for applications in dashes, etc. Keywords=déjà;deja;dup; diff --git a/data/post-install.sh b/data/post-install.sh index 2de522d8..7648584b 100644 --- a/data/post-install.sh +++ b/data/post-install.sh @@ -12,4 +12,7 @@ if [ -z "$DESTDIR" ]; then echo "Updating gsettings cache..." glib-compile-schemas "$datadir/glib-2.0/schemas" + + echo "Updating desktop mime cache..." + update-desktop-database -q "$datadir/applications" fi diff --git a/deja-dup/AssistantOperation.vala b/deja-dup/AssistantOperation.vala index 08e77654..543c6c89 100644 --- a/deja-dup/AssistantOperation.vala +++ b/deja-dup/AssistantOperation.vala @@ -103,6 +103,11 @@ public abstract class AssistantOperation : Assistant delete_event.connect(do_minimize_to_tray); } + public DejaDup.Backend? get_backend() + { + return op == null ? null : op.backend; + } + /* * Creates confirmation page for particular assistant * diff --git a/deja-dup/main.vala b/deja-dup/main.vala index 93ef0adb..0bceba80 100644 --- a/deja-dup/main.vala +++ b/deja-dup/main.vala @@ -43,8 +43,14 @@ public class DejaDupApp : Gtk.Application public DejaDupApp() { - Object(application_id: Config.APPLICATION_ID, - flags: ApplicationFlags.HANDLES_COMMAND_LINE); + Object( + application_id: Config.APPLICATION_ID, + flags: ApplicationFlags.HANDLES_COMMAND_LINE | + // HANDLES_OPEN is required to support Open calls over dbus, which + // we use for our registered custom schemes (which support our + // oauth2 workflow). + ApplicationFlags.HANDLES_OPEN + ); add_main_option_entries(OPTIONS); } @@ -61,10 +67,11 @@ public class DejaDupApp : Gtk.Application { var options = command_line.get_options_dict(); - string[] filenames = {}; + File[] files = {}; if (options.contains("")) { var variant = options.lookup_value("", VariantType.BYTESTRING_ARRAY); - filenames = variant.get_bytestring_array(); + foreach (var filename in variant.get_bytestring_array()) + files += command_line.create_file_for_arg(filename); } if (options.contains("restore")) { @@ -74,10 +81,9 @@ public class DejaDupApp : Gtk.Application } List<File> file_list = new List<File>(); - if (filenames.length > 0) { - int i = 0; - while (filenames[i] != null) - file_list.append(command_line.create_file_for_arg(filenames[i++])); + if (files.length > 0) { + foreach (var file in files) + file_list.append(file); } restore_full(file_list); @@ -88,16 +94,16 @@ public class DejaDupApp : Gtk.Application return 1; } - if (filenames.length == 0) { + if (files.length == 0) { command_line.printerr("%s\n", _("No directory provided")); return 1; } - else if (filenames.length > 1) { + else if (files.length > 1) { command_line.printerr("%s\n", _("Only one directory can be shown at once")); return 1; } - File list_directory = command_line.create_file_for_arg(filenames[0]); + File list_directory = files[0]; if (!list_directory.query_exists(null)) { command_line.printerr("%s\n", _("Directory does not exist")); return 1; @@ -123,6 +129,14 @@ public class DejaDupApp : Gtk.Application } else if (options.contains("prompt")) { prompt(this); + } + else if (files.length > 0) { + // If we were called without a mode (like --restore) but with file arguments, + // let's do our "Open" action (which is mostly used for our oauth flow). + // That oauth flow can happen via command line in some environments like + // snaps, whereas the dbus Open call might happen for flatpaks. Regardless + // of how they come in, treat them the same. + open(files, ""); } else { activate(); } @@ -182,6 +196,28 @@ public class DejaDupApp : Gtk.Application } } + public override void open(GLib.File[] files, string hint) + { + var google_backend = this.op == null ? null : this.op.get_backend() as DejaDup.BackendGoogle; + + // We might be in middle of oauth flow, and are given an expected redirect uri + // like 'com.googleusercontent.apps.123:/oauth2redirect?code=xxx' + if (files.length == 1 && google_backend != null) + { + var provided_uri = files[0].get_uri(); + // Normalize backend URI through gio, so it matches incoming URI format (slashes after colon, etc) + var expected_uri = File.new_for_uri(google_backend.get_redirect_uri()).get_uri(); + if (provided_uri.has_prefix(expected_uri) && google_backend.continue_authorization(provided_uri)) { + activate(); + return; + } + } + + // Got passed files, but we don't know what to do with them. + foreach (var file in files) + warning("Ignoring unexpected file: %s", file.get_parse_name()); + } + bool exit_cleanly() { quit(); diff --git a/libdeja/BackendGoogle.vala b/libdeja/BackendGoogle.vala index 3f79da40..047ddcb1 100644 --- a/libdeja/BackendGoogle.vala +++ b/libdeja/BackendGoogle.vala @@ -23,9 +23,7 @@ public const string GOOGLE_SERVER = "google.com"; public class BackendGoogle : Backend { - Soup.Server server; Soup.Session session; - string local_address; string pkce; string credentials_dir; string access_token; @@ -443,7 +441,7 @@ public class BackendGoogle : Backend "GET", "https://accounts.google.com/o/oauth2/v2/auth", "client_id", Config.GOOGLE_CLIENT_ID, - "redirect_uri", local_address, + "redirect_uri", get_redirect_uri(), "response_type", "code", "code_challenge", pkce, "scope", "https://www.googleapis.com/auth/drive.file" @@ -457,7 +455,7 @@ public class BackendGoogle : Backend "POST", "https://www.googleapis.com/oauth2/v4/token", "client_id", Config.GOOGLE_CLIENT_ID, - "redirect_uri", local_address, + "redirect_uri", get_redirect_uri(), "grant_type", "authorization_code", "code_verifier", pkce, "code", code @@ -477,49 +475,12 @@ public class BackendGoogle : Backend yield get_tokens(message); } - void oauth_server_request_received(Soup.Server server, Soup.Message message, - string path, - HashTable<string, string>? query, - Soup.ClientContext client) - { - if (path != "/") { - message.status_code = Soup.Status.NOT_FOUND; - return; - } - - message.status_code = Soup.Status.ACCEPTED; - server = null; - - string? error = query == null ? null : query.lookup("error"); - if (error != null) { - stop_login(error); - return; - } - - string? code = query == null ? null : query.lookup("code"); - if (code == null) { - stop_login(null); - return; - } - - // Show consent granted screen - var html = DejaDup.get_access_granted_html(); - message.response_body.append_take(html.data); - - show_oauth_consent_page(null, null); // continue on from paused screen - get_credentials.begin(code); - } - void start_authorization() throws Error { - // Start a server and listen on it - server = new Soup.Server("server-header", - "%s/%s ".printf(Config.PACKAGE, Config.VERSION)); - server.listen_local(0, Soup.ServerListenOptions.IPV4_ONLY); - local_address = server.get_uris().data.to_string(false); - // Prepare to handle requests that finish the consent process - server.add_handler(null, oauth_server_request_received); + error_msg = null; + code = null; + authorization_instance.set(this); // We need a random string between 43 and 128 chars. UUIDs are an easy way // to get random strings, but they are only 37 chars long. So just add two. @@ -589,6 +550,53 @@ public class BackendGoogle : Backend envp.append("GOOGLE_DRIVE_SETTINGS=%s/settings.yaml".printf(credentials_dir)); envp_ready(true, envp); } + + static WeakRef authorization_instance; + string error_msg; + string code; + + public string get_redirect_uri() + { + var id_parts = Config.GOOGLE_CLIENT_ID.split("."); + string[] reversed = {}; + for (int i = id_parts.length - 1; i >= 0; i--) { + reversed += id_parts[i]; + } + // Exact path does not matter and is optional. But it seems wise to use some + // path and this one seems reasonable (and is the example in their oauth docs). + return "%s:/oauth2redirect".printf(string.joinv(".", reversed)); + } + + // Meant to be called externally when the oauth service redirects back to us. + // This passed-in uri will hold the code and error values from the service. + public bool continue_authorization(string command_line_redirect_uri) + { + if (authorization_instance.get() == null) + return false; // no auth in progress + + var active_backend = (BackendGoogle)authorization_instance.get(); + active_backend.continue_authorization_helper(command_line_redirect_uri); + return true; + } + + void continue_authorization_helper(string command_line_redirect_uri) + { + var uri = new Soup.URI(command_line_redirect_uri); + var query = Soup.Form.decode(uri.query); + + if (error_msg == null && query != null) + error_msg = query.lookup("error"); + + if (error_msg == null && query != null) + code = query.lookup("code"); + + if (error_msg == null && code == null) + error_msg = ""; // default to just a blank contextual error message + + authorization_instance.set(null); + show_oauth_consent_page(null, null); // continue on from paused screen + get_credentials.begin(code); + } } } // end namespace
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor