diff options
author | Dan Winship <danw@gnome.org> | 2014-09-09 12:16:46 -0400 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2014-09-09 12:16:46 -0400 |
commit | a098c8789b5231cadcd7cd9aadd89b8b09535170 (patch) | |
tree | 39f248f9066df503398846a5e9d97dd3ef77ef25 | |
parent | 8723bbd3e886e5f4ec879fa77ed092a06f237deb (diff) | |
parent | d3b53b707a957f76e0b032a118058b2b04ce6ece (diff) | |
download | NetworkManager-a098c8789b5231cadcd7cd9aadd89b8b09535170.tar.gz |
libnm: add test-secret-agent, glib-ify NMSecretAgent APIs (bgo #735916)
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | clients/tui/nmt-secret-agent.c | 7 | ||||
-rw-r--r-- | clients/tui/nmtui-connect.c | 7 | ||||
-rw-r--r-- | libnm/libnm.ver | 4 | ||||
-rw-r--r-- | libnm/nm-secret-agent.c | 595 | ||||
-rw-r--r-- | libnm/nm-secret-agent.h | 28 | ||||
-rw-r--r-- | libnm/tests/Makefile.am | 7 | ||||
-rw-r--r-- | libnm/tests/test-secret-agent.c | 681 | ||||
-rwxr-xr-x | tools/test-networkmanager-service.py | 92 |
9 files changed, 1272 insertions, 150 deletions
diff --git a/.gitignore b/.gitignore index fb475a2b72..41c0d90796 100644 --- a/.gitignore +++ b/.gitignore @@ -165,6 +165,7 @@ valgrind-*.log /libnm/tests/test-nm-client /libnm/tests/test-remote-settings-client +/libnm/tests/test-secret-agent /m4/codeset.m4 /m4/gettext.m4 diff --git a/clients/tui/nmt-secret-agent.c b/clients/tui/nmt-secret-agent.c index cabe5b4a87..7550ce64f6 100644 --- a/clients/tui/nmt-secret-agent.c +++ b/clients/tui/nmt-secret-agent.c @@ -639,8 +639,7 @@ nmt_secret_agent_class_init (NmtSecretAgentClass *klass) NMSecretAgent * nmt_secret_agent_new (void) { - return g_object_new (NMT_TYPE_SECRET_AGENT, - NM_SECRET_AGENT_IDENTIFIER, "nmtui", - NM_SECRET_AGENT_AUTO_REGISTER, FALSE, - NULL); + return g_initable_new (NMT_TYPE_SECRET_AGENT, NULL, NULL, + NM_SECRET_AGENT_IDENTIFIER, "nmtui", + NULL); } diff --git a/clients/tui/nmtui-connect.c b/clients/tui/nmtui-connect.c index ca523ebbaf..dfeb186f90 100644 --- a/clients/tui/nmtui-connect.c +++ b/clients/tui/nmtui-connect.c @@ -142,7 +142,6 @@ activate_connection (NMConnection *connection, nmt_newt_form_set_content (form, label); agent = nmt_secret_agent_new (); - nm_secret_agent_register (agent); g_signal_connect (agent, "request-secrets", G_CALLBACK (secrets_requested), NULL); specific_object_path = specific_object ? nm_object_get_path (specific_object) : NULL; @@ -203,11 +202,7 @@ activate_connection (NMConnection *connection, nmt_newt_form_quit (form); g_object_unref (form); - /* If the activation failed very quickly, then agent won't be registered yet, - * and nm_secret_agent_unregister() would complain if we called it... - */ - if (nm_secret_agent_get_registered (agent)) - nm_secret_agent_unregister (agent); + nm_secret_agent_unregister (agent, NULL, NULL); g_object_unref (agent); } diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 0597c493c4..e0592f548f 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -370,8 +370,12 @@ global: nm_secret_agent_get_secrets_flags_get_type; nm_secret_agent_get_type; nm_secret_agent_register; + nm_secret_agent_register_async; + nm_secret_agent_register_finish; nm_secret_agent_save_secrets; nm_secret_agent_unregister; + nm_secret_agent_unregister_async; + nm_secret_agent_unregister_finish; nm_setting_802_1x_add_altsubject_match; nm_setting_802_1x_add_eap_method; nm_setting_802_1x_add_phase2_altsubject_match; diff --git a/libnm/nm-secret-agent.c b/libnm/nm-secret-agent.c index 3fbc74286b..d90789bd24 100644 --- a/libnm/nm-secret-agent.c +++ b/libnm/nm-secret-agent.c @@ -54,21 +54,25 @@ static void impl_secret_agent_delete_secrets (NMSecretAgent *self, #include "nm-secret-agent-glue.h" -G_DEFINE_ABSTRACT_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT) +static void nm_secret_agent_initable_iface_init (GInitableIface *iface); +static void nm_secret_agent_async_initable_iface_init (GAsyncInitableIface *iface); +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_secret_agent_initable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_secret_agent_async_initable_iface_init); + ) #define NM_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT, NMSecretAgentPrivate)) -static gboolean auto_register_cb (gpointer user_data); - typedef struct { gboolean registered; + gboolean registering; NMSecretAgentCapabilities capabilities; DBusGConnection *bus; gboolean private_bus; + gboolean session_bus; DBusGProxy *dbus_proxy; DBusGProxy *manager_proxy; - DBusGProxyCall *reg_call; /* GetSecretsInfo structs of in-flight GetSecrets requests */ GSList *pending_gets; @@ -78,7 +82,6 @@ typedef struct { char *identifier; gboolean auto_register; gboolean suppress_auto; - gboolean auto_register_id; } NMSecretAgentPrivate; enum { @@ -91,15 +94,6 @@ enum { LAST_PROP }; -enum { - REGISTRATION_RESULT, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - - /********************************************************************/ GQuark @@ -145,6 +139,7 @@ _internal_unregister (NMSecretAgent *self) if (priv->registered) { dbus_g_connection_unregister_g_object (priv->bus, G_OBJECT (self)); priv->registered = FALSE; + priv->registering = FALSE; g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED); } } @@ -170,6 +165,17 @@ get_secrets_info_finalize (NMSecretAgent *self, GetSecretsInfo *info) g_free (info); } +static inline gboolean +should_auto_register (NMSecretAgent *self) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + return ( priv->auto_register + && !priv->suppress_auto + && !priv->registered + && !priv->registering); +} + static void name_owner_changed (DBusGProxy *proxy, const char *name, @@ -189,7 +195,8 @@ name_owner_changed (DBusGProxy *proxy, if (!old_owner_good && new_owner_good) { /* NM appeared */ - auto_register_cb (self); + if (should_auto_register (self)) + nm_secret_agent_register_async (self, NULL, NULL, NULL); } else if (old_owner_good && !new_owner_good) { /* Cancel any pending secrets requests */ for (iter = priv->pending_gets; iter; iter = g_slist_next (iter)) { @@ -207,7 +214,8 @@ name_owner_changed (DBusGProxy *proxy, } else if (old_owner_good && new_owner_good && strcmp (old_owner, new_owner)) { /* Hmm, NM magically restarted */ _internal_unregister (self); - auto_register_cb (self); + if (should_auto_register (self)) + nm_secret_agent_register_async (self, NULL, NULL, NULL); } } } @@ -233,9 +241,7 @@ verify_sender (NMSecretAgent *self, if (priv->private_bus) return TRUE; - /* Verify the sender's UID is 0, and that the sender is the same as - * NetworkManager's bus name owner. - */ + /* Verify that the sender is the same as NetworkManager's bus name owner. */ nm_owner = get_nm_owner (self); if (!nm_owner) { @@ -273,6 +279,14 @@ verify_sender (NMSecretAgent *self, goto out; } + /* If we're connected to the session bus, then this must be a test program, + * so skip the UID check. + */ + if (priv->session_bus) { + allowed = TRUE; + goto out; + } + dbus_error_init (&dbus_error); sender_uid = dbus_bus_get_unix_user (bus, sender, &dbus_error); if (dbus_error_is_set (&dbus_error)) { @@ -531,20 +545,124 @@ impl_secret_agent_delete_secrets (NMSecretAgent *self, /**************************************************************/ +static gboolean +check_nm_running (NMSecretAgent *self, GError **error) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + if (priv->nm_owner || priv->private_bus) + return TRUE; + + g_set_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, + "NetworkManager is not running"); + return FALSE; +} + +/**************************************************************/ + +/** + * nm_secret_agent_register: + * @self: a #NMSecretAgent + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL + * + * Registers the #NMSecretAgent with the NetworkManager secret manager, + * indicating to NetworkManager that the agent is able to provide and save + * secrets for connections on behalf of its user. + * + * It is a programmer error to attempt to register an agent that is already + * registered, or in the process of registering. + * + * Returns: %TRUE if registration was successful, %FALSE on error. + **/ +gboolean +nm_secret_agent_register (NMSecretAgent *self, + GCancellable *cancellable, + GError **error) +{ + NMSecretAgentPrivate *priv; + NMSecretAgentClass *class; + + g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); + + priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + g_return_val_if_fail (priv->registered == FALSE, FALSE); + g_return_val_if_fail (priv->registering == FALSE, FALSE); + g_return_val_if_fail (priv->bus != NULL, FALSE); + g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + + /* Also make sure the subclass can actually respond to secrets requests */ + class = NM_SECRET_AGENT_GET_CLASS (self); + g_return_val_if_fail (class->get_secrets != NULL, FALSE); + g_return_val_if_fail (class->save_secrets != NULL, FALSE); + g_return_val_if_fail (class->delete_secrets != NULL, FALSE); + + if (!check_nm_running (self, error)) + return FALSE; + + priv->suppress_auto = FALSE; + + /* Export our secret agent interface before registering with the manager */ + dbus_g_connection_register_g_object (priv->bus, + NM_DBUS_PATH_SECRET_AGENT, + G_OBJECT (self)); + + priv->registering = TRUE; + if (dbus_g_proxy_call_with_timeout (priv->manager_proxy, + "RegisterWithCapabilities", + 5000, NULL, + G_TYPE_STRING, priv->identifier, + G_TYPE_UINT, priv->capabilities, + G_TYPE_INVALID, + G_TYPE_INVALID)) + goto success; + + /* Might be an old NetworkManager that doesn't support capabilities; + * fall back to old Register() method instead. + */ + if (dbus_g_proxy_call_with_timeout (priv->manager_proxy, + "Register", + 5000, error, + G_TYPE_STRING, priv->identifier, + G_TYPE_INVALID, + G_TYPE_INVALID)) + goto success; + + /* Failure */ + priv->registering = FALSE; + _internal_unregister (self); + return FALSE; + +success: + priv->registering = FALSE; + priv->registered = TRUE; + g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED); + return TRUE; +} + static void -reg_result (NMSecretAgent *self, GError *error) +reg_result (NMSecretAgent *self, GSimpleAsyncResult *simple, GError *error) { NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + priv->registering = FALSE; + if (error) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete (simple); + /* If registration failed we shouldn't expose ourselves on the bus */ _internal_unregister (self); } else { priv->registered = TRUE; g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED); + + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete (simple); } - g_signal_emit (self, signals[REGISTRATION_RESULT], 0, error); + g_object_unref (simple); } static void @@ -552,15 +670,15 @@ reg_request_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) { - NMSecretAgent *self = NM_SECRET_AGENT (user_data); - NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GSimpleAsyncResult *simple = user_data; + NMSecretAgent *self; GError *error = NULL; - priv->reg_call = NULL; + self = NM_SECRET_AGENT (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); /* drop extra ref added by get_source_object() */ dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); - reg_result (self, error); - g_clear_error (&error); + reg_result (self, simple, error); } static void @@ -568,66 +686,81 @@ reg_with_caps_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) { - NMSecretAgent *self = NM_SECRET_AGENT (user_data); - NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GSimpleAsyncResult *simple = user_data; + NMSecretAgent *self; + NMSecretAgentPrivate *priv; - priv->reg_call = NULL; + self = NM_SECRET_AGENT (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); /* drop extra ref added by get_source_object() */ + priv = NM_SECRET_AGENT_GET_PRIVATE (self); if (dbus_g_proxy_end_call (proxy, call, NULL, G_TYPE_INVALID)) { - reg_result (self, NULL); + reg_result (self, simple, NULL); return; } /* Might be an old NetworkManager that doesn't support capabilities; * fall back to old Register() method instead. */ - priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, - "Register", - reg_request_cb, - self, - NULL, - 5000, - G_TYPE_STRING, priv->identifier, - G_TYPE_INVALID); + dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, + "Register", + reg_request_cb, + self, + NULL, + 5000, + G_TYPE_STRING, priv->identifier, + G_TYPE_INVALID); } /** - * nm_secret_agent_register: + * nm_secret_agent_register_async: * @self: a #NMSecretAgent + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is registered + * @user_data: data for @callback * - * Registers the #NMSecretAgent with the NetworkManager secret manager, - * indicating to NetworkManager that the agent is able to provide and save - * secrets for connections on behalf of its user. Registration is an - * asynchronous operation and its success or failure is indicated via the - * 'registration-result' signal. + * Asynchronously registers the #NMSecretAgent with the NetworkManager secret + * manager, indicating to NetworkManager that the agent is able to provide and + * save secrets for connections on behalf of its user. * - * Returns: a new %TRUE if registration was successfully requested (this does - * not mean registration itself was successful), %FALSE if registration was not - * successfully requested. + * It is a programmer error to attempt to register an agent that is already + * registered, or in the process of registering. **/ -gboolean -nm_secret_agent_register (NMSecretAgent *self) +void +nm_secret_agent_register_async (NMSecretAgent *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { NMSecretAgentPrivate *priv; NMSecretAgentClass *class; + GSimpleAsyncResult *simple; + GError *error = NULL; - g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); + g_return_if_fail (NM_IS_SECRET_AGENT (self)); priv = NM_SECRET_AGENT_GET_PRIVATE (self); - g_return_val_if_fail (priv->registered == FALSE, FALSE); - g_return_val_if_fail (priv->reg_call == NULL, FALSE); - g_return_val_if_fail (priv->bus != NULL, FALSE); - g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + g_return_if_fail (priv->registered == FALSE); + g_return_if_fail (priv->registering == FALSE); + g_return_if_fail (priv->bus != NULL); + g_return_if_fail (priv->manager_proxy != NULL); /* Also make sure the subclass can actually respond to secrets requests */ class = NM_SECRET_AGENT_GET_CLASS (self); - g_return_val_if_fail (class->get_secrets != NULL, FALSE); - g_return_val_if_fail (class->save_secrets != NULL, FALSE); - g_return_val_if_fail (class->delete_secrets != NULL, FALSE); + g_return_if_fail (class->get_secrets != NULL); + g_return_if_fail (class->save_secrets != NULL); + g_return_if_fail (class->delete_secrets != NULL); - if (!priv->nm_owner && !priv->private_bus) - return FALSE; + simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + nm_secret_agent_register_async); + + if (!check_nm_running (self, &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } priv->suppress_auto = FALSE; @@ -636,32 +769,63 @@ nm_secret_agent_register (NMSecretAgent *self) NM_DBUS_PATH_SECRET_AGENT, G_OBJECT (self)); - priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, - "RegisterWithCapabilities", - reg_with_caps_cb, - self, - NULL, - 5000, - G_TYPE_STRING, priv->identifier, - G_TYPE_UINT, priv->capabilities, - G_TYPE_INVALID); - return TRUE; + priv->registering = TRUE; + dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, + "RegisterWithCapabilities", + reg_with_caps_cb, + simple, + NULL, + 5000, + G_TYPE_STRING, priv->identifier, + G_TYPE_UINT, priv->capabilities, + G_TYPE_INVALID); +} + +/** + * nm_secret_agent_register_finish: + * @self: a #NMSecretAgent + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_register_async(). + * + * Returns: %TRUE if registration was successful, %FALSE on error. + **/ +gboolean +nm_secret_agent_register_finish (NMSecretAgent *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_secret_agent_register_async), FALSE); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + else + return TRUE; } /** * nm_secret_agent_unregister: * @self: a #NMSecretAgent + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL * * Unregisters the #NMSecretAgent with the NetworkManager secret manager, - * indicating to NetworkManager that the agent is will no longer provide or + * indicating to NetworkManager that the agent will no longer provide or * store secrets on behalf of this user. * - * Returns: a new %TRUE if unregistration was successful, %FALSE if it was not. + * It is a programmer error to attempt to unregister an agent that is not + * registered. + * + * Returns: %TRUE if unregistration was successful, %FALSE on error **/ gboolean -nm_secret_agent_unregister (NMSecretAgent *self) +nm_secret_agent_unregister (NMSecretAgent *self, + GCancellable *cancellable, + GError **error) { NMSecretAgentPrivate *priv; + gboolean success; g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); @@ -671,15 +835,118 @@ nm_secret_agent_unregister (NMSecretAgent *self) g_return_val_if_fail (priv->bus != NULL, FALSE); g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); - if (!priv->nm_owner && !priv->private_bus) + if (!check_nm_running (self, error)) return FALSE; - dbus_g_proxy_call_no_reply (priv->manager_proxy, "Unregister", G_TYPE_INVALID); + priv->suppress_auto = TRUE; + + success = dbus_g_proxy_call_with_timeout (priv->manager_proxy, + "Unregister", + 5000, error, + G_TYPE_INVALID, + G_TYPE_INVALID); + _internal_unregister (self); + + return success; +} + +static void +unregister_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + NMSecretAgent *self; + GError *error = NULL; + + self = NM_SECRET_AGENT (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); /* drop extra ref added by get_source_object() */ _internal_unregister (self); + + if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + else + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +/** + * nm_secret_agent_unregister_async: + * @self: a #NMSecretAgent + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is unregistered + * @user_data: data for @callback + * + * Asynchronously unregisters the #NMSecretAgent with the NetworkManager secret + * manager, indicating to NetworkManager that the agent will no longer provide + * or store secrets on behalf of this user. + * + * It is a programmer error to attempt to unregister an agent that is not + * registered. + **/ +void +nm_secret_agent_unregister_async (NMSecretAgent *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentPrivate *priv; + GSimpleAsyncResult *simple; + GError *error = NULL; + + g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); + + priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + g_return_val_if_fail (priv->registered == TRUE, FALSE); + g_return_val_if_fail (priv->bus != NULL, FALSE); + g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + + simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + nm_secret_agent_unregister_async); + + if (!check_nm_running (self, &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + priv->suppress_auto = TRUE; - return TRUE; + dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, + "Unregister", + unregister_cb, + simple, + NULL, + 5000, + G_TYPE_INVALID); +} + +/** + * nm_secret_agent_unregister_finish: + * @self: a #NMSecretAgent + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_unregister_async(). + * + * Returns: %TRUE if unregistration was successful, %FALSE on error. + **/ +gboolean +nm_secret_agent_unregister_finish (NMSecretAgent *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_secret_agent_unregister_async), FALSE); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + else + return TRUE; } /** @@ -696,19 +963,6 @@ nm_secret_agent_get_registered (NMSecretAgent *self) return NM_SECRET_AGENT_GET_PRIVATE (self)->registered; } -static gboolean -auto_register_cb (gpointer user_data) -{ - NMSecretAgent *self = NM_SECRET_AGENT (user_data); - NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - - priv->auto_register_id = 0; - if (priv->auto_register && !priv->suppress_auto && - (priv->reg_call == NULL && !priv->registered)) - nm_secret_agent_register (self); - return FALSE; -} - /**************************************************************/ /** @@ -721,7 +975,7 @@ auto_register_cb (gpointer user_data) * @callback: (scope async): a callback, to be invoked when the operation is done * @user_data: (closure): caller-specific data to be passed to @callback * - * Asyncronously retrieve secrets belonging to @connection for the + * Asynchronously retrieves secrets belonging to @connection for the * setting @setting_name. @flags indicate specific behavior that the secret * agent should use when performing the request, for example returning only * existing secrets without user interaction, or requesting entirely new @@ -763,8 +1017,8 @@ nm_secret_agent_get_secrets (NMSecretAgent *self, * @callback: (scope async): a callback, to be invoked when the operation is done * @user_data: (closure): caller-specific data to be passed to @callback * - * Asyncronously ensure that all secrets inside @connection - * are stored to disk. + * Asynchronously ensures that all secrets inside @connection are stored to + * disk. * * Virtual: save_secrets */ @@ -792,7 +1046,7 @@ nm_secret_agent_save_secrets (NMSecretAgent *self, * @callback: (scope async): a callback, to be invoked when the operation is done * @user_data: (closure): caller-specific data to be passed to @callback * - * Asynchronously ask the agent to delete all saved secrets belonging to + * Asynchronously asks the agent to delete all saved secrets belonging to * @connection. * * Virtual: delete_secrets @@ -845,17 +1099,25 @@ validate_identifier (const char *identifier) static void nm_secret_agent_init (NMSecretAgent *self) { +} + +static gboolean +init_common (NMSecretAgent *self, GError **error) +{ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - GError *error = NULL; + DBusGConnection *session_bus; - priv->bus = _nm_dbus_new_connection (&error); - if (!priv->bus) { - g_warning ("Couldn't connect to system bus: %s", error->message); - g_error_free (error); - return; - } + priv->bus = _nm_dbus_new_connection (error); + if (!priv->bus) + return FALSE; priv->private_bus = _nm_dbus_is_connection_private (priv->bus); + session_bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + if (priv->bus == session_bus) + priv->session_bus = TRUE; + if (session_bus) + dbus_g_connection_unref (session_bus); + if (priv->private_bus == FALSE) { priv->dbus_proxy = dbus_g_proxy_new_for_name (priv->bus, DBUS_SERVICE_DBUS, @@ -882,12 +1144,82 @@ nm_secret_agent_init (NMSecretAgent *self) NM_DBUS_PATH_AGENT_MANAGER, NM_DBUS_INTERFACE_AGENT_MANAGER); if (!priv->manager_proxy) { - g_warning ("Couldn't create NM agent manager proxy."); + g_set_error_literal (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, + "Couldn't create NM agent manager proxy."); + return FALSE; + } + + return TRUE; +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMSecretAgent *self = NM_SECRET_AGENT (initable); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + if (!init_common (self, error)) + return FALSE; + + if (priv->auto_register) + return nm_secret_agent_register (self, cancellable, error); + else + return TRUE; +} + +static void +init_async_registered (GObject *initable, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgent *self = NM_SECRET_AGENT (initable); + GSimpleAsyncResult *simple = user_data; + GError *error = NULL; + + if (nm_secret_agent_register_finish (self, result, &error)) + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + else + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); +} + +static void +init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgent *self = NM_SECRET_AGENT (initable); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GSimpleAsyncResult *simple; + GError *error = NULL; + + simple = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, init_async); + + if (!init_common (self, &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); return; } - if (priv->nm_owner || priv->private_bus) - priv->auto_register_id = g_idle_add (auto_register_cb, self); + if (priv->auto_register) + nm_secret_agent_register_async (self, cancellable, init_async_registered, simple); + else { + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + } +} + +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + else + return TRUE; } static void @@ -954,12 +1286,7 @@ dispose (GObject *object) NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); if (priv->registered) - nm_secret_agent_unregister (self); - - if (priv->auto_register_id) { - g_source_remove (priv->auto_register_id); - priv->auto_register_id = 0; - } + nm_secret_agent_unregister_async (self, NULL, NULL, NULL); g_free (priv->identifier); priv->identifier = NULL; @@ -1014,13 +1341,23 @@ nm_secret_agent_class_init (NMSecretAgentClass *class) /** * NMSecretAgent:auto-register: * - * If TRUE, the agent will attempt to automatically register itself after - * it is created (via an idle handler) and to re-register itself if - * NetworkManager restarts. If FALSE, the agent does not automatically - * register with NetworkManager, and nm_secret_agent_register() must be - * called. If 'auto-register' is TRUE, calling nm_secret_agent_unregister() - * will suppress auto-registration until nm_secret_agent_register() is - * called, which re-enables auto-registration. + * If %TRUE (the default), the agent will always be registered when + * NetworkManager is running; if NetworkManager exits and restarts, the + * agent will re-register itself automatically. + * + * In particular, if this property is %TRUE at construct time, then the + * agent will register itself with NetworkManager during + * construction/initialization, and initialization will fail with an error + * if the agent is unable to register itself. + * + * If the property is %FALSE, the agent will not automatically register with + * NetworkManager, and nm_secret_agent_register() or + * nm_secret_agent_register_async() must be called to register it. + * + * Calling nm_secret_agent_unregister() will suppress auto-registration + * until nm_secret_agent_register() is called, which re-enables + * auto-registration. This ensures that the agent remains un-registered when + * you expect it to be unregistered. **/ g_object_class_install_property (object_class, PROP_AUTO_REGISTER, @@ -1056,21 +1393,6 @@ nm_secret_agent_class_init (NMSecretAgentClass *class) G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - /** - * NMSecretAgent::registration-result: - * @agent: the agent that received the signal - * @error: the error, if any, that occured while registering - * - * Indicates the result of a registration request; if @error is NULL the - * request was successful. - **/ - signals[REGISTRATION_RESULT] = - g_signal_new (NM_SECRET_AGENT_REGISTRATION_RESULT, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_POINTER); - dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class), &dbus_glib_nm_secret_agent_object_info); @@ -1078,3 +1400,16 @@ nm_secret_agent_class_init (NMSecretAgentClass *class) NM_DBUS_INTERFACE_SECRET_AGENT, NM_TYPE_SECRET_AGENT_ERROR); } + +static void +nm_secret_agent_initable_iface_init (GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_secret_agent_async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} diff --git a/libnm/nm-secret-agent.h b/libnm/nm-secret-agent.h index ad62916bce..4b88d55be7 100644 --- a/libnm/nm-secret-agent.h +++ b/libnm/nm-secret-agent.h @@ -25,6 +25,7 @@ #error "Only <NetworkManager.h> can be included directly." #endif +#include <gio/gio.h> #include <nm-connection.h> G_BEGIN_DECLS @@ -71,8 +72,6 @@ typedef enum { #define NM_SECRET_AGENT_REGISTERED "registered" #define NM_SECRET_AGENT_CAPABILITIES "capabilities" -#define NM_SECRET_AGENT_REGISTRATION_RESULT "registration-result" - typedef struct { GObject parent; } NMSecretAgent; @@ -170,9 +169,6 @@ typedef void (*NMSecretAgentDeleteSecretsFunc) (NMSecretAgent *agent, typedef struct { GObjectClass parent; - /* Signals */ - void (*registration_result) (NMSecretAgent *agent, GError *error); - /* Virtual methods for subclasses */ /* Called when the subclass should retrieve and return secrets. Subclass @@ -231,9 +227,27 @@ typedef struct { GType nm_secret_agent_get_type (void); -gboolean nm_secret_agent_register (NMSecretAgent *self); +gboolean nm_secret_agent_register (NMSecretAgent *self, + GCancellable *cancellable, + GError **error); +void nm_secret_agent_register_async (NMSecretAgent *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean nm_secret_agent_register_finish (NMSecretAgent *self, + GAsyncResult *result, + GError **error); -gboolean nm_secret_agent_unregister (NMSecretAgent *self); +gboolean nm_secret_agent_unregister (NMSecretAgent *self, + GCancellable *cancellable, + GError **error); +void nm_secret_agent_unregister_async (NMSecretAgent *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean nm_secret_agent_unregister_finish (NMSecretAgent *self, + GAsyncResult *result, + GError **error); gboolean nm_secret_agent_get_registered (NMSecretAgent *self); diff --git a/libnm/tests/Makefile.am b/libnm/tests/Makefile.am index 40763d0109..cbf2da9629 100644 --- a/libnm/tests/Makefile.am +++ b/libnm/tests/Makefile.am @@ -19,7 +19,7 @@ LDADD = \ noinst_PROGRAMS = $(TESTS) -TESTS = test-nm-client test-remote-settings-client +TESTS = test-nm-client test-remote-settings-client test-secret-agent test_nm_client_SOURCES = \ common.c \ @@ -31,6 +31,11 @@ test_remote_settings_client_SOURCES = \ common.h \ test-remote-settings-client.c +test_secret_agent_SOURCES = \ + common.c \ + common.h \ + test-secret-agent.c + TESTS_ENVIRONMENT = $(srcdir)/libnm-test-launch.sh endif diff --git a/libnm/tests/test-secret-agent.c b/libnm/tests/test-secret-agent.c new file mode 100644 index 0000000000..bd540124c6 --- /dev/null +++ b/libnm/tests/test-secret-agent.c @@ -0,0 +1,681 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <string.h> +#include <sys/types.h> +#include <signal.h> + +#include <NetworkManager.h> +#include "nm-glib-compat.h" +#include "nm-test-utils.h" + +#include "common.h" + +/*******************************************************************/ + +enum { + SECRET_REQUESTED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef NMSecretAgent TestSecretAgent; +typedef NMSecretAgentClass TestSecretAgentClass; + +GType test_secret_agent_get_type (void); +G_DEFINE_TYPE (TestSecretAgent, test_secret_agent, NM_TYPE_SECRET_AGENT) + +static void +test_secret_agent_init (TestSecretAgent *agent) +{ +} + +static void +test_secret_agent_get_secrets (NMSecretAgent *agent, + NMConnection *connection, + const char *connection_path, + const char *setting_name, + const char **hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentGetSecretsFunc callback, + gpointer callback_data) +{ + NMSettingWirelessSecurity *s_wsec; + GHashTable *hash = NULL, *setting_hash; + GValue value = G_VALUE_INIT; + char *secret = NULL; + GError *error = NULL; + + g_assert_cmpstr (setting_name, ==, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + + s_wsec = nm_connection_get_setting_wireless_security (connection); + g_assert (s_wsec); + g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "wpa-psk"); + g_assert_cmpstr (nm_setting_wireless_security_get_psk (s_wsec), ==, NULL); + + g_signal_emit (agent, signals[SECRET_REQUESTED], 0, + connection, + connection_path, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK, + &secret); + + if (!secret) { + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, + "No secrets"); + goto done; + } + + if (!strcmp (secret, "CANCEL")) { + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED, + "User canceled"); + goto done; + } + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, (GDestroyNotify) g_hash_table_unref); + setting_hash = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (hash, (char *) setting_name, setting_hash); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, secret); + + g_hash_table_insert (setting_hash, NM_SETTING_WIRELESS_SECURITY_PSK, &value); + +done: + callback (agent, connection, hash, error, callback_data); + g_clear_error (&error); + g_clear_pointer (&hash, g_hash_table_unref); + g_free (secret); +} + +static void +test_secret_agent_cancel_get_secrets (NMSecretAgent *agent, + const gchar *connection_path, + const gchar *setting_name) +{ + g_assert_not_reached (); +} + +static void +test_secret_agent_save_secrets (NMSecretAgent *agent, + NMConnection *connection, + const gchar *connection_path, + NMSecretAgentSaveSecretsFunc callback, + gpointer callback_data) +{ + g_assert_not_reached (); +} + +static void +test_secret_agent_delete_secrets (NMSecretAgent *agent, + NMConnection *connection, + const gchar *connection_path, + NMSecretAgentDeleteSecretsFunc callback, + gpointer callback_data) +{ + g_assert_not_reached (); +} + +static void +test_secret_agent_class_init (TestSecretAgentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSecretAgentClass *agent_class = NM_SECRET_AGENT_CLASS (klass); + + agent_class->get_secrets = test_secret_agent_get_secrets; + agent_class->cancel_get_secrets = test_secret_agent_cancel_get_secrets; + agent_class->save_secrets = test_secret_agent_save_secrets; + agent_class->delete_secrets = test_secret_agent_delete_secrets; + + signals[SECRET_REQUESTED] = + g_signal_new ("secret-requested", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_STRING, 4, + NM_TYPE_CONNECTION, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + +} + +static NMSecretAgent * +test_secret_agent_new (void) +{ + NMSecretAgent *agent; + GError *error = NULL; + + agent = g_initable_new (test_secret_agent_get_type (), NULL, &error, + NM_SECRET_AGENT_IDENTIFIER, "test-secret-agent", + NM_SECRET_AGENT_AUTO_REGISTER, FALSE, + NULL); + g_assert_no_error (error); + + return agent; +} + +/*******************************************************************/ + +typedef struct { + NMTestServiceInfo *sinfo; + NMClient *client; + NMRemoteSettings *settings; + + NMSecretAgent *agent; + NMDevice *device; + NMConnection *connection; + + GMainLoop *loop; + guint timeout_id; + + char *ifname; + char *con_id; + char *devpath; + + int secrets_requested; +} TestSecretAgentData; + +static gboolean +timeout_assert (gpointer user_data) +{ + g_assert_not_reached (); +} + +static void +device_added_cb (NMClient *c, + NMDevice *device, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert (device); + g_assert_cmpstr (nm_device_get_iface (device), ==, sadata->ifname); + + sadata->device = device; + g_main_loop_quit (sadata->loop); +} + +static void +connection_added_cb (NMRemoteSettings *s, + NMRemoteConnection *connection, + GError *error, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_no_error (error); + g_assert_cmpstr (nm_connection_get_id (NM_CONNECTION (connection)), ==, sadata->con_id); + + sadata->connection = NM_CONNECTION (connection); + g_main_loop_quit (sadata->loop); +} + +static void +register_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + GError *error = NULL; + + nm_secret_agent_register_finish (sadata->agent, result, &error); + g_assert_no_error (error); + g_assert (nm_secret_agent_get_registered (sadata->agent)); + + g_main_loop_quit (sadata->loop); +} + +#define TEST_CON_ID_PREFIX "test-secret-agent" + +static void +test_setup (TestSecretAgentData *sadata, gconstpointer test_data) +{ + static int counter = 0; + const char *agent_notes = test_data; + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + GBytes *ssid; + NMSetting *s_wsec; + gboolean success; + GError *error = NULL; + GVariant *ret; + gulong handler; + + sadata->sinfo = nm_test_service_init (); + sadata->client = nm_client_new (NULL, &error); + g_assert_no_error (error); + sadata->settings = nm_remote_settings_new (NULL, &error); + g_assert_no_error (error); + + sadata->loop = g_main_loop_new (NULL, FALSE); + sadata->timeout_id = g_timeout_add_seconds (5, timeout_assert, NULL); + + sadata->ifname = g_strdup_printf ("wlan%d", counter); + sadata->con_id = g_strdup_printf ("%s-%d", TEST_CON_ID_PREFIX, counter); + counter++; + + /* Create the device */ + ret = g_dbus_proxy_call_sync (sadata->sinfo->proxy, + "AddWifiDevice", + g_variant_new ("(s)", sadata->ifname), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpstr (g_variant_get_type_string (ret), ==, "(o)"); + g_variant_get (ret, "(o)", &sadata->devpath); + g_variant_unref (ret); + + handler = g_signal_connect (sadata->client, "device-added", + G_CALLBACK (device_added_cb), sadata); + g_main_loop_run (sadata->loop); + g_signal_handler_disconnect (sadata->client, handler); + g_assert (sadata->device); + + /* Create the connection */ + connection = nmtst_create_minimal_connection (sadata->con_id, NULL, NM_SETTING_WIRELESS_SETTING_NAME, &s_con); + g_object_set (s_con, + NM_SETTING_CONNECTION_INTERFACE_NAME, sadata->ifname, + NULL); + + s_wireless = nm_connection_get_setting_wireless (connection); + ssid = g_bytes_new ("foo", 3); + g_object_set (s_wireless, + NM_SETTING_WIRELESS_SSID, ssid, + NULL); + g_bytes_unref (ssid); + + s_wsec = g_object_new (NM_TYPE_SETTING_WIRELESS_SECURITY, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", + NULL); + nm_connection_add_setting (connection, s_wsec); + + success = nm_remote_settings_add_connection (sadata->settings, + connection, + connection_added_cb, + sadata); + g_assert (success == TRUE); + g_object_unref (connection); + + g_main_loop_run (sadata->loop); + g_assert (sadata->connection); + + if (agent_notes) { + sadata->agent = test_secret_agent_new (); + + if (!strcmp (agent_notes, "sync")) { + nm_secret_agent_register (sadata->agent, NULL, &error); + g_assert_no_error (error); + g_assert (nm_secret_agent_get_registered (sadata->agent)); + } else { + nm_secret_agent_register_async (sadata->agent, NULL, + register_cb, sadata); + g_main_loop_run (sadata->loop); + } + } +} + +static void +test_cleanup (TestSecretAgentData *sadata, gconstpointer test_data) +{ + GVariant *ret; + GError *error = NULL; + + if (sadata->agent) { + if (nm_secret_agent_get_registered (sadata->agent)) { + nm_secret_agent_unregister (sadata->agent, NULL, &error); + g_assert_no_error (error); + } + g_object_unref (sadata->agent); + } + + g_object_unref (sadata->client); + g_object_unref (sadata->settings); + + ret = g_dbus_proxy_call_sync (sadata->sinfo->proxy, + "RemoveDevice", + g_variant_new ("(s)", sadata->devpath), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error (error); + g_variant_unref (ret); + + nm_test_service_cleanup (sadata->sinfo); + + g_source_remove (sadata->timeout_id); + g_main_loop_unref (sadata->loop); + + g_free (sadata->ifname); + g_free (sadata->con_id); + g_free (sadata->devpath); +} + +/*******************************************************************/ + +static void +connection_activated_none_cb (NMClient *c, + NMActiveConnection *ac, + GError *error, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert (error != NULL); + g_assert_cmpstr (error->message, ==, "No secret agent available"); + + g_main_loop_quit (sadata->loop); +} + +static void +test_secret_agent_none (TestSecretAgentData *sadata, gconstpointer test_data) +{ + nm_client_activate_connection (sadata->client, + sadata->connection, + sadata->device, + NULL, + connection_activated_none_cb, + sadata); + g_main_loop_run (sadata->loop); +} + +/*******************************************************************/ + +static char * +secrets_requested_no_secrets_cb (TestSecretAgent *agent, + NMConnection *connection, + const char *connection_path, + const char *setting_name, + const char *secret_name, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_cmpstr (connection_path, ==, nm_connection_get_path (sadata->connection)); + sadata->secrets_requested++; + + return NULL; +} + +static void +connection_activated_no_secrets_cb (NMClient *c, + NMActiveConnection *ac, + GError *error, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert (error != NULL); + g_assert_cmpstr (error->message, ==, "No secrets provided"); + + g_main_loop_quit (sadata->loop); +} + +static void +test_secret_agent_no_secrets (TestSecretAgentData *sadata, gconstpointer test_data) +{ + g_signal_connect (sadata->agent, "secret-requested", + G_CALLBACK (secrets_requested_no_secrets_cb), + sadata); + + nm_client_activate_connection (sadata->client, + sadata->connection, + sadata->device, + NULL, + connection_activated_no_secrets_cb, + sadata); + g_main_loop_run (sadata->loop); + + g_assert_cmpint (sadata->secrets_requested, ==, 1); +} + +/*******************************************************************/ + +static void +connection_activated_cancel_cb (NMClient *c, + NMActiveConnection *ac, + GError *error, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert (error != NULL); + g_assert_cmpstr (error->message, ==, "User canceled"); + + g_main_loop_quit (sadata->loop); +} + +static char * +secrets_requested_cancel_cb (TestSecretAgent *agent, + NMConnection *connection, + const char *connection_path, + const char *setting_name, + const char *secret_name, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_cmpstr (connection_path, ==, nm_connection_get_path (sadata->connection)); + sadata->secrets_requested++; + + return g_strdup ("CANCEL"); +} + +static void +test_secret_agent_cancel (TestSecretAgentData *sadata, gconstpointer test_data) +{ + g_signal_connect (sadata->agent, "secret-requested", + G_CALLBACK (secrets_requested_cancel_cb), + sadata); + + nm_client_activate_connection (sadata->client, + sadata->connection, + sadata->device, + NULL, + connection_activated_cancel_cb, + sadata); + g_main_loop_run (sadata->loop); + + g_assert_cmpint (sadata->secrets_requested, ==, 1); +} + +/*******************************************************************/ + +static void +connection_activated_good_cb (NMClient *c, + NMActiveConnection *ac, + GError *error, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + /* test-networkmanager-service.py doesn't implement activation, but + * we should at least get as far as the error telling us that (which the + * other tests won't get to). + */ + g_assert (error != NULL); + g_assert_cmpstr (error->message, ==, "Not yet implemented"); + + g_main_loop_quit (sadata->loop); +} + +static char * +secrets_requested_good_cb (TestSecretAgent *agent, + NMConnection *connection, + const char *connection_path, + const char *setting_name, + const char *secret_name, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_cmpstr (connection_path, ==, nm_connection_get_path (sadata->connection)); + sadata->secrets_requested++; + + return g_strdup ("password"); +} + +static void +test_secret_agent_good (TestSecretAgentData *sadata, gconstpointer test_data) +{ + g_signal_connect (sadata->agent, "secret-requested", + G_CALLBACK (secrets_requested_good_cb), + sadata); + + nm_client_activate_connection (sadata->client, + sadata->connection, + sadata->device, + NULL, + connection_activated_good_cb, + sadata); + g_main_loop_run (sadata->loop); + + g_assert_cmpint (sadata->secrets_requested, ==, 1); +} + + +static void +async_init_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + GMainLoop *loop = user_data; + GError *error = NULL; + GObject *agent; + + agent = g_async_initable_new_finish (G_ASYNC_INITABLE (object), result, &error); + g_assert_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR); + g_assert (agent == NULL); + g_clear_error (&error); + + g_main_loop_quit (loop); +} + +static void +test_secret_agent_nm_not_running (void) +{ + NMSecretAgent *agent; + GMainLoop *loop; + GError *error = NULL; + + agent = g_initable_new (test_secret_agent_get_type (), NULL, &error, + NM_SECRET_AGENT_IDENTIFIER, "test-secret-agent", + NULL); + g_assert_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR); + g_assert (agent == NULL); + g_clear_error (&error); + + loop = g_main_loop_new (NULL, FALSE); + g_async_initable_new_async (test_secret_agent_get_type (), + G_PRIORITY_DEFAULT, + NULL, async_init_cb, loop, + NM_SECRET_AGENT_IDENTIFIER, "test-secret-agent", + NULL); + g_main_loop_run (loop); + g_main_loop_unref (loop); +} + + +static void +registered_changed (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); +} + +static void +test_secret_agent_auto_register (void) +{ + NMTestServiceInfo *sinfo; + NMSecretAgent *agent; + GMainLoop *loop; + GError *error = NULL; + + sinfo = nm_test_service_init (); + loop = g_main_loop_new (NULL, FALSE); + + agent = test_secret_agent_new (); + g_object_set (agent, + NM_SECRET_AGENT_AUTO_REGISTER, TRUE, + NULL); + g_signal_connect (agent, "notify::" NM_SECRET_AGENT_REGISTERED, + G_CALLBACK (registered_changed), loop); + + g_assert (!nm_secret_agent_get_registered (agent)); + nm_secret_agent_register (agent, NULL, &error); + g_assert_no_error (error); + g_assert (nm_secret_agent_get_registered (agent)); + + /* Shut down test service */ + nm_test_service_cleanup (sinfo); + g_main_loop_run (loop); + g_assert (!nm_secret_agent_get_registered (agent)); + + /* Restart test service */ + sinfo = nm_test_service_init (); + g_main_loop_run (loop); + g_assert (nm_secret_agent_get_registered (agent)); + + /* Shut down test service again */ + nm_test_service_cleanup (sinfo); + g_main_loop_run (loop); + g_assert (!nm_secret_agent_get_registered (agent)); + + g_object_unref (agent); + g_main_loop_unref (loop); +} + +/*******************************************************************/ + +int +main (int argc, char **argv) +{ + int ret; + + g_setenv ("LIBNM_USE_SESSION_BUS", "1", TRUE); + +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + g_test_init (&argc, &argv, NULL); + + g_test_add ("/libnm/secret-agent/none", TestSecretAgentData, NULL, + test_setup, test_secret_agent_none, test_cleanup); + g_test_add ("/libnm/secret-agent/no-secrets", TestSecretAgentData, "sync", + test_setup, test_secret_agent_no_secrets, test_cleanup); + g_test_add ("/libnm/secret-agent/cancel", TestSecretAgentData, "async", + test_setup, test_secret_agent_cancel, test_cleanup); + g_test_add ("/libnm/secret-agent/good", TestSecretAgentData, "async", + test_setup, test_secret_agent_good, test_cleanup); + g_test_add_func ("/libnm/secret-agent/nm-not-running", test_secret_agent_nm_not_running); + g_test_add_func ("/libnm/secret-agent/auto-register", test_secret_agent_auto_register); + + ret = g_test_run (); + + return ret; +} + diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py index 2f80ca516c..7716dca9ac 100755 --- a/tools/test-networkmanager-service.py +++ b/tools/test-networkmanager-service.py @@ -517,6 +517,9 @@ class PermissionDeniedException(dbus.DBusException): class UnknownDeviceException(dbus.DBusException): _dbus_error_name = IFACE_NM + '.UnknownDevice' +class UnknownConnectionException(dbus.DBusException): + _dbus_error_name = IFACE_NM + '.UnknownConnection' + PM_DEVICES = 'Devices' PM_NETWORKING_ENABLED = 'NetworkingEnabled' PM_WWAN_ENABLED = 'WwanEnabled' @@ -575,6 +578,26 @@ class NetworkManager(ExportedObj): break if not device: raise UnknownDeviceException("No device found for the requested iface.") + + try: + connection = settings.get_connection(conpath) + except Exception as e: + raise UnknownConnectionException("Connection not found") + + # See if we need secrets. For the moment, we only support WPA + hash = connection.GetSettings() + if hash.has_key('802-11-wireless-security'): + s_wsec = hash['802-11-wireless-security'] + if (s_wsec['key-mgmt'] == 'wpa-psk' and not s_wsec.has_key('psk')): + secrets = agent_manager.get_secrets(hash, conpath, '802-11-wireless-security') + if secrets is None: + raise NoSecretsException("No secret agent available") + if not secrets.has_key('802-11-wireless-security'): + raise NoSecretsException("No secrets provided") + s_wsec = secrets['802-11-wireless-security'] + if not s_wsec.has_key('psk'): + raise NoSecretsException("No secrets provided") + raise PermissionDeniedException("Not yet implemented") @dbus.service.method(dbus_interface=IFACE_NM, in_signature='a{sa{sv}}oo', out_signature='oo') @@ -586,7 +609,9 @@ class NetworkManager(ExportedObj): break if not device: raise UnknownDeviceException("No device found for the requested iface.") - raise PermissionDeniedException("Not yet implemented") + + conpath = manager.AddConnection(connection) + return self.ActivateConnection(conpath, devpath, specific_object) @dbus.service.method(dbus_interface=IFACE_NM, in_signature='o', out_signature='') def DeactivateConnection(self, active_connection): @@ -822,6 +847,9 @@ class Settings(dbus.service.Object): def auto_remove_next_connection(self): self.remove_next_connection = True; + def get_connection(self, path): + return self.connections[path] + @dbus.service.method(dbus_interface=IFACE_SETTINGS, in_signature='', out_signature='ao') def ListConnections(self): return self.connections.keys() @@ -871,6 +899,65 @@ class Settings(dbus.service.Object): mainloop.quit() ################################################################### +IFACE_AGENT_MANAGER = 'org.freedesktop.NetworkManager.AgentManager' +IFACE_AGENT = 'org.freedesktop.NetworkManager.SecretAgent' + +PATH_SECRET_AGENT = '/org/freedesktop/NetworkManager/SecretAgent' + +FLAG_ALLOW_INTERACTION = 0x1 +FLAG_REQUEST_NEW = 0x2 +FLAG_USER_REQUESTED = 0x4 + +class NoSecretsException(dbus.DBusException): + _dbus_error_name = IFACE_AGENT_MANAGER + '.NoSecrets' + +class UserCanceledException(dbus.DBusException): + _dbus_error_name = IFACE_AGENT_MANAGER + '.UserCanceled' + +class AgentManager(dbus.service.Object): + def __init__(self, bus, object_path): + dbus.service.Object.__init__(self, bus, object_path) + self.agents = {} + self.bus = bus + + @dbus.service.method(dbus_interface=IFACE_AGENT_MANAGER, + in_signature='s', out_signature='', + sender_keyword='sender') + def Register(self, name, sender=None): + self.RegisterWithCapabilities(name, 0, sender) + + @dbus.service.method(dbus_interface=IFACE_AGENT_MANAGER, + in_signature='su', out_signature='', + sender_keyword='sender') + def RegisterWithCapabilities(self, name, caps, sender=None): + self.agents[sender] = self.bus.get_object(sender, PATH_SECRET_AGENT) + + @dbus.service.method(dbus_interface=IFACE_AGENT_MANAGER, + in_signature='', out_signature='', + sender_keyword='sender') + def Unregister(self, sender=None): + del self.agents[sender] + + def get_secrets(self, connection, path, setting_name): + if len(self.agents) == 0: + return None + + secrets = {} + for sender in self.agents: + agent = self.agents[sender] + try: + secrets = agent.GetSecrets(connection, path, setting_name, + dbus.Array([], 's'), + FLAG_ALLOW_INTERACTION | FLAG_USER_REQUESTED, + dbus_interface=IFACE_AGENT) + break + except dbus.DBusException as e: + if e.get_dbus_name() == IFACE_AGENT + '.UserCanceled': + raise UserCanceledException('User canceled') + continue + return secrets + +################################################################### def stdin_cb(io, condition): mainloop.quit() @@ -885,9 +972,10 @@ def main(): bus = dbus.SessionBus() - global manager, settings + global manager, settings, agent_manager manager = NetworkManager(bus, "/org/freedesktop/NetworkManager") settings = Settings(bus, "/org/freedesktop/NetworkManager/Settings") + agent_manager = AgentManager(bus, "/org/freedesktop/NetworkManager/AgentManager") if not bus.request_name("org.freedesktop.NetworkManager"): sys.exit(1) |