password: Add secret_password_search* functions

Previously there were no functions in the simple API that return the
matched attributes other than the secret value, while there were needs
for augumenting user input with additional information (such as
completing web forms).

This adds a set of functions which wrap secret_service_search*.  Note
that the return value is a list of GHashTable not of SecretItem,
because SecretItem is a subclass of GDBusProxy, which we don't want to
expose from the simple API.

Fixes #16
This commit is contained in:
Daiki Ueno 2018-10-19 08:33:49 +02:00
parent 5ce2540785
commit 7716449b1d
5 changed files with 349 additions and 7 deletions

View File

@ -844,6 +844,231 @@ secret_password_clearv_sync (const SecretSchema *schema,
return result; return result;
} }
/**
* secret_password_search: (skip)
* @schema: the schema for the attributes
* @flags: search option flags
* @cancellable: optional cancellation object
* @callback: called when the operation completes
* @user_data: data to be passed to the callback
* @...: the attribute keys and values, terminated with %NULL
*
* Search for items in the secret service.
*
* The variable argument list should contain pairs of a) The attribute name as
* a null-terminated string, followed by b) attribute value, either a character
* string, an int number, or a gboolean value, as defined in the password
* @schema. The list of attribtues should be terminated with a %NULL.
*
* This method will return immediately and complete asynchronously.
*
* Since: 0.18.7
*/
void
secret_password_search (const SecretSchema *schema,
SecretSearchFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data,
...)
{
GHashTable *attributes;
va_list va;
g_return_if_fail (schema != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
va_start (va, user_data);
attributes = secret_attributes_buildv (schema, va);
va_end (va);
/* Precondition failed, already warned */
if (!attributes)
return;
secret_password_searchv (schema, attributes, flags, cancellable,
callback, user_data);
g_hash_table_unref (attributes);
}
/**
* secret_password_searchv: (rename-to secret_password_search)
* @schema: the schema for attributes
* @attributes: (element-type utf8 utf8): the attribute keys and values
* @flags: search option flags
* @cancellable: optional cancellation object
* @callback: called when the operation completes
* @user_data: data to be passed to the callback
*
* Search for items in the secret service.
*
* The @attributes should be a set of key and value string pairs.
*
* This method will return immediately and complete asynchronously.
*
* Since: 0.18.7
*/
void
secret_password_searchv (const SecretSchema *schema,
GHashTable *attributes,
SecretSearchFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (schema != NULL);
g_return_if_fail (attributes != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
/* Warnings raised already */
if (!_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
return;
secret_service_search (NULL, schema, attributes, flags,
cancellable, callback, user_data);
}
/**
* secret_password_search_finish:
* @result: the asynchronous result passed to the callback
* @error: location to place an error on failure
*
* Finish an asynchronous operation to search for items in the secret service.
*
* Returns: (transfer full) (element-type GHashTable): a list of hash tables containing attributes of the matched items
* Since: 0.18.7
*/
GList *
secret_password_search_finish (GAsyncResult *result,
GError **error)
{
GList *value;
GList *items = NULL;
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
value = secret_service_search_finish (NULL, result, error);
while (value) {
SecretItem *item = SECRET_ITEM (value->data);
GHashTable *attributes = secret_item_get_attributes (item);
items = g_list_append (items, attributes);
g_object_unref (item);
value = g_list_delete_link (value, value);
}
return items;
}
/**
* secret_password_search_sync: (skip)
* @schema: the schema for the attributes
* @flags: search option flags
* @cancellable: optional cancellation object
* @error: location to place an error on failure
* @...: the attribute keys and values, terminated with %NULL
*
* Search for items in the secret service.
*
* The variable argument list should contain pairs of a) The attribute name as
* a null-terminated string, followed by b) attribute value, either a character
* string, an int number, or a gboolean value, as defined in the password
* @schema. The list of attributes should be terminated with a %NULL.
*
* If no secret is found then %NULL is returned.
*
* This method may block indefinitely and should not be used in user interface
* threads.
*
* Returns: (transfer full) (element-type GHashTable): a list of hash tables containing attributes of the matched items
* Since: 0.18.7
*/
GList *
secret_password_search_sync (const SecretSchema *schema,
SecretSearchFlags flags,
GCancellable *cancellable,
GError **error,
...)
{
GHashTable *attributes;
GList *items;
va_list va;
g_return_val_if_fail (schema != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
va_start (va, error);
attributes = secret_attributes_buildv (schema, va);
va_end (va);
/* Precondition failed, already warned */
if (!attributes)
return NULL;
items = secret_password_searchv_sync (schema, attributes, flags,
cancellable, error);
g_hash_table_unref (attributes);
return items;
}
/**
* secret_password_searchv_sync: (rename-to secret_password_search_sync)
* @schema: the schema for attributes
* @attributes: (element-type utf8 utf8): the attribute keys and values
* @flags: search option flags
* @cancellable: optional cancellation object
* @error: location to place an error on failure
*
* Search for items in the secret service.
*
* The @attributes should be a set of key and value string pairs.
*
* If no secret is found then %NULL is returned.
*
* This method may block indefinitely and should not be used in user interface
* threads.
*
* Returns: (transfer full) (element-type GHashTable): a list of hash tables containing attributes of the matched items
* Since: 0.18.7
*/
GList *
secret_password_searchv_sync (const SecretSchema *schema,
GHashTable *attributes,
SecretSearchFlags flags,
GCancellable *cancellable,
GError **error)
{
SecretSync *sync;
GList *items;
g_return_val_if_fail (schema != NULL, NULL);
g_return_val_if_fail (attributes != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* Warnings raised already */
if (!_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
return NULL;
sync = _secret_sync_new ();
g_main_context_push_thread_default (sync->context);
secret_password_searchv (schema, attributes, flags, cancellable,
_secret_sync_on_result, sync);
g_main_loop_run (sync->loop);
items = secret_password_search_finish (sync->result, error);
g_main_context_pop_thread_default (sync->context);
_secret_sync_free (sync);
return items;
}
/** /**
* secret_password_free: (skip) * secret_password_free: (skip)
* @password: (allow-none): password to free * @password: (allow-none): password to free

View File

@ -126,6 +126,35 @@ gboolean secret_password_clearv_sync (const SecretSchema *sche
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
void secret_password_search (const SecretSchema *schema,
SecretSearchFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data,
...) G_GNUC_NULL_TERMINATED;
void secret_password_searchv (const SecretSchema *schema,
GHashTable *attributes,
SecretSearchFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GList * secret_password_search_sync (const SecretSchema *schema,
SecretSearchFlags flags,
GCancellable *cancellable,
GError **error,
...) G_GNUC_NULL_TERMINATED;
GList * secret_password_searchv_sync (const SecretSchema *schema,
GHashTable *attributes,
SecretSearchFlags flags,
GCancellable *cancellable,
GError **error);
GList * secret_password_search_finish (GAsyncResult *result,
GError **error);
void secret_password_free (gchar *password); void secret_password_free (gchar *password);
void secret_password_wipe (gchar *password); void secret_password_wipe (gchar *password);

View File

@ -35,13 +35,6 @@ typedef enum {
SECRET_SERVICE_LOAD_COLLECTIONS = 1 << 2, SECRET_SERVICE_LOAD_COLLECTIONS = 1 << 2,
} SecretServiceFlags; } SecretServiceFlags;
typedef enum {
SECRET_SEARCH_NONE = 0,
SECRET_SEARCH_ALL = 1 << 1,
SECRET_SEARCH_UNLOCK = 1 << 2,
SECRET_SEARCH_LOAD_SECRETS = 1 << 3,
} SecretSearchFlags;
#define SECRET_TYPE_SERVICE (secret_service_get_type ()) #define SECRET_TYPE_SERVICE (secret_service_get_type ())
#define SECRET_SERVICE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), SECRET_TYPE_SERVICE, SecretService)) #define SECRET_SERVICE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), SECRET_TYPE_SERVICE, SecretService))
#define SECRET_SERVICE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), SECRET_TYPE_SERVICE, SecretServiceClass)) #define SECRET_SERVICE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), SECRET_TYPE_SERVICE, SecretServiceClass))

View File

@ -38,6 +38,13 @@ typedef enum {
#define SECRET_COLLECTION_SESSION "session" #define SECRET_COLLECTION_SESSION "session"
typedef enum {
SECRET_SEARCH_NONE = 0,
SECRET_SEARCH_ALL = 1 << 1,
SECRET_SEARCH_UNLOCK = 1 << 2,
SECRET_SEARCH_LOAD_SECRETS = 1 << 3,
} SecretSearchFlags;
G_END_DECLS G_END_DECLS
#endif /* __G_SERVICE_H___ */ #endif /* __G_SERVICE_H___ */

View File

@ -354,6 +354,90 @@ test_clear_no_name (Test *test,
g_assert (ret == TRUE); g_assert (ret == TRUE);
} }
static void
free_attributes (gpointer data,
gpointer user_data)
{
g_hash_table_unref ((GHashTable *)data);
}
static void
test_search_sync (Test *test,
gconstpointer used)
{
GList *items;
GError *error = NULL;
items = secret_password_search_sync (&MOCK_SCHEMA, SECRET_SEARCH_ALL,
NULL, &error,
"even", FALSE,
"string", "one",
"number", 1,
NULL);
g_assert_no_error (error);
g_assert_cmpint (g_list_length (items), ==, 1);
g_list_foreach (items, free_attributes, NULL);
g_list_free (items);
}
static void
test_search_async (Test *test,
gconstpointer used)
{
GAsyncResult *result = NULL;
GError *error = NULL;
GList *items;
secret_password_search (&MOCK_SCHEMA, SECRET_SEARCH_ALL,
NULL, on_complete_get_result, &result,
"even", FALSE,
"string", "one",
"number", 1,
NULL);
g_assert (result == NULL);
egg_test_wait ();
items = secret_password_search_finish (result, &error);
g_assert_no_error (error);
g_object_unref (result);
g_assert_cmpint (g_list_length (items), ==, 1);
g_list_foreach (items, free_attributes, NULL);
g_list_free (items);
}
static void
test_search_no_name (Test *test,
gconstpointer used)
{
GError *error = NULL;
GList *items;
/* should return null, because nothing with mock schema and 5 */
items = secret_password_search_sync (&MOCK_SCHEMA, SECRET_SEARCH_ALL,
NULL, &error,
"number", 5,
NULL);
g_assert_no_error (error);
g_assert (items == NULL);
/* should return an item, because we have a prime schema with 5, and flags not to match name */
items = secret_password_search_sync (&NO_NAME_SCHEMA, SECRET_SEARCH_ALL,
NULL, &error,
"number", 5,
NULL);
g_assert_no_error (error);
g_assert_cmpint (g_list_length (items), ==, 1);
g_list_foreach (items, free_attributes, NULL);
g_list_free (items);
}
static void static void
test_password_free_null (void) test_password_free_null (void)
{ {
@ -381,6 +465,10 @@ main (int argc, char **argv)
g_test_add ("/password/delete-async", Test, "mock-service-delete.py", setup, test_delete_async, teardown); g_test_add ("/password/delete-async", Test, "mock-service-delete.py", setup, test_delete_async, teardown);
g_test_add ("/password/clear-no-name", Test, "mock-service-delete.py", setup, test_clear_no_name, teardown); g_test_add ("/password/clear-no-name", Test, "mock-service-delete.py", setup, test_clear_no_name, teardown);
g_test_add ("/password/search-sync", Test, "mock-service-normal.py", setup, test_search_sync, teardown);
g_test_add ("/password/search-async", Test, "mock-service-normal.py", setup, test_search_async, teardown);
g_test_add ("/password/search-no-name", Test, "mock-service-normal.py", setup, test_search_no_name, teardown);
g_test_add_func ("/password/free-null", test_password_free_null); g_test_add_func ("/password/free-null", test_password_free_null);
return egg_tests_run_with_loop (); return egg_tests_run_with_loop ();