From 2bc2140d4323cfb6a828d8d28313025b867e0f4c Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Sun, 11 Mar 2012 13:35:43 +0100 Subject: [PATCH] Complete javascript testing of password functionality * And fix bugs, nullable collection_path --- .gitignore | 2 + library/secret-methods.c | 47 ++++++++++++------ library/secret-password.c | 27 ++++++++--- library/secret-types.h | 4 ++ library/tests/Makefile.am | 20 ++++++-- library/tests/test-javascript.js | 14 ++++++ library/tests/test-lookup-password.js | 52 ++++++++++++++++++++ library/tests/test-remove-password.js | 68 +++++++++++++++++++++++++++ library/tests/test-store-password.js | 58 +++++++++++++++++++++++ 9 files changed, 266 insertions(+), 26 deletions(-) create mode 100644 library/tests/test-javascript.js create mode 100644 library/tests/test-lookup-password.js create mode 100644 library/tests/test-remove-password.js create mode 100644 library/tests/test-store-password.js diff --git a/.gitignore b/.gitignore index 94b017b..e9f73af 100644 --- a/.gitignore +++ b/.gitignore @@ -65,4 +65,6 @@ stamp* /library/secret-enum-types.[ch] /library/tests/test-* !/library/tests/test-*.c +!/library/tests/test-*.js +!/library/tests/test-*.py diff --git a/library/secret-methods.c b/library/secret-methods.c index 42a8f60..86ff008 100644 --- a/library/secret-methods.c +++ b/library/secret-methods.c @@ -1659,7 +1659,7 @@ secret_service_unlock_sync (SecretService *self, * secret_service_store: * @self: the secret service * @schema: the schema to for attributes - * @collection_path: the dbus path to the collection where to store the secret + * @collection_path: (allow-none): the dbus path to the collection where to store the secret * @label: label for the secret * @value: the secret value * @cancellable: optional cancellation object @@ -1677,6 +1677,10 @@ secret_service_unlock_sync (SecretService *self, * If the attributes match a secret item already stored in the collection, then * the item will be updated with these new values. * + * If @collection_path is not specified, then the default collection will be + * used. Use #SECRET_COLLECTION_SESSION to store the password in the session + * collection, which doesn't get stored across login sessions. + * * This method will return immediately and complete asynchronously. */ void @@ -1695,7 +1699,6 @@ secret_service_store (SecretService *self, g_return_if_fail (SECRET_IS_SERVICE (self)); g_return_if_fail (schema != NULL); - g_return_if_fail (collection_path != NULL); g_return_if_fail (label != NULL); g_return_if_fail (value != NULL); @@ -1704,7 +1707,7 @@ secret_service_store (SecretService *self, va_end (va); secret_service_storev (self, schema, attributes, collection_path, - label, value, cancellable, callback, user_data); + label, value, cancellable, callback, user_data); g_hash_table_unref (attributes); } @@ -1714,7 +1717,7 @@ secret_service_store (SecretService *self, * @self: the secret service * @schema: the schema to for attributes * @attributes: (element-type utf8 utf8): the attribute keys and values - * @collection_path: the dbus path to the collection where to store the secret + * @collection_path: (allow-none): the dbus path to the collection where to store the secret * @label: label for the secret * @value: the secret value * @cancellable: optional cancellation object @@ -1728,6 +1731,10 @@ secret_service_store (SecretService *self, * If the attributes match a secret item already stored in the collection, then * the item will be updated with these new values. * + * If @collection_path is not specified, then the default collection will be + * used. Use #SECRET_COLLECTION_SESSION to store the password in the session + * collection, which doesn't get stored across login sessions. + * * This method will return immediately and complete asynchronously. */ void @@ -1747,7 +1754,6 @@ secret_service_storev (SecretService *self, g_return_if_fail (SECRET_IS_SERVICE (self)); g_return_if_fail (schema != NULL); g_return_if_fail (attributes != NULL); - g_return_if_fail (collection_path != NULL); g_return_if_fail (label != NULL); g_return_if_fail (value != NULL); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); @@ -1775,7 +1781,7 @@ secret_service_storev (SecretService *self, g_variant_ref_sink (propval)); secret_service_create_item_path (self, collection_path, properties, value, - TRUE, cancellable, callback, user_data); + TRUE, cancellable, callback, user_data); g_hash_table_unref (properties); } @@ -1810,7 +1816,7 @@ secret_service_store_finish (SecretService *self, * secret_service_store_sync: * @self: the secret service * @schema: the schema to for attributes - * @collection_path: the dbus path to the collection where to store the secret + * @collection_path: (allow-none): the dbus path to the collection where to store the secret * @label: label for the secret * @value: the secret value * @cancellable: optional cancellation object @@ -1827,6 +1833,10 @@ secret_service_store_finish (SecretService *self, * If the attributes match a secret item already stored in the collection, then * the item will be updated with these new values. * + * If @collection_path is %NULL, then the default collection will be + * used. Use #SECRET_COLLECTION_SESSION to store the password in the session + * collection, which doesn't get stored across login sessions. + * * This method may block indefinitely and should not be used in user interface * threads. * @@ -1848,7 +1858,6 @@ secret_service_store_sync (SecretService *self, g_return_val_if_fail (SECRET_IS_SERVICE (self), FALSE); g_return_val_if_fail (schema != NULL, FALSE); - g_return_val_if_fail (collection_path != NULL, FALSE); g_return_val_if_fail (label != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); @@ -1859,7 +1868,7 @@ secret_service_store_sync (SecretService *self, va_end (va); ret = secret_service_storev_sync (self, schema, attributes, collection_path, - label, value, cancellable, error); + label, value, cancellable, error); g_hash_table_unref (attributes); @@ -1871,7 +1880,7 @@ secret_service_store_sync (SecretService *self, * @self: the secret service * @schema: the schema to for attributes * @attributes: (element-type utf8 utf8): the attribute keys and values - * @collection_path: the dbus path to the collection where to store the secret + * @collection_path: (allow-none): the dbus path to the collection where to store the secret * @label: label for the secret * @value: the secret value * @cancellable: optional cancellation object @@ -1884,6 +1893,10 @@ secret_service_store_sync (SecretService *self, * If the attributes match a secret item already stored in the collection, then * the item will be updated with these new values. * + * If @collection_path is %NULL, then the default collection will be + * used. Use #SECRET_COLLECTION_SESSION to store the password in the session + * collection, which doesn't get stored across login sessions. + * * This method may block indefinitely and should not be used in user interface * threads. * @@ -1905,7 +1918,6 @@ secret_service_storev_sync (SecretService *self, g_return_val_if_fail (SECRET_IS_SERVICE (self), FALSE); g_return_val_if_fail (schema != NULL, FALSE); g_return_val_if_fail (attributes != NULL, FALSE); - g_return_val_if_fail (collection_path != NULL, FALSE); g_return_val_if_fail (label != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); @@ -1919,7 +1931,7 @@ secret_service_storev_sync (SecretService *self, g_main_context_push_thread_default (sync->context); secret_service_storev (self, schema, attributes, collection_path, - label, value, cancellable, _secret_sync_on_result, sync); + label, value, cancellable, _secret_sync_on_result, sync); g_main_loop_run (sync->loop); @@ -3137,7 +3149,7 @@ on_create_item_session (GObject *source, /** * secret_service_create_item_path: * @self: a secret service object - * @collection_path: dbus path to collection in which to create item + * @collection_path: (allow-none): dbus path to collection in which to create item * @properties: hash table of properties for the new collection * @value: the secret value to store in the item * @replace: whether to replace an item with the matching attributes @@ -3159,6 +3171,10 @@ on_create_item_session (GObject *source, * org.freedesktop.Secret.Item.Label. The values * in the hash table should be #GVariant values of the properties. * + * If @collection_path is %NULL, then the default collection will be + * used. Use #SECRET_COLLECTION_SESSION to store the password in the session + * collection, which doesn't get stored across login sessions. + * * This method will return immediately and complete asynchronously. The secret * service may prompt the user. secret_service_prompt() will be used to handle * any prompts that are required. @@ -3181,6 +3197,9 @@ secret_service_create_item_path (SecretService *self, g_return_if_fail (value != NULL); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + if (collection_path == NULL) + collection_path = SECRET_COLLECTION_DEFAULT; + res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, secret_service_create_item_path); closure = g_slice_new0 (ItemClosure); @@ -3238,7 +3257,7 @@ secret_service_create_item_path_finish (SecretService *self, /** * secret_service_create_item_path_sync: * @self: a secret service object - * @collection_path: dbus path to collection in which to create item + * @collection_path: (allow-none): dbus path to collection in which to create item * @properties: hash table of properties for the new collection * @value: the secret value to store in the item * @replace: whether to replace an item with the matching attributes diff --git a/library/secret-password.c b/library/secret-password.c index e40a29d..2f5a4cd 100644 --- a/library/secret-password.c +++ b/library/secret-password.c @@ -110,7 +110,7 @@ on_store_connected (GObject *source, /** * secret_password_store: * @schema: the schema for attributes - * @collection_path: the dbus path to the collection where to store the secret + * @collection_path: (allow-none): the dbus path to the collection where to store the secret * @label: label for the secret * @password: the null-terminated password to store * @cancellable: optional cancellation object @@ -128,6 +128,10 @@ on_store_connected (GObject *source, * If the attributes match a secret item already stored in the collection, then * the item will be updated with these new values. * + * If @collection_path is %NULL, then the default collection will be + * used. Use #SECRET_COLLECTION_SESSION to store the password in the session + * collection, which doesn't get stored across login sessions. + * * This method will return immediately and complete asynchronously. */ void @@ -144,7 +148,6 @@ secret_password_store (const SecretSchema *schema, va_list va; g_return_if_fail (schema != NULL); - g_return_if_fail (collection_path != NULL); g_return_if_fail (label != NULL); g_return_if_fail (password != NULL); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); @@ -163,7 +166,7 @@ secret_password_store (const SecretSchema *schema, * secret_password_storev: * @schema: the schema for attributes * @attributes: (element-type utf8 utf8): the attribute keys and values - * @collection_path: the dbus path to the collection where to store the secret + * @collection_path: (allow-none): the dbus path to the collection where to store the secret * @label: label for the secret * @password: the null-terminated password to store * @cancellable: optional cancellation object @@ -177,6 +180,10 @@ secret_password_store (const SecretSchema *schema, * If the attributes match a secret item already stored in the collection, then * the item will be updated with these new values. * + * If @collection_path is %NULL, then the default collection will be + * used. Use #SECRET_COLLECTION_SESSION to store the password in the session + * collection, which doesn't get stored across login sessions. + * * This method will return immediately and complete asynchronously. * * Rename to: secret_password_store @@ -195,7 +202,6 @@ secret_password_storev (const SecretSchema *schema, StoreClosure *closure; g_return_if_fail (schema != NULL); - g_return_if_fail (collection_path != NULL); g_return_if_fail (label != NULL); g_return_if_fail (password != NULL); g_return_if_fail (attributes != NULL); @@ -254,7 +260,7 @@ secret_password_store_finish (GAsyncResult *result, /** * secret_password_store_sync: * @schema: the schema for attributes - * @collection_path: the dbus path to the collection where to store the secret + * @collection_path: (allow-none): the dbus path to the collection where to store the secret * @label: label for the secret * @password: the null-terminated password to store * @cancellable: optional cancellation object @@ -271,6 +277,10 @@ secret_password_store_finish (GAsyncResult *result, * If the attributes match a secret item already stored in the collection, then * the item will be updated with these new values. * + * If @collection_path is %NULL, then the default collection will be + * used. Use #SECRET_COLLECTION_SESSION to store the password in the session + * collection, which doesn't get stored across login sessions. + * * This method may block indefinitely and should not be used in user interface * threads. * @@ -310,7 +320,7 @@ secret_password_store_sync (const SecretSchema *schema, * secret_password_storev_sync: * @schema: the schema for attributes * @attributes: (element-type utf8 utf8): the attribute keys and values - * @collection_path: the dbus path to the collection where to store the secret + * @collection_path: (allow-none): the dbus path to the collection where to store the secret * @label: label for the secret * @password: the null-terminated password to store * @cancellable: optional cancellation object @@ -323,6 +333,10 @@ secret_password_store_sync (const SecretSchema *schema, * If the attributes match a secret item already stored in the collection, then * the item will be updated with these new values. * + * If @collection_path is %NULL, then the default collection will be + * used. Use #SECRET_COLLECTION_SESSION to store the password in the session + * collection, which doesn't get stored across login sessions. + * * This method may block indefinitely and should not be used in user interface * threads. * @@ -343,7 +357,6 @@ secret_password_storev_sync (const SecretSchema *schema, gboolean ret; g_return_val_if_fail (schema != NULL, FALSE); - g_return_val_if_fail (collection_path != NULL, FALSE); g_return_val_if_fail (label != NULL, FALSE); g_return_val_if_fail (password != NULL, FALSE); g_return_val_if_fail (attributes != NULL, FALSE); diff --git a/library/secret-types.h b/library/secret-types.h index 96d8637..c7f61fa 100644 --- a/library/secret-types.h +++ b/library/secret-types.h @@ -35,6 +35,10 @@ typedef struct _SecretPrompt SecretPrompt; typedef struct _SecretService SecretService; typedef struct _SecretValue SecretValue; +#define SECRET_COLLECTION_DEFAULT "/org/freedesktop/secrets/aliases/default" + +#define SECRET_COLLECTION_SESSION "/org/freedesktop/secrets/aliases/session" + G_END_DECLS #endif /* __G_SERVICE_H___ */ diff --git a/library/tests/Makefile.am b/library/tests/Makefile.am index db8c0e3..9490f65 100644 --- a/library/tests/Makefile.am +++ b/library/tests/Makefile.am @@ -56,12 +56,22 @@ EXTRA_DIST = \ mock-service-prompt.py \ $(NULL) -test: $(TEST_PROGS) - gtester --verbose -m $(TEST_MODE) --g-fatal-warnings $(TEST_PROGS) +JS_TESTS = \ + test-lookup-password.js \ + test-remove-password.js \ + test-store-password.js -test-javascript: - LD_LIBRARY_PATH=$(builddir)/.libs GI_TYPELIB_PATH=$(builddir)/..:$(builddir) \ - gjs test-lookup-password.js +JS_ENV = \ + LD_LIBRARY_PATH=$(builddir)/.libs \ + GI_TYPELIB_PATH=$(builddir)/..:$(builddir) + +test: test-c test-js + +test-c: $(TEST_PROGS) + @gtester --verbose -m $(TEST_MODE) --g-fatal-warnings $(TEST_PROGS) + +test-js: + @for js in $(JS_TESTS); do echo "TEST: $$js"; $(JS_ENV) gjs $$js; done # ------------------------------------------------------------------ # INTROSPECTION diff --git a/library/tests/test-javascript.js b/library/tests/test-javascript.js new file mode 100644 index 0000000..95b3c3f --- /dev/null +++ b/library/tests/test-javascript.js @@ -0,0 +1,14 @@ + +const Mock = imports.gi.MockService; +const Secret = imports.gi.Secret; + +Mock.start("mock-service-normal.py"); + +var schema = new Secret.Schema.new("org.test", + Secret.SchemaFlags.NONE, + { "blah": Secret.SchemaAttributeType.STRING }); +log(schema.identifier); +log(schema.flags); +/* log(schema.attributes); */ + +Mock.stop(); \ No newline at end of file diff --git a/library/tests/test-lookup-password.js b/library/tests/test-lookup-password.js new file mode 100644 index 0000000..32de442 --- /dev/null +++ b/library/tests/test-lookup-password.js @@ -0,0 +1,52 @@ + +const Mock = imports.gi.MockService; +const Secret = imports.gi.Secret; +const GLib = imports.gi.GLib; + +const JsUnit = imports.jsUnit; +const assertEquals = JsUnit.assertEquals; +const assertRaises = JsUnit.assertRaises; +const assertTrue = JsUnit.assertTrue; + +Mock.start("mock-service-normal.py"); + +const STORE_SCHEMA = new Secret.Schema.new("org.mock.type.Store", + Secret.SchemaFlags.NONE, + { + "number": Secret.SchemaAttributeType.INTEGER, + "string": Secret.SchemaAttributeType.STRING, + "even": Secret.SchemaAttributeType.BOOLEAN, + } +); + +/* Synchronous */ + +var password = Secret.password_lookup_sync (STORE_SCHEMA, { "number": "1", "even": "false" }, null); +assertEquals("111", password); + +var password = Secret.password_lookup_sync (STORE_SCHEMA, { "number": "5", "even": "true" }, null); +assertEquals(null, password); + +/* Asynchronous */ + +var loop = new GLib.MainLoop.new(null, false); + +Secret.password_lookup (STORE_SCHEMA, { "number": "2", "string": "two" }, + null, function(source, result) { + loop.quit(); + var password = Secret.password_lookup_finish(result); + assertEquals("222", password); +}); + +loop.run(); + +Secret.password_lookup (STORE_SCHEMA, { "number": "7", "string": "five" }, + null, function(source, result) { + loop.quit(); + var password = Secret.password_lookup_finish(result); + assertEquals(null, password); +}); + +loop.run(); + +Mock.stop(); diff --git a/library/tests/test-remove-password.js b/library/tests/test-remove-password.js new file mode 100644 index 0000000..8425cef --- /dev/null +++ b/library/tests/test-remove-password.js @@ -0,0 +1,68 @@ + +const Mock = imports.gi.MockService; +const Secret = imports.gi.Secret; +const GLib = imports.gi.GLib; + +const JsUnit = imports.jsUnit; +const assertEquals = JsUnit.assertEquals; +const assertRaises = JsUnit.assertRaises; +const assertTrue = JsUnit.assertTrue; + +Mock.start("mock-service-normal.py"); + +const STORE_SCHEMA = new Secret.Schema.new("org.mock.type.Store", + Secret.SchemaFlags.NONE, + { + "number": Secret.SchemaAttributeType.INTEGER, + "string": Secret.SchemaAttributeType.STRING, + "even": Secret.SchemaAttributeType.BOOLEAN, + } +); + +/* Synchronous */ + +var attributes = { "number": "1", "string": "one", "even": "false" }; + +var password = Secret.password_lookup_sync (STORE_SCHEMA, attributes, null); +assertEquals("111", password); + +var deleted = Secret.password_remove_sync (STORE_SCHEMA, attributes, null); +assertEquals(true, deleted); + +var password = Secret.password_lookup_sync (STORE_SCHEMA, attributes, null); +assertEquals(null, password); + +var deleted = Secret.password_remove_sync (STORE_SCHEMA, attributes, null); +assertEquals(false, deleted); + +/* Asynchronous */ + +var attributes = { "number": "2", "string": "two", "even": "true" }; + +var password = Secret.password_lookup_sync (STORE_SCHEMA, attributes, null); +assertEquals("222", password); + +var loop = new GLib.MainLoop.new(null, false); + +Secret.password_remove (STORE_SCHEMA, attributes, + null, function(source, result) { + loop.quit(); + var deleted = Secret.password_remove_finish(result); + assertEquals(true, deleted); +}); + +loop.run(); + +var password = Secret.password_lookup_sync (STORE_SCHEMA, attributes, null); +assertEquals(null, password); + +Secret.password_remove (STORE_SCHEMA, attributes, + null, function(source, result) { + loop.quit(); + var deleted = Secret.password_remove_finish(result); + assertEquals(false, deleted); +}); + +loop.run(); + +Mock.stop(); diff --git a/library/tests/test-store-password.js b/library/tests/test-store-password.js new file mode 100644 index 0000000..254c162 --- /dev/null +++ b/library/tests/test-store-password.js @@ -0,0 +1,58 @@ + +const Mock = imports.gi.MockService; +const Secret = imports.gi.Secret; +const GLib = imports.gi.GLib; + +const JsUnit = imports.jsUnit; +const assertEquals = JsUnit.assertEquals; +const assertRaises = JsUnit.assertRaises; +const assertTrue = JsUnit.assertTrue; + +Mock.start("mock-service-normal.py"); + +const STORE_SCHEMA = new Secret.Schema.new("org.mock.type.Store", + Secret.SchemaFlags.NONE, + { + "number": Secret.SchemaAttributeType.INTEGER, + "string": Secret.SchemaAttributeType.STRING, + "even": Secret.SchemaAttributeType.BOOLEAN, + } +); + +/* Synchronous */ + +var attributes = { "number": "9", "string": "nine", "even": "false" }; + +var password = Secret.password_lookup_sync (STORE_SCHEMA, attributes, null); +assertEquals(null, password); + +var stored = Secret.password_store_sync (STORE_SCHEMA, attributes, Secret.COLLECTION_DEFAULT, + "The number nine", "999", null); +assertEquals(true, stored); + +var password = Secret.password_lookup_sync (STORE_SCHEMA, attributes, null); +assertEquals("999", password); + + +/* Asynchronous */ + +var attributes = { "number": "888", "string": "eight", "even": "true" }; + +var password = Secret.password_lookup_sync (STORE_SCHEMA, attributes, null); +assertEquals(null, password); + +var loop = new GLib.MainLoop.new(null, false); + +Secret.password_store (STORE_SCHEMA, attributes, null, "The number eight", "888", + null, function(source, result) { + loop.quit(); + var stored = Secret.password_store_finish(result); + assertEquals(true, stored); +}); + +loop.run(); + +var password = Secret.password_lookup_sync (STORE_SCHEMA, attributes, null); +assertEquals("888", password); + +Mock.stop();