[gnome-software] Add functionality to load and save the GsAuth to disk



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]