From afc1d656791c938796894e7ef458a5d60875fc50 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 14 Nov 2012 10:30:48 +0100 Subject: [PATCH] When storing a secret, create default keyring if necessary * If the default keyring does not exist when storing a secret try and create it. * We handle both secrets that correctly return NoSuchObject and ones that just return the silly DBus UnknownMethod error. https://bugzilla.gnome.org/show_bug.cgi?id=688165 --- libsecret/secret-collection.c | 8 ++-- libsecret/secret-methods.c | 65 ++++++++++++++++++++++++--- libsecret/secret-private.h | 4 ++ libsecret/tests/mock-service-empty.py | 17 +++++++ libsecret/tests/test-methods.c | 50 +++++++++++++++++++++ 5 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 libsecret/tests/mock-service-empty.py diff --git a/libsecret/secret-collection.c b/libsecret/secret-collection.c index 585f2af..24942b7 100644 --- a/libsecret/secret-collection.c +++ b/libsecret/secret-collection.c @@ -1042,8 +1042,8 @@ on_create_service (GObject *source, g_object_unref (async); } -static GHashTable * -collection_properties_new (const gchar *label) +GHashTable * +_secret_collection_properties_new (const gchar *label) { GHashTable *properties; GVariant *value; @@ -1104,7 +1104,7 @@ secret_collection_create (SecretService *service, secret_collection_create); closure = g_slice_new0 (CreateClosure); closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - closure->properties = collection_properties_new (label); + closure->properties = _secret_collection_properties_new (label); closure->alias = g_strdup (alias); closure->flags = flags; g_simple_async_result_set_op_res_gpointer (res, closure, create_closure_free); @@ -1208,7 +1208,7 @@ secret_collection_create_sync (SecretService *service, g_object_ref (service); } - properties = collection_properties_new (label); + properties = _secret_collection_properties_new (label); path = secret_service_create_collection_dbus_path_sync (service, properties, alias, flags, cancellable, error); diff --git a/libsecret/secret-methods.c b/libsecret/secret-methods.c index b768857..9dfaa7e 100644 --- a/libsecret/secret-methods.c +++ b/libsecret/secret-methods.c @@ -955,6 +955,7 @@ typedef struct { gchar *collection_path; SecretValue *value; GHashTable *properties; + gboolean created_collection; } StoreClosure; static void @@ -972,18 +973,72 @@ store_closure_free (gpointer data) static void on_store_create (GObject *source, GAsyncResult *result, - gpointer user_data) + gpointer user_data); + +static void +on_store_keyring (GObject *source, + GAsyncResult *result, + gpointer user_data) { GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); + StoreClosure *store = g_simple_async_result_get_op_res_gpointer (async); + SecretService *service = SECRET_SERVICE (source); GError *error = NULL; gchar *path; - path = secret_service_create_item_dbus_path_finish (SECRET_SERVICE (source), result, &error); - if (error != NULL) + path = secret_service_create_collection_dbus_path_finish (service, result, &error); + if (error == NULL) { + store->created_collection = TRUE; + secret_service_create_item_dbus_path (service, store->collection_path, + store->properties, store->value, + SECRET_ITEM_CREATE_REPLACE, store->cancellable, + on_store_create, g_object_ref (async)); + } else { g_simple_async_result_take_error (async, error); - g_free (path); + g_simple_async_result_complete (async); + } - g_simple_async_result_complete (async); + g_object_unref (async); + g_free (path); +} + +static void +on_store_create (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); + StoreClosure *store = g_simple_async_result_get_op_res_gpointer (async); + SecretService *service = SECRET_SERVICE (source); + GError *error = NULL; + GHashTable *properties; + gchar *path; + + path = secret_service_create_item_dbus_path_finish (service, result, &error); + + /* + * This happens when the collection doesn't exist. If the collection is + * the default alias, we should try and create it + */ + + if (!store->created_collection && + (g_error_matches (error, SECRET_ERROR, SECRET_ERROR_NO_SUCH_OBJECT) || + g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) && + g_strcmp0 (store->collection_path, SECRET_ALIAS_PREFIX "default") == 0) { + properties = _secret_collection_properties_new ("Default keyring"); + secret_service_create_collection_dbus_path (service, properties, "default", + SECRET_COLLECTION_CREATE_NONE, store->cancellable, + on_store_keyring, g_object_ref (async)); + g_hash_table_unref (properties); + g_error_free (error); + + } else { + if (error != NULL) + g_simple_async_result_take_error (async, error); + g_simple_async_result_complete (async); + } + + g_free (path); g_object_unref (async); } diff --git a/libsecret/secret-private.h b/libsecret/secret-private.h index 74c9297..5e05032 100644 --- a/libsecret/secret-private.h +++ b/libsecret/secret-private.h @@ -31,6 +31,8 @@ typedef struct { typedef struct _SecretSession SecretSession; +#define SECRET_ALIAS_PREFIX "/org/freedesktop/secrets/aliases/" + #define SECRET_SERVICE_PATH "/org/freedesktop/secrets" #define SECRET_SERVICE_BUS_NAME "org.freedesktop.secrets" @@ -166,6 +168,8 @@ gint _secret_service_xlock_paths_finish (SecretService *se gchar ***xlocked, GError **error); +GHashTable * _secret_collection_properties_new (const gchar *label); + SecretItem * _secret_collection_find_item_instance (SecretCollection *self, const gchar *item_path); diff --git a/libsecret/tests/mock-service-empty.py b/libsecret/tests/mock-service-empty.py new file mode 100644 index 0000000..27bd3d4 --- /dev/null +++ b/libsecret/tests/mock-service-empty.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +# +# Copyright 2012 Red Hat Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; either version 2.1 of the licence or (at +# your option) any later version. +# +# See the included COPYING file for more information. +# + +import mock + +service = mock.SecretService() +service.listen() diff --git a/libsecret/tests/test-methods.c b/libsecret/tests/test-methods.c index ecaf6f5..fe77134 100644 --- a/libsecret/tests/test-methods.c +++ b/libsecret/tests/test-methods.c @@ -865,6 +865,55 @@ test_store_async (Test *test, g_strfreev (paths); } +static void +test_store_no_default (Test *test, + gconstpointer used) +{ + SecretValue *value = secret_value_new ("apassword", -1, "text/plain"); + GHashTable *attributes; + GError *error = NULL; + gchar **paths; + gboolean ret; + gsize length; + + attributes = secret_attributes_build (&MOCK_SCHEMA, + "even", FALSE, + "string", "seventeen", + "number", 17, + NULL); + + ret = secret_service_store_sync (test->service, &MOCK_SCHEMA, attributes, SECRET_COLLECTION_DEFAULT, + "New Item Label", value, NULL, &error); + g_assert_no_error (error); + secret_value_unref (value); + g_hash_table_unref (attributes); + + attributes = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (attributes, "even", "false"); + g_hash_table_insert (attributes, "string", "seventeen"); + g_hash_table_insert (attributes, "number", "17"); + + ret = secret_service_search_for_dbus_paths_sync (test->service, &MOCK_SCHEMA, attributes, + NULL, &paths, NULL, &error); + g_hash_table_unref (attributes); + g_assert (ret == TRUE); + + g_assert (paths != NULL); + g_assert (paths[0] != NULL); + g_assert (paths[1] == NULL); + + value = secret_service_get_secret_for_dbus_path_sync (test->service, paths[0], + NULL, &error); + g_assert_no_error (error); + + g_assert (value != NULL); + g_assert_cmpstr (secret_value_get (value, &length), ==, "apassword"); + g_assert_cmpuint (length, ==, 9); + + secret_value_unref (value); + g_strfreev (paths); +} + static void test_set_alias_sync (Test *test, gconstpointer used) @@ -941,6 +990,7 @@ main (int argc, char **argv) g_test_add ("/service/store-sync", Test, "mock-service-normal.py", setup, test_store_sync, teardown); g_test_add ("/service/store-async", Test, "mock-service-normal.py", setup, test_store_async, teardown); g_test_add ("/service/store-replace", Test, "mock-service-normal.py", setup, test_store_replace, teardown); + g_test_add ("/service/store-no-default", Test, "mock-service-empty.py", setup, test_store_no_default, teardown); g_test_add ("/service/set-alias-sync", Test, "mock-service-normal.py", setup, test_set_alias_sync, teardown);