diff options
Diffstat (limited to 'daemon/dbus/test-dbus-signals.c')
-rw-r--r-- | daemon/dbus/test-dbus-signals.c | 782 |
1 files changed, 782 insertions, 0 deletions
diff --git a/daemon/dbus/test-dbus-signals.c b/daemon/dbus/test-dbus-signals.c new file mode 100644 index 00000000..4d3ab434 --- /dev/null +++ b/daemon/dbus/test-dbus-signals.c @@ -0,0 +1,782 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* test-secret-util.c: Test secret utils + + Copyright (C) 2012 Red Hat Inc + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + <http://www.gnu.org/licenses/>. + + Author: Stef Walter <stefw@gnome.org> +*/ + +#include "config.h" + +#include "test-service.h" + +#include "gkd-secret-types.h" + +#include "egg/egg-testing.h" + +#include <gcr/gcr-base.h> + +#include <glib.h> +#include <glib/gstdio.h> +#include <gio/gio.h> + +#include <fcntl.h> + +typedef struct { + gchar *path; + gchar *iface; + gchar *name; + GVariant *parameters; +} ReceivedSignal; + +typedef struct { + TestService service; + guint signal_id; + GList *received_signals; +} Test; + +static void +on_signal_received (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + Test *test = user_data; + ReceivedSignal *sig; + + g_assert (object_path != NULL); + g_assert (interface_name != NULL); + g_assert (signal_name != NULL); + g_assert (parameters != NULL); + + sig = g_slice_new0 (ReceivedSignal); + sig->path = g_strdup (object_path); + sig->iface = g_strdup (interface_name); + sig->name = g_strdup (signal_name); + sig->parameters = g_variant_ref (parameters); + test->received_signals = g_list_prepend (test->received_signals, sig); +} + +static void +received_signal_free (gpointer data) +{ + ReceivedSignal *sig = data; + g_free (sig->path); + g_free (sig->iface); + g_free (sig->name); + g_variant_unref (sig->parameters); + g_slice_free (ReceivedSignal, sig); +} + +static void +received_signals_flush (Test *test) +{ + g_list_free_full (test->received_signals, received_signal_free); + test->received_signals = NULL; +} + +static void +expect_signal_with_path (Test *test, + const gchar *signal_path, + const gchar *signal_iface, + const gchar *signal_name, + const gchar *param_path) +{ + ReceivedSignal *sig; + const gchar *path; + GList *l; + + g_assert (signal_path != NULL); + g_assert (signal_iface != NULL); + g_assert (signal_name != NULL); + g_assert (param_path != NULL); + + for (l = test->received_signals; l != NULL; l = g_list_next (l)) { + sig = l->data; + + if (g_str_equal (signal_path, sig->path) && + g_str_equal (signal_iface, sig->iface) && + g_str_equal (signal_name, sig->name)) { + g_assert (g_variant_is_of_type (sig->parameters, G_VARIANT_TYPE ("(o)"))); + g_variant_get (sig->parameters, "(&o)", &path); + if (!g_str_equal (path, param_path)) { + g_critical ("received invalid path from signal %s on interface %s at object %s: " + "expected path %s but got %s", + sig->name, sig->iface, sig->path, param_path, path); + } + + return; + } + } + + g_critical ("didn't receive signal %s on interface %s at object %s", + signal_name, signal_iface, signal_path); +} + +static void +expect_property_changed (Test *test, + const gchar *signal_path, + const gchar *property_iface, + const gchar *property_name) +{ + ReceivedSignal *sig; + const gchar *iface; + GVariant *properties; + GVariant *invalidated; + GVariant *value; + GList *l; + + g_assert (signal_path != NULL); + g_assert (property_iface != NULL); + g_assert (property_name != NULL); + + for (l = test->received_signals; l != NULL; l = g_list_next (l)) { + sig = l->data; + + if (g_str_equal (signal_path, sig->path) && + g_str_equal ("org.freedesktop.DBus.Properties", sig->iface) && + g_str_equal ("PropertiesChanged", sig->name)) { + value = NULL; + g_assert (g_variant_is_of_type (sig->parameters, G_VARIANT_TYPE ("(sa{sv}as)"))); + + g_variant_get (sig->parameters, "(&s@a{sv}@as)", &iface, &properties, &invalidated); + if (g_str_equal (iface, property_iface)) { + value = g_variant_lookup_value (properties, property_name, NULL); + g_variant_unref (value); + } + + g_variant_unref (properties); + g_variant_unref (invalidated); + + if (value != NULL) + return; + } + } + + g_critical ("didn't receive PropertiesChanged for %s property on interface %s at object %s", + property_name, property_iface, signal_path); +} + +static void +on_complete_get_result (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GAsyncResult **res = user_data; + g_assert (res != NULL); + g_assert (*res == NULL); + *res = g_object_ref (result); + egg_test_wait_stop (); +} + +static GVariant * +dbus_call_perform (Test *test, + const gchar *object_path, + const gchar *interface, + const gchar *member, + GVariant *parameters, + const GVariantType *restype, + GError **error) +{ + GAsyncResult *result = NULL; + GVariant *retval; + + /* + * Do an async call with a full main loop, so that the signals + * arrive before the method result. + */ + + g_dbus_connection_call (test->service.connection, + test->service.bus_name, + object_path, + interface, + member, + parameters, + restype, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, + on_complete_get_result, + &result); + + g_assert (result == NULL); + egg_test_wait (); + g_assert (result != NULL); + + retval = g_dbus_connection_call_finish (test->service.connection, + result, error); + g_object_unref (result); + + return retval; +} + +static void +setup (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + GVariant *retval; + + test->service.mock_prompter = gcr_mock_prompter_start (); + g_assert (test->service.mock_prompter != NULL); + + test_service_setup (&test->service); + + /* Unlock the test collection */ + retval = g_dbus_connection_call_sync (test->service.connection, + test->service.bus_name, + SECRET_SERVICE_PATH, + INTERNAL_SERVICE_INTERFACE, + "UnlockWithMasterPassword", + g_variant_new ("(o@(oayays))", + "/org/freedesktop/secrets/collection/test", + test_service_build_secret (&test->service, "booo")), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, &error); + g_assert_no_error (error); + g_variant_unref (retval); + + /* Wait for the prompt's completed signal */ + test->signal_id = g_dbus_connection_signal_subscribe (test->service.connection, + test->service.bus_name, + NULL, NULL, NULL, NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_signal_received, + test, NULL); + + received_signals_flush (test); +} + +static void +setup_locked (Test *test, + gconstpointer unused) +{ + GVariant *element; + GVariant *retval; + GError *error = NULL; + const gchar *prompt; + GVariant *locked; + + /* Main setup */ + setup (test, unused); + + element = g_variant_new_object_path ("/org/freedesktop/secrets/collection/test"); + retval = dbus_call_perform (test, + SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, + "Lock", + g_variant_new ("(@ao)", + g_variant_new_array (G_VARIANT_TYPE ("o"), &element, 1)), + G_VARIANT_TYPE ("(aoo)"), + &error); + g_assert_no_error (error); + + /* Not expecting a prompt */ + g_variant_get (retval, "(@ao&o)", &locked, &prompt); + g_assert_cmpstr (prompt, ==, "/"); + g_variant_unref (locked); + g_variant_unref (retval); + + /* Don't carry over any received signals into test */ + received_signals_flush (test); +} + +static void +teardown (Test *test, + gconstpointer unused) +{ + received_signals_flush (test); + + g_dbus_connection_signal_unsubscribe (test->service.connection, test->signal_id); + + test_service_teardown (&test->service); + + gcr_mock_prompter_stop (); +} + +static void +on_prompt_completed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GVariant **prompt_result = user_data; + gboolean dismissed; + GVariant *result; + + g_assert (prompt_result != NULL); + g_assert (*prompt_result == NULL); + + g_assert (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(bv)"))); + g_variant_get (parameters, "(b@v)", &dismissed, &result); + + if (dismissed) + *prompt_result = NULL; + else + *prompt_result = g_variant_ref (result); + g_variant_unref (result); + + egg_test_wait_stop (); +} + +static GVariant * +prompt_password_perform (Test *test, + const gchar *prompt_path, + const gchar *password, + const GVariantType *type) +{ + GVariant *prompt_result = NULL; + GError *error = NULL; + GVariant *inside; + GVariant *retval; + guint sig; + + /* Tell the mock prompter which password to use */ + gcr_mock_prompter_expect_password_ok (password, NULL); + + /* Wait for the prompt's completed signal */ + sig = g_dbus_connection_signal_subscribe (test->service.connection, + test->service.bus_name, + SECRET_PROMPT_INTERFACE, + "Completed", + prompt_path, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_prompt_completed, + &prompt_result, + NULL); + + /* Perform the prompt, this will use the mock prompter */ + retval = g_dbus_connection_call_sync (test->service.connection, + test->service.bus_name, + prompt_path, + SECRET_PROMPT_INTERFACE, + "Prompt", + g_variant_new ("(s)", ""), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &error); + g_assert_no_error (error); + g_variant_unref (retval); + + egg_test_wait (); + + /* Done, now stop waiting for the prompts signal, make sure mock was used */ + g_dbus_connection_signal_unsubscribe (test->service.connection, sig); + g_assert (!gcr_mock_prompter_is_expecting ()); + + /* Check prompt result for right type */ + g_assert (prompt_result != NULL); + inside = g_variant_get_variant (prompt_result); + g_assert (g_variant_is_of_type (inside, type)); + g_variant_unref (prompt_result); + + return inside; +} + +static void +test_collection_created (Test *test, + gconstpointer unused) +{ + const gchar *collection; + GError *error = NULL; + const gchar *prompt; + GVariant *properties; + GVariant *retval; + GVariant *result; + GVariant *label; + + /* Create a new collection */ + label = g_variant_new_dict_entry (g_variant_new_string ("org.freedesktop.Secret.Collection.Label"), + g_variant_new_variant (g_variant_new_string ("My Collection"))); + properties = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), &label, 1); + + retval = dbus_call_perform (test, + SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, + "CreateCollection", + g_variant_new ("(@a{sv}s)", properties, ""), + G_VARIANT_TYPE ("(oo)"), + &error); + g_assert_no_error (error); + + /* We expect that a prompt is necessary */ + g_variant_get (retval, "(&o&o)", &collection, &prompt); + g_assert_cmpstr (collection, ==, "/"); + g_assert_cmpstr (prompt, !=, "/"); + + /* + * Perform the password prompt to create the collection, which returns + * the new collection path + */ + result = prompt_password_perform (test, prompt, "booo", G_VARIANT_TYPE_OBJECT_PATH); + g_variant_unref (retval); + + expect_signal_with_path (test, SECRET_SERVICE_PATH, SECRET_SERVICE_INTERFACE, + "CollectionCreated", g_variant_get_string (result, NULL)); + expect_property_changed (test, SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, "Collections"); + + g_variant_unref (result); +} + +static void +test_collection_created_no_prompt (Test *test, + gconstpointer unused) +{ + const gchar *collection; + GError *error = NULL; + GVariant *properties; + GVariant *retval; + GVariant *label; + + /* Create a new collection */ + label = g_variant_new_dict_entry (g_variant_new_string ("org.freedesktop.Secret.Collection.Label"), + g_variant_new_variant (g_variant_new_string ("Without Prompt"))); + properties = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), &label, 1); + + retval = dbus_call_perform (test, + SECRET_SERVICE_PATH, + INTERNAL_SERVICE_INTERFACE, + "CreateWithMasterPassword", + g_variant_new ("(@a{sv}@(oayays))", + properties, + test_service_build_secret (&test->service, "booo")), + G_VARIANT_TYPE ("(o)"), + &error); + g_assert_no_error (error); + + g_variant_get (retval, "(&o)", &collection); + g_assert_cmpstr (collection, !=, "/"); + + expect_signal_with_path (test, SECRET_SERVICE_PATH, SECRET_SERVICE_INTERFACE, + "CollectionCreated", collection); + expect_property_changed (test, SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, "Collections"); + + g_variant_unref (retval); +} + +static void +test_collection_deleted (Test *test, + gconstpointer unused) +{ + const gchar *prompt; + GError *error = NULL; + GVariant *retval; + + /* Delete a collection */ + retval = dbus_call_perform (test, + "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, + "Delete", + g_variant_new ("()"), + G_VARIANT_TYPE ("(o)"), + &error); + g_assert_no_error (error); + + /* Expect that no prompt is returned */ + g_variant_get (retval, "(&o)", &prompt); + g_assert_cmpstr (prompt, ==, "/"); + + expect_signal_with_path (test, SECRET_SERVICE_PATH, SECRET_SERVICE_INTERFACE, + "CollectionDeleted", "/org/freedesktop/secrets/collection/test"); + expect_property_changed (test, SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, "Collections"); + + g_variant_unref (retval); +} + +static void +test_collection_changed (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + GVariant *retval; + + retval = dbus_call_perform (test, + "/org/freedesktop/secrets/collection/test", + "org.freedesktop.DBus.Properties", + "Set", + g_variant_new ("(ssv)", + SECRET_COLLECTION_INTERFACE, + "Label", + g_variant_new_string ("New label")), + G_VARIANT_TYPE ("()"), + &error); + g_assert_no_error (error); + + expect_signal_with_path (test, SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, "CollectionChanged", + "/org/freedesktop/secrets/collection/test"); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "Label"); + + g_variant_unref (retval); +} + +static void +test_collection_lock (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + const gchar *prompt; + GVariant *element; + GVariant *locked; + GVariant *retval; + + element = g_variant_new_object_path ("/org/freedesktop/secrets/collection/test"); + retval = dbus_call_perform (test, + SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, + "Lock", + g_variant_new ("(@ao)", + g_variant_new_array (G_VARIANT_TYPE ("o"), &element, 1)), + G_VARIANT_TYPE ("(aoo)"), + &error); + g_assert_no_error (error); + + /* Not expecting a prompt */ + g_variant_get (retval, "(@ao&o)", &locked, &prompt); + g_assert_cmpstr (prompt, ==, "/"); + g_variant_unref (locked); + + expect_signal_with_path (test, SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, "CollectionChanged", + "/org/freedesktop/secrets/collection/test"); + expect_signal_with_path (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "ItemChanged", + "/org/freedesktop/secrets/collection/test/1"); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "Locked"); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test/1", + SECRET_ITEM_INTERFACE, "Locked"); + + g_variant_unref (retval); +} + +static void +test_collection_unlock (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + const gchar *prompt; + GVariant *unlocked; + GVariant *retval; + GVariant *element; + + element = g_variant_new_object_path ("/org/freedesktop/secrets/collection/test"); + retval = dbus_call_perform (test, + SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, + "Unlock", + g_variant_new ("(@ao)", + g_variant_new_array (G_VARIANT_TYPE ("o"), &element, 1)), + G_VARIANT_TYPE ("(aoo)"), + &error); + g_assert_no_error (error); + + /* Not expecting a prompt */ + g_variant_get (retval, "(@ao&o)", &unlocked, &prompt); + g_assert_cmpstr (prompt, !=, "/"); + g_variant_unref (unlocked); + + unlocked = prompt_password_perform (test, prompt, "booo", G_VARIANT_TYPE ("ao")); + g_variant_unref (unlocked); + + expect_signal_with_path (test, SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, "CollectionChanged", + "/org/freedesktop/secrets/collection/test"); + expect_signal_with_path (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "ItemChanged", + "/org/freedesktop/secrets/collection/test/1"); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "Locked"); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test/1", + SECRET_ITEM_INTERFACE, "Locked"); + + g_variant_unref (retval); +} + +static void +test_collection_unlock_no_prompt (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + GVariant *retval; + + retval = dbus_call_perform (test, + SECRET_SERVICE_PATH, + INTERNAL_SERVICE_INTERFACE, + "UnlockWithMasterPassword", + g_variant_new ("(o@(oayays))", + "/org/freedesktop/secrets/collection/test", + test_service_build_secret (&test->service, "booo")), + G_VARIANT_TYPE ("()"), + &error); + g_assert_no_error (error); + + expect_signal_with_path (test, SECRET_SERVICE_PATH, + SECRET_SERVICE_INTERFACE, "CollectionChanged", + "/org/freedesktop/secrets/collection/test"); + expect_signal_with_path (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "ItemChanged", + "/org/freedesktop/secrets/collection/test/1"); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "Locked"); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test/1", + SECRET_ITEM_INTERFACE, "Locked"); + + g_variant_unref (retval); +} + +static void +test_item_created (Test *test, + gconstpointer unused) +{ + const gchar *item; + const gchar *prompt; + GError *error = NULL; + GVariant *properties; + GVariant *retval; + GVariant *label; + + /* Create a new collection */ + label = g_variant_new_dict_entry (g_variant_new_string ("org.freedesktop.Secret.Item.Label"), + g_variant_new_variant (g_variant_new_string ("My Item"))); + properties = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), &label, 1); + + retval = dbus_call_perform (test, + "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, + "CreateItem", + g_variant_new ("(@a{sv}@(oayays)b)", + properties, + test_service_build_secret (&test->service, "booo"), + FALSE), + G_VARIANT_TYPE ("(oo)"), + &error); + g_assert_no_error (error); + + /* Not expecting a prompt */ + g_variant_get (retval, "(&o&o)", &item, &prompt); + g_assert_cmpstr (item, !=, "/"); + g_assert_cmpstr (prompt, ==, "/"); + + expect_signal_with_path (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "ItemCreated", item); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "Items"); + + g_variant_unref (retval); +} + +static void +test_item_deleted (Test *test, + gconstpointer unused) +{ + const gchar *prompt; + GError *error = NULL; + GVariant *retval; + + retval = dbus_call_perform (test, + "/org/freedesktop/secrets/collection/test/1", + SECRET_ITEM_INTERFACE, + "Delete", + g_variant_new ("()"), + G_VARIANT_TYPE ("(o)"), + &error); + g_assert_no_error (error); + + /* Not expecting a prompt */ + g_variant_get (retval, "(&o)", &prompt); + g_assert_cmpstr (prompt, ==, "/"); + + expect_signal_with_path (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "ItemDeleted", + "/org/freedesktop/secrets/collection/test/1"); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "Items"); + + g_variant_unref (retval); +} + +static void +test_item_changed (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + GVariant *retval; + + retval = dbus_call_perform (test, + "/org/freedesktop/secrets/collection/test/1", + "org.freedesktop.DBus.Properties", + "Set", + g_variant_new ("(ssv)", + SECRET_ITEM_INTERFACE, + "Label", + g_variant_new_string ("New label")), + G_VARIANT_TYPE ("()"), + &error); + g_assert_no_error (error); + + expect_signal_with_path (test, "/org/freedesktop/secrets/collection/test", + SECRET_COLLECTION_INTERFACE, "ItemChanged", + "/org/freedesktop/secrets/collection/test/1"); + expect_property_changed (test, "/org/freedesktop/secrets/collection/test/1", + SECRET_ITEM_INTERFACE, "Label"); + + g_variant_unref (retval); +} + +int +main (int argc, char **argv) +{ +#if !GLIB_CHECK_VERSION(2,35,0) + g_type_init (); +#endif + g_test_init (&argc, &argv, NULL); + + g_test_add ("/secret-signals/collection-created", Test, NULL, + setup, test_collection_created, teardown); + g_test_add ("/secret-signals/collection-created-no-prompt", Test, NULL, + setup, test_collection_created_no_prompt, teardown); + g_test_add ("/secret-signals/collection-changed", Test, NULL, + setup, test_collection_changed, teardown); + g_test_add ("/secret-signals/collection-deleted", Test, NULL, + setup, test_collection_deleted, teardown); + g_test_add ("/secret-signals/collection-lock", Test, NULL, + setup, test_collection_lock, teardown); + g_test_add ("/secret-signals/collection-unlock", Test, NULL, + setup_locked, test_collection_unlock, teardown); + g_test_add ("/secret-signals/collection-unlock-no-prompt", Test, NULL, + setup_locked, test_collection_unlock_no_prompt, teardown); + g_test_add ("/secret-signals/item-created", Test, NULL, + setup, test_item_created, teardown); + g_test_add ("/secret-signals/item-changed", Test, NULL, + setup, test_item_changed, teardown); + g_test_add ("/secret-signals/item-deleted", Test, NULL, + setup, test_item_deleted, teardown); + + return egg_tests_run_with_loop (); +} |