[Spice-devel] [spice-gtk 4/5] Add SpiceAuthFile class

Christophe Fergeau cfergeau at redhat.com
Tue Jun 4 07:19:13 PDT 2013


This class can parse the configuration file format described at
http://libvirt.org/auth.html to get SPICE authentication information.
This uses 'spice' as the service name for the auth-$SERVICE-$HOSTNAME
groups.
---
 Makefile.am                                        |   2 +-
 configure.ac                                       |   1 +
 gtk/Makefile.am                                    |   2 +
 gtk/map-file                                       |   5 +
 gtk/spice-auth-file.c                              | 192 +++++++++++++++++++++
 gtk/spice-auth-file.h                              |  62 +++++++
 gtk/spice-glib-sym-file                            |   5 +
 po/POTFILES.in                                     |   1 +
 tests/Makefile.am                                  |  26 +++
 tests/test-spice-auth-file-data/libvirt/auth.conf  |   6 +
 .../test-spice-auth-file.conf                      |  25 +++
 tests/test-spice-auth-file.c                       | 144 ++++++++++++++++
 12 files changed, 470 insertions(+), 1 deletion(-)
 create mode 100644 gtk/spice-auth-file.c
 create mode 100644 gtk/spice-auth-file.h
 create mode 100644 tests/Makefile.am
 create mode 100644 tests/test-spice-auth-file-data/libvirt/auth.conf
 create mode 100644 tests/test-spice-auth-file-data/test-spice-auth-file.conf
 create mode 100644 tests/test-spice-auth-file.c

diff --git a/Makefile.am b/Makefile.am
index ffa1649..ab10f5f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
 ACLOCAL_AMFLAGS = -I m4
 NULL =
 
-SUBDIRS = spice-common gtk po doc data
+SUBDIRS = spice-common gtk po doc data tests
 
 if HAVE_INTROSPECTION
 if WITH_VALA
diff --git a/configure.ac b/configure.ac
index 8ab5b6b..4637bd5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -669,6 +669,7 @@ gtk/Makefile
 gtk/controller/Makefile
 doc/Makefile
 doc/reference/Makefile
+tests/Makefile
 vapi/Makefile
 ])
 
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index d31a396..34ea0c2 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -212,6 +212,7 @@ libspice_client_glib_2_0_la_SOURCES =			\
 	glib-compat.h					\
 	spice-audio.c					\
 	spice-audio-priv.h				\
+	spice-auth-file.c				\
 	spice-common.h					\
 	spice-util.c					\
 	spice-util-priv.h				\
@@ -277,6 +278,7 @@ nodist_libspice_client_glib_2_0_la_SOURCES =	\
 libspice_client_glibincludedir = $(includedir)/spice-client-glib-2.0
 libspice_client_glibinclude_HEADERS =	\
 	spice-audio.h			\
+	spice-auth-file.h		\
 	spice-client.h			\
 	spice-types.h			\
 	spice-session.h			\
diff --git a/gtk/map-file b/gtk/map-file
index 03648a8..3d470f4 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -3,6 +3,11 @@ global:
 spice_audio_get;
 spice_audio_get_type;
 spice_audio_new;
+spice_auth_file_get_credential;
+spice_auth_file_get_type;
+spice_auth_file_lookup_credential;
+spice_auth_file_new;
+spice_auth_file_new_from_file;
 spice_channel_connect;
 spice_channel_destroy;
 spice_channel_disconnect;
diff --git a/gtk/spice-auth-file.c b/gtk/spice-auth-file.c
new file mode 100644
index 0000000..bee3467
--- /dev/null
+++ b/gtk/spice-auth-file.c
@@ -0,0 +1,192 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2013 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include "spice-auth-file.h"
+
+struct _SpiceAuthFilePrivate {
+    GKeyFile* keyfile;
+};
+
+G_DEFINE_TYPE(SpiceAuthFile, spice_auth_file, G_TYPE_OBJECT);
+
+#define SPICE_AUTH_FILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), SPICE_TYPE_AUTH_FILE, SpiceAuthFilePrivate))
+
+/* Lookup auth.conf in this order:
+ *  - The file path specified by the $LIBVIRT_AUTH_FILE environment variable.
+ *  - The file path specified by the "authfile=/some/file" URI query
+ *    parameter
+ *  - The file $XDG_CONFIG_HOME/libvirt/auth.conf
+ *  - The file /etc/libvirt/auth.conf
+ */
+static SpiceAuthFile*
+spice_auth_file_new_with_user_file(const char *user_file, GError** error)
+{
+    SpiceAuthFile *auth_file;
+    char *location;
+
+    g_return_val_if_fail(error == NULL || *error == NULL, NULL);
+
+    location = (char *)g_getenv("LIBVIRT_AUTH_FILE");
+    if (location != NULL) {
+        auth_file = spice_auth_file_new_from_file(location, NULL);
+        if (auth_file != NULL) {
+            return auth_file;
+        }
+    }
+
+    if (user_file != NULL) {
+        auth_file = spice_auth_file_new_from_file(user_file, NULL);
+        if (auth_file != NULL) {
+            return auth_file;
+        }
+    }
+
+    location = g_build_filename(g_get_user_config_dir(),
+                                "libvirt", "auth.conf",
+                                NULL);
+    auth_file =  spice_auth_file_new_from_file(location, NULL);
+    g_free(location);
+    if (auth_file != NULL) {
+        return auth_file;
+    }
+
+    auth_file =  spice_auth_file_new_from_file("/etc/libvirt/auth.conf", NULL);
+    if (auth_file != NULL) {
+        return auth_file;
+    }
+
+    g_set_error_literal(error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
+                        _("Could not locate auth.conf file"));
+
+    return NULL;
+}
+
+SpiceAuthFile*
+spice_auth_file_new(GError** error)
+{
+    return spice_auth_file_new_with_user_file(NULL, error);
+}
+
+
+SpiceAuthFile*
+spice_auth_file_new_from_file(const gchar* location, GError** error)
+{
+    GError* inner_error = NULL;
+
+    g_return_val_if_fail (location != NULL, NULL);
+    g_return_val_if_fail(error == NULL || *error == NULL, NULL);
+
+    SpiceAuthFile* self = SPICE_AUTH_FILE(g_object_new(SPICE_TYPE_AUTH_FILE, NULL));
+    GKeyFile* keyfile = self->priv->keyfile;
+
+    g_key_file_load_from_file(keyfile, location, G_KEY_FILE_NONE, &inner_error);
+    if (inner_error != NULL) {
+        g_propagate_error(error, inner_error);
+        g_object_unref(self);
+        return NULL;
+    }
+
+    return self;
+}
+
+
+char *spice_auth_file_get_credential(SpiceAuthFile *self,
+                                     const char *hostname,
+                                     const char *credential_name,
+                                     GError **error)
+{
+    char *auth_group;
+    char *creds;
+    char *creds_group;
+    char *credential_value;
+
+    g_return_val_if_fail(SPICE_IS_AUTH_FILE(self), NULL);
+    g_return_val_if_fail(hostname != NULL, NULL);
+    g_return_val_if_fail(credential_name != NULL, NULL);
+    g_return_val_if_fail(error == NULL || *error == NULL, NULL);
+
+    auth_group = g_strdup_printf("auth-spice-%s", hostname);
+    creds = g_key_file_get_string(self->priv->keyfile,
+                                  auth_group, "credentials",
+                                  error);
+    if (creds == NULL)
+        return NULL;
+    g_free(auth_group);
+
+    creds_group = g_strdup_printf("credentials-%s", creds);
+    g_free(creds);
+
+    credential_value = g_key_file_get_string(self->priv->keyfile,
+                                             creds_group, credential_name,
+                                             error);
+    g_free(creds_group);
+
+    return credential_value;
+}
+
+
+char *spice_auth_file_lookup_credential(const char *user_auth_file,
+                                        const char *hostname,
+                                        const char *credential_name,
+                                        GError **error)
+{
+    SpiceAuthFile *auth_file;
+    char *credential_value;
+
+    auth_file = spice_auth_file_new_with_user_file(user_auth_file, error);
+    if (auth_file == NULL) {
+        return NULL;
+    }
+
+    credential_value = spice_auth_file_get_credential(auth_file, hostname,
+                                                      credential_name, error);
+    g_object_unref(auth_file);
+
+    return credential_value;
+}
+
+
+static void
+spice_auth_file_finalize(GObject* object)
+{
+    SpiceAuthFile *self = SPICE_AUTH_FILE(object);
+
+    g_clear_pointer(&self->priv->keyfile, g_key_file_free);
+
+    G_OBJECT_CLASS(spice_auth_file_parent_class)->finalize(object);
+}
+
+
+static void
+spice_auth_file_init(SpiceAuthFile* self)
+{
+    self->priv = SPICE_AUTH_FILE_GET_PRIVATE(self);
+
+    self->priv->keyfile = g_key_file_new();
+}
+
+static void
+spice_auth_file_class_init(SpiceAuthFileClass* klass)
+{
+    spice_auth_file_parent_class = g_type_class_peek_parent(klass);
+    g_type_class_add_private(klass, sizeof(SpiceAuthFilePrivate));
+
+    G_OBJECT_CLASS(klass)->finalize = spice_auth_file_finalize;
+}
diff --git a/gtk/spice-auth-file.h b/gtk/spice-auth-file.h
new file mode 100644
index 0000000..f0aa237
--- /dev/null
+++ b/gtk/spice-auth-file.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2013 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __SPICE_AUTH_FILE_H__
+#define __SPICE_AUTH_FILE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define SPICE_TYPE_AUTH_FILE            (spice_auth_file_get_type ())
+#define SPICE_AUTH_FILE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_AUTH_FILE, SpiceAuthFile))
+#define SPICE_AUTH_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_AUTH_FILE, SpiceAuthFileClass))
+#define SPICE_IS_AUTH_FILE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_AUTH_FILE))
+#define SPICE_IS_AUTH_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_AUTH_FILE))
+#define SPICE_AUTH_FILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_AUTH_FILE, SpiceAuthFileClass))
+
+typedef struct _SpiceAuthFile SpiceAuthFile;
+typedef struct _SpiceAuthFileClass SpiceAuthFileClass;
+typedef struct _SpiceAuthFilePrivate SpiceAuthFilePrivate;
+
+struct _SpiceAuthFile
+{
+    GObject parent;
+    SpiceAuthFilePrivate *priv;
+};
+
+struct _SpiceAuthFileClass
+{
+    GObjectClass parent_class;
+};
+
+GType spice_auth_file_get_type(void);
+
+SpiceAuthFile* spice_auth_file_new(GError** error);
+SpiceAuthFile* spice_auth_file_new_from_file(const gchar* path, GError** error);
+char *spice_auth_file_get_credential(SpiceAuthFile *self, const char *hostname,
+                                     const char *credential_name,
+                                     GError **error);
+char *spice_auth_file_lookup_credential(const char *user_auth_file,
+                                        const char *hostname,
+                                        const char *credential_name,
+                                        GError **error);
+
+G_END_DECLS
+
+#endif /* __SPICE_AUTH_FILE_H__ */
diff --git a/gtk/spice-glib-sym-file b/gtk/spice-glib-sym-file
index fc18388..93227f6 100644
--- a/gtk/spice-glib-sym-file
+++ b/gtk/spice-glib-sym-file
@@ -1,6 +1,11 @@
 spice_audio_get
 spice_audio_get_type
 spice_audio_new
+spice_auth_file_get_credential
+spice_auth_file_get_type
+spice_auth_file_lookup_credential
+spice_auth_file_new
+spice_auth_file_new_from_file
 spice_channel_connect
 spice_channel_destroy
 spice_channel_disconnect
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8809121..7f746d4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,6 +2,7 @@ data/spice-mime.xml.in
 data/spicy.desktop.in.in
 gtk/channel-usbredir.c
 gtk/desktop-integration.c
+gtk/spice-auth-file.c
 gtk/spice-cmdline.c
 gtk/spice-option.c
 gtk/spicy-screenshot.c
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..f0c77a5
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,26 @@
+noinst_PROGRAMS = test-auth-file
+
+NULL =
+
+AM_CPPFLAGS =					\
+	-I$(top_srcdir)/gtk			\
+	$(GIO_CFLAGS)				\
+	$(COMMON_CFLAGS)			\
+	$(NULL)
+
+AM_LDFLAGS =					\
+	$(GIO_LIBS)				\
+	$(NULL)
+
+test_auth_file_CPPFLAGS =				\
+	$(AM_CPPFLAGS)					\
+	-DTEST_SPICE_AUTH_FILE_PATH="\"$(abs_srcdir)\""	\
+	$(NULL)
+test_auth_file_SOURCES = test-spice-auth-file.c
+test_auth_file_LDADD = $(top_builddir)/gtk/libspice-client-glib-2.0.la
+
+EXTRA_DIST =					\
+	test-spice-auth-file.conf		\
+	$(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/tests/test-spice-auth-file-data/libvirt/auth.conf b/tests/test-spice-auth-file-data/libvirt/auth.conf
new file mode 100644
index 0000000..f5a53cf
--- /dev/null
+++ b/tests/test-spice-auth-file-data/libvirt/auth.conf
@@ -0,0 +1,6 @@
+[credentials-test-spice3]
+authname=alice
+password=789
+
+[auth-spice-test3.example.com]
+credentials=test-spice3
diff --git a/tests/test-spice-auth-file-data/test-spice-auth-file.conf b/tests/test-spice-auth-file-data/test-spice-auth-file.conf
new file mode 100644
index 0000000..b18212b
--- /dev/null
+++ b/tests/test-spice-auth-file-data/test-spice-auth-file.conf
@@ -0,0 +1,25 @@
+[credentials-test]
+authname=fred
+password=123456
+
+[credentials-test-spice]
+authname=bob
+password=654321
+
+[credentials-test-spice2]
+authname=bill
+
+[auth-libvirt-test1.example.com]
+credentials=test
+
+[auth-spice-test1.example.com]
+credentials=test-non-existing
+
+[auth-spice-test1.example.com:5900]
+credentials=test-spice
+
+[auth-spice-test1.example.com:5901]
+credentials=test-non-existing
+
+[auth-spice-test2.example.com:5900]
+credentials=test-spice2
diff --git a/tests/test-spice-auth-file.c b/tests/test-spice-auth-file.c
new file mode 100644
index 0000000..50298d0
--- /dev/null
+++ b/tests/test-spice-auth-file.c
@@ -0,0 +1,144 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2013 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include "spice-auth-file.h"
+#include "spice-session.h"
+
+static void test_auth_file(void)
+{
+    GError *error = NULL;
+    SpiceAuthFile *file;
+    const char *bad_file = "/non/existing/file";
+    char *good_file = g_build_filename(TEST_SPICE_AUTH_FILE_PATH,
+                                       "test-spice-auth-file-data",
+                                       "test-spice-auth-file.conf",
+                                       NULL);
+    char *test_config_dir = g_build_filename(TEST_SPICE_AUTH_FILE_PATH,
+                                             "test-spice-auth-file-data",
+                                             NULL);
+    char *username;
+    char *password;
+
+    /* Set it before calling any g_get_user_XXX functions
+     * as glib will only read the env variable the first time
+     * it needs it
+     */
+    g_setenv("XDG_CONFIG_HOME", test_config_dir, TRUE);
+
+    file = spice_auth_file_new_from_file(bad_file, &error);
+    g_assert(file == NULL);
+    g_assert_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
+    g_clear_error(&error);
+
+    file = spice_auth_file_new_from_file(good_file, &error);
+    g_assert(file != NULL);
+    g_assert_no_error(error);
+    g_object_unref(file);
+
+    g_setenv("LIBVIRT_AUTH_FILE", good_file, TRUE);
+    file = spice_auth_file_new(&error);
+    g_assert(file != NULL);
+    g_assert_no_error(error);
+    g_unsetenv("LIBVIRT_AUTH_FILE");
+
+    username = spice_auth_file_get_credential(file, "test1.example.com:5900",
+                                              "authname", &error);
+    g_assert_no_error(error);
+    g_assert_cmpstr(username, ==, "bob");
+    g_free(username);
+
+    password  = spice_auth_file_get_credential(file, "test1.example.com:5900",
+                                               "password", &error);
+    g_assert_no_error(error);
+    g_assert_cmpstr(password, ==, "654321");
+    g_free(password);
+
+    g_object_unref(file);
+
+    g_setenv("LIBVIRT_AUTH_FILE", good_file, TRUE);
+    password = spice_auth_file_lookup_credential(NULL, "test1.example.com:5900",
+                                                 "password", &error);
+    g_assert_no_error(error);
+    g_assert_cmpstr(password, ==, "654321");
+    g_free(password);
+    g_unsetenv("LIBVIRT_AUTH_FILE");
+
+    password = spice_auth_file_lookup_credential(good_file, "test1.example.com:5900",
+                                                 "password", &error);
+    g_assert_no_error(error);
+    g_assert_cmpstr(password, ==, "654321");
+    g_free(password);
+
+    g_setenv("LIBVIRT_AUTH_FILE", bad_file, TRUE);
+    password = spice_auth_file_lookup_credential(NULL, "test1.example.com:5900",
+                                                 "password", &error);
+    g_assert_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND);
+    g_clear_error(&error);
+    g_unsetenv("LIBVIRT_AUTH_FILE");
+
+    g_setenv("LIBVIRT_AUTH_FILE", bad_file, TRUE);
+    password = spice_auth_file_lookup_credential(good_file, "test1.example.com:5900",
+                                                 "password", &error);
+    g_assert_no_error(error);
+    g_assert_cmpstr(password, ==, "654321");
+    g_free(password);
+    g_unsetenv("LIBVIRT_AUTH_FILE");
+
+    g_setenv("LIBVIRT_AUTH_FILE", good_file, TRUE);
+    password = spice_auth_file_lookup_credential(NULL, "test3.example.com",
+                                                 "password", &error);
+    g_assert_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND);
+    g_clear_error(&error);
+    g_unsetenv("LIBVIRT_AUTH_FILE");
+    password = spice_auth_file_lookup_credential(good_file, "test3.example.com",
+                                                 "password", &error);
+    g_assert_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND);
+    g_clear_error(&error);
+    password = spice_auth_file_lookup_credential(NULL, "test3.example.com",
+                                                 "password", &error);
+    g_assert_no_error(error);
+    g_assert_cmpstr(password, ==, "789");
+    g_free(password);
+    g_unsetenv("XDG_CONFIG_HOME");
+
+    g_free(good_file);
+}
+
+#define TEST_AUTH_FILE "/tmp/spice-auth.conf"
+static void test_spice_uri_auth(void)
+{
+    SpiceSession *session;
+    char *auth_file;
+    const char *spice_uri = "spice://example.com:5900?authfile="TEST_AUTH_FILE;
+
+    session = spice_session_new();
+    g_object_set(G_OBJECT(session), "uri", spice_uri, NULL);
+    g_object_get(G_OBJECT(session), "auth-file", &auth_file, NULL);
+    g_assert_cmpstr(auth_file, ==, TEST_AUTH_FILE);
+    g_free(auth_file);
+    g_object_unref(session);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init (&argc, &argv, NULL);
+
+    g_test_add_func ("/glib/auth-file", test_auth_file);
+    g_test_add_func ("/glib/auth-file-uri", test_spice_uri_auth);
+
+    return g_test_run();
+}
-- 
1.8.2.1



More information about the Spice-devel mailing list