[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