From e1cce7348411c6790044d2c9cd96b8696ca5c126 Mon Sep 17 00:00:00 2001 From: Andrea Azzarone Date: Mon, 4 Jun 2018 14:51:47 -0500 Subject: [PATCH 1/2] auth: Use gnome-online-accounts to handle the authentication --- .gitlab-ci.yml | 1 + contrib/gnome-software.spec.in | 2 +- data/org.gnome.software.gschema.xml | 7 + lib/gs-auth.c | 793 +++++++++------------------- lib/gs-auth.h | 96 +--- lib/gs-plugin-job.c | 2 +- lib/gs-plugin-loader.c | 15 +- lib/gs-plugin-loader.h | 2 +- lib/gs-plugin-types.h | 14 - lib/gs-plugin-vfuncs.h | 64 --- lib/gs-plugin.c | 36 +- lib/gs-plugin.h | 2 +- lib/gs-self-test.c | 49 -- lib/meson.build | 6 +- meson.build | 2 +- plugins/dummy/gs-plugin-dummy.c | 103 +--- plugins/dummy/gs-self-test.c | 83 --- plugins/dummy/meson.build | 2 +- plugins/meson.build | 1 + plugins/snap/gs-plugin-snap.c | 187 ++----- src/gs-auth-dialog.c | 559 ++++++++++++-------- src/gs-auth-dialog.h | 8 +- src/gs-auth-dialog.ui | 383 ++++---------- src/gs-page.c | 8 +- src/gs-page.h | 2 +- src/gs-shell.c | 170 +++--- src/meson.build | 4 +- 27 files changed, 889 insertions(+), 1712 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d99b81b2..900932057 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,6 +11,7 @@ before_script: # Some deps may not be sync'd - dnf -y install rpm-ostree-devel - dnf -y install libstemmer-devel + - dnf -y install gnome-online-accounts-devel build-gnome-software: stage: build diff --git a/contrib/gnome-software.spec.in b/contrib/gnome-software.spec.in index 0dec552d6..aba9e4871 100644 --- a/contrib/gnome-software.spec.in +++ b/contrib/gnome-software.spec.in @@ -34,11 +34,11 @@ BuildRequires: gtk3-devel >= %{gtk3_version} BuildRequires: gtk-doc BuildRequires: json-glib-devel >= %{json_glib_version} BuildRequires: libappstream-glib-devel >= %{appstream_glib_version} +BuildRequires: gnome-online-accounts-devel BuildRequires: libsoup-devel BuildRequires: meson BuildRequires: PackageKit-glib-devel >= %{packagekit_version} BuildRequires: polkit-devel -BuildRequires: libsecret-devel BuildRequires: flatpak-devel >= %{flatpak_version} BuildRequires: ostree-devel BuildRequires: rpm-ostree-devel diff --git a/data/org.gnome.software.gschema.xml b/data/org.gnome.software.gschema.xml index fab37c9e3..97780e0d3 100644 --- a/data/org.gnome.software.gschema.xml +++ b/data/org.gnome.software.gschema.xml @@ -139,5 +139,12 @@ true Enable GNOME Shell extensions repository + + + + + '' + A string storing the gnome-online-account id used to login + diff --git a/lib/gs-auth.c b/lib/gs-auth.c index 098efd13a..ecd60e500 100644 --- a/lib/gs-auth.c +++ b/lib/gs-auth.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2016 Richard Hughes + * Copyright (C) 2018 Canonical Ltd * * Licensed under the GNU General Public License Version 2 * @@ -32,612 +33,307 @@ #include "config.h" -#include - #include "gs-auth.h" #include "gs-plugin.h" struct _GsAuth { - GObject parent_instance; - - GsAuthFlags flags; - gchar *provider_id; - gchar *provider_name; - gchar *provider_logo; - gchar *provider_uri; - gchar *provider_schema; - gchar *username; - gchar *password; - gchar *pin; - GHashTable *metadata; /* utf8: utf8 */ + GObject parent_instance; + + gchar *header_none; + gchar *header_single; + gchar *header_multiple; + + gchar *auth_id; + gchar *provider_name; + gchar *provider_type; + + GoaClient *goa_client; + GoaObject *goa_object; + + GSettings *settings; +}; + +static void gs_auth_initable_iface_init (GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GsAuth, gs_auth, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, gs_auth_initable_iface_init)) + +enum { + SIGNAL_CHANGED, + SIGNAL_LAST }; enum { PROP_0, - PROP_USERNAME, - PROP_PASSWORD, - PROP_PIN, - PROP_FLAGS, + PROP_AUTH_ID, + PROP_PROVIDER_TYPE, + PROP_GOA_OBJECT, PROP_LAST }; -G_DEFINE_TYPE (GsAuth, gs_auth, G_TYPE_OBJECT) +static guint signals [SIGNAL_LAST] = { 0 }; -/** - * gs_auth_get_provider_id: - * @auth: a #GsAuth - * - * Gets the authentication service ID. - * - * Returns: the string to use for searching, e.g. "UbuntuOne" - */ -const gchar * -gs_auth_get_provider_id (GsAuth *auth) -{ - g_return_val_if_fail (GS_IS_AUTH (auth), NULL); - return auth->provider_id; -} /** - * gs_auth_get_provider_name: + * gs_auth_get_header: * @auth: a #GsAuth + * @n: the number of accounts * - * Gets the authentication service name. + * Gets the header to be used in the authentication dialog in case there are @n + * available accounts. * - * Returns: the string to show in the UI + * Returns: (transfer none) : a string */ const gchar * -gs_auth_get_provider_name (GsAuth *auth) +gs_auth_get_header (GsAuth *auth, guint n) { g_return_val_if_fail (GS_IS_AUTH (auth), NULL); - return auth->provider_name; + + if (n == 0) + return auth->header_none; + else if (n == 1) + return auth->header_single; + else + return auth->header_multiple; } /** - * gs_auth_set_provider_name: + * gs_auth_set_header: * @auth: a #GsAuth - * @provider_name: a service name, e.g. "GNOME Online Accounts" + * @header_none: the header to be used if no account is present + * @header_single: the header to be used if one account is present + * @header_multiple: the header to be used if two or more accounts are present * - * Sets the name to be used for the authentication dialog. + * Sets the headers to be used for the authentication dialog. */ void -gs_auth_set_provider_name (GsAuth *auth, const gchar *provider_name) +gs_auth_set_header (GsAuth *auth, + const gchar *header_none, + const gchar *header_single, + const gchar *header_multiple) { g_return_if_fail (GS_IS_AUTH (auth)); - g_free (auth->provider_name); - auth->provider_name = g_strdup (provider_name); + g_return_if_fail (header_none != NULL); + g_return_if_fail (header_single != NULL); + g_return_if_fail (header_multiple != NULL); + + g_free (auth->header_none); + auth->header_none = g_strdup (header_none); + + g_free (auth->header_single); + auth->header_single = g_strdup (header_single); + + g_free (auth->header_multiple); + auth->header_multiple = g_strdup (header_multiple); } /** - * gs_auth_get_provider_logo: + * gs_auth_get_auth_id: * @auth: a #GsAuth * - * Gets the authentication service image. + * Gets the authentication service ID. * - * Returns: the filename of an image, or %NULL + * Returns: (transfer none): a string */ const gchar * -gs_auth_get_provider_logo (GsAuth *auth) +gs_auth_get_auth_id (GsAuth *auth) { g_return_val_if_fail (GS_IS_AUTH (auth), NULL); - return auth->provider_logo; -} - -/** - * gs_auth_set_provider_logo: - * @auth: a #GsAuth - * @provider_logo: an image, e.g. "/usr/share/icons/gnome-online.png" - * - * Sets the image to be used for the authentication dialog. - */ -void -gs_auth_set_provider_logo (GsAuth *auth, const gchar *provider_logo) -{ - g_return_if_fail (GS_IS_AUTH (auth)); - g_free (auth->provider_logo); - auth->provider_logo = g_strdup (provider_logo); + return auth->auth_id; } /** - * gs_auth_get_provider_uri: + * gs_auth_get_provider_name: * @auth: a #GsAuth * - * Gets the authentication service website. + * Gets the authentication service name. * - * Returns: the URI, or %NULL + * Returns: (transfer none): a string */ const gchar * -gs_auth_get_provider_uri (GsAuth *auth) +gs_auth_get_provider_name (GsAuth *auth) { g_return_val_if_fail (GS_IS_AUTH (auth), NULL); - return auth->provider_uri; + return auth->provider_name; } /** - * gs_auth_set_provider_uri: + * gs_auth_set_provider_name: * @auth: a #GsAuth - * @provider_uri: a URI, e.g. "http://www.gnome.org/sso" + * @provider_name: a service name, e.g. "Snap Store" * - * Sets the website to be used for the authentication dialog. + * Sets the name to be used for the authentication dialog. */ void -gs_auth_set_provider_uri (GsAuth *auth, const gchar *provider_uri) +gs_auth_set_provider_name (GsAuth *auth, const gchar *provider_name) { g_return_if_fail (GS_IS_AUTH (auth)); - g_free (auth->provider_uri); - auth->provider_uri = g_strdup (provider_uri); + g_return_if_fail (provider_name != NULL); + + g_free (auth->provider_name); + auth->provider_name = g_strdup (provider_name); } /** - * gs_auth_get_provider_schema: + * gs_auth_get_provider_type: * @auth: a #GsAuth * - * Gets the authentication schema ID. + * Gets the GoaProvider type to be used for the authentication dialog. * - * Returns: the URI, or %NULL + * Returns: (transfer none): a string */ const gchar * -gs_auth_get_provider_schema (GsAuth *auth) +gs_auth_get_provider_type (GsAuth *auth) { g_return_val_if_fail (GS_IS_AUTH (auth), NULL); - return auth->provider_schema; + return auth->provider_type; } /** - * gs_auth_set_provider_schema: + * gs_auth_peek_goa_object: * @auth: a #GsAuth - * @provider_schema: a URI, e.g. "com.distro.provider" * - * Sets the schema ID to be used for saving the state to disk. - */ -void -gs_auth_set_provider_schema (GsAuth *auth, const gchar *provider_schema) -{ - g_return_if_fail (GS_IS_AUTH (auth)); - g_free (auth->provider_schema); - auth->provider_schema = g_strdup (provider_schema); -} - -/** - * gs_auth_get_username: - * @auth: a #GsAuth - * - * Gets the auth username. + * Gets the logged #GoaObject if any. * - * Returns: the username to be used for the authentication + * Returns: (transfer none) (nullable): a #GoaObject, or %NULL */ -const gchar * -gs_auth_get_username (GsAuth *auth) +GoaObject * +gs_auth_peek_goa_object (GsAuth *auth) { g_return_val_if_fail (GS_IS_AUTH (auth), NULL); - return auth->username; + return auth->goa_object; } -/** - * gs_auth_set_username: - * @auth: a #GsAuth - * @username: a username, e.g. "hughsie" - * - * Sets the username to be used for the authentication. - */ -void -gs_auth_set_username (GsAuth *auth, const gchar *username) +static gboolean +gs_auth_goa_account_equal (GoaAccount *acc1, GoaAccount *acc2) { - g_return_if_fail (GS_IS_AUTH (auth)); - g_free (auth->username); - auth->username = g_strdup (username); -} + if (acc1 == acc2) + return TRUE; -/** - * gs_auth_get_password: - * @auth: a #GsAuth - * - * Gets the password to be used for the authentication. - * - * Returns: the string, or %NULL - **/ -const gchar * -gs_auth_get_password (GsAuth *auth) -{ - g_return_val_if_fail (GS_IS_AUTH (auth), NULL); - return auth->password; -} + if (acc1 == NULL || acc2 == NULL) + return FALSE; -/** - * gs_auth_set_password: - * @auth: a #GsAuth - * @password: password string, e.g. "p@ssw0rd" - * - * Sets the password to be used for the authentication. - */ -void -gs_auth_set_password (GsAuth *auth, const gchar *password) -{ - g_return_if_fail (GS_IS_AUTH (auth)); - g_free (auth->password); - auth->password = g_strdup (password); + return !g_strcmp0 (goa_account_get_id (acc1), + goa_account_get_id (acc2)); } -/** - * gs_auth_get_flags: - * @auth: a #GsAuth - * - * Gets any flags set on the authentication, for example if we should remember - * credentials. - * - * Returns: a #GsAuthFlags, e.g. %GS_AUTH_FLAG_REMEMBER - */ -GsAuthFlags -gs_auth_get_flags (GsAuth *auth) +static gboolean +gs_auth_goa_object_equal (GoaObject *obj1, GoaObject *obj2) { - g_return_val_if_fail (GS_IS_AUTH (auth), 0); - return auth->flags; -} + if (obj1 == obj2) + return TRUE; -/** - * gs_auth_set_flags: - * @auth: a #GsAuth - * @flags: a #GsAuthFlags, e.g. %GS_AUTH_FLAG_REMEMBER - * - * Gets any flags set on the authentication. - */ -void -gs_auth_set_flags (GsAuth *auth, GsAuthFlags flags) -{ - g_return_if_fail (GS_IS_AUTH (auth)); - auth->flags = flags; -} + if (obj1 == NULL || obj2 == NULL) + return FALSE; -/** - * gs_auth_add_flags: - * @auth: a #GsAuth - * @flags: a #GsAuthFlags, e.g. %GS_AUTH_FLAG_REMEMBER - * - * Adds flags to an existing authentication without replacing the other flags. - */ -void -gs_auth_add_flags (GsAuth *auth, GsAuthFlags flags) -{ - g_return_if_fail (GS_IS_AUTH (auth)); - auth->flags |= flags; + return gs_auth_goa_account_equal(goa_object_peek_account (obj1), + goa_object_peek_account (obj2)); } -/** - * gs_auth_has_flag: - * @auth: a #GsAuth - * @flags: a #GsAuthFlags, e.g. %GS_AUTH_FLAG_REMEMBER - * - * Finds out if the authentication has a flag. - * - * Returns: %TRUE if set - */ -gboolean -gs_auth_has_flag (GsAuth *auth, GsAuthFlags flags) +static void +gs_auth_account_changed_cb (GoaClient *client, + GoaObject *goa_object, + GsAuth *auth) { - g_return_val_if_fail (GS_IS_AUTH (auth), FALSE); - return (auth->flags & flags) > 0; -} + if (!gs_auth_goa_object_equal (auth->goa_object, goa_object)) + return; -/** - * gs_auth_get_pin: - * @auth: a #GsAuth - * - * Gets the PIN code. - * - * Returns: the 2 factor authentication PIN, or %NULL - **/ -const gchar * -gs_auth_get_pin (GsAuth *auth) -{ - g_return_val_if_fail (GS_IS_AUTH (auth), NULL); - return auth->pin; + g_signal_emit (auth, signals[SIGNAL_CHANGED], 0); } -/** - * gs_auth_set_pin: - * @auth: a #GsAuth - * @pin: the PIN code, e.g. "12345" - * - * Sets the 2 factor authentication PIN, which can be left unset. - */ -void -gs_auth_set_pin (GsAuth *auth, const gchar *pin) +static void +gs_auth_account_removed_cb (GoaClient *client, + GoaObject *goa_object, + GsAuth *auth) { - g_return_if_fail (GS_IS_AUTH (auth)); - g_free (auth->pin); - auth->pin = g_strdup (pin); -} + if (!gs_auth_goa_object_equal (auth->goa_object, goa_object)) + return; -/** - * gs_auth_get_metadata_item: - * @auth: a #GsAuth - * @key: a string - * - * Gets some metadata from a authentication object. - * It is left for the the plugin to use this method as required, but a - * typical use would be to retrieve some secure auth token. - * - * Returns: A string value, or %NULL for not found - */ -const gchar * -gs_auth_get_metadata_item (GsAuth *auth, const gchar *key) -{ - g_return_val_if_fail (GS_IS_AUTH (auth), NULL); - g_return_val_if_fail (key != NULL, NULL); - return g_hash_table_lookup (auth->metadata, key); + gs_auth_set_goa_object (auth, NULL); } /** - * gs_auth_add_metadata: + * gs_auth_set_goa_object: * @auth: a #GsAuth - * @key: a string - * @value: a string + * @goa_object: (nullable) a #GoaObject * - * Adds metadata to the authentication object. - * It is left for the the plugin to use this method as required, but a - * typical use would be to store some secure auth token. + * Set the #GoaObject used to login in. */ void -gs_auth_add_metadata (GsAuth *auth, const gchar *key, const gchar *value) +gs_auth_set_goa_object (GsAuth *auth, + GoaObject *goa_object) { g_return_if_fail (GS_IS_AUTH (auth)); - g_hash_table_insert (auth->metadata, g_strdup (key), g_strdup (value)); -} - -static gboolean -_g_error_is_set (GError **error) -{ - if (error == NULL) - return FALSE; - return *error != NULL; -} - -/** - * gs_auth_store_load: - * @auth: a #GsAuth - * @flags: some #GsAuthStoreFlags, e.g. %GS_AUTH_STORE_FLAG_USERNAME - * @cancellable: a #GCancellable or %NULL - * @error: a #GError or %NULL - * - * Loads authentication tokens from disk in a secure way. - * By default only the username and password are loaded, but they are not - * overwritten if already set. - * - * If additional tokens are required to be loaded you must first tell the - * GsAuth instance what metadata to load. This can be done using: - * `gs_auth_add_metadata("additional-secret-key-name",NULL)` - * - * This function is expected to be called from gs_plugin_setup(). - * - * Returns: %TRUE if the tokens were loaded correctly. - */ -gboolean -gs_auth_store_load (GsAuth *auth, GsAuthStoreFlags flags, - GCancellable *cancellable, GError **error) -{ - SecretSchema schema = { - auth->provider_schema, - SECRET_SCHEMA_NONE, - { { "key", SECRET_SCHEMA_ATTRIBUTE_STRING } } - }; - - /* no schema */ - if (auth->provider_schema == NULL) { - g_set_error (error, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_FAILED, - "No provider schema set for %s", - auth->provider_id); - return FALSE; - } - /* username */ - if ((flags & GS_AUTH_STORE_FLAG_USERNAME) > 0 && auth->username == NULL) { - auth->username = secret_password_lookup_sync (&schema, - cancellable, - error, - "key", "username", - NULL); - if (_g_error_is_set (error)) - return FALSE; - } - - /* password */ - if ((flags & GS_AUTH_STORE_FLAG_PASSWORD) > 0 && auth->password == NULL) { - auth->password = secret_password_lookup_sync (&schema, - cancellable, - error, - "key", "password", - NULL); - if (_g_error_is_set (error)) - return FALSE; - } + if (gs_auth_goa_object_equal (auth->goa_object, goa_object)) + return; - /* metadata */ - if (flags & GS_AUTH_STORE_FLAG_METADATA) { - g_autoptr(GList) keys = NULL; - keys = g_hash_table_get_keys (auth->metadata); - for (GList *l = keys; l != NULL; l = l->next) { - g_autofree gchar *tmp = NULL; - const gchar *key = l->data; - const gchar *value = g_hash_table_lookup (auth->metadata, key); - if (value != NULL) - continue; - tmp = secret_password_lookup_sync (&schema, - cancellable, - error, - "key", key, - NULL); - if (_g_error_is_set (error)) - return FALSE; - if (tmp != NULL) - gs_auth_add_metadata (auth, key, tmp); - } - } + g_clear_object (&auth->goa_object); + if (goa_object) + auth->goa_object = g_object_ref (goa_object); - /* success */ - return TRUE; + g_object_notify (G_OBJECT (auth), "goa-object"); + g_signal_emit (auth, signals[SIGNAL_CHANGED], 0); } -/** - * gs_auth_store_save: - * @auth: a #GsAuth - * @flags: some #GsAuthStoreFlags, e.g. %GS_AUTH_STORE_FLAG_USERNAME - * @cancellable: a #GCancellable or %NULL - * @error: a #GError or %NULL - * - * Saves the username, password and all added metadata to disk in a secure way. - * - * This function is expected to be called from gs_plugin_setup(). - * - * Returns: %TRUE if the tokens were all saved correctly. - */ -gboolean -gs_auth_store_save (GsAuth *auth, GsAuthStoreFlags flags, - GCancellable *cancellable, GError **error) +static gboolean +string_to_goa_object (GValue *value, + GVariant *variant, + gpointer user_data) { - SecretSchema schema = { - auth->provider_schema, - SECRET_SCHEMA_NONE, - { { "key", SECRET_SCHEMA_ATTRIBUTE_STRING } } - }; - - /* no schema */ - if (auth->provider_schema == NULL) { - g_set_error (error, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_FAILED, - "No provider schema set for %s", - auth->provider_id); - return FALSE; - } + GsAuth *auth = GS_AUTH (user_data); + GoaObject *goa_object; + const gchar *account_id; - /* username */ - if ((flags & GS_AUTH_STORE_FLAG_USERNAME) > 0 && auth->username != NULL) { - if (!secret_password_store_sync (&schema, - NULL, /* collection */ - auth->provider_schema, - auth->username, - cancellable, error, - "key", "username", NULL)) - return FALSE; - } - - /* password */ - if ((flags & GS_AUTH_STORE_FLAG_PASSWORD) > 0 && auth->password != NULL) { - if (!secret_password_store_sync (&schema, - NULL, /* collection */ - auth->provider_schema, - auth->password, - cancellable, error, - "key", "password", NULL)) - return FALSE; - } + account_id = g_variant_get_string (variant, NULL); - /* metadata */ - if (flags & GS_AUTH_STORE_FLAG_METADATA) { - g_autoptr(GList) keys = NULL; - keys = g_hash_table_get_keys (auth->metadata); - for (GList *l = keys; l != NULL; l = l->next) { - const gchar *key = l->data; - const gchar *value = g_hash_table_lookup (auth->metadata, key); - if (value == NULL) - continue; - if (!secret_password_store_sync (&schema, - NULL, /* collection */ - auth->provider_schema, - value, - cancellable, error, - "key", key, NULL)) - return FALSE; - } - } + goa_object = goa_client_lookup_by_id (auth->goa_client, account_id); + if (!goa_object) + return TRUE; - /* success */ + g_value_take_object (value, goa_object); return TRUE; } -gboolean -gs_auth_store_clear (GsAuth *auth, GsAuthStoreFlags flags, - GCancellable *cancellable, GError **error) +static GVariant * +goa_object_to_string (const GValue *value, + const GVariantType *expected_type, + gpointer user_data) { - SecretSchema schema = { - auth->provider_schema, - SECRET_SCHEMA_NONE, - { { "key", SECRET_SCHEMA_ATTRIBUTE_STRING } } - }; - - /* no schema */ - if (auth->provider_schema == NULL) { - g_set_error (error, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_FAILED, - "No provider schema set for %s", - auth->provider_id); - return FALSE; - } + GObject *object = g_value_get_object (value); - /* username */ - if ((flags & GS_AUTH_STORE_FLAG_USERNAME) > 0) { - if (!secret_password_clear_sync (&schema, - cancellable, error, - "key", "username", NULL)) - return FALSE; - } + GoaObject *goa_object = object != NULL ? GOA_OBJECT (object) : NULL; + GoaAccount *goa_account = goa_object != NULL ? goa_object_peek_account (goa_object) : NULL; - g_free (auth->username); - auth->username = NULL; - - /* password */ - if ((flags & GS_AUTH_STORE_FLAG_PASSWORD) > 0) { - if (!secret_password_clear_sync (&schema, - cancellable, error, - "key", "password", NULL)) - return FALSE; - } - - g_free (auth->password); - auth->password = NULL; - - /* metadata */ - if (flags & GS_AUTH_STORE_FLAG_METADATA) { - g_autoptr(GList) keys = NULL; - keys = g_hash_table_get_keys (auth->metadata); - for (GList *l = keys; l != NULL; l = l->next) { - const gchar *key = l->data; - if (!secret_password_clear_sync (&schema, - cancellable, error, - "key", key, NULL)) - return FALSE; - - } - } + if (goa_account != NULL) + return g_variant_new_string (goa_account_get_id (goa_account)); + else + return g_variant_new_string (""); +} - g_hash_table_remove_all (auth->metadata); +/* GObject */ - /* success */ - return TRUE; +static void +gs_auth_init (GsAuth *auth) +{ } static void gs_auth_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) + GValue *value, GParamSpec *pspec) { GsAuth *auth = GS_AUTH (object); switch (prop_id) { - case PROP_USERNAME: - g_value_set_string (value, auth->username); - break; - case PROP_PASSWORD: - g_value_set_string (value, auth->password); + case PROP_AUTH_ID: + g_value_set_string (value, auth->auth_id); break; - case PROP_FLAGS: - g_value_set_uint64 (value, auth->flags); + case PROP_PROVIDER_TYPE: + g_value_set_string (value, auth->provider_type); break; - case PROP_PIN: - g_value_set_string (value, auth->pin); + case PROP_GOA_OBJECT: + g_value_set_object (value, auth->goa_object); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -647,22 +343,19 @@ gs_auth_get_property (GObject *object, guint prop_id, static void gs_auth_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) + const GValue *value, GParamSpec *pspec) { GsAuth *auth = GS_AUTH (object); switch (prop_id) { - case PROP_USERNAME: - gs_auth_set_username (auth, g_value_get_string (value)); + case PROP_AUTH_ID: + auth->auth_id = g_value_dup_string (value); break; - case PROP_PASSWORD: - gs_auth_set_password (auth, g_value_get_string (value)); + case PROP_PROVIDER_TYPE: + auth->provider_type = g_value_dup_string (value); break; - case PROP_FLAGS: - gs_auth_set_flags (auth, g_value_get_uint64 (value)); - break; - case PROP_PIN: - gs_auth_set_pin (auth, g_value_get_string (value)); + case PROP_GOA_OBJECT: + gs_auth_set_goa_object (auth, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -675,15 +368,16 @@ gs_auth_finalize (GObject *object) { GsAuth *auth = GS_AUTH (object); - g_free (auth->provider_id); + g_free (auth->header_none); + g_free (auth->header_single); + g_free (auth->header_multiple); + g_free (auth->auth_id); g_free (auth->provider_name); - g_free (auth->provider_logo); - g_free (auth->provider_uri); - g_free (auth->provider_schema); - g_free (auth->username); - g_free (auth->password); - g_free (auth->pin); - g_hash_table_unref (auth->metadata); + + g_clear_object (&auth->goa_client); + g_clear_object (&auth->goa_object); + + g_clear_object (&auth->settings); G_OBJECT_CLASS (gs_auth_parent_class)->finalize (object); } @@ -693,64 +387,99 @@ gs_auth_class_init (GsAuthClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = gs_auth_finalize; object_class->get_property = gs_auth_get_property; object_class->set_property = gs_auth_set_property; - /** - * GsAuth:username: - */ - pspec = g_param_spec_string ("username", NULL, NULL, - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_USERNAME, pspec); - - /** - * GsAuth:password: - */ - pspec = g_param_spec_string ("password", NULL, NULL, - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_PASSWORD, pspec); - - /** - * GsAuth:flags: - */ - pspec = g_param_spec_uint64 ("flags", NULL, NULL, - GS_AUTH_FLAG_NONE, - GS_AUTH_FLAG_LAST, - GS_AUTH_FLAG_NONE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_FLAGS, pspec); - - /** - * GsAuth:pin: - */ - pspec = g_param_spec_string ("pin", NULL, NULL, - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_PIN, pspec); + pspec = g_param_spec_string ("auth-id", NULL, NULL, NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_AUTH_ID, pspec); + + pspec = g_param_spec_string ("provider-type", NULL, NULL, NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_PROVIDER_TYPE, pspec); + + pspec = g_param_spec_object ("goa-object", NULL, NULL, + GOA_TYPE_OBJECT, + G_PARAM_READWRITE | + G_PARAM_EXPLICIT_NOTIFY); + g_object_class_install_property (object_class, PROP_GOA_OBJECT, pspec); + + signals [SIGNAL_CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_generic, + G_TYPE_NONE, 0); +} + +/* GInitable */ + +static gboolean +gs_auth_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GsAuth *self; + g_autofree gchar *path = NULL; + + g_return_val_if_fail (GS_IS_AUTH (initable), FALSE); + + self = GS_AUTH (initable); + + self->goa_client = goa_client_new_sync (NULL, error); + if (self->goa_client == NULL) + return FALSE; + + g_signal_connect (self->goa_client, "account-changed", + G_CALLBACK (gs_auth_account_changed_cb), self); + g_signal_connect (self->goa_client, "account-removed", + G_CALLBACK (gs_auth_account_removed_cb), self); + + path = g_strdup_printf ("/org/gnome/software/auth/%s/", self->auth_id); + self->settings = g_settings_new_with_path ("org.gnome.software.auth", path); + + g_settings_bind_with_mapping (self->settings, "account-id", + self, "goa-object", + G_SETTINGS_BIND_DEFAULT, + string_to_goa_object, + goa_object_to_string, + self, NULL); + + return TRUE; } static void -gs_auth_init (GsAuth *auth) +gs_auth_initable_iface_init (GInitableIface *iface) { - auth->metadata = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_free); + iface->init = gs_auth_initable_init; } /** * gs_auth_new: - * @provider_id: a provider ID used for mapping, e.g. "GnomeSSO" + * @auth_id: an identifier used for mapping, e.g. "snapd" + * @provider_type: the name of the GoaProvider466 to be used, e.g. "ubuntusso" + * @error: A #GError * - * Return value: a new #GsAuth object. + * Return value: (transfer full) (nullable): a new #GsAuth object. **/ GsAuth * -gs_auth_new (const gchar *provider_id) +gs_auth_new (const gchar *auth_id, + const gchar *provider_type, + GError **error) { GsAuth *auth; - auth = g_object_new (GS_TYPE_AUTH, NULL); - auth->provider_id = g_strdup (provider_id); + + g_return_val_if_fail (auth_id != NULL, NULL); + g_return_val_if_fail (provider_type != NULL, NULL); + + auth = g_initable_new (GS_TYPE_AUTH, NULL, error, + "auth-id", auth_id, + "provider-type", provider_type, + NULL); + return GS_AUTH (auth); } diff --git a/lib/gs-auth.h b/lib/gs-auth.h index 55df1a7d6..c2018f67a 100644 --- a/lib/gs-auth.h +++ b/lib/gs-auth.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2016 Richard Hughes + * Copyright (C) 2018 Canonical Ltd * * Licensed under the GNU General Public License Version 2 * @@ -24,6 +25,8 @@ #include #include +#define GOA_API_IS_SUBJECT_TO_CHANGE +#include G_BEGIN_DECLS @@ -31,88 +34,23 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (GsAuth, gs_auth, GS, AUTH, GObject) -/** - * GsAuthFlags: - * @GS_AUTH_FLAG_NONE: No special flags set - * @GS_AUTH_FLAG_VALID: Authorisation is valid - * @GS_AUTH_FLAG_REMEMBER: Remember this authentication if possible - * - * The flags for the auth. - **/ -typedef enum { - GS_AUTH_FLAG_NONE = 0, - GS_AUTH_FLAG_VALID = 1 << 0, - GS_AUTH_FLAG_REMEMBER = 1 << 1, - /*< private >*/ - GS_AUTH_FLAG_LAST -} GsAuthFlags; - -/** - * GsAuthStoreFlags: - * @GS_AUTH_STORE_FLAG_NONE: No special flags set - * @GS_AUTH_STORE_FLAG_USERNAME: Load or save the username - * @GS_AUTH_STORE_FLAG_PASSWORD: Load or save the password - * @GS_AUTH_STORE_FLAG_METADATA: Load or save any metadata - * - * The flags used when loading or saving the authentication to disk. - **/ -typedef enum { - GS_AUTH_STORE_FLAG_NONE = 0, - GS_AUTH_STORE_FLAG_USERNAME = 1 << 0, - GS_AUTH_STORE_FLAG_PASSWORD = 1 << 1, - GS_AUTH_STORE_FLAG_METADATA = 1 << 2, - /*< private >*/ - GS_AUTH_STORE_FLAG_LAST -} GsAuthStoreFlags; - -GsAuth *gs_auth_new (const gchar *provider_id); -const gchar *gs_auth_get_provider_id (GsAuth *auth); +GsAuth *gs_auth_new (const gchar *auth_id, + const gchar *provider_type, + GError **error); +const gchar *gs_auth_get_header (GsAuth *auth, + guint n); +void gs_auth_set_header (GsAuth *auth, + const gchar *header_none, + const gchar *header_single, + const gchar *header_multiple); +const gchar *gs_auth_get_auth_id (GsAuth *auth); const gchar *gs_auth_get_provider_name (GsAuth *auth); void gs_auth_set_provider_name (GsAuth *auth, const gchar *provider_name); -const gchar *gs_auth_get_provider_logo (GsAuth *auth); -void gs_auth_set_provider_logo (GsAuth *auth, - const gchar *provider_logo); -const gchar *gs_auth_get_provider_uri (GsAuth *auth); -void gs_auth_set_provider_uri (GsAuth *auth, - const gchar *provider_uri); -const gchar *gs_auth_get_provider_schema (GsAuth *auth); -void gs_auth_set_provider_schema (GsAuth *auth, - const gchar *provider_schema); -const gchar *gs_auth_get_username (GsAuth *auth); -void gs_auth_set_username (GsAuth *auth, - const gchar *username); -const gchar *gs_auth_get_password (GsAuth *auth); -void gs_auth_set_password (GsAuth *auth, - const gchar *password); -const gchar *gs_auth_get_pin (GsAuth *auth); -void gs_auth_set_pin (GsAuth *auth, - const gchar *pin); -GsAuthFlags gs_auth_get_flags (GsAuth *auth); -void gs_auth_set_flags (GsAuth *auth, - GsAuthFlags flags); -void gs_auth_add_flags (GsAuth *auth, - GsAuthFlags flags); -gboolean gs_auth_has_flag (GsAuth *auth, - GsAuthFlags flags); -const gchar *gs_auth_get_metadata_item (GsAuth *auth, - const gchar *key); -void gs_auth_add_metadata (GsAuth *auth, - const gchar *key, - const gchar *value); -gboolean gs_auth_store_load (GsAuth *auth, - GsAuthStoreFlags flags, - GCancellable *cancellable, - GError **error); -gboolean gs_auth_store_save (GsAuth *auth, - GsAuthStoreFlags flags, - GCancellable *cancellable, - GError **error); -gboolean gs_auth_store_clear (GsAuth *auth, - GsAuthStoreFlags flags, - GCancellable *cancellable, - GError **error); - +const gchar *gs_auth_get_provider_type (GsAuth *auth); +GoaObject *gs_auth_peek_goa_object (GsAuth *auth); +void gs_auth_set_goa_object (GsAuth *auth, + GoaObject *goa_object); G_END_DECLS #endif /* __GS_AUTH_H */ diff --git a/lib/gs-plugin-job.c b/lib/gs-plugin-job.c index 4254194cd..e1d8bc790 100644 --- a/lib/gs-plugin-job.c +++ b/lib/gs-plugin-job.c @@ -131,7 +131,7 @@ gs_plugin_job_to_string (GsPluginJob *self) } if (self->auth != NULL) { g_string_append_printf (str, " with auth=%s", - gs_auth_get_provider_id (self->auth)); + gs_auth_get_auth_id (self->auth)); } if (self->file != NULL) { g_autofree gchar *path = g_file_get_path (self->file); diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c index 5f469262a..d298d4f68 100644 --- a/lib/gs-plugin-loader.c +++ b/lib/gs-plugin-loader.c @@ -720,17 +720,6 @@ gs_plugin_loader_call_vfunc (GsPluginLoaderHelper *helper, cancellable, &error_local); } break; - case GS_PLUGIN_ACTION_AUTH_LOGIN: - case GS_PLUGIN_ACTION_AUTH_LOGOUT: - case GS_PLUGIN_ACTION_AUTH_REGISTER: - case GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD: - { - GsPluginAuthFunc plugin_func = func; - ret = plugin_func (plugin, - gs_plugin_job_get_auth (helper->plugin_job), - cancellable, &error_local); - } - break; default: g_critical ("no handler for %s", helper->function_name); break; @@ -2140,7 +2129,7 @@ gs_plugin_loader_get_scale (GsPluginLoader *plugin_loader) GsAuth * gs_plugin_loader_get_auth_by_id (GsPluginLoader *plugin_loader, - const gchar *provider_id) + const gchar *auth_id) { GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader); guint i; @@ -2148,7 +2137,7 @@ gs_plugin_loader_get_auth_by_id (GsPluginLoader *plugin_loader, /* match on ID */ for (i = 0; i < priv->auth_array->len; i++) { GsAuth *auth = g_ptr_array_index (priv->auth_array, i); - if (g_strcmp0 (gs_auth_get_provider_id (auth), provider_id) == 0) + if (g_strcmp0 (gs_auth_get_auth_id (auth), auth_id) == 0) return auth; } return NULL; diff --git a/lib/gs-plugin-loader.h b/lib/gs-plugin-loader.h index bb0138266..16ad468ab 100644 --- a/lib/gs-plugin-loader.h +++ b/lib/gs-plugin-loader.h @@ -80,7 +80,7 @@ gboolean gs_plugin_loader_get_enabled (GsPluginLoader *plugin_loader, void gs_plugin_loader_add_location (GsPluginLoader *plugin_loader, const gchar *location); GsAuth *gs_plugin_loader_get_auth_by_id (GsPluginLoader *plugin_loader, - const gchar *provider_id); + const gchar *auth_id); GPtrArray *gs_plugin_loader_get_auths (GsPluginLoader *plugin_loader); guint gs_plugin_loader_get_scale (GsPluginLoader *plugin_loader); void gs_plugin_loader_set_scale (GsPluginLoader *plugin_loader, diff --git a/lib/gs-plugin-types.h b/lib/gs-plugin-types.h index f3c67e8e4..aae566461 100644 --- a/lib/gs-plugin-types.h +++ b/lib/gs-plugin-types.h @@ -73,9 +73,6 @@ typedef guint64 GsPluginFlags; * @GS_PLUGIN_ERROR_NO_SPACE: No disk space to allow action * @GS_PLUGIN_ERROR_AUTH_REQUIRED: Authentication was required * @GS_PLUGIN_ERROR_AUTH_INVALID: Provided authentication was invalid - * @GS_PLUGIN_ERROR_PIN_REQUIRED: PIN required for authentication - * @GS_PLUGIN_ERROR_ACCOUNT_SUSPENDED: User account has been suspended - * @GS_PLUGIN_ERROR_ACCOUNT_DEACTIVATED: User account has been deactivated * @GS_PLUGIN_ERROR_PLUGIN_DEPSOLVE_FAILED: The plugins installed are incompatible * @GS_PLUGIN_ERROR_DOWNLOAD_FAILED: The download action failed * @GS_PLUGIN_ERROR_WRITE_FAILED: The save-to-disk failed @@ -98,9 +95,6 @@ typedef enum { GS_PLUGIN_ERROR_NO_SPACE, GS_PLUGIN_ERROR_AUTH_REQUIRED, GS_PLUGIN_ERROR_AUTH_INVALID, - GS_PLUGIN_ERROR_PIN_REQUIRED, - GS_PLUGIN_ERROR_ACCOUNT_SUSPENDED, - GS_PLUGIN_ERROR_ACCOUNT_DEACTIVATED, GS_PLUGIN_ERROR_PLUGIN_DEPSOLVE_FAILED, GS_PLUGIN_ERROR_DOWNLOAD_FAILED, GS_PLUGIN_ERROR_WRITE_FAILED, @@ -242,10 +236,6 @@ typedef enum { * @GS_PLUGIN_ACTION_REFINE: Refine the application * @GS_PLUGIN_ACTION_REFRESH: Refresh all the sources * @GS_PLUGIN_ACTION_FILE_TO_APP: Convert the file to an application - * @GS_PLUGIN_ACTION_AUTH_LOGIN: Authentication login action - * @GS_PLUGIN_ACTION_AUTH_LOGOUT: Authentication logout action - * @GS_PLUGIN_ACTION_AUTH_REGISTER: Authentication register action - * @GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD: Authentication lost password action * @GS_PLUGIN_ACTION_URL_TO_APP: Convert the file to an application * @GS_PLUGIN_ACTION_GET_RECENT: Get the apps recently released * @GS_PLUGIN_ACTION_GET_UPDATES_HISTORICAL: Get the list of historical updates @@ -291,10 +281,6 @@ typedef enum { GS_PLUGIN_ACTION_REFINE, GS_PLUGIN_ACTION_REFRESH, GS_PLUGIN_ACTION_FILE_TO_APP, - GS_PLUGIN_ACTION_AUTH_LOGIN, - GS_PLUGIN_ACTION_AUTH_LOGOUT, - GS_PLUGIN_ACTION_AUTH_REGISTER, - GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD, GS_PLUGIN_ACTION_URL_TO_APP, GS_PLUGIN_ACTION_GET_RECENT, GS_PLUGIN_ACTION_GET_UPDATES_HISTORICAL, diff --git a/lib/gs-plugin-vfuncs.h b/lib/gs-plugin-vfuncs.h index 011acdf4e..a30fe7a5e 100644 --- a/lib/gs-plugin-vfuncs.h +++ b/lib/gs-plugin-vfuncs.h @@ -977,70 +977,6 @@ gboolean gs_plugin_update (GsPlugin *plugin, GCancellable *cancellable, GError **error); -/** - * gs_plugin_auth_login: - * @plugin: a #GsPlugin - * @auth: a #GsAuth - * @cancellable: a #GCancellable, or %NULL - * @error: a #GError, or %NULL - * - * Performs a login using the given authentication details. - * - * Returns: %TRUE for success or if not relevant - **/ -gboolean gs_plugin_auth_login (GsPlugin *plugin, - GsAuth *auth, - GCancellable *cancellable, - GError **error); - -/** - * gs_plugin_auth_logout: - * @plugin: a #GsPlugin - * @auth: a #GsAuth - * @cancellable: a #GCancellable, or %NULL - * @error: a #GError, or %NULL - * - * Performs a logout using the given authentication details. - * - * Returns: %TRUE for success or if not relevant - **/ -gboolean gs_plugin_auth_logout (GsPlugin *plugin, - GsAuth *auth, - GCancellable *cancellable, - GError **error); - -/** - * gs_plugin_auth_lost_password: - * @plugin: a #GsPlugin - * @auth: a #GsAuth - * @cancellable: a #GCancellable, or %NULL - * @error: a #GError, or %NULL - * - * Performs the lost password action using the given authentication details. - * - * Returns: %TRUE for success or if not relevant - **/ -gboolean gs_plugin_auth_lost_password (GsPlugin *plugin, - GsAuth *auth, - GCancellable *cancellable, - GError **error); - -/** - * gs_plugin_auth_register: - * @plugin: a #GsPlugin - * @auth: a #GsAuth - * @cancellable: a #GCancellable, or %NULL - * @error: a #GError, or %NULL - * - * Performs the registration action using the given authentication details. - * - * Returns: %TRUE for success or if not relevant - **/ -gboolean gs_plugin_auth_register (GsPlugin *plugin, - GsAuth *auth, - GCancellable *cancellable, - GError **error); - G_END_DECLS #endif /* __GS_PLUGIN_VFUNCS_H */ diff --git a/lib/gs-plugin.c b/lib/gs-plugin.c index 4af3bc8ad..28374bfc0 100644 --- a/lib/gs-plugin.c +++ b/lib/gs-plugin.c @@ -645,7 +645,7 @@ gs_plugin_add_auth (GsPlugin *plugin, GsAuth *auth) /** * gs_plugin_get_auth_by_id: * @plugin: a #GsPlugin - * @provider_id: an ID, e.g. "dummy-sso" + * @auth_id: an ID, e.g. "dummy-sso" * * Gets a specific authentication object. * @@ -654,7 +654,7 @@ gs_plugin_add_auth (GsPlugin *plugin, GsAuth *auth) * Since: 3.22 **/ GsAuth * -gs_plugin_get_auth_by_id (GsPlugin *plugin, const gchar *provider_id) +gs_plugin_get_auth_by_id (GsPlugin *plugin, const gchar *auth_id) { GsPluginPrivate *priv = gs_plugin_get_instance_private (plugin); guint i; @@ -662,7 +662,7 @@ gs_plugin_get_auth_by_id (GsPlugin *plugin, const gchar *provider_id) /* match on ID */ for (i = 0; i < priv->auth_array->len; i++) { GsAuth *auth = g_ptr_array_index (priv->auth_array, i); - if (g_strcmp0 (gs_auth_get_provider_id (auth), provider_id) == 0) + if (g_strcmp0 (gs_auth_get_auth_id (auth), auth_id) == 0) return auth; } return NULL; @@ -1544,12 +1544,6 @@ gs_plugin_error_to_string (GsPluginError error) return "auth-required"; if (error == GS_PLUGIN_ERROR_AUTH_INVALID) return "auth-invalid"; - if (error == GS_PLUGIN_ERROR_PIN_REQUIRED) - return "pin-required"; - if (error == GS_PLUGIN_ERROR_ACCOUNT_SUSPENDED) - return "account-suspended"; - if (error == GS_PLUGIN_ERROR_ACCOUNT_DEACTIVATED) - return "account-deactivated"; if (error == GS_PLUGIN_ERROR_PLUGIN_DEPSOLVE_FAILED) return "plugin-depsolve-failed"; if (error == GS_PLUGIN_ERROR_DOWNLOAD_FAILED) @@ -1650,14 +1644,6 @@ gs_plugin_action_to_function_name (GsPluginAction action) return "gs_plugin_add_search_files"; if (action == GS_PLUGIN_ACTION_SEARCH_PROVIDES) return "gs_plugin_add_search_what_provides"; - if (action == GS_PLUGIN_ACTION_AUTH_LOGIN) - return "gs_plugin_auth_login"; - if (action == GS_PLUGIN_ACTION_AUTH_LOGOUT) - return "gs_plugin_auth_logout"; - if (action == GS_PLUGIN_ACTION_AUTH_REGISTER) - return "gs_plugin_auth_register"; - if (action == GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD) - return "gs_plugin_auth_lost_password"; if (action == GS_PLUGIN_ACTION_GET_CATEGORY_APPS) return "gs_plugin_add_category_apps"; if (action == GS_PLUGIN_ACTION_GET_CATEGORIES) @@ -1756,14 +1742,6 @@ gs_plugin_action_to_string (GsPluginAction action) return "file-to-app"; if (action == GS_PLUGIN_ACTION_URL_TO_APP) return "url-to-app"; - if (action == GS_PLUGIN_ACTION_AUTH_LOGIN) - return "auth-login"; - if (action == GS_PLUGIN_ACTION_AUTH_LOGOUT) - return "auth-logout"; - if (action == GS_PLUGIN_ACTION_AUTH_REGISTER) - return "auth-register"; - if (action == GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD) - return "auth-lost-password"; if (action == GS_PLUGIN_ACTION_GET_RECENT) return "get-recent"; if (action == GS_PLUGIN_ACTION_GET_UPDATES_HISTORICAL) @@ -1860,14 +1838,6 @@ gs_plugin_action_from_string (const gchar *action) return GS_PLUGIN_ACTION_FILE_TO_APP; if (g_strcmp0 (action, "url-to-app") == 0) return GS_PLUGIN_ACTION_URL_TO_APP; - if (g_strcmp0 (action, "auth-login") == 0) - return GS_PLUGIN_ACTION_AUTH_LOGIN; - if (g_strcmp0 (action, "auth-logout") == 0) - return GS_PLUGIN_ACTION_AUTH_LOGOUT; - if (g_strcmp0 (action, "auth-register") == 0) - return GS_PLUGIN_ACTION_AUTH_REGISTER; - if (g_strcmp0 (action, "auth-lost-password") == 0) - return GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD; if (g_strcmp0 (action, "get-recent") == 0) return GS_PLUGIN_ACTION_GET_RECENT; if (g_strcmp0 (action, "get-updates-historical") == 0) diff --git a/lib/gs-plugin.h b/lib/gs-plugin.h index 692fa85ef..ac0ffaa76 100644 --- a/lib/gs-plugin.h +++ b/lib/gs-plugin.h @@ -89,7 +89,7 @@ void gs_plugin_set_soup_session (GsPlugin *plugin, void gs_plugin_add_auth (GsPlugin *plugin, GsAuth *auth); GsAuth *gs_plugin_get_auth_by_id (GsPlugin *plugin, - const gchar *provider_id); + const gchar *auth_id); void gs_plugin_add_rule (GsPlugin *plugin, GsPluginRule rule, const gchar *name); diff --git a/lib/gs-self-test.c b/lib/gs-self-test.c index b5ba37363..9fd4a45ca 100644 --- a/lib/gs-self-test.c +++ b/lib/gs-self-test.c @@ -707,54 +707,6 @@ gs_app_func (void) gs_app_set_state_recover (app); } -static void -gs_auth_secret_func (void) -{ - gboolean ret; - g_autoptr(GDBusConnection) conn = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GsAuth) auth1 = NULL; - g_autoptr(GsAuth) auth2 = NULL; - - /* we not have an active session bus */ - conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - if (conn == NULL) { - g_prefix_error (&error, "no session bus available: "); - g_test_skip (error->message); - return; - } - - /* save secrets to disk */ - auth1 = gs_auth_new ("self-test"); - gs_auth_set_provider_schema (auth1, "org.gnome.Software.Dummy"); - gs_auth_set_username (auth1, "hughsie"); - gs_auth_set_password (auth1, "foobarbaz"); - gs_auth_add_metadata (auth1, "day", "monday"); - ret = gs_auth_store_save (auth1, - GS_AUTH_STORE_FLAG_USERNAME | - GS_AUTH_STORE_FLAG_PASSWORD | - GS_AUTH_STORE_FLAG_METADATA, - NULL, &error); - g_assert_no_error (error); - g_assert (ret); - - /* load secrets from disk */ - auth2 = gs_auth_new ("self-test"); - gs_auth_add_metadata (auth2, "day", NULL); - gs_auth_add_metadata (auth2, "notgoingtoexist", NULL); - gs_auth_set_provider_schema (auth2, "org.gnome.Software.Dummy"); - ret = gs_auth_store_load (auth2, - GS_AUTH_STORE_FLAG_USERNAME | - GS_AUTH_STORE_FLAG_PASSWORD | - GS_AUTH_STORE_FLAG_METADATA, - NULL, &error); - g_assert_no_error (error); - g_assert (ret); - g_assert_cmpstr (gs_auth_get_username (auth2), ==, "hughsie"); - g_assert_cmpstr (gs_auth_get_password (auth2), ==, "foobarbaz"); - g_assert_cmpstr (gs_auth_get_metadata_item (auth2, "day"), ==, "monday"); -} - static void gs_app_list_func (void) { @@ -831,7 +783,6 @@ main (int argc, char **argv) g_test_add_func ("/gnome-software/lib/app{list-related}", gs_app_list_related_func); g_test_add_func ("/gnome-software/lib/plugin", gs_plugin_func); g_test_add_func ("/gnome-software/lib/plugin{download-rewrite}", gs_plugin_download_rewrite_func); - g_test_add_func ("/gnome-software/lib/auth{secret}", gs_auth_secret_func); return g_test_run (); } diff --git a/lib/meson.build b/lib/meson.build index 7b11e72c5..766d379cc 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -57,10 +57,10 @@ librarydeps = [ appstream_glib, gio_unix, gmodule, + goa, gtk, json_glib, libm, - libsecret, libsoup, valgrind, ] @@ -108,10 +108,10 @@ executable( appstream_glib, gio_unix, gmodule, + goa, gtk, json_glib, libm, - libsecret, libsoup, valgrind, ], @@ -138,10 +138,10 @@ if get_option('tests') appstream_glib, gio_unix, gmodule, + goa, gtk, json_glib, libm, - libsecret, libsoup ], link_with : [ diff --git a/meson.build b/meson.build index ef349e670..34877ecea 100644 --- a/meson.build +++ b/meson.build @@ -101,10 +101,10 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', version : '>= 2.32.0') libxmlb = dependency('xmlb', version : '>= 0.1.4', fallback : ['libxmlb', 'libxmlb_dep']) gio_unix = dependency('gio-unix-2.0', version : '>= 2.56.0') gmodule = dependency('gmodule-2.0') +goa = dependency('goa-1.0') gtk = dependency('gtk+-3.0', version : '>= 3.22.4') json_glib = dependency('json-glib-1.0', version : '>= 1.2.0') libm = cc.find_library('m', required: false) -libsecret = dependency('libsecret-1') libsoup = dependency('libsoup-2.4', version : '>= 2.52.0') if get_option('valgrind') diff --git a/plugins/dummy/gs-plugin-dummy.c b/plugins/dummy/gs-plugin-dummy.c index cb6c6ce11..cf1769c9b 100644 --- a/plugins/dummy/gs-plugin-dummy.c +++ b/plugins/dummy/gs-plugin-dummy.c @@ -69,14 +69,11 @@ gs_plugin_initialize (GsPlugin *plugin) } /* set up a dummy authentication provider */ - priv->auth = gs_auth_new (gs_plugin_get_name (plugin)); - gs_auth_set_provider_name (priv->auth, "GNOME SSO"); - gs_auth_set_provider_logo (priv->auth, "/usr/share/pixmaps/gnome-about-logo.png"); - gs_auth_set_provider_uri (priv->auth, "http://www.gnome.org/sso"); - gs_plugin_add_auth (plugin, priv->auth); - - /* lets assume we read this from disk somewhere */ - gs_auth_set_username (priv->auth, "dummy"); + priv->auth = gs_auth_new (gs_plugin_get_name (plugin), "google", NULL); + if (priv->auth != NULL) { + gs_auth_set_provider_name (priv->auth, "GNOME SSO"); + gs_plugin_add_auth (plugin, priv->auth); + } /* add source */ priv->cached_origin = gs_app_new (gs_plugin_get_name (plugin)); @@ -980,93 +977,3 @@ gs_plugin_review_remove (GsPlugin *plugin, g_debug ("Removing dummy self-review"); return TRUE; } - -gboolean -gs_plugin_auth_login (GsPlugin *plugin, GsAuth *auth, - GCancellable *cancellable, GError **error) -{ - GsPluginData *priv = gs_plugin_get_data (plugin); - - /* not us */ - if (g_strcmp0 (gs_auth_get_provider_id (auth), - gs_auth_get_provider_id (priv->auth)) != 0) - return TRUE; - - /* already logged in */ - if (priv->has_auth) - return TRUE; - - /* check username and password */ - if (g_strcmp0 (gs_auth_get_username (priv->auth), "dummy") != 0 || - g_strcmp0 (gs_auth_get_password (priv->auth), "dummy") != 0) { - g_set_error (error, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_AUTH_INVALID, - "The password was not correct."); - return FALSE; - } - - priv->has_auth = TRUE; - gs_auth_add_flags (priv->auth, GS_AUTH_FLAG_VALID); - g_debug ("dummy now authenticated"); - return TRUE; -} - -gboolean -gs_plugin_auth_logout (GsPlugin *plugin, GsAuth *auth, - GCancellable *cancellable, GError **error) -{ - GsPluginData *priv = gs_plugin_get_data (plugin); - - /* not us */ - if (g_strcmp0 (gs_auth_get_provider_id (auth), - gs_auth_get_provider_id (priv->auth)) != 0) - return TRUE; - - /* not logged in */ - if (!priv->has_auth) - return TRUE; - - priv->has_auth = FALSE; - gs_auth_set_flags (priv->auth, 0); - g_debug ("dummy now not authenticated"); - return TRUE; -} - -gboolean -gs_plugin_auth_lost_password (GsPlugin *plugin, GsAuth *auth, - GCancellable *cancellable, GError **error) -{ - GsPluginData *priv = gs_plugin_get_data (plugin); - - /* not us */ - if (g_strcmp0 (gs_auth_get_provider_id (auth), - gs_auth_get_provider_id (priv->auth)) != 0) - return TRUE; - - /* return with data */ - g_set_error_literal (error, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_AUTH_INVALID, - "do online using @http://www.gnome.org/lost-password/"); - return FALSE; -} - -gboolean -gs_plugin_auth_register (GsPlugin *plugin, GsAuth *auth, - GCancellable *cancellable, GError **error) -{ - GsPluginData *priv = gs_plugin_get_data (plugin); - - /* not us */ - if (g_strcmp0 (gs_auth_get_provider_id (auth), - gs_auth_get_provider_id (priv->auth)) != 0) - return TRUE; - - /* return with data */ - g_set_error_literal (error, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_AUTH_INVALID, - "do online using @http://www.gnome.org/register/"); - return FALSE; -} diff --git a/plugins/dummy/gs-self-test.c b/plugins/dummy/gs-self-test.c index 87a35d0f1..1c7cc694f 100644 --- a/plugins/dummy/gs-self-test.c +++ b/plugins/dummy/gs-self-test.c @@ -566,86 +566,6 @@ gs_plugins_dummy_plugin_cache_func (GsPluginLoader *plugin_loader) g_assert (app1 == app2); } -static void -gs_plugins_dummy_authentication_func (GsPluginLoader *plugin_loader) -{ - GsAuth *auth; - gboolean ret; - g_autoptr(GError) error = NULL; - g_autoptr(GsApp) app = NULL; - g_autoptr(GsAppList) list = NULL; - g_autoptr(AsReview) review = NULL; - g_autoptr(AsReview) review2 = NULL; - g_autoptr(GsPluginJob) plugin_job = NULL; - - /* check initial state */ - auth = gs_plugin_loader_get_auth_by_id (plugin_loader, "dummy"); - g_assert (GS_IS_AUTH (auth)); - g_assert_cmpint (gs_auth_get_flags (auth), ==, 0); - - /* do an action that returns a URL */ - plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_AUTH_REGISTER, - "auth", auth, - NULL); - list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error); - gs_test_flush_main_context (); - g_assert_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_AUTH_INVALID); - g_assert (list == NULL); - g_clear_error (&error); - g_assert (!gs_auth_has_flag (auth, GS_AUTH_FLAG_VALID)); - - /* do an action that requires a login */ - app = gs_app_new (NULL); - review = as_review_new (); - g_object_unref (plugin_job); - plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REVIEW_REMOVE, - "app", app, - "review", review, - NULL); - ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error); - gs_test_flush_main_context (); - g_assert_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_AUTH_REQUIRED); - g_assert (!ret); - g_clear_error (&error); - - /* pretend to auth with no credentials */ - g_object_unref (plugin_job); - plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_AUTH_LOGIN, - "auth", auth, - NULL); - list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error); - gs_test_flush_main_context (); - g_assert_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_AUTH_INVALID); - g_assert (list == NULL); - g_clear_error (&error); - g_assert (!gs_auth_has_flag (auth, GS_AUTH_FLAG_VALID)); - - /* auth again with correct credentials */ - gs_auth_set_username (auth, "dummy"); - gs_auth_set_password (auth, "dummy"); - g_object_unref (plugin_job); - plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_AUTH_LOGIN, - "auth", auth, - NULL); - list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error); - gs_test_flush_main_context (); - g_assert_no_error (error); - g_assert (list != NULL); - g_assert (gs_auth_has_flag (auth, GS_AUTH_FLAG_VALID)); - - /* do the action that requires a login */ - review2 = as_review_new (); - g_object_unref (plugin_job); - plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REVIEW_REMOVE, - "app", app, - "review", review2, - NULL); - ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error); - gs_test_flush_main_context (); - g_assert_no_error (error); - g_assert (ret); -} - static void gs_plugins_dummy_wildcard_func (GsPluginLoader *plugin_loader) { @@ -937,9 +857,6 @@ main (int argc, char **argv) g_test_add_data_func ("/gnome-software/plugins/dummy/wildcard", plugin_loader, (GTestDataFunc) gs_plugins_dummy_wildcard_func); - g_test_add_data_func ("/gnome-software/plugins/dummy/authentication", - plugin_loader, - (GTestDataFunc) gs_plugins_dummy_authentication_func); g_test_add_data_func ("/gnome-software/plugins/dummy/plugin-cache", plugin_loader, (GTestDataFunc) gs_plugins_dummy_plugin_cache_func); diff --git a/plugins/dummy/meson.build b/plugins/dummy/meson.build index 2359f2956..1259c330a 100644 --- a/plugins/dummy/meson.build +++ b/plugins/dummy/meson.build @@ -12,7 +12,7 @@ shared_module( install : true, install_dir: plugin_dir, c_args : cargs, - dependencies : [appstream_glib, gio_unix, gtk, libsoup] + dependencies : [appstream_glib, gio_unix, goa, gtk, libsoup] ) if get_option('tests') diff --git a/plugins/meson.build b/plugins/meson.build index bd1821413..f38fc0483 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -2,6 +2,7 @@ plugin_dir = join_paths(get_option('libdir'), 'gs-plugins-' + gs_plugin_api_vers plugin_libs = [ appstream_glib, gio_unix, + goa, gtk, json_glib, libsoup diff --git a/plugins/snap/gs-plugin-snap.c b/plugins/snap/gs-plugin-snap.c index 85b616a48..0ba513853 100644 --- a/plugins/snap/gs-plugin-snap.c +++ b/plugins/snap/gs-plugin-snap.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * - * Copyright (C) 2015-2016 Canonical Ltd + * Copyright (C) 2015-2018 Canonical Ltd * * Licensed under the GNU General Public License Version 2 * @@ -54,6 +54,9 @@ get_client (GsPlugin *plugin, GError **error) return g_steal_pointer (&client); } +static void +load_auth (GsPlugin *plugin); + void gs_plugin_initialize (GsPlugin *plugin) { @@ -72,10 +75,19 @@ gs_plugin_initialize (GsPlugin *plugin) priv->store_snaps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); - priv->auth = gs_auth_new ("snapd"); - gs_auth_set_provider_name (priv->auth, "Snap Store"); - gs_auth_set_provider_schema (priv->auth, "com.ubuntu.SnapStore.GnomeSoftware"); - gs_plugin_add_auth (plugin, priv->auth); + priv->auth = gs_auth_new ("snapd", "ubuntusso", &error); + if (priv->auth) { + gs_auth_set_provider_name (priv->auth, "Snap Store"); + gs_auth_set_header (priv->auth, _("To continue, you need to use an Ubuntu One account."), + _("To continue, you need to use your Ubuntu One account."), + _("To continue, you need to use an Ubuntu One account.")); + gs_plugin_add_auth (plugin, priv->auth); + g_signal_connect_object (priv->auth, "changed", + G_CALLBACK (load_auth), + plugin, G_CONNECT_SWAPPED); + } else { + g_warning ("Failed to instantiate the snapd authentication object: %s", error->message); + } gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "desktop-categories"); gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_BETTER_THAN, "packagekit"); @@ -116,9 +128,6 @@ snapd_error_convert (GError **perror) g_free (error->message); error->message = g_strdup ("Requires authentication with @snapd"); break; - case SNAPD_ERROR_TWO_FACTOR_REQUIRED: - error->code = GS_PLUGIN_ERROR_PIN_REQUIRED; - break; case SNAPD_ERROR_AUTH_DATA_INVALID: case SNAPD_ERROR_TWO_FACTOR_INVALID: error->code = GS_PLUGIN_ERROR_AUTH_INVALID; @@ -160,32 +169,53 @@ load_auth (GsPlugin *plugin) { GsPluginData *priv = gs_plugin_get_data (plugin); GsAuth *auth; - const gchar *serialized_macaroon; - g_autoptr(GVariant) macaroon_variant = NULL; - const gchar *macaroon; - g_auto(GStrv) discharges = NULL; + GoaObject *goa_object; + GoaPasswordBased *password_based; + g_autofree gchar *macaroon = NULL; + g_autofree gchar *discharges_str = NULL; + g_autoptr(GVariant) discharges_var = NULL; + g_autofree const gchar **discharges = NULL; g_autoptr(SnapdAuthData) auth_data = NULL; + g_autoptr(GError) error = NULL; auth = gs_plugin_get_auth_by_id (plugin, "snapd"); if (auth == NULL) return; - serialized_macaroon = gs_auth_get_metadata_item (auth, "macaroon"); - if (serialized_macaroon == NULL) + g_clear_object (&priv->auth_data); + goa_object = gs_auth_peek_goa_object (auth); + if (goa_object == NULL) + return; + + password_based = goa_object_peek_password_based (goa_object); + g_return_if_fail (password_based != NULL); + + goa_password_based_call_get_password_sync (password_based, + "macaroon", + &macaroon, + NULL, &error); + if (error != NULL) { + g_warning ("Failed to get macaroon: %s", error->message); return; + } - macaroon_variant = g_variant_parse (G_VARIANT_TYPE ("(sas)"), - serialized_macaroon, - NULL, - NULL, - NULL); - if (macaroon_variant == NULL) + goa_password_based_call_get_password_sync (password_based, + "discharges", + &discharges_str, + NULL, &error); + if (error != NULL) { + g_warning ("Failed to get discharges %s", error->message); return; + } + + if (discharges_str) + discharges_var = g_variant_parse (G_VARIANT_TYPE ("as"), + discharges_str, + NULL, NULL, NULL); + if (discharges_var) + discharges = g_variant_get_strv (discharges_var, NULL); - g_variant_get (macaroon_variant, "(&s^as)", &macaroon, &discharges); - g_clear_object (&priv->auth_data); priv->auth_data = snapd_auth_data_new (macaroon, discharges); - gs_auth_add_flags (priv->auth, GS_AUTH_FLAG_VALID); } gboolean @@ -208,15 +238,6 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error) _("Snap Store")); priv->system_confinement = snapd_system_information_get_confinement (system_information); - /* load from disk */ - gs_auth_add_metadata (priv->auth, "macaroon", NULL); - if (!gs_auth_store_load (priv->auth, - GS_AUTH_STORE_FLAG_USERNAME | - GS_AUTH_STORE_FLAG_METADATA, - cancellable, error)) - return FALSE; - load_auth (plugin); - /* success */ return TRUE; } @@ -1042,105 +1063,3 @@ gs_plugin_app_remove (GsPlugin *plugin, gs_app_set_state (app, AS_APP_STATE_AVAILABLE); return TRUE; } - -gboolean -gs_plugin_auth_login (GsPlugin *plugin, GsAuth *auth, - GCancellable *cancellable, GError **error) -{ - GsPluginData *priv = gs_plugin_get_data (plugin); - g_autoptr(SnapdClient) client = NULL; - g_autoptr(SnapdUserInformation) user_information = NULL; - g_autoptr(GVariant) macaroon_variant = NULL; - g_autofree gchar *serialized_macaroon = NULL; - - if (auth != priv->auth) - return TRUE; - - g_clear_object (&priv->auth_data); - - client = get_client (plugin, error); - if (client == NULL) - return FALSE; - - user_information = snapd_client_login2_sync (client, gs_auth_get_username (auth), gs_auth_get_password (auth), gs_auth_get_pin (auth), NULL, error); - if (user_information == NULL) { - snapd_error_convert (error); - return FALSE; - } - - priv->auth_data = g_object_ref (snapd_user_information_get_auth_data (user_information)); - - macaroon_variant = g_variant_new ("(s^as)", - snapd_auth_data_get_macaroon (priv->auth_data), - snapd_auth_data_get_discharges (priv->auth_data)); - serialized_macaroon = g_variant_print (macaroon_variant, FALSE); - gs_auth_add_metadata (auth, "macaroon", serialized_macaroon); - - /* store */ - if (!gs_auth_store_save (auth, - GS_AUTH_STORE_FLAG_USERNAME | - GS_AUTH_STORE_FLAG_METADATA, - cancellable, error)) - return FALSE; - - gs_auth_add_flags (priv->auth, GS_AUTH_FLAG_VALID); - - return TRUE; -} - -gboolean -gs_plugin_auth_logout (GsPlugin *plugin, GsAuth *auth, - GCancellable *cancellable, GError **error) -{ - GsPluginData *priv = gs_plugin_get_data (plugin); - - if (auth != priv->auth) - return TRUE; - - /* clear */ - if (!gs_auth_store_clear (auth, - GS_AUTH_STORE_FLAG_USERNAME | - GS_AUTH_STORE_FLAG_METADATA, - cancellable, error)) - return FALSE; - - g_clear_object (&priv->auth_data); - gs_auth_set_flags (priv->auth, 0); - return TRUE; -} - -gboolean -gs_plugin_auth_lost_password (GsPlugin *plugin, GsAuth *auth, - GCancellable *cancellable, GError **error) -{ - GsPluginData *priv = gs_plugin_get_data (plugin); - - if (auth != priv->auth) - return TRUE; - - // FIXME: snapd might not be using Ubuntu One accounts - // https://bugs.launchpad.net/bugs/1598667 - g_set_error_literal (error, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_AUTH_INVALID, - "do online using @https://login.ubuntu.com/+forgot_password"); - return FALSE; -} - -gboolean -gs_plugin_auth_register (GsPlugin *plugin, GsAuth *auth, - GCancellable *cancellable, GError **error) -{ - GsPluginData *priv = gs_plugin_get_data (plugin); - - if (auth != priv->auth) - return TRUE; - - // FIXME: snapd might not be using Ubuntu One accounts - // https://bugs.launchpad.net/bugs/1598667 - g_set_error_literal (error, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_AUTH_INVALID, - "do online using @https://login.ubuntu.com/+login"); - return FALSE; -} diff --git a/src/gs-auth-dialog.c b/src/gs-auth-dialog.c index 0b90fa81c..6716b7c32 100644 --- a/src/gs-auth-dialog.c +++ b/src/gs-auth-dialog.c @@ -2,6 +2,7 @@ * * Copyright (C) 2016 Richard Hughes * Copyright (C) 2017 Kalev Lember + * Copyright (C) 2018 Canonical Ltd * * Licensed under the GNU General Public License Version 2 * @@ -23,272 +24,355 @@ #include "config.h" #include +#define GOA_API_IS_SUBJECT_TO_CHANGE +#include #include "gs-auth-dialog.h" #include "gs-common.h" struct _GsAuthDialog { - GtkDialog parent_instance; - - GCancellable *cancellable; - GsPluginLoader *plugin_loader; - GsApp *app; - GsAuth *auth; - GtkWidget *box_dialog; - GtkWidget *box_error; - GtkWidget *button_cancel; - GtkWidget *button_continue; - GtkWidget *checkbutton_remember; - GtkWidget *entry_password; - GtkWidget *entry_pin; - GtkWidget *entry_username; - GtkWidget *image_vendor; - GtkWidget *label_error; - GtkWidget *label_title; - GtkWidget *radiobutton_already; - GtkWidget *radiobutton_lost_pwd; - GtkWidget *radiobutton_register; - GtkWidget *stack; + GtkDialog parent_instance; + + GoaClient *goa_client; + GtkListStore *liststore_account; + + GtkWidget *label_header; + GtkWidget *combobox_account; + GtkWidget *label_account; + GtkWidget *button_add_another; + GtkWidget *button_cancel; + GtkWidget *button_continue; + + gboolean dispose_on_new_account; + + GCancellable *cancellable; + GsPluginLoader *plugin_loader; + GsApp *app; + GsAuth *auth; }; -G_DEFINE_TYPE (GsAuthDialog, gs_auth_dialog, GTK_TYPE_DIALOG) +static void gs_auth_dialog_initable_iface_init (GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GsAuthDialog, gs_auth_dialog, GTK_TYPE_DIALOG, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gs_auth_dialog_initable_iface_init)) + +enum { + COLUMN_ID, + COLUMN_EMAIL, + COLUMN_ACCOUNT, + N_COLUMNS +}; + +static gboolean +gs_auth_dialog_ignore_account (GsAuthDialog *self, GoaAccount *account) +{ + return g_strcmp0 (goa_account_get_provider_type (account), + gs_auth_get_provider_type (self->auth)) != 0; +} static void -gs_auth_dialog_check_ui (GsAuthDialog *dialog) +gs_auth_dialog_set_header (GsAuthDialog *self, + const gchar *text) { - g_autofree gchar *title = NULL; - const gchar *tmp; - const gchar *username = gtk_entry_get_text (GTK_ENTRY (dialog->entry_username)); - const gchar *password = gtk_entry_get_text (GTK_ENTRY (dialog->entry_password)); - - /* set the header */ - tmp = gs_auth_get_provider_name (dialog->auth); - if (tmp == NULL) { - /* TRANSLATORS: this is when the service name is not known */ - title = g_strdup (_("To continue you need to sign in.")); - gtk_label_set_label (GTK_LABEL (dialog->label_title), title); - } else { - /* TRANSLATORS: the %s is a service name, e.g. "Ubuntu One" */ - title = g_strdup_printf (_("To continue you need to sign in to %s."), tmp); - gtk_label_set_label (GTK_LABEL (dialog->label_title), title); - } + g_autofree gchar *markup = NULL; + markup = g_strdup_printf ("%s", text); + gtk_label_set_markup (GTK_LABEL (self->label_header), markup); +} - /* set the vendor image */ - tmp = gs_auth_get_provider_logo (dialog->auth); - if (tmp == NULL) { - gtk_widget_hide (dialog->image_vendor); - } else { - gtk_image_set_from_file (GTK_IMAGE (dialog->image_vendor), tmp); - gtk_widget_show (dialog->image_vendor); +static gint +gs_auth_dialog_get_naccounts (GsAuthDialog *self) +{ + return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (self->liststore_account), NULL); +} + +static gboolean +gs_auth_dialog_get_nth_account_data (GsAuthDialog *self, + gint n, + ...) +{ + GtkTreeIter iter; + va_list var_args; + + if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->liststore_account), &iter, NULL, n)) + return FALSE; + + va_start (var_args, n); + gtk_tree_model_get_valist (GTK_TREE_MODEL (self->liststore_account), &iter, var_args); + va_end (var_args); + + return TRUE; +} + +static gboolean +gs_auth_dialog_get_account_iter (GsAuthDialog *self, + GoaAccount *account, + GtkTreeIter *iter) +{ + gboolean valid; + + valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->liststore_account), iter, NULL, 0); + + while (valid) { + g_autofree gchar *id; + gtk_tree_model_get (GTK_TREE_MODEL (self->liststore_account), iter, COLUMN_ID, &id, -1); + if (g_strcmp0 (id, goa_account_get_id (account)) == 0) + return TRUE; + else + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (self->liststore_account), iter); } - /* need username and password to continue for known account */ - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radiobutton_already))) { - gtk_widget_set_sensitive (dialog->button_continue, - username[0] != '\0' && password[0] != '\0'); - gtk_widget_set_sensitive (dialog->checkbutton_remember, TRUE); + return FALSE; +} + + +static void +gs_auth_dialog_check_ui (GsAuthDialog *self, + gboolean select) +{ + gint naccounts = gs_auth_dialog_get_naccounts (self); + + gs_auth_dialog_set_header (self, gs_auth_get_header (self->auth, naccounts)); + + if (naccounts == 0) { + gtk_widget_set_visible (self->combobox_account, FALSE); + gtk_widget_set_visible (self->label_account, FALSE); + gtk_widget_set_visible (self->button_add_another, FALSE); + gtk_button_set_label (GTK_BUTTON (self->button_continue), _("Sign In / Register…")); + } else if (naccounts == 1) { + g_autofree gchar *email = NULL; + + gtk_widget_set_visible (self->combobox_account, FALSE); + gtk_widget_set_visible (self->label_account, TRUE); + gtk_widget_set_visible (self->button_add_another, TRUE); + gtk_button_set_label (GTK_BUTTON (self->button_continue), _("Continue")); + gs_auth_dialog_get_nth_account_data (self, 0, COLUMN_EMAIL, &email, -1); + gtk_label_set_text (GTK_LABEL (self->label_account), email); } else { - gtk_entry_set_text (GTK_ENTRY (dialog->entry_password), ""); - gtk_widget_set_sensitive (dialog->button_continue, - username[0] != '\0'); - gtk_widget_set_sensitive (dialog->checkbutton_remember, FALSE); + gtk_widget_set_visible (self->combobox_account, TRUE); + gtk_widget_set_visible (self->label_account, FALSE); + gtk_widget_set_visible (self->button_add_another, TRUE); + gtk_button_set_label (GTK_BUTTON (self->button_continue), _("Use")); + + if (select) + gtk_combo_box_set_active (GTK_COMBO_BOX (self->combobox_account), naccounts - 1); + else if (gtk_combo_box_get_active (GTK_COMBO_BOX (self->combobox_account)) == -1) + gtk_combo_box_set_active (GTK_COMBO_BOX (self->combobox_account), 0); } } static void -gs_auth_dialog_cancel_button_cb (GtkWidget *widget, GsAuthDialog *dialog) +gs_auth_dialog_add_account (GsAuthDialog *self, + GoaAccount *account, + gboolean select) { - gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); + GtkTreeIter iter; + + if (gs_auth_dialog_ignore_account (self, account) || + gs_auth_dialog_get_account_iter (self, account, &iter)) + return; + + gtk_list_store_append (self->liststore_account, &iter); + gtk_list_store_set (self->liststore_account, &iter, + COLUMN_ID, goa_account_get_id (account), + COLUMN_EMAIL, goa_account_get_presentation_identity (account), + COLUMN_ACCOUNT, account, + -1); + + gs_auth_dialog_check_ui (self, select); } static void -gs_auth_dialog_authenticate_cb (GObject *source, - GAsyncResult *res, - gpointer user_data) +gs_auth_dialog_remove_account (GsAuthDialog *self, + GoaAccount *account) { - GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source); - GsAuthDialog *dialog = GS_AUTH_DIALOG (user_data); - g_autoptr(GError) error = NULL; + GtkTreeIter iter; - gtk_widget_set_sensitive (dialog->box_dialog, TRUE); - gtk_widget_set_sensitive (dialog->button_continue, TRUE); - - gtk_widget_set_visible (dialog->box_error, FALSE); - - /* we failed */ - if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) { - const gchar *url; - - if (g_error_matches (error, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_PIN_REQUIRED)) { - gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "2fa"); - gtk_widget_grab_focus (dialog->entry_pin); - return; - } - - /* have we been given a link */ - url = gs_utils_get_error_value (error); - if (url != NULL) { - g_autoptr(GError) error_local = NULL; - g_debug ("showing link in: %s", error->message); - if (!gtk_show_uri_on_window (GTK_WINDOW (dialog), - url, - GDK_CURRENT_TIME, - &error_local)) { - g_warning ("failed to show URI %s: %s", - url, error_local->message); - } - return; - } - - g_warning ("failed to authenticate: %s", error->message); - gtk_label_set_label (GTK_LABEL (dialog->label_error), error->message); - gtk_widget_set_visible (dialog->box_error, TRUE); + if (gs_auth_dialog_ignore_account (self, account) || + !gs_auth_dialog_get_account_iter (self, account, &iter)) return; + + + gtk_list_store_remove (self->liststore_account, &iter); + gs_auth_dialog_check_ui (self, FALSE); +} + +static void +gs_auth_dialog_setup_model (GsAuthDialog *self) +{ + g_autoptr(GList) accounts = goa_client_get_accounts (self->goa_client); + + for (GList *l = accounts; l != NULL; l = l->next) { + gs_auth_dialog_add_account (self, goa_object_peek_account (l->data), FALSE); + g_object_unref (l->data); } +} - /* we didn't get authenticated */ - if (!gs_auth_has_flag (dialog->auth, GS_AUTH_FLAG_VALID)) { - return; +static GVariant* +gs_auth_dialog_build_dbus_parameters (const gchar *action, + const gchar *arg) +{ + GVariantBuilder builder; + GVariant *array[1], *params2[3]; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("av")); + + if (!action && !arg) { + g_variant_builder_add (&builder, "v", g_variant_new_string ("")); + } else { + if (action) + g_variant_builder_add (&builder, "v", g_variant_new_string (action)); + + if (arg) + g_variant_builder_add (&builder, "v", g_variant_new_string (arg)); } - /* success */ - gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + array[0] = g_variant_new ("v", g_variant_new ("(sav)", "online-accounts", &builder)); + + params2[0] = g_variant_new_string ("launch-panel"); + params2[1] = g_variant_new_array (G_VARIANT_TYPE ("v"), array, 1); + params2[2] = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0); + + return g_variant_new_tuple (params2, 3); } static void -gs_auth_dialog_continue_cb (GtkWidget *widget, GsAuthDialog *dialog) +gs_auth_dialog_spawn_goa_with_args (const gchar *action, + const gchar *arg) { - g_autoptr(GsPluginJob) plugin_job = NULL; - - gtk_widget_set_sensitive (dialog->box_dialog, FALSE); - gtk_widget_set_sensitive (dialog->button_continue, FALSE); - - /* alternate actions */ - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radiobutton_lost_pwd))) { - plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD, - "auth", dialog->auth, - NULL); - } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radiobutton_register))) { - plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_AUTH_REGISTER, - "auth", dialog->auth, - NULL); - } else { - plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_AUTH_LOGIN, - "auth", dialog->auth, - NULL); + g_autoptr(GDBusProxy) proxy = NULL; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.gnome.ControlCenter", + "/org/gnome/ControlCenter", + "org.gtk.Actions", + NULL, + NULL); + + if (!proxy) + { + g_warning ("Couldn't open Online Accounts panel"); + return; } - gs_plugin_loader_job_process_async (dialog->plugin_loader, plugin_job, - dialog->cancellable, - gs_auth_dialog_authenticate_cb, - dialog); + + g_dbus_proxy_call_sync (proxy, + "Activate", + gs_auth_dialog_build_dbus_parameters (action, arg), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); } static void -gs_auth_dialog_setup (GsAuthDialog *dialog) +gs_auth_dialog_ensure_crendentials_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) { + GsAuthDialog *self = (GsAuthDialog*) user_data; + GoaAccount *account = GOA_ACCOUNT (source_object); + g_autoptr(GError) error = NULL; - /* update widgets with known values */ - if (gs_auth_get_username (dialog->auth) != NULL) { - gtk_entry_set_text (GTK_ENTRY (dialog->entry_username), - gs_auth_get_username (dialog->auth)); - } - if (gs_auth_get_password (dialog->auth) != NULL) { - gtk_entry_set_text (GTK_ENTRY (dialog->entry_password), - gs_auth_get_password (dialog->auth)); - } + if (!goa_account_call_ensure_credentials_finish (account, NULL, res, &error)) { + gs_auth_dialog_spawn_goa_with_args (goa_account_get_id (account), NULL); + } else { + GoaObject *goa_object; - /* refresh UI */ - gs_auth_dialog_check_ui (dialog); + goa_object = goa_client_lookup_by_id (self->goa_client, + goa_account_get_id (account)); + + gs_auth_set_goa_object (self->auth, goa_object); + gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_OK); + } } static void -gs_auth_dialog_notify_username_cb (GtkEntry *entry, - GParamSpec *pspec, - GsAuthDialog *dialog) +gs_auth_dialog_response_if_valid (GsAuthDialog *self, + GoaAccount *account) { - gs_auth_set_username (dialog->auth, gtk_entry_get_text (entry)); - gs_auth_dialog_check_ui (dialog); + goa_account_call_ensure_credentials (account, + self->cancellable, + gs_auth_dialog_ensure_crendentials_cb, + self); } static void -gs_auth_dialog_notify_password_cb (GtkEntry *entry, - GParamSpec *pspec, - GsAuthDialog *dialog) +gs_auth_dialog_account_added_cb (GoaClient *client, + GoaObject *object, + GsAuthDialog *self) { - gs_auth_set_password (dialog->auth, gtk_entry_get_text (entry)); - gs_auth_dialog_check_ui (dialog); + GoaAccount *account = goa_object_peek_account (object); + + if (gs_auth_dialog_ignore_account (self, account)) + return; + + if (!self->dispose_on_new_account) + gs_auth_dialog_add_account (self, account, FALSE); + else + gs_auth_dialog_response_if_valid (self, account); } static void -gs_auth_dialog_notify_pin_cb (GtkEntry *entry, - GParamSpec *pspec, - GsAuthDialog *dialog) +gs_auth_dialog_account_removed_cb (GoaClient *client, + GoaObject *object, + GsAuthDialog *self) { - gs_auth_set_pin (dialog->auth, gtk_entry_get_text (entry)); - gs_auth_dialog_check_ui (dialog); + GoaAccount *account = goa_object_peek_account (object); + gs_auth_dialog_remove_account (self, account); } static void -gs_auth_dialog_toggled_cb (GtkToggleButton *togglebutton, GsAuthDialog *dialog) +gs_auth_dialog_button_add_another_cb (GtkButton *button, + GsAuthDialog *self) { - gs_auth_dialog_check_ui (dialog); + gs_auth_dialog_spawn_goa_with_args ("add", gs_auth_get_provider_type (self->auth)); + self->dispose_on_new_account = TRUE; } static void -gs_auth_dialog_remember_cb (GtkToggleButton *togglebutton, GsAuthDialog *dialog) +gs_auth_dialog_button_cancel_cb (GtkButton *button, + GsAuthDialog *self) { - if (gtk_toggle_button_get_active (togglebutton)) - gs_auth_add_flags (dialog->auth, GS_AUTH_FLAG_REMEMBER); - gs_auth_dialog_check_ui (dialog); + gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_CANCEL); } static void -gs_auth_dialog_dispose (GObject *object) +gs_auth_dialog_button_continue_cb (GtkButton *button, + GsAuthDialog *self) { - GsAuthDialog *dialog = GS_AUTH_DIALOG (object); + GoaAccount *account = NULL; + gint naccounts = gs_auth_dialog_get_naccounts (self); - g_clear_object (&dialog->plugin_loader); - g_clear_object (&dialog->app); - g_clear_object (&dialog->auth); + if (naccounts == 1) { + gs_auth_dialog_get_nth_account_data (self, 0, COLUMN_ACCOUNT, &account, -1); + } else { + gint active = gtk_combo_box_get_active (GTK_COMBO_BOX (self->combobox_account)); + gs_auth_dialog_get_nth_account_data (self, active, COLUMN_ACCOUNT, &account, -1); + } - g_cancellable_cancel (dialog->cancellable); - g_clear_object (&dialog->cancellable); + if (account == NULL) + gs_auth_dialog_button_add_another_cb (GTK_BUTTON (self->button_add_another), self); + else + gs_auth_dialog_response_if_valid (self, account); - G_OBJECT_CLASS (gs_auth_dialog_parent_class)->dispose (object); + g_clear_object (&account); } +/* GObject */ + static void -gs_auth_dialog_init (GsAuthDialog *dialog) +gs_auth_dialog_dispose (GObject *object) { - gtk_widget_init_template (GTK_WIDGET (dialog)); - - dialog->cancellable = g_cancellable_new (); - - g_signal_connect (dialog->entry_username, "notify::text", - G_CALLBACK (gs_auth_dialog_notify_username_cb), dialog); - g_signal_connect (dialog->entry_password, "notify::text", - G_CALLBACK (gs_auth_dialog_notify_password_cb), dialog); - g_signal_connect (dialog->entry_password, "activate", - G_CALLBACK (gs_auth_dialog_continue_cb), dialog); - g_signal_connect (dialog->entry_pin, "notify::text", - G_CALLBACK (gs_auth_dialog_notify_pin_cb), dialog); - g_signal_connect (dialog->entry_pin, "activate", - G_CALLBACK (gs_auth_dialog_continue_cb), dialog); - g_signal_connect (dialog->checkbutton_remember, "toggled", - G_CALLBACK (gs_auth_dialog_remember_cb), dialog); - g_signal_connect (dialog->radiobutton_already, "toggled", - G_CALLBACK (gs_auth_dialog_toggled_cb), dialog); - g_signal_connect (dialog->radiobutton_register, "toggled", - G_CALLBACK (gs_auth_dialog_toggled_cb), dialog); - g_signal_connect (dialog->radiobutton_lost_pwd, "toggled", - G_CALLBACK (gs_auth_dialog_toggled_cb), dialog); - g_signal_connect (dialog->button_cancel, "clicked", - G_CALLBACK (gs_auth_dialog_cancel_button_cb), dialog); - g_signal_connect (dialog->button_continue, "clicked", - G_CALLBACK (gs_auth_dialog_continue_cb), dialog); + GsAuthDialog *self = GS_AUTH_DIALOG (object); + + g_clear_object (&self->goa_client); + + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + + G_OBJECT_CLASS (gs_auth_dialog_parent_class)->dispose (object); } static void @@ -301,34 +385,72 @@ gs_auth_dialog_class_init (GsAuthDialogClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-auth-dialog.ui"); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, box_dialog); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, box_error); + gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, label_header); + gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, combobox_account); + gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, label_account); + gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, button_add_another); gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, button_cancel); gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, button_continue); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, checkbutton_remember); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, entry_password); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, entry_pin); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, entry_username); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, image_vendor); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, label_error); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, label_title); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, radiobutton_already); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, radiobutton_lost_pwd); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, radiobutton_register); - gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, stack); + gtk_widget_class_bind_template_child (widget_class, GsAuthDialog, liststore_account); + + + gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), gs_auth_dialog_button_add_another_cb); + gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), gs_auth_dialog_button_cancel_cb); + gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), gs_auth_dialog_button_continue_cb); +} + +static void +gs_auth_dialog_init (GsAuthDialog *self) +{ + self->cancellable = g_cancellable_new (); + + gtk_widget_init_template (GTK_WIDGET (self)); + gtk_widget_grab_focus (self->button_continue); } +/* GInitable */ + +static gboolean +gs_auth_dialog_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GsAuthDialog *self; + + g_return_val_if_fail (GS_IS_AUTH_DIALOG (initable), FALSE); + + self = GS_AUTH_DIALOG (initable); + + self->goa_client = goa_client_new_sync (NULL, error); + if (!self->goa_client) + return FALSE; + + /* Be ready to other accounts */ + g_signal_connect (self->goa_client, "account-added", G_CALLBACK (gs_auth_dialog_account_added_cb), self); + g_signal_connect (self->goa_client, "account-removed", G_CALLBACK (gs_auth_dialog_account_removed_cb), self); + + return TRUE; +} + +static void +gs_auth_dialog_initable_iface_init (GInitableIface *iface) +{ + iface->init = gs_auth_dialog_initable_init; +} + +/* Public API */ + GtkWidget * gs_auth_dialog_new (GsPluginLoader *plugin_loader, GsApp *app, - const gchar *provider_id, + const gchar *auth_id, GError **error) { GsAuthDialog *dialog; GsAuth *auth; /* get the authentication provider */ - if (provider_id == NULL) { + if (auth_id == NULL) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, @@ -336,25 +458,32 @@ gs_auth_dialog_new (GsPluginLoader *plugin_loader, app != NULL ? gs_app_get_id (app) : NULL); return NULL; } - auth = gs_plugin_loader_get_auth_by_id (plugin_loader, provider_id); + auth = gs_plugin_loader_get_auth_by_id (plugin_loader, auth_id); if (auth == NULL) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, "no auth-provider %s for %s", - provider_id, + auth_id, app != NULL ? gs_app_get_id (app) : NULL); return NULL; } /* create dialog */ - dialog = g_object_new (GS_TYPE_AUTH_DIALOG, - "use-header-bar", TRUE, - NULL); + dialog = g_initable_new (GS_TYPE_AUTH_DIALOG, + NULL, error, + "use-header-bar", FALSE, + NULL); + + if (dialog == NULL) + return NULL; + dialog->plugin_loader = g_object_ref (plugin_loader); dialog->app = app != NULL ? g_object_ref (app) : NULL; dialog->auth = g_object_ref (auth); - gs_auth_dialog_setup (dialog); + + gs_auth_dialog_setup_model (dialog); + gs_auth_dialog_check_ui (dialog, FALSE); return GTK_WIDGET (dialog); } diff --git a/src/gs-auth-dialog.h b/src/gs-auth-dialog.h index 721a307e9..63b7877b0 100644 --- a/src/gs-auth-dialog.h +++ b/src/gs-auth-dialog.h @@ -32,10 +32,10 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (GsAuthDialog, gs_auth_dialog, GS, AUTH_DIALOG, GtkDialog) -GtkWidget *gs_auth_dialog_new (GsPluginLoader *plugin_loader, - GsApp *app, - const gchar *provider_id, - GError **error); +GtkWidget *gs_auth_dialog_new (GsPluginLoader *plugin_loader, + GsApp *app, + const gchar *auth_id, + GError **error); G_END_DECLS diff --git a/src/gs-auth-dialog.ui b/src/gs-auth-dialog.ui index aec7b2472..f96d04618 100644 --- a/src/gs-auth-dialog.ui +++ b/src/gs-auth-dialog.ui @@ -1,317 +1,164 @@ - + + + + + + + + + + + - - - - - - diff --git a/src/gs-page.c b/src/gs-page.c index aa5321ec4..823ce338b 100644 --- a/src/gs-page.c +++ b/src/gs-page.c @@ -99,10 +99,10 @@ gs_page_authenticate_cb (GtkDialog *dialog, void gs_page_authenticate (GsPage *page, GsApp *app, - const gchar *provider_id, + const gchar *auth_id, GCancellable *cancellable, - GsPageAuthCallback callback, - gpointer user_data) + GsPageAuthCallback callback, + gpointer user_data) { GsPagePrivate *priv = gs_page_get_instance_private (page); g_autoptr(GsPageHelper) helper = NULL; @@ -117,7 +117,7 @@ gs_page_authenticate (GsPage *page, dialog = gs_auth_dialog_new (priv->plugin_loader, app, - provider_id, + auth_id, &error); if (dialog == NULL) { g_warning ("%s", error->message); diff --git a/src/gs-page.h b/src/gs-page.h index 539a0b587..874e8cbca 100644 --- a/src/gs-page.h +++ b/src/gs-page.h @@ -63,7 +63,7 @@ void gs_page_set_header_end_widget (GsPage *page, GtkWidget *widget); void gs_page_authenticate (GsPage *page, GsApp *app, - const gchar *provider_id, + const gchar *auth_id, GCancellable *cancellable, GsPageAuthCallback callback, gpointer user_data); diff --git a/src/gs-shell.c b/src/gs-shell.c index 85d66a31d..03aeba9c5 100644 --- a/src/gs-shell.c +++ b/src/gs-shell.c @@ -656,86 +656,104 @@ static void signin_activated_cb (GSimpleAction *action, GVariant *parameter, GsShell *shell) { GsShellPrivate *priv = gs_shell_get_instance_private (shell); - const gchar *action_name, *provider_id; + const gchar *action_name, *auth_id; GsAuth *auth; action_name = g_action_get_name (G_ACTION (action)); g_return_if_fail (g_str_has_prefix (action_name, "signin-")); - provider_id = action_name + strlen ("signin-"); + auth_id = action_name + strlen ("signin-"); - auth = gs_plugin_loader_get_auth_by_id (priv->plugin_loader, provider_id); + auth = gs_plugin_loader_get_auth_by_id (priv->plugin_loader, auth_id); g_return_if_fail (auth != NULL); gs_page_authenticate (priv->page, NULL, - gs_auth_get_provider_id (auth), + gs_auth_get_auth_id (auth), priv->cancellable, NULL, NULL); } -static void -gs_shell_logout_cb (GObject *source, - GAsyncResult *res, - gpointer user_data) -{ - GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source); - g_autoptr(GError) error = NULL; - - if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) { - g_warning ("failed to logout: %s", error->message); - return; - } -} - static void signout_activated_cb (GSimpleAction *action, GVariant *parameter, GsShell *shell) { GsShellPrivate *priv = gs_shell_get_instance_private (shell); - const gchar *action_name, *provider_id; + const gchar *action_name, *auth_id; g_autoptr(GsPluginJob) plugin_job = NULL; GsAuth *auth; action_name = g_action_get_name (G_ACTION (action)); g_return_if_fail (g_str_has_prefix (action_name, "signout-")); - provider_id = action_name + strlen ("signout-"); + auth_id = action_name + strlen ("signout-"); - auth = gs_plugin_loader_get_auth_by_id (priv->plugin_loader, provider_id); + auth = gs_plugin_loader_get_auth_by_id (priv->plugin_loader, auth_id); g_return_if_fail (auth != NULL); - plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_AUTH_LOGOUT, - "interactive", TRUE, - "auth", auth, - NULL); - - gs_plugin_loader_job_process_async (priv->plugin_loader, plugin_job, - priv->cancellable, - gs_shell_logout_cb, - shell); + gs_auth_set_goa_object (auth, NULL); } static void -menu_button_clicked_cb (GtkButton *button, GsShell *shell) +gs_shell_reload_auth_menus (GsShell *shell) { GsShellPrivate *priv = gs_shell_get_instance_private (shell); + GMenu *accounts_menu; GPtrArray *auth_array; + accounts_menu = G_MENU (gtk_builder_get_object (priv->builder, "accounts_menu")); + g_menu_remove_all (accounts_menu); + auth_array = gs_plugin_loader_get_auths (priv->plugin_loader); for (guint i = 0; i < auth_array->len; i++) { GsAuth *auth = g_ptr_array_index (auth_array, i); gboolean logged_in; g_autofree gchar *signin_action_name = NULL; GSimpleAction *signin_action; + g_autofree gchar *signin_target = NULL; g_autofree gchar *signout_action_name = NULL; GSimpleAction *signout_action; + g_autofree gchar *signout_target = NULL; + GoaObject *goa_object; + g_autofree gchar *signin_label = NULL; + g_autofree gchar *signout_label = NULL; + g_autoptr(GMenu) auth_menu = NULL; + g_autoptr(GMenuItem) signin_item = NULL; + g_autoptr(GMenuItem) signout_item = NULL; + + + goa_object = gs_auth_peek_goa_object (auth); + logged_in = goa_object != NULL; - logged_in = gs_auth_has_flag (auth, GS_AUTH_FLAG_VALID); + auth_menu = g_menu_new (); + accounts_menu = G_MENU (gtk_builder_get_object (priv->builder, "accounts_menu")); + g_menu_append_section (accounts_menu, gs_auth_get_provider_name (auth), G_MENU_MODEL (auth_menu)); - signin_action_name = g_strdup_printf ("signin-%s", gs_auth_get_provider_id (auth)); + signin_action_name = g_strdup_printf ("signin-%s", gs_auth_get_auth_id (auth)); signin_action = G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (priv->auth_actions), signin_action_name)); g_simple_action_set_enabled (signin_action, !logged_in); - signout_action_name = g_strdup_printf ("signout-%s", gs_auth_get_provider_id (auth)); + signout_action_name = g_strdup_printf ("signout-%s", gs_auth_get_auth_id (auth)); signout_action = G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (priv->auth_actions), signout_action_name)); g_simple_action_set_enabled (signout_action, logged_in); + + + if (logged_in) { + GoaAccount *goa_account = goa_object_peek_account (goa_object); + + /* TRANSLATORS: menu item that signs into the named account with a particular username */ + signin_label = g_strdup_printf (_("Signed in as %s"), + goa_account_get_presentation_identity (goa_account)); + } else { + /* TRANSLATORS: menu item that signs into the named account */ + signin_label = g_strdup_printf (_("Sign in…")); + } + + signin_target = g_strdup_printf ("auth.%s", signin_action_name); + signin_item = g_menu_item_new (signin_label, signin_target); + g_menu_append_item (auth_menu, signin_item); + + /* TRANSLATORS: menu item for signing out from the named account */ + signout_label = g_strdup_printf (_("Sign out")); + signout_target = g_strdup_printf ("auth.%s", signout_action_name); + signout_item = g_menu_item_new (signout_label, signout_target); + g_menu_append_item (auth_menu, signout_item); } } @@ -1030,7 +1048,6 @@ gs_shell_show_event_refresh (GsShell *shell, GsPluginEvent *event) buttons |= GS_SHELL_EVENT_BUTTON_NO_SPACE; break; case GS_PLUGIN_ERROR_AUTH_REQUIRED: - case GS_PLUGIN_ERROR_PIN_REQUIRED: /* TRANSLATORS: failure text for the in-app notification */ g_string_append (str, _("Unable to download updates: " "authentication was required")); @@ -1093,7 +1110,6 @@ gs_shell_show_event_purchase (GsShell *shell, GsPluginEvent *event) str_app = gs_shell_get_title_from_app (app); switch (error->code) { case GS_PLUGIN_ERROR_AUTH_REQUIRED: - case GS_PLUGIN_ERROR_PIN_REQUIRED: /* TRANSLATORS: failure text for the in-app notification, * where the %s is the application name (e.g. "GIMP") */ g_string_append_printf (str, _("Unable to purchase %s: " @@ -1208,7 +1224,6 @@ gs_shell_show_event_install (GsShell *shell, GsPluginEvent *event) buttons |= GS_SHELL_EVENT_BUTTON_NO_SPACE; break; case GS_PLUGIN_ERROR_AUTH_REQUIRED: - case GS_PLUGIN_ERROR_PIN_REQUIRED: /* TRANSLATORS: failure text for the in-app notification */ g_string_append_printf (str, _("Unable to install %s: " "authentication was required"), @@ -1229,35 +1244,6 @@ gs_shell_show_event_install (GsShell *shell, GsPluginEvent *event) "install software"), str_app); break; - case GS_PLUGIN_ERROR_ACCOUNT_SUSPENDED: - case GS_PLUGIN_ERROR_ACCOUNT_DEACTIVATED: - if (origin != NULL) { - const gchar *url_homepage; - - /* TRANSLATORS: failure text for the in-app notification, - * the %s is the name of the authentication service, - * e.g. "Ubuntu One" */ - g_string_append_printf (str, _("Your %s account has been suspended."), - gs_app_get_name (origin)); - g_string_append (str, " "); - /* TRANSLATORS: failure text for the in-app notification */ - g_string_append (str, _("It is not possible to install " - "software until this has been resolved.")); - url_homepage = gs_app_get_url (origin, AS_URL_KIND_HOMEPAGE); - if (url_homepage != NULL) { - g_autofree gchar *url = NULL; - url = g_strdup_printf ("%s", - url_homepage, - url_homepage); - /* TRANSLATORS: failure text for the in-app notification, - * where the %s is the clickable link (e.g. - * "http://example.com/what-did-i-do-wrong/") */ - msg = g_strdup_printf (_("For more information, visit %s."), - url); - g_string_append_printf (str, " %s", msg); - } - } - break; case GS_PLUGIN_ERROR_AC_POWER_REQUIRED: /* TRANSLATORS: failure text for the in-app notification, * where the %s is the application name (e.g. "Dell XPS 13") */ @@ -1342,7 +1328,6 @@ gs_shell_show_event_update (GsShell *shell, GsPluginEvent *event) buttons |= GS_SHELL_EVENT_BUTTON_NO_SPACE; break; case GS_PLUGIN_ERROR_AUTH_REQUIRED: - case GS_PLUGIN_ERROR_PIN_REQUIRED: /* TRANSLATORS: failure text for the in-app notification, * where the %s is the application name (e.g. "GIMP") */ g_string_append_printf (str, _("Unable to update %s: " @@ -1447,7 +1432,6 @@ gs_shell_show_event_upgrade (GsShell *shell, GsPluginEvent *event) buttons |= GS_SHELL_EVENT_BUTTON_NO_SPACE; break; case GS_PLUGIN_ERROR_AUTH_REQUIRED: - case GS_PLUGIN_ERROR_PIN_REQUIRED: /* TRANSLATORS: failure text for the in-app notification, * where the %s is the distro name (e.g. "Fedora 25") */ g_string_append_printf (str, _("Unable to upgrade to %s: " @@ -1519,7 +1503,6 @@ gs_shell_show_event_remove (GsShell *shell, GsPluginEvent *event) str_app = gs_shell_get_title_from_app (app); switch (error->code) { case GS_PLUGIN_ERROR_AUTH_REQUIRED: - case GS_PLUGIN_ERROR_PIN_REQUIRED: /* TRANSLATORS: failure text for the in-app notification, * where the %s is the application name (e.g. "GIMP") */ g_string_append_printf (str, _("Unable to remove %s: authentication was required"), @@ -2021,10 +2004,10 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can shell); /* show the account popover when clicking on the account button */ - widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "menu_button")); + /* widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "menu_button")); g_signal_connect (widget, "clicked", G_CALLBACK (menu_button_clicked_cb), - shell); + shell); */ /* setup buttons */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_back")); @@ -2115,58 +2098,25 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can auth_array = gs_plugin_loader_get_auths (priv->plugin_loader); for (guint i = 0; i < auth_array->len; i++) { GsAuth *auth = g_ptr_array_index (auth_array, i); - GMenu *accounts_menu; - gboolean logged_in; - g_autoptr(GMenu) auth_menu = NULL; g_autoptr(GSimpleAction) signin_action = NULL; g_autofree gchar *signin_action_name = NULL; - g_autoptr(GMenuItem) signin_item = NULL; - g_autofree gchar *signin_label = NULL; - g_autofree gchar *signin_target = NULL; g_autoptr(GSimpleAction) signout_action = NULL; g_autofree gchar *signout_action_name = NULL; - g_autoptr(GMenuItem) signout_item = NULL; - g_autofree gchar *signout_label = NULL; - g_autofree gchar *signout_target = NULL; - - logged_in = gs_auth_has_flag (auth, GS_AUTH_FLAG_VALID); - signin_action_name = g_strdup_printf ("signin-%s", gs_auth_get_provider_id (auth)); + signin_action_name = g_strdup_printf ("signin-%s", gs_auth_get_auth_id (auth)); signin_action = g_simple_action_new (signin_action_name, NULL); - g_simple_action_set_enabled (signin_action, !logged_in); g_signal_connect (signin_action, "activate", G_CALLBACK (signin_activated_cb), shell); g_action_map_add_action (G_ACTION_MAP (priv->auth_actions), G_ACTION (signin_action)); - signout_action_name = g_strdup_printf ("signout-%s", gs_auth_get_provider_id (auth)); + signout_action_name = g_strdup_printf ("signout-%s", gs_auth_get_auth_id (auth)); signout_action = g_simple_action_new (signout_action_name, NULL); - g_simple_action_set_enabled (signout_action, logged_in); g_signal_connect (signout_action, "activate", G_CALLBACK (signout_activated_cb), shell); g_action_map_add_action (G_ACTION_MAP (priv->auth_actions), G_ACTION (signout_action)); - auth_menu = g_menu_new (); - accounts_menu = G_MENU (gtk_builder_get_object (priv->builder, "accounts_menu")); - g_menu_append_section (accounts_menu, NULL, G_MENU_MODEL (auth_menu)); - - if (logged_in) { - /* TRANSLATORS: menu item that signs into the named account with a particular username */ - signin_label = g_strdup_printf (_("Signed in into %s as %s"), - gs_auth_get_provider_name (auth), - gs_auth_get_username (auth)); - } else { - /* TRANSLATORS: menu item that signs into the named account */ - signin_label = g_strdup_printf (_("Sign in to %s…"), - gs_auth_get_provider_name (auth)); - } - signin_target = g_strdup_printf ("auth.%s", signin_action_name); - signin_item = g_menu_item_new (signin_label, signin_target); - g_menu_append_item (auth_menu, signin_item); - - /* TRANSLATORS: menu item for signing out from the named account */ - signout_label = g_strdup_printf (_("Sign out from %s"), - gs_auth_get_provider_name (auth)); - signout_target = g_strdup_printf ("auth.%s", signout_action_name); - signout_item = g_menu_item_new (signout_label, signout_target); - g_menu_append_item (auth_menu, signout_item); + g_signal_connect_object (auth, "changed", + G_CALLBACK (gs_shell_reload_auth_menus), + shell, G_CONNECT_SWAPPED); + gs_shell_reload_auth_menus (shell); } /* show loading page, which triggers the initial refresh */ diff --git a/src/meson.build b/src/meson.build index ebcaaa98c..aafc9b7a0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -74,10 +74,10 @@ gnome_software_dependencies = [ appstream_glib, gio_unix, gmodule, + goa, gtk, json_glib, libm, - libsecret, libsoup ] @@ -296,10 +296,10 @@ if get_option('tests') appstream_glib, gio_unix, gmodule, + goa, gtk, json_glib, libm, - libsecret, libsoup, ], link_with : [ -- GitLab From c74269ed69d2d4e9451db9797d4bb39171f18f22 Mon Sep 17 00:00:00 2001 From: Andrea Azzarone Date: Wed, 12 Dec 2018 12:35:03 +0000 Subject: [PATCH 2/2] trivial: Bump the plugin version as we're planning to break plugin API --- contrib/gnome-software.spec.in | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/gnome-software.spec.in b/contrib/gnome-software.spec.in index aba9e4871..e5974895b 100644 --- a/contrib/gnome-software.spec.in +++ b/contrib/gnome-software.spec.in @@ -71,7 +71,7 @@ Requires: libsoup%{?_isa} >= %{libsoup_version} Requires: PackageKit%{?_isa} >= %{packagekit_version} # this is not a library version -%define gs_plugin_version 12 +%define gs_plugin_version 13 %description gnome-software is an application that makes it easy to add, remove diff --git a/meson.build b/meson.build index 34877ecea..e717de276 100644 --- a/meson.build +++ b/meson.build @@ -12,7 +12,7 @@ conf.set_quoted('PACKAGE_VERSION', meson.project_version()) # this refers to the gnome-software plugin API version # this is not in any way related to a package or soname version -gs_plugin_api_version = '12' +gs_plugin_api_version = '13' conf.set_quoted('GS_PLUGIN_API_VERSION', gs_plugin_api_version) # install docs -- GitLab