[gnome-software] Add functionality to load and save the GsAuth to disk
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Add functionality to load and save the GsAuth to disk
- Date: Mon, 4 Jul 2016 09:48:42 +0000 (UTC)
commit f071b5fcc801a7278c5a1a38096a682b2060a221
Author: Richard Hughes <richard hughsie com>
Date: Mon Jul 4 10:40:32 2016 +0100
Add functionality to load and save the GsAuth to disk
Based on a patch from Robert Ancell, many thanks.
configure.ac | 1 +
contrib/gnome-software.spec.in | 1 +
src/Makefile.am | 4 +
src/gs-auth.c | 204 +++++++++++++++++++++++++++++++++++++++-
src/gs-auth.h | 10 ++
src/gs-self-test.c | 32 ++++++
6 files changed, 251 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7f2133e..47705ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,6 +68,7 @@ PKG_CHECK_MODULES(SQLITE, sqlite3)
PKG_CHECK_MODULES(SOUP, libsoup-2.4 >= 2.51.92)
PKG_CHECK_MODULES(GSETTINGS_DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.11.5)
PKG_CHECK_MODULES(GNOME_DESKTOP, gnome-desktop-3.0 >= 3.17.92)
+PKG_CHECK_MODULES(LIBSECRET, libsecret-1)
AC_PATH_PROG(APPSTREAM_UTIL, [appstream-util], [unfound])
AC_ARG_ENABLE(man,
[AS_HELP_STRING([--enable-man],
diff --git a/contrib/gnome-software.spec.in b/contrib/gnome-software.spec.in
index 4e47917..880982b 100644
--- a/contrib/gnome-software.spec.in
+++ b/contrib/gnome-software.spec.in
@@ -52,6 +52,7 @@ BuildRequires: libappstream-glib-devel >= %{appstream_glib_version}
BuildRequires: libsoup-devel
BuildRequires: PackageKit-glib-devel >= %{packagekit_version}
BuildRequires: polkit-devel
+BuildRequires: libsecret-devel
BuildRequires: flatpak-devel >= %{flatpak_version}
# this is not a library version
diff --git a/src/Makefile.am b/src/Makefile.am
index fc34770..6af354f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,6 +9,7 @@ AM_CPPFLAGS = \
$(PACKAGEKIT_CFLAGS) \
$(GNOME_DESKTOP_CFLAGS) \
$(POLKIT_CFLAGS) \
+ $(LIBSECRET_CFLAGS) \
-DG_LOG_DOMAIN=\"Gs\" \
-DI_KNOW_THE_PACKAGEKIT_GLIB2_API_IS_SUBJECT_TO_CHANGE \
-DLIBDIR=\"$(libdir)\" \
@@ -123,6 +124,7 @@ gnome_software_cmd_LDADD = \
$(APPSTREAM_LIBS) \
$(POLKIT_LIBS) \
$(SOUP_LIBS) \
+ $(LIBSECRET_LIBS) \
$(GLIB_LIBS) \
$(GTK_LIBS)
@@ -272,6 +274,7 @@ gnome_software_LDADD = \
$(GLIB_LIBS) \
$(GTK_LIBS) \
$(SOUP_LIBS) \
+ $(LIBSECRET_LIBS) \
$(PACKAGEKIT_LIBS) \
$(GNOME_DESKTOP_LIBS) \
$(POLKIT_LIBS) \
@@ -346,6 +349,7 @@ gs_self_test_LDADD = \
$(APPSTREAM_LIBS) \
$(POLKIT_LIBS) \
$(SOUP_LIBS) \
+ $(LIBSECRET_LIBS) \
$(GLIB_LIBS) \
$(GTK_LIBS)
diff --git a/src/gs-auth.c b/src/gs-auth.c
index 167f4c6..3a13f4b 100644
--- a/src/gs-auth.c
+++ b/src/gs-auth.c
@@ -32,7 +32,10 @@
#include "config.h"
+#include <libsecret/secret.h>
+
#include "gs-auth.h"
+#include "gs-plugin.h"
struct _GsAuth
{
@@ -43,10 +46,11 @@ struct _GsAuth
gchar *provider_name;
gchar *provider_logo;
gchar *provider_uri;
+ gchar *provider_schema;
gchar *username;
gchar *password;
gchar *pin;
- GHashTable *metadata;
+ GHashTable *metadata; /* utf8: utf8 */
};
enum {
@@ -167,6 +171,36 @@ gs_auth_set_provider_uri (GsAuth *auth, const gchar *provider_uri)
}
/**
+ * gs_auth_get_provider_schema:
+ * @auth: a #GsAuth
+ *
+ * Gets the authentication schema ID.
+ *
+ * Returns: the URI, or %NULL
+ */
+const gchar *
+gs_auth_get_provider_schema (GsAuth *auth)
+{
+ g_return_val_if_fail (GS_IS_AUTH (auth), NULL);
+ return auth->provider_schema;
+}
+
+/**
+ * gs_auth_set_provider_schema:
+ * @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
*
@@ -352,6 +386,173 @@ gs_auth_add_metadata (GsAuth *auth, const gchar *key, const gchar *value)
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_load:
+ * @auth: a #GsAuth
+ * @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_load (GsAuth *auth, GCancellable *cancellable, GError **error)
+{
+ GList *l;
+ g_autoptr(GList) keys = NULL;
+ 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 (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 (auth->password == NULL) {
+ auth->password = secret_password_lookup_sync (&schema,
+ cancellable,
+ error,
+ "key", "password",
+ NULL);
+ if (_g_error_is_set (error))
+ return FALSE;
+ }
+
+ /* metadata */
+ keys = g_hash_table_get_keys (auth->metadata);
+ for (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);
+ }
+
+ /* success */
+ return TRUE;
+}
+
+/**
+ * gs_auth_save:
+ * @auth: a #GsAuth
+ * @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_save (GsAuth *auth, GCancellable *cancellable, GError **error)
+{
+ GList *l;
+ g_autoptr(GList) keys = NULL;
+ 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 (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 (auth->password != NULL) {
+ if (!secret_password_store_sync (&schema,
+ NULL, /* collection */
+ auth->provider_schema,
+ auth->password,
+ cancellable, error,
+ "key", "password", NULL))
+ return FALSE;
+ }
+
+ /* metadata */
+ keys = g_hash_table_get_keys (auth->metadata);
+ for (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;
+ }
+
+ /* success */
+ return TRUE;
+}
+
static void
gs_auth_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
@@ -411,6 +612,7 @@ gs_auth_finalize (GObject *object)
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);
diff --git a/src/gs-auth.h b/src/gs-auth.h
index b841e9c..25a1ba0 100644
--- a/src/gs-auth.h
+++ b/src/gs-auth.h
@@ -23,6 +23,7 @@
#define __GS_AUTH_H
#include <glib-object.h>
+#include <gio/gio.h>
G_BEGIN_DECLS
@@ -75,6 +76,9 @@ void gs_auth_set_provider_logo (GsAuth *auth,
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);
@@ -96,6 +100,12 @@ const gchar *gs_auth_get_metadata_item (GsAuth *auth,
void gs_auth_add_metadata (GsAuth *auth,
const gchar *key,
const gchar *value);
+gboolean gs_auth_load (GsAuth *auth,
+ GCancellable *cancellable,
+ GError **error);
+gboolean gs_auth_save (GsAuth *auth,
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
diff --git a/src/gs-self-test.c b/src/gs-self-test.c
index f5b1213..97d0fd5 100644
--- a/src/gs-self-test.c
+++ b/src/gs-self-test.c
@@ -897,6 +897,37 @@ gs_plugin_loader_authentication_func (GsPluginLoader *plugin_loader)
}
static void
+gs_auth_secret_func (void)
+{
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAuth) auth1 = NULL;
+ g_autoptr(GsAuth) auth2 = NULL;
+
+ /* 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_save (auth1, 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_load (auth2, 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_plugin_loader_wildcard_func (GsPluginLoader *plugin_loader)
{
g_autoptr(GError) error = NULL;
@@ -1038,6 +1069,7 @@ main (int argc, char **argv)
g_test_add_func ("/gnome-software/os-release", gs_os_release_func);
g_test_add_func ("/gnome-software/app", gs_app_func);
g_test_add_func ("/gnome-software/plugin", gs_plugin_func);
+ g_test_add_func ("/gnome-software/auth{secret}", gs_auth_secret_func);
/* we can only load this once per process */
plugin_loader = gs_plugin_loader_new ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]