summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Michael Brummer <jan.brummer@tabos.org>2023-02-14 16:59:22 +0100
committerJan-Michael Brummer <jan.brummer@tabos.org>2023-04-02 12:57:36 +0000
commitbdba10db542cde903d188b97e55a9ab2418bb3fd (patch)
tree2e5278d10bf7c3a7d6a5c4060eb29f932768b21c
parent8111af5f88d6a32e430bf46db9ea67c3929a3143 (diff)
downloadgupnp-bdba10db542cde903d188b97e55a9ab2418bb3fd.tar.gz
Add gupnp_service_proxy_set_credentials ()
Fixes: https://gitlab.gnome.org/GNOME/gupnp/-/issues/85
-rw-r--r--libgupnp/gupnp-service-proxy.c69
-rw-r--r--libgupnp/gupnp-service-proxy.h5
-rw-r--r--tests/test-service-proxy.c188
3 files changed, 262 insertions, 0 deletions
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 ();
}