From bdba10db542cde903d188b97e55a9ab2418bb3fd Mon Sep 17 00:00:00 2001 From: Jan-Michael Brummer Date: Tue, 14 Feb 2023 16:59:22 +0100 Subject: Add gupnp_service_proxy_set_credentials () Fixes: https://gitlab.gnome.org/GNOME/gupnp/-/issues/85 --- libgupnp/gupnp-service-proxy.c | 69 +++++++++++++++ libgupnp/gupnp-service-proxy.h | 5 ++ tests/test-service-proxy.c | 188 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+) diff --git a/libgupnp/gupnp-service-proxy.c b/libgupnp/gupnp-service-proxy.c index 21b307b..af5f254 100644 --- a/libgupnp/gupnp-service-proxy.c +++ b/libgupnp/gupnp-service-proxy.c @@ -32,6 +32,10 @@ struct _GUPnPServiceProxyPrivate { char *path; /* Path to this proxy */ + // Credentials + char *user; + char *password; + char *sid; /* Subscription ID */ GSource *subscription_timeout_src; @@ -325,6 +329,42 @@ gupnp_service_proxy_class_init (GUPnPServiceProxyClass *klass) G_TYPE_POINTER); } +static gboolean +on_authenticate (SoupMessage *msg, + SoupAuth *auth, + gboolean retrying, + gpointer user_data) +{ + GUPnPServiceProxy *proxy = user_data; + GUPnPServiceProxyPrivate *priv; + + priv = gupnp_service_proxy_get_instance_private (proxy); + + if (!retrying && priv->user != NULL && priv->password != NULL) { + soup_auth_authenticate (auth, priv->user, priv->password); + return FALSE; + } + + return FALSE; +} + +static void +on_restarted (SoupMessage *msg, gpointer user_data) +{ + GUPnPServiceProxyAction *action = user_data; + g_autoptr (GBytes) body = NULL; + const char *service_type; + + service_type = gupnp_service_info_get_service_type + (GUPNP_SERVICE_INFO (action->proxy)); + gupnp_service_proxy_action_serialize (action, service_type); + body = g_string_free_to_bytes (action->msg_str); + soup_message_set_request_body_from_bytes (msg, + "text/xml; charset=\"utf-8\"", + body); + action->msg_str = NULL; +} + /* Begins a basic action message */ static gboolean prepare_action_msg (GUPnPServiceProxy *proxy, @@ -371,6 +411,8 @@ prepare_action_msg (GUPnPServiceProxy *proxy, g_clear_object (&action->msg); action->msg = soup_message_new (method, local_control_url); + g_signal_connect_object (G_OBJECT (action->msg), "authenticate", G_CALLBACK (on_authenticate), G_OBJECT (proxy), 0); + g_signal_connect (G_OBJECT (action->msg), "restarted", G_CALLBACK (on_restarted), action); g_free (local_control_url); SoupMessageHeaders *headers = @@ -1660,3 +1702,30 @@ out: return action; } + + +/** + * gupnp_service_proxy_set_credentials: + * @proxy: A #GUPnPServiceProxy + * @user: user name for authentication + * @password: user password for authentication + * + * Sets user and password for authentication + * + * Since: 1.6.4 + **/ +void +gupnp_service_proxy_set_credentials (GUPnPServiceProxy *proxy, + const char *user, + const char *password) +{ + GUPnPServiceProxyPrivate *priv; + + priv = gupnp_service_proxy_get_instance_private (proxy); + + g_clear_pointer (&priv->user, g_free); + g_clear_pointer (&priv->password, g_free); + + priv->user = g_strdup (user); + priv->password = g_strdup (password); +} diff --git a/libgupnp/gupnp-service-proxy.h b/libgupnp/gupnp-service-proxy.h index c3ffdc6..8668673 100644 --- a/libgupnp/gupnp-service-proxy.h +++ b/libgupnp/gupnp-service-proxy.h @@ -186,6 +186,11 @@ gupnp_service_proxy_call_action (GUPnPServiceProxy *proxy, GCancellable *cancellable, GError **error); +void +gupnp_service_proxy_set_credentials (GUPnPServiceProxy *proxy, + const char *user, + const char *password); + G_END_DECLS #endif /* GUPNP_SERVICE_PROXY_H */ diff --git a/tests/test-service-proxy.c b/tests/test-service-proxy.c index f81b134..7e291bf 100644 --- a/tests/test-service-proxy.c +++ b/tests/test-service-proxy.c @@ -638,6 +638,173 @@ test_finish_soap_error_sync (ProxyTestFixture *tf, gconstpointer user_data) g_thread_unref (t); } +void +auth_message_callback (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + ProxyTestFixture *tf = user_data; + GError *error = NULL; + GBytes *bytes = soup_session_send_and_read_finish (SOUP_SESSION (source), + res, + &error); + g_assert_no_error (error); + g_assert_null (g_bytes_get_data (bytes, NULL)); + + g_main_loop_quit (tf->loop); +} + +static gboolean +on_auth_verification_callback (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username, + const char *password, + gpointer user_data) +{ + if (g_strcmp0 (username, "user") == 0 && g_strcmp0 (password, "password") == 0) + return TRUE; + + return FALSE; +} + +void +on_test_async_unauth_call (GObject *source, GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + g_assert_nonnull (user_data); + + gupnp_service_proxy_call_action_finish (GUPNP_SERVICE_PROXY (source), + res, + &error); + g_assert_nonnull (error); + + ProxyTestFixture *tf = (ProxyTestFixture *) user_data; + g_main_loop_quit (tf->loop); +} + +void +on_test_async_auth_call (GObject *source, GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + g_assert_nonnull (user_data); + + gupnp_service_proxy_call_action_finish (GUPNP_SERVICE_PROXY (source), + res, + &error); + g_assert_no_error (error); + + ProxyTestFixture *tf = (ProxyTestFixture *) user_data; + g_main_loop_quit (tf->loop); +} + +void +test_finish_soap_authentication_no_credentials (ProxyTestFixture *tf, gconstpointer user_data) +{ + SoupServer *soup_server = gupnp_context_get_server (tf->server_context); + SoupAuthDomain *auth_domain; + GUPnPServiceProxyAction *action; + + auth_domain = soup_auth_domain_basic_new ("realm", "Test", NULL); + soup_auth_domain_add_path (auth_domain, "/TestService/Control"); + soup_auth_domain_basic_set_auth_callback (auth_domain, + on_auth_verification_callback, + tf, + NULL); + soup_server_add_auth_domain (soup_server, auth_domain); + + g_signal_connect (tf->service, + "action-invoked::Ping", + G_CALLBACK (on_test_async_call_ping_success), + tf); + + action = gupnp_service_proxy_action_new ("Ping", NULL); + + + gupnp_service_proxy_call_action_async (tf->proxy, + action, + NULL, + on_test_async_unauth_call, + tf); + gupnp_service_proxy_action_unref (action); + test_run_loop(tf->loop, g_test_get_path()); + + // Spin the loop for a bit... + g_timeout_add (500, (GSourceFunc) delayed_loop_quitter, tf->loop); + g_main_loop_run (tf->loop); +} + +void +test_finish_soap_authentication_wrong_credentials (ProxyTestFixture *tf, gconstpointer user_data) +{ + SoupServer *soup_server = gupnp_context_get_server (tf->server_context); + SoupAuthDomain *auth_domain; + GUPnPServiceProxyAction *action; + + auth_domain = soup_auth_domain_basic_new ("realm", "Test", NULL); + soup_auth_domain_add_path (auth_domain, "/TestService/Control"); + soup_auth_domain_basic_set_auth_callback (auth_domain, + on_auth_verification_callback, + tf, + NULL); + soup_server_add_auth_domain (soup_server, auth_domain); + + g_signal_connect (tf->service, + "action-invoked::Ping", + G_CALLBACK (on_test_async_call_ping_success), + tf); + + gupnp_service_proxy_set_credentials (tf->proxy, "user", "wrong_password"); + action = gupnp_service_proxy_action_new ("Ping", NULL); + + gupnp_service_proxy_call_action_async (tf->proxy, + action, + NULL, + on_test_async_unauth_call, + tf); + gupnp_service_proxy_action_unref (action); + test_run_loop(tf->loop, g_test_get_path()); + + // Spin the loop for a bit... + g_timeout_add (500, (GSourceFunc) delayed_loop_quitter, tf->loop); + g_main_loop_run (tf->loop); +} + +void +test_finish_soap_authentication_valid_credentials (ProxyTestFixture *tf, gconstpointer user_data) +{ + SoupServer *soup_server = gupnp_context_get_server (tf->server_context); + SoupAuthDomain *auth_domain; + GUPnPServiceProxyAction *action; + + auth_domain = soup_auth_domain_basic_new ("realm", "Test", NULL); + soup_auth_domain_add_path (auth_domain, "/TestService/Control"); + soup_auth_domain_basic_set_auth_callback (auth_domain, + on_auth_verification_callback, + tf, + NULL); + soup_server_add_auth_domain (soup_server, auth_domain); + + g_signal_connect (tf->service, + "action-invoked::Ping", + G_CALLBACK (on_test_async_call_ping_success), + tf); + + gupnp_service_proxy_set_credentials (tf->proxy, "user", "password"); + action = gupnp_service_proxy_action_new ("Ping", NULL); + + gupnp_service_proxy_call_action_async (tf->proxy, + action, + NULL, + on_test_async_auth_call, + tf); + gupnp_service_proxy_action_unref (action); + test_run_loop(tf->loop, g_test_get_path()); + + // Spin the loop for a bit... + g_timeout_add (500, (GSourceFunc) delayed_loop_quitter, tf->loop); + g_main_loop_run (tf->loop); +} + int main (int argc, char *argv[]) { @@ -699,5 +866,26 @@ main (int argc, char *argv[]) test_finish_soap_error_sync, test_fixture_teardown); + g_test_add ("/service-proxy/sync/authentication-no-credentials", + ProxyTestFixture, + "127.0.0.1", + test_fixture_setup, + test_finish_soap_authentication_no_credentials, + test_fixture_teardown); + + g_test_add ("/service-proxy/sync/authentication-wrong-credentials", + ProxyTestFixture, + "127.0.0.1", + test_fixture_setup, + test_finish_soap_authentication_no_credentials, + test_fixture_teardown); + + g_test_add ("/service-proxy/sync/authentication-valid-credentials", + ProxyTestFixture, + "127.0.0.1", + test_fixture_setup, + test_finish_soap_authentication_valid_credentials, + test_fixture_teardown); + return g_test_run (); } -- cgit v1.2.1