plugins: add plugin proxy and plugin proxy factory
authorAlexander Kanavin <alexander.kanavin@intel.com>
Fri, 1 Feb 2013 16:34:47 +0000 (18:34 +0200)
committerAlexander Kanavin <alexander.kanavin@intel.com>
Mon, 11 Feb 2013 13:50:49 +0000 (15:50 +0200)
15 files changed:
.gitignore
configure.ac
include/gsignond/gsignond-plugin-interface.h
src/common/gsignond-plugin-loader.c
src/daemon/Makefile.am
src/daemon/plugins/Makefile.am [new file with mode: 0644]
src/daemon/plugins/gsignond-plugin-proxy-factory.c [new file with mode: 0644]
src/daemon/plugins/gsignond-plugin-proxy-factory.h [new file with mode: 0644]
src/daemon/plugins/gsignond-plugin-proxy.c [new file with mode: 0644]
src/daemon/plugins/gsignond-plugin-proxy.h [new file with mode: 0644]
test/Makefile.am
test/passwordplugin/Makefile.am [deleted file]
test/plugins/Makefile.am [new file with mode: 0644]
test/plugins/passwordplugintest.c [moved from test/passwordplugin/passwordplugintest.c with 96% similarity]
test/plugins/pluginproxytest.c [new file with mode: 0644]

index 3563ea2..d2340eb 100644 (file)
@@ -26,6 +26,7 @@ build-aux
 *dbus*gen*
 src/daemon/gsignond
 test/db/dbtest
-test/passwordplugin/passwordplugintest
+test/plugins/passwordplugintest
+test/plugins/pluginproxytest
 stamp-h1
 *service
index f36f115..4639044 100644 (file)
@@ -59,13 +59,14 @@ src/daemon/Makefile
 src/daemon/db/Makefile
 src/daemon/dbus/Makefile
 src/daemon/dbus/services/com.google.code.AccountsSSO.SingleSignOn.service
+src/daemon/plugins/Makefile
 src/extensions/Makefile
 src/extensions/test/Makefile
 src/plugins/Makefile
 src/plugins/password/Makefile
 test/Makefile
 test/db/Makefile
-test/passwordplugin/Makefile
 test/daemon/Makefile
 test/daemon/services/com.google.code.AccountsSSO.SingleSignOn.service
+test/plugins/Makefile
 ])
index ea849ce..8bb6151 100644 (file)
@@ -57,27 +57,38 @@ struct _GSignondPluginInterface {
 GType gsignond_plugin_get_type (void);
 
 /* Methods */
-void gsignond_plugin_cancel (GSignondPlugin *self);
-void gsignond_plugin_abort (GSignondPlugin *self);
-void gsignond_plugin_process (GSignondPlugin *self, 
+void 
+gsignond_plugin_cancel (GSignondPlugin *self);
+void 
+gsignond_plugin_abort (GSignondPlugin *self);
+void 
+gsignond_plugin_process (GSignondPlugin *self, 
                               GSignondSessionData *session_data, 
                               const gchar *mechanism);
-void gsignond_plugin_user_action_finished (GSignondPlugin *self, 
+void 
+gsignond_plugin_user_action_finished (GSignondPlugin *self, 
                                            GSignondSessionData *session_data);
-void gsignond_plugin_refresh (GSignondPlugin *self, 
+void 
+gsignond_plugin_refresh (GSignondPlugin *self, 
                               GSignondSessionData *session_data);
 
 /* Signals */
-void gsignond_plugin_result (GSignondPlugin *self, 
+void 
+gsignond_plugin_result (GSignondPlugin *self, 
                              GSignondSessionData *session_data);
-void gsignond_plugin_store (GSignondPlugin *self, 
+void 
+gsignond_plugin_store (GSignondPlugin *self, 
                             GSignondSessionData *session_data);
-void gsignond_plugin_error (GSignondPlugin *self, GError *error);
-void gsignond_plugin_user_action_required (GSignondPlugin *self, 
+void 
+gsignond_plugin_error (GSignondPlugin *self, GError *error);
+void 
+gsignond_plugin_user_action_required (GSignondPlugin *self, 
                                            GSignondSessionData *session_data);
-void gsignond_plugin_refreshed (GSignondPlugin *self, 
+void 
+gsignond_plugin_refreshed (GSignondPlugin *self, 
                                 GSignondSessionData *session_data);
-void gsignond_plugin_status_changed (GSignondPlugin *self, 
+void 
+gsignond_plugin_status_changed (GSignondPlugin *self, 
                                      const gchar *status, 
                                      const gchar *message);
 
index cd7e2c0..378ad5b 100644 (file)
@@ -52,9 +52,9 @@ gsignond_load_plugin(GSignondConfig* config, gchar* plugin_type)
     gboolean symfound = g_module_symbol (plugin_module,
         plugin_get_type, &p);
     g_free(plugin_get_type);
-    g_module_close (plugin_module);
     if (!symfound) {
         DBG("Symbol couldn't be resolved");
+        g_module_close (plugin_module);
         return NULL;
     }
     
@@ -63,8 +63,9 @@ gsignond_load_plugin(GSignondConfig* config, gchar* plugin_type)
     GSignondPlugin* plugin = g_object_new(plugin_get_type_f(), NULL);
     if (plugin == NULL) {
         DBG("Plugin couldn't be created");
+        g_module_close (plugin_module);
         return NULL;
     }
-    
+    g_module_make_resident (plugin_module);
     return plugin;
 }
index 23673ac..934924b 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS = db dbus
+SUBDIRS = db dbus plugins
 NULL=
 
 bin_PROGRAMS = gsignond
@@ -39,8 +39,6 @@ gsignond_SOURCES = \
     $(NULL)
 
 gsignond_CFLAGS = \
-    -DG_LOG_DOMAIN=\"gsignon-daemon\" \
-    -DGSIGNOND_EXTENSIONS_DIR="\"$(libdir)/signon/extensions\"" \
     -I$(top_srcdir) \
     -I$(top_builddir)/include/ \
     -I$(top_builddir)/src/ \
@@ -51,6 +49,7 @@ gsignond_LDADD = \
     $(top_builddir)/src/common/libgsignond-common.la \
     $(top_builddir)/src/daemon/db/libgsignond-db.la \
     $(top_builddir)/src/daemon/dbus/libgsignond-dbus.la \
+    $(top_builddir)/src/daemon/plugins/libgsignond-plugins.la \
     $(GSIGNOND_LIBS) \
     $(NULL)
 
diff --git a/src/daemon/plugins/Makefile.am b/src/daemon/plugins/Makefile.am
new file mode 100644 (file)
index 0000000..fbf23e7
--- /dev/null
@@ -0,0 +1,17 @@
+lib_LTLIBRARIES = libgsignond-plugins.la
+
+libgsignond_plugins_la_CPPFLAGS = \
+    -I$(top_srcdir) \
+    -I$(top_srcdir)/src \
+    -I$(top_srcdir)/include \
+    $(GSIGNOND_CFLAGS)
+
+libgsignond_plugins_la_LIBADD =    \
+        $(top_srcdir)/src/common/libgsignond-common.la \
+        $(GSIGNOND_LIBS)
+
+libgsignond_plugins_la_SOURCES = \
+   gsignond-plugin-proxy.c \
+   gsignond-plugin-proxy-factory.c
+
+CLEANFILES = 
diff --git a/src/daemon/plugins/gsignond-plugin-proxy-factory.c b/src/daemon/plugins/gsignond-plugin-proxy-factory.c
new file mode 100644 (file)
index 0000000..23efec3
--- /dev/null
@@ -0,0 +1,219 @@
+/* vi: set et sw=4 ts=4 cino=t0,(0: */
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of gsignond
+ *
+ * Copyright (C) 2012 Intel Corporation.
+ *
+ * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include "gsignond-plugin-proxy-factory.h"
+
+G_DEFINE_TYPE (GSignondPluginProxyFactory, gsignond_plugin_proxy_factory, G_TYPE_OBJECT);
+
+
+enum
+{
+    PROP_0,
+    
+    PROP_CONFIG,
+    
+    N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+
+static GObject *
+gsignond_plugin_proxy_factory_constructor (GType                  gtype,
+                                   guint                  n_properties,
+                                   GObjectConstructParam *properties)
+{
+  GObject *obj;
+
+  {
+    /* Always chain up to the parent constructor */
+    obj = G_OBJECT_CLASS (gsignond_plugin_proxy_factory_parent_class)->constructor (
+        gtype, n_properties, properties);
+  }
+  
+  /* update the object state depending on constructor properties */
+  GSignondPluginProxyFactory* self = GSIGNOND_PLUGIN_PROXY_FACTORY(obj);
+
+  return obj;
+}
+
+static void
+gsignond_plugin_proxy_factory_set_property (GObject      *object,
+                                       guint         property_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+    GSignondPluginProxyFactory *self = GSIGNOND_PLUGIN_PROXY_FACTORY (object);
+    switch (property_id)
+    {
+        case PROP_CONFIG:
+            g_assert (self->config == NULL);
+            self->config = g_value_dup_object (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+gsignond_plugin_proxy_factory_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+    GSignondPluginProxyFactory *self = GSIGNOND_PLUGIN_PROXY_FACTORY (object);
+    
+    switch (prop_id)
+    {
+        case PROP_CONFIG:
+            g_value_set_object (value, self->config);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gsignond_plugin_proxy_factory_dispose (GObject *gobject)
+{
+    GSignondPluginProxyFactory *self = GSIGNOND_PLUGIN_PROXY_FACTORY (gobject);
+
+    if (self->config) {
+        g_object_unref (self->config);
+        self->config = NULL;
+    }
+
+  /* Chain up to the parent class */
+  G_OBJECT_CLASS (gsignond_plugin_proxy_factory_parent_class)->dispose (gobject);
+}
+
+static void
+gsignond_plugin_proxy_factory_finalize (GObject *gobject)
+{
+    GSignondPluginProxyFactory *self = GSIGNOND_PLUGIN_PROXY_FACTORY (gobject);
+
+    g_hash_table_destroy (self->plugins);
+
+    /* Chain up to the parent class */
+    G_OBJECT_CLASS (gsignond_plugin_proxy_factory_parent_class)->finalize (gobject);
+}
+
+
+static void
+gsignond_plugin_proxy_factory_class_init (GSignondPluginProxyFactoryClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    
+    gobject_class->constructor = gsignond_plugin_proxy_factory_constructor;
+    gobject_class->set_property = gsignond_plugin_proxy_factory_set_property;
+    gobject_class->get_property = gsignond_plugin_proxy_factory_get_property;
+    gobject_class->dispose = gsignond_plugin_proxy_factory_dispose;
+    gobject_class->finalize = gsignond_plugin_proxy_factory_finalize;
+
+    obj_properties[PROP_CONFIG] = g_param_spec_object ("config",
+                                                   "config",
+                                                   "Configuration object",
+                                                   GSIGNOND_TYPE_CONFIG,
+                                                   G_PARAM_CONSTRUCT_ONLY |
+                                                   G_PARAM_READWRITE |
+                                                   G_PARAM_STATIC_STRINGS);
+    
+
+    g_object_class_install_properties (gobject_class,
+                                       N_PROPERTIES,
+                                       obj_properties);
+}
+
+static void
+gsignond_plugin_proxy_factory_init (GSignondPluginProxyFactory *self)
+{
+    self->plugins = g_hash_table_new_full ((GHashFunc)g_str_hash,
+                            (GEqualFunc)g_str_equal,
+                            (GDestroyNotify)g_free,
+                            (GDestroyNotify)g_object_unref);
+    
+}
+
+GSignondPluginProxyFactory* 
+gsignond_plugin_proxy_factory_new(GSignondConfig *config)
+{
+    GSignondPluginProxyFactory* proxy = g_object_new(
+                                              GSIGNOND_TYPE_PLUGIN_PROXY_FACTORY,
+                                              "config", config,
+                                              NULL);
+    return proxy;
+}
+
+GSignondPluginProxy*
+gsignond_plugin_proxy_factory_get_plugin(GSignondPluginProxyFactory* factory,
+                                         guint32 identity_id,
+                                         const gchar* plugin_type)
+{
+    g_return_val_if_fail (factory && GSIGNOND_IS_PLUGIN_PROXY_FACTORY(factory), NULL);
+    g_return_val_if_fail (plugin_type, NULL);
+  
+    gchar* key = g_strdup_printf("%d %s", identity_id, plugin_type);
+    
+    GSignondPluginProxy* proxy = g_hash_table_lookup(factory->plugins, key);
+    
+    if (proxy != NULL) {
+        g_free(key);
+        return proxy;
+    }
+    proxy = gsignond_plugin_proxy_new(factory->config, plugin_type);
+    if (proxy == NULL) {
+        g_free(key);
+        return NULL;
+    }
+    g_hash_table_insert(factory->plugins, key, proxy);
+    return proxy;
+    
+}
+
+gboolean gsignond_plugin_proxy_factory_add_plugin(
+    GSignondPluginProxyFactory* factory,
+    guint32 identity_id,
+    GSignondPluginProxy* proxy)
+{
+    g_return_val_if_fail (factory && GSIGNOND_IS_PLUGIN_PROXY_FACTORY(factory), FALSE);
+    g_return_val_if_fail (proxy && GSIGNOND_IS_PLUGIN_PROXY(proxy), FALSE);
+    
+    gchar* plugin_type;
+    g_object_get (proxy, "type", &plugin_type, NULL);
+    gchar* key = g_strdup_printf("%d %s", identity_id, plugin_type);
+    g_free(plugin_type);
+
+    if (g_hash_table_contains(factory->plugins, key)) {
+        g_free(key);
+        return FALSE;
+    }
+    g_object_ref(proxy);
+    g_hash_table_insert(factory->plugins, key, proxy);
+
+    return TRUE;
+        
+    
+}
diff --git a/src/daemon/plugins/gsignond-plugin-proxy-factory.h b/src/daemon/plugins/gsignond-plugin-proxy-factory.h
new file mode 100644 (file)
index 0000000..8701215
--- /dev/null
@@ -0,0 +1,73 @@
+/* vi: set et sw=4 ts=4 cino=t0,(0: */
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of gsignond
+ *
+ * Copyright (C) 2012 Intel Corporation.
+ *
+ * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __GSIGNOND_PLUGIN_PROXY_FACTORY_H__
+#define __GSIGNOND_PLUGIN_PROXY_FACTORY_H__
+
+#include <glib-object.h>
+#include "gsignond-plugin-proxy.h"
+#include <gsignond/gsignond-config.h>
+
+#define GSIGNOND_TYPE_PLUGIN_PROXY_FACTORY             (gsignond_plugin_proxy_factory_get_type ())
+#define GSIGNOND_PLUGIN_PROXY_FACTORY(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSIGNOND_TYPE_PLUGIN_PROXY_FACTORY, GSignondPluginProxyFactory))
+#define GSIGNOND_IS_PLUGIN_PROXY_FACTORY(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSIGNOND_TYPE_PLUGIN_PROXY_FACTORY))
+#define GSIGNOND_PLUGIN_PROXY_FACTORY_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GSIGNOND_TYPE_PLUGIN_PROXY_FACTORY, GSignondPluginProxyFactoryClass))
+#define GSIGNOND_IS_PLUGIN_PROXY_FACTORY_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GSIGNOND_TYPE_PLUGIN_PROXY_FACTORY))
+#define GSIGNOND_PLUGIN_PROXY_FACTORY_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GSIGNOND_TYPE_PLUGIN_PROXY_FACTORY, GSignondPluginProxyFactoryClass))
+
+
+typedef struct _GSignondPluginProxyFactory        GSignondPluginProxyFactory;
+typedef struct _GSignondPluginProxyFactoryClass   GSignondPluginProxyFactoryClass;
+
+struct _GSignondPluginProxyFactory
+{
+    GObject parent_instance;
+    
+    /* Private */
+    GSignondConfig *config;
+    GHashTable* plugins;
+};
+
+struct _GSignondPluginProxyFactoryClass
+{
+    GObjectClass parent_class;
+};
+
+GType gsignond_plugin_proxy_factory_get_type (void);
+
+GSignondPluginProxyFactory* 
+gsignond_plugin_proxy_factory_new(GSignondConfig *config);
+
+GSignondPluginProxy*
+gsignond_plugin_proxy_factory_get_plugin(GSignondPluginProxyFactory* factory,
+                                         guint32 identity_id,
+                                         const gchar* plugin_type);
+
+gboolean gsignond_plugin_proxy_factory_add_plugin(
+    GSignondPluginProxyFactory* factory,
+    guint32 identity_id,
+    GSignondPluginProxy* proxy);
+
+#endif /* __GSIGNOND_PLUGIN_PROXY_FACTORY_H__ */
\ No newline at end of file
diff --git a/src/daemon/plugins/gsignond-plugin-proxy.c b/src/daemon/plugins/gsignond-plugin-proxy.c
new file mode 100644 (file)
index 0000000..d4f715f
--- /dev/null
@@ -0,0 +1,462 @@
+/* vi: set et sw=4 ts=4 cino=t0,(0: */
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of gsignond
+ *
+ * Copyright (C) 2012 Intel Corporation.
+ *
+ * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include "gsignond-plugin-proxy.h"
+#include <gsignond/gsignond-plugin-loader.h>
+#include <gsignond/gsignond-error.h>
+#include <gsignond/gsignond-log.h>
+
+typedef struct {
+    GSignondAuthSessionIface* auth_session;
+    GSignondSessionData* session_data;
+    gchar* mechanism;
+} GSignondProcessData;
+
+static GSignondProcessData* 
+gsignond_process_data_new(GSignondAuthSessionIface* auth_session,
+                          GSignondSessionData* session_data,
+                          const gchar* mechanism) 
+{
+    GSignondProcessData* data = g_new0(GSignondProcessData, 1);
+    g_object_ref(auth_session);
+    data->auth_session = auth_session;
+    data->session_data = gsignond_dictionary_copy(session_data);
+    data->mechanism = g_strdup(mechanism);
+    return data;
+}
+
+static void gsignond_process_data_free(GSignondProcessData* data)
+{
+    g_object_unref(data->auth_session);
+    gsignond_dictionary_free(data->session_data);
+    g_free(data->mechanism);
+    g_free(data);
+}
+
+
+G_DEFINE_TYPE (GSignondPluginProxy, gsignond_plugin_proxy, G_TYPE_OBJECT);
+
+
+enum
+{
+    PROP_0,
+    
+    PROP_TYPE,
+    PROP_MECHANISMS,
+    PROP_CONFIG,
+    
+    N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+
+static void gsignond_plugin_proxy_result_callback(GSignondPlugin* plugin, 
+                                                  GSignondSessionData* result,
+                                                  gpointer user_data);
+static void gsignond_plugin_proxy_store_callback(GSignondPlugin* plugin, 
+                                                  GSignondSessionData* result,
+                                                  gpointer user_data);
+static void gsignond_plugin_proxy_refreshed_callback(GSignondPlugin* plugin, 
+                                                  GSignondSessionData* result,
+                                                  gpointer user_data);
+static void gsignond_plugin_proxy_user_action_required_callback(
+                                          GSignondPlugin* plugin, 
+                                          GSignondSessionData* ui_request, 
+                                          gpointer user_data);
+static void gsignond_plugin_proxy_error_callback(GSignondPlugin* plugin, 
+                                                 GError* error,
+                                                 gpointer user_data);
+static void gsignond_plugin_proxy_status_changed_callback(GSignondPlugin* plugin, 
+                                                  const gchar* status,
+                                                  const gchar* message,
+                                                  gpointer user_data);
+
+static GObject *
+gsignond_plugin_proxy_constructor (GType                  gtype,
+                                   guint                  n_properties,
+                                   GObjectConstructParam *properties)
+{
+    GObject *obj;
+
+    {
+        /* Always chain up to the parent constructor */
+        obj = G_OBJECT_CLASS (gsignond_plugin_proxy_parent_class)->constructor (
+            gtype, n_properties, properties);
+    }
+  
+    /* update the object state depending on constructor properties */
+    GSignondPluginProxy* self = GSIGNOND_PLUGIN_PROXY(obj);
+    self->plugin = gsignond_load_plugin(self->config, self->plugin_type);
+
+    if (self->plugin != NULL) {
+        g_signal_connect(self->plugin, "result", G_CALLBACK(
+            gsignond_plugin_proxy_result_callback), self);
+        g_signal_connect(self->plugin, "user-action-required", G_CALLBACK(
+            gsignond_plugin_proxy_user_action_required_callback), self);
+        g_signal_connect(self->plugin, "error", G_CALLBACK(
+            gsignond_plugin_proxy_error_callback), self);
+        g_signal_connect(self->plugin, "store", G_CALLBACK(
+            gsignond_plugin_proxy_store_callback), self);
+        g_signal_connect(self->plugin, "refreshed", G_CALLBACK(
+            gsignond_plugin_proxy_refreshed_callback), self);
+        g_signal_connect(self->plugin, "status-changed", G_CALLBACK(
+            gsignond_plugin_proxy_status_changed_callback), self);
+    }
+    return obj;
+}
+
+static void
+gsignond_plugin_proxy_set_property (GObject      *object,
+                                       guint         property_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+    GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (object);
+    switch (property_id)
+    {
+        case PROP_TYPE:
+            g_free (self->plugin_type);
+            self->plugin_type = g_value_dup_string (value);
+            break;
+        case PROP_CONFIG:
+            g_assert (self->config == NULL);
+            self->config = g_value_dup_object (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+gsignond_plugin_proxy_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+    GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (object);
+    
+    switch (prop_id)
+    {
+        case PROP_TYPE:
+            g_value_set_string (value, self->plugin_type);
+            break;
+        case PROP_MECHANISMS:
+            g_object_get_property(G_OBJECT(self->plugin), "mechanisms", value);
+            break;
+        case PROP_CONFIG:
+            g_value_set_object (value, self->config);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gsignond_plugin_proxy_dispose (GObject *gobject)
+{
+    GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (gobject);
+
+    if (self->plugin) {
+        g_object_unref (self->plugin);
+        self->plugin = NULL;
+    }
+    if (self->config) {
+        g_object_unref (self->config);
+        self->config = NULL;
+    }
+    if (self->active_session) {
+        g_object_unref (self->active_session);
+        self->active_session = NULL;
+    }
+
+  /* Chain up to the parent class */
+  G_OBJECT_CLASS (gsignond_plugin_proxy_parent_class)->dispose (gobject);
+}
+
+static void
+gsignond_plugin_proxy_finalize (GObject *gobject)
+{
+    GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (gobject);
+
+    g_free (self->plugin_type);
+    g_queue_free_full(self->session_queue, (GDestroyNotify)gsignond_process_data_free);
+
+    /* Chain up to the parent class */
+    G_OBJECT_CLASS (gsignond_plugin_proxy_parent_class)->finalize (gobject);
+}
+
+
+static void
+gsignond_plugin_proxy_class_init (GSignondPluginProxyClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    
+    gobject_class->constructor = gsignond_plugin_proxy_constructor;
+    gobject_class->set_property = gsignond_plugin_proxy_set_property;
+    gobject_class->get_property = gsignond_plugin_proxy_get_property;
+    gobject_class->dispose = gsignond_plugin_proxy_dispose;
+    gobject_class->finalize = gsignond_plugin_proxy_finalize;
+
+    obj_properties[PROP_TYPE] =
+    g_param_spec_string ("type",
+                         "Plugin type",
+                         "Set the plugin type for the proxy",
+                         "" /* default value */,
+                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE 
+                        | G_PARAM_STATIC_STRINGS);
+    
+    obj_properties[PROP_MECHANISMS] = g_param_spec_boxed ("mechanisms", 
+                            "Mechanisms", 
+                            "List of plugin mechanisms", 
+                            G_TYPE_STRV, G_PARAM_READABLE);
+
+
+    obj_properties[PROP_CONFIG] = g_param_spec_object ("config",
+                                                   "config",
+                                                   "Configuration object",
+                                                   GSIGNOND_TYPE_CONFIG,
+                                                   G_PARAM_CONSTRUCT_ONLY |
+                                                   G_PARAM_READWRITE |
+                                                   G_PARAM_STATIC_STRINGS);
+    
+
+    g_object_class_install_properties (gobject_class,
+                                       N_PROPERTIES,
+                                       obj_properties);
+}
+
+static void
+gsignond_plugin_proxy_init (GSignondPluginProxy *self)
+{
+    self->session_queue = g_queue_new();
+    self->active_session = NULL;
+    
+}
+
+GSignondPluginProxy* 
+gsignond_plugin_proxy_new(GSignondConfig *config, const gchar* plugin_type)
+{
+    GSignondPluginProxy* proxy = g_object_new(GSIGNOND_TYPE_PLUGIN_PROXY,
+                                              "config", config,
+                                              "type", plugin_type,
+                                              NULL);
+    if (proxy->plugin != NULL) {
+        gchar *type;
+        g_object_get(proxy->plugin, "type", &type);
+        if (g_strcmp0(type, plugin_type) == 0) {
+            g_free(type);
+            return proxy;
+        }
+        g_free(type);
+    }
+    g_object_unref(proxy);
+    return NULL;
+}
+
+static 
+gsignond_plugin_proxy_process_queue(GSignondPluginProxy *self)
+{
+    GSignondProcessData* next_data = g_queue_pop_head(self->session_queue);
+    if (next_data) {
+        self->active_session = next_data->auth_session;
+        g_object_ref(self->active_session);
+        gsignond_plugin_process(self->plugin, next_data->session_data, 
+                                next_data->mechanism);
+        gsignond_process_data_free(next_data);
+    }
+}
+
+void gsignond_plugin_proxy_process (GSignondPluginProxy *self, 
+                              GSignondAuthSessionIface* session,
+                              GSignondSessionData *session_data, 
+                              const gchar *mechanism)
+{
+    g_queue_push_tail(self->session_queue, gsignond_process_data_new(session, 
+                                                                     session_data, 
+                                                                     mechanism));
+    if (self->active_session == NULL) {
+        gsignond_plugin_proxy_process_queue(self);
+    }
+}
+
+static gint
+gsignond_plugin_proxy_compare_process_data (gconstpointer process_data,
+                                            gconstpointer auth_session)
+{
+    if (auth_session == ((GSignondProcessData*)process_data)->auth_session)
+        return 0;
+    else
+        return 1;
+}
+
+static GSignondProcessData*
+gsignond_plugin_proxy_find_by_session_iface(GSignondPluginProxy *self, 
+                                            GSignondAuthSessionIface* session)
+{
+    return (GSignondProcessData*)g_queue_find_custom(self->session_queue, 
+                               session,
+                               gsignond_plugin_proxy_compare_process_data);
+}
+
+void 
+gsignond_plugin_proxy_cancel (GSignondPluginProxy *self, 
+                        GSignondAuthSessionIface* session)
+{
+    if (session == self->active_session) {
+        gsignond_plugin_cancel(self->plugin);
+        g_object_unref(self->active_session);
+        self->active_session = NULL;
+        gsignond_plugin_proxy_process_queue(self);
+    } else {
+        GSignondProcessData* data = gsignond_plugin_proxy_find_by_session_iface(
+            self, session);
+        if (data == NULL) {
+            GError* error = g_error_new(GSIGNOND_ERROR, 
+                                GSIGNOND_ERROR_WRONG_STATE,
+                                "Canceling an unknown session");
+            gsignond_auth_session_iface_notify_process_error(session, error);
+            g_error_free(error);
+            return;
+        }
+        g_queue_remove(self->session_queue, data);
+    }
+}
+
+void gsignond_plugin_proxy_user_action_finished (GSignondPluginProxy *self, 
+                                           GSignondSessionData *session_data)
+{
+    if (self->active_session == NULL) {
+        ERR("Error: 'user_action_finished' requested for plugin %s but no \
+            active session", self->plugin_type);
+        return;
+    }
+    gsignond_plugin_user_action_finished(self->plugin, session_data);
+}
+
+void gsignond_plugin_proxy_refresh (GSignondPluginProxy *self, 
+                              GSignondSessionData *session_data)
+{
+    if (self->active_session == NULL) {
+        ERR("Error: 'refresh' requested for plugin %s but no active session",
+            self->plugin_type);
+        return;
+    }
+    gsignond_plugin_refresh(self->plugin, session_data);
+}
+
+static void gsignond_plugin_proxy_result_callback(GSignondPlugin* plugin, 
+                                                  GSignondSessionData* result,
+                                                  gpointer user_data)
+{
+    GSignondPluginProxy* self = GSIGNOND_PLUGIN_PROXY(user_data);
+    if (self->active_session == NULL) {
+        ERR("Error: plugin %s reported 'result', but no active session \
+            in plugin proxy", self->plugin_type);
+        return;
+    }
+    // This avoids problems if cancel() is called from AuthSession handler
+    GSignondAuthSessionIface* active_session = self->active_session;
+    self->active_session = NULL;
+    gsignond_auth_session_iface_notify_process_result(active_session, result);
+    g_object_unref(active_session);
+    gsignond_plugin_proxy_process_queue(self);
+}
+
+static void gsignond_plugin_proxy_store_callback(GSignondPlugin* plugin, 
+                                                  GSignondSessionData* result,
+                                                  gpointer user_data)
+{    
+    GSignondPluginProxy* self = GSIGNOND_PLUGIN_PROXY(user_data);
+    if (self->active_session == NULL) {
+        ERR("Error: plugin %s reported 'store', but no active session \
+            in plugin proxy", self->plugin_type);
+        return;
+    }
+    gsignond_auth_session_iface_notify_store(self->active_session, result);
+}
+
+static void gsignond_plugin_proxy_refreshed_callback(GSignondPlugin* plugin, 
+                                                  GSignondSessionData* result,
+                                                  gpointer user_data)
+{
+    GSignondPluginProxy* self = GSIGNOND_PLUGIN_PROXY(user_data);
+    if (self->active_session == NULL) {
+        ERR("Error: plugin %s reported 'refreshed', but no active session \
+            in plugin proxy", self->plugin_type);
+        return;
+    }
+    gsignond_auth_session_iface_notify_refreshed(self->active_session, result);
+}
+
+static void gsignond_plugin_proxy_user_action_required_callback(
+                                          GSignondPlugin* plugin, 
+                                          GSignondSessionData* ui_request, 
+                                          gpointer user_data)
+{
+    GSignondPluginProxy* self = GSIGNOND_PLUGIN_PROXY(user_data);
+    if (self->active_session == NULL) {
+        ERR("Error: plugin %s reported 'user_action_required', but no active session \
+            in plugin proxy", self->plugin_type);
+        return;
+    }
+    gsignond_auth_session_iface_notify_user_action_required(
+        self->active_session, ui_request);
+}
+
+static void gsignond_plugin_proxy_error_callback(GSignondPlugin* plugin, 
+                                                 GError* error,
+                                                 gpointer user_data)
+{
+    GSignondPluginProxy* self = GSIGNOND_PLUGIN_PROXY(user_data);
+    if (self->active_session == NULL) {
+        ERR("Error: plugin %s reported error %s, but no active session \
+            in plugin proxy", self->plugin_type, error->message);
+        return;
+    }
+    // This avoids problems if cancel() is called from AuthSession handler
+    GSignondAuthSessionIface* active_session = self->active_session;
+    self->active_session = NULL;
+    gsignond_auth_session_iface_notify_process_error(active_session, error);
+    g_object_unref(active_session);
+    gsignond_plugin_proxy_process_queue(self);
+}
+
+static void gsignond_plugin_proxy_status_changed_callback(GSignondPlugin* plugin, 
+                                                  const gchar* status,
+                                                  const gchar* message,
+                                                  gpointer user_data)
+{
+    GSignondPluginProxy* self = GSIGNOND_PLUGIN_PROXY(user_data);
+    if (self->active_session == NULL) {
+        ERR("Error: plugin %s reported change in status %s with message %s, \
+            but no active session in plugin proxy", self->plugin_type, status,
+            message);
+        return;
+    }
+    gsignond_auth_session_iface_notify_status_changed(self->active_session,
+                                                      status, message);
+}
diff --git a/src/daemon/plugins/gsignond-plugin-proxy.h b/src/daemon/plugins/gsignond-plugin-proxy.h
new file mode 100644 (file)
index 0000000..c01cabb
--- /dev/null
@@ -0,0 +1,81 @@
+/* vi: set et sw=4 ts=4 cino=t0,(0: */
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of gsignond
+ *
+ * Copyright (C) 2012 Intel Corporation.
+ *
+ * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __GSIGNOND_PLUGIN_PROXY_H__
+#define __GSIGNOND_PLUGIN_PROXY_H__
+
+#include <glib-object.h>
+#include <gsignond/gsignond-plugin-interface.h>
+#include <gsignond/gsignond-config.h>
+#include "../gsignond-auth-session-iface.h"
+
+#define GSIGNOND_TYPE_PLUGIN_PROXY             (gsignond_plugin_proxy_get_type ())
+#define GSIGNOND_PLUGIN_PROXY(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSIGNOND_TYPE_PLUGIN_PROXY, GSignondPluginProxy))
+#define GSIGNOND_IS_PLUGIN_PROXY(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSIGNOND_TYPE_PLUGIN_PROXY))
+#define GSIGNOND_PLUGIN_PROXY_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GSIGNOND_TYPE_PLUGIN_PROXY, GSignondPluginProxyClass))
+#define GSIGNOND_IS_PLUGIN_PROXY_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GSIGNOND_TYPE_PLUGIN_PROXY))
+#define GSIGNOND_PLUGIN_PROXY_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GSIGNOND_TYPE_PLUGIN_PROXY, GSignondPluginProxyClass))
+
+
+typedef struct _GSignondPluginProxy        GSignondPluginProxy;
+typedef struct _GSignondPluginProxyClass   GSignondPluginProxyClass;
+
+struct _GSignondPluginProxy
+{
+    GObject parent_instance;
+    
+    /* Private */
+    GSignondConfig *config;
+    gchar* plugin_type;
+    GSignondPlugin* plugin;
+    GQueue* session_queue;
+    GSignondAuthSessionIface* active_session;
+};
+
+struct _GSignondPluginProxyClass
+{
+    GObjectClass parent_class;
+};
+
+GType gsignond_plugin_proxy_get_type (void);
+
+GSignondPluginProxy* 
+gsignond_plugin_proxy_new(GSignondConfig *config, const gchar* plugin_type);
+
+
+void 
+gsignond_plugin_proxy_cancel (GSignondPluginProxy *self, 
+                        GSignondAuthSessionIface* session);
+void gsignond_plugin_proxy_process (GSignondPluginProxy *self, 
+                              GSignondAuthSessionIface* session,
+                              GSignondSessionData *session_data, 
+                              const gchar *mechanism);
+void gsignond_plugin_proxy_user_action_finished (GSignondPluginProxy *self, 
+                                           GSignondSessionData *session_data);
+void gsignond_plugin_proxy_refresh (GSignondPluginProxy *self, 
+                              GSignondSessionData *session_data);
+
+
+#endif /* __GSIGNOND_PLUGIN_PROXY_H__ */
\ No newline at end of file
index 4ee0c4c..6a5232c 100644 (file)
@@ -1 +1 @@
-SUBDIRS = db passwordplugin daemon
+SUBDIRS = db plugins daemon
diff --git a/test/passwordplugin/Makefile.am b/test/passwordplugin/Makefile.am
deleted file mode 100644 (file)
index d91b568..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-TESTS = passwordplugintest
-TESTS_ENVIRONMENT= SSO_PLUGINS_DIR=$(top_builddir)/src/plugins/password/.libs
-
-check_PROGRAMS = passwordplugintest
-passwordplugintest_SOURCES = passwordplugintest.c
-passwordplugintest_CFLAGS = \
-    $(GSIGNOND_CFLAGS) \
-    $(CHECK_CFLAGS) \
-    -I$(top_srcdir)/src/plugins/password \
-    -I$(top_srcdir)/include/ 
-
-passwordplugintest_LDADD = \
-    $(top_builddir)/src/common/libgsignond-common.la \
-    $(top_builddir)/src/plugins/password/libpassword.la \
-    $(GSIGNOND_LIBS) \
-    $(CHECK_LIBS)
diff --git a/test/plugins/Makefile.am b/test/plugins/Makefile.am
new file mode 100644 (file)
index 0000000..e19a31b
--- /dev/null
@@ -0,0 +1,30 @@
+TESTS = passwordplugintest pluginproxytest
+TESTS_ENVIRONMENT= SSO_PLUGINS_DIR=$(top_builddir)/src/plugins/password/.libs
+
+check_PROGRAMS = passwordplugintest pluginproxytest
+passwordplugintest_SOURCES = passwordplugintest.c
+passwordplugintest_CFLAGS = \
+    $(GSIGNOND_CFLAGS) \
+    $(CHECK_CFLAGS) \
+    -I$(top_srcdir)/src/plugins/password \
+    -I$(top_srcdir)/include/ 
+
+passwordplugintest_LDADD = \
+    $(top_builddir)/src/common/libgsignond-common.la \
+    $(top_builddir)/src/plugins/password/libpassword.la \
+    $(GSIGNOND_LIBS) \
+    $(CHECK_LIBS)
+
+pluginproxytest_SOURCES = pluginproxytest.c
+pluginproxytest_CFLAGS = \
+    $(GSIGNOND_CFLAGS) \
+    $(CHECK_CFLAGS) \
+    -I$(top_srcdir)/src/daemon/plugins \
+    -I$(top_srcdir)/include/ 
+
+pluginproxytest_LDADD = \
+    $(top_builddir)/src/common/libgsignond-common.la \
+    $(top_builddir)/src/daemon/plugins/libgsignond-plugins.la \
+    $(GSIGNOND_LIBS) \
+    $(CHECK_LIBS)
+    
\ No newline at end of file
similarity index 96%
rename from test/passwordplugin/passwordplugintest.c
rename to test/plugins/passwordplugintest.c
index e1993cf..5980aa3 100644 (file)
@@ -308,22 +308,6 @@ START_TEST (test_passwordplugin_refresh)
 }
 END_TEST
 
-START_TEST (test_passwordplugin_loader)
-{
-    GSignondConfig* config = gsignond_config_new();
-    fail_if(config == NULL);
-
-    GSignondPlugin* absent_plugin = gsignond_load_plugin(config, "absentplugin");
-    fail_if(absent_plugin != NULL);
-    
-    GSignondPlugin* plugin = gsignond_load_plugin(config, "password");
-    check_plugin(plugin);    
-    
-    g_object_unref(plugin);
-    g_object_unref(config);
-}
-END_TEST
-
 Suite* passwordplugin_suite (void)
 {
     Suite *s = suite_create ("Password plugin");
@@ -335,7 +319,6 @@ Suite* passwordplugin_suite (void)
     tcase_add_test (tc_core, test_passwordplugin_process);
     tcase_add_test (tc_core, test_passwordplugin_user_action_finished);
     tcase_add_test (tc_core, test_passwordplugin_refresh);
-    tcase_add_test (tc_core, test_passwordplugin_loader);
     suite_add_tcase (s, tc_core);
     return s;
 }
diff --git a/test/plugins/pluginproxytest.c b/test/plugins/pluginproxytest.c
new file mode 100644 (file)
index 0000000..f1f49b7
--- /dev/null
@@ -0,0 +1,389 @@
+/* vi: set et sw=4 ts=4 cino=t0,(0: */
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of gsignond
+ *
+ * Copyright (C) 2012 Intel Corporation.
+ *
+ * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <check.h>
+#include <stdlib.h>
+#include <glib-object.h>
+#include "gsignond-plugin-proxy.h"
+#include "gsignond-plugin-proxy-factory.h"
+#include <gsignond/gsignond-plugin-loader.h>
+#include <gsignond/gsignond-error.h>
+
+
+static void check_plugin(GSignondPlugin* plugin)
+{
+    gchar* type;
+    gchar** mechanisms;
+
+    fail_if(plugin == NULL);
+    
+    g_object_get(plugin, "type", &type, "mechanisms", &mechanisms, NULL);
+    
+    fail_unless(g_strcmp0(type, "password") == 0);
+    fail_unless(g_strcmp0(mechanisms[0], "password") == 0);
+    fail_unless(mechanisms[1] == NULL);
+    
+    g_free(type);
+    g_strfreev(mechanisms);
+}
+
+
+
+START_TEST (test_plugin_loader)
+{
+    GSignondConfig* config = gsignond_config_new();
+    fail_if(config == NULL);
+
+    GSignondPlugin* absent_plugin = gsignond_load_plugin(config, "absentplugin");
+    fail_if(absent_plugin != NULL);
+    
+    GSignondPlugin* plugin = gsignond_load_plugin(config, "password");
+    check_plugin(plugin);    
+    
+    g_object_unref(plugin);
+    g_object_unref(config);
+}
+END_TEST
+
+static void check_plugin_proxy(GSignondPluginProxy* proxy)
+{
+    gchar* type;
+    gchar** mechanisms;
+
+    fail_if(proxy == NULL);
+    
+    g_object_get(proxy, "type", &type, "mechanisms", &mechanisms, NULL);
+    
+    fail_unless(g_strcmp0(type, "password") == 0);
+    fail_unless(g_strcmp0(mechanisms[0], "password") == 0);
+    fail_unless(mechanisms[1] == NULL);
+    
+    g_free(type);
+    g_strfreev(mechanisms);
+}
+
+START_TEST (test_pluginproxy_create)
+{
+    GSignondConfig* config = gsignond_config_new();
+    fail_if(config == NULL);
+    
+    GSignondPluginProxy* proxy = gsignond_plugin_proxy_new(config, "password");
+    fail_if (proxy == NULL);
+    check_plugin_proxy(proxy);
+
+    GSignondPluginProxy* proxy2 = gsignond_plugin_proxy_new(config, "absentplugin");
+    fail_if (proxy2 != NULL);
+
+    g_object_unref(proxy);
+    g_object_unref(config);
+}
+END_TEST
+
+gboolean testing_proxy_process = FALSE;
+gboolean testing_proxy_process_cancel = FALSE;
+gboolean testing_proxy_process_queue = FALSE;
+gint proxy_process_queue_results = 0;
+gboolean testing_proxy_process_queue_cancel = FALSE;
+gint proxy_process_queue_cancel_results = 0;
+
+void
+gsignond_auth_session_iface_notify_process_result (
+                                                GSignondAuthSessionIface *iface,
+                                                GSignondSessionData *result)
+{
+    int i;
+    
+    if (testing_proxy_process) {
+        testing_proxy_process = FALSE;
+        fail_if(g_strcmp0(
+            gsignond_session_data_get_username(result), "megauser") != 0);
+        fail_if(g_strcmp0(
+            gsignond_session_data_get_secret(result), "megapassword") != 0);
+    } else if (testing_proxy_process_cancel) {
+        GSignondPluginProxy* proxy = GSIGNOND_PLUGIN_PROXY(iface); //hackish
+        gsignond_plugin_proxy_cancel(proxy, iface);
+    } else if (testing_proxy_process_queue) {
+        proxy_process_queue_results++;
+        if (proxy_process_queue_results == 1) {
+            GSignondPluginProxy* proxy = GSIGNOND_PLUGIN_PROXY(iface); //hackish
+            GSignondSessionData* data = gsignond_dictionary_new();
+            fail_if(data == NULL);
+            gsignond_session_data_set_username(data, "megauser");
+            gsignond_session_data_set_secret(data, "megapassword");
+
+            gsignond_plugin_proxy_process(proxy, iface, data, "password");
+            gsignond_plugin_proxy_process(proxy, iface, data, "password");
+    
+            gsignond_dictionary_free(data);
+        }
+        if (proxy_process_queue_results == 3) {
+            testing_proxy_process_queue = FALSE;
+        }
+    } else if (testing_proxy_process_queue_cancel) {
+        proxy_process_queue_cancel_results++;
+        if (proxy_process_queue_cancel_results == 1) {
+            GSignondPluginProxy* proxy = GSIGNOND_PLUGIN_PROXY(iface); //hackish
+            GSignondSessionData* data = gsignond_dictionary_new();
+            fail_if(data == NULL);
+            gsignond_session_data_set_username(data, "megauser");
+            gsignond_session_data_set_secret(data, "megapassword");
+
+            for (i = 0; i < 9; i++)
+                gsignond_plugin_proxy_process(proxy, iface, data, "password");
+    
+            gsignond_dictionary_free(data);
+        }
+        if (proxy_process_queue_cancel_results == 5) {
+            GSignondPluginProxy* proxy = GSIGNOND_PLUGIN_PROXY(iface); //hackish
+            gsignond_plugin_proxy_cancel(proxy, iface);
+        }
+        if (proxy_process_queue_cancel_results == 10) {
+            testing_proxy_process_queue_cancel = FALSE;
+        }
+    } else 
+        fail_if(TRUE);    
+}
+
+void
+gsignond_auth_session_iface_notify_process_error (
+                                                GSignondAuthSessionIface *iface,
+                                                const GError *error)
+{
+    if (testing_proxy_process_cancel) {
+        fail_if(error->code != GSIGNOND_ERROR_WRONG_STATE);
+        testing_proxy_process_cancel = FALSE;
+    } else if (testing_proxy_process_queue_cancel) {
+        fail_if(error->code != GSIGNOND_ERROR_WRONG_STATE);
+    } else
+        fail_if(TRUE);
+}
+
+void 
+gsignond_auth_session_iface_notify_store (GSignondAuthSessionIface *self, 
+                            GSignondSessionData *session_data)
+{
+    fail_if(TRUE);
+}
+
+void 
+gsignond_auth_session_iface_notify_user_action_required (GSignondAuthSessionIface *self, 
+                                           GSignondSessionData *session_data)
+{
+    fail_if(TRUE);
+}
+
+void 
+gsignond_auth_session_iface_notify_refreshed (GSignondAuthSessionIface *self, 
+                                GSignondSessionData *session_data)
+{
+    fail_if(TRUE);
+}
+
+void 
+gsignond_auth_session_iface_notify_status_changed (GSignondAuthSessionIface *self, 
+                                     const gchar *status, 
+                                     const gchar *message)
+{
+    fail_if(TRUE);
+}
+
+START_TEST (test_pluginproxy_process)
+{
+    GSignondConfig* config = gsignond_config_new();
+    fail_if(config == NULL);
+    
+    GSignondPluginProxy* proxy = gsignond_plugin_proxy_new(config, "password");
+    fail_if (proxy == NULL);
+    
+    GSignondSessionData* data = gsignond_dictionary_new();
+    fail_if(data == NULL);
+    gsignond_session_data_set_username(data, "megauser");
+    gsignond_session_data_set_secret(data, "megapassword");
+    
+    gpointer obj = g_object_new(G_TYPE_OBJECT, NULL);
+
+    testing_proxy_process = TRUE;
+    gsignond_plugin_proxy_process(proxy, obj, data, "password");
+    
+    gsignond_dictionary_free(data);
+    g_object_unref(obj);
+    g_object_unref(proxy);
+    g_object_unref(config);
+}
+END_TEST
+
+START_TEST (test_pluginproxy_process_cancel)
+{
+    GSignondConfig* config = gsignond_config_new();
+    fail_if(config == NULL);
+    
+    GSignondPluginProxy* proxy = gsignond_plugin_proxy_new(config, "password");
+    fail_if (proxy == NULL);
+    
+    GSignondSessionData* data = gsignond_dictionary_new();
+    fail_if(data == NULL);
+    gsignond_session_data_set_username(data, "megauser");
+    gsignond_session_data_set_secret(data, "megapassword");
+
+    testing_proxy_process_cancel = TRUE;
+    gsignond_plugin_proxy_process(proxy, (GSignondAuthSessionIface*)proxy, data, "password");
+    fail_if(testing_proxy_process_cancel);
+    
+    gsignond_dictionary_free(data);
+    g_object_unref(proxy);
+    g_object_unref(config);
+}
+END_TEST
+
+START_TEST (test_pluginproxy_process_queue)
+{
+    GSignondConfig* config = gsignond_config_new();
+    fail_if(config == NULL);
+    
+    GSignondPluginProxy* proxy = gsignond_plugin_proxy_new(config, "password");
+    fail_if (proxy == NULL);
+    
+    GSignondSessionData* data = gsignond_dictionary_new();
+    fail_if(data == NULL);
+    gsignond_session_data_set_username(data, "megauser");
+    gsignond_session_data_set_secret(data, "megapassword");
+
+    testing_proxy_process_queue = TRUE;
+    gsignond_plugin_proxy_process(proxy, (GSignondAuthSessionIface*)proxy, data, "password");
+    fail_if(testing_proxy_process_queue);
+    fail_if(proxy_process_queue_results < 3);
+
+    gsignond_dictionary_free(data);
+    g_object_unref(proxy);
+    g_object_unref(config);
+}
+END_TEST
+
+START_TEST (test_pluginproxy_process_queue_cancel)
+{
+    GSignondConfig* config = gsignond_config_new();
+    fail_if(config == NULL);
+    
+    GSignondPluginProxy* proxy = gsignond_plugin_proxy_new(config, "password");
+    fail_if (proxy == NULL);
+    
+    GSignondSessionData* data = gsignond_dictionary_new();
+    fail_if(data == NULL);
+    gsignond_session_data_set_username(data, "megauser");
+    gsignond_session_data_set_secret(data, "megapassword");
+
+    testing_proxy_process_queue_cancel = TRUE;
+    gsignond_plugin_proxy_process(proxy, (GSignondAuthSessionIface*)proxy, data, "password");
+    fail_if(testing_proxy_process_queue_cancel);
+    fail_if(proxy_process_queue_cancel_results != 10);
+
+    gsignond_dictionary_free(data);
+    g_object_unref(proxy);
+    g_object_unref(config);
+}
+END_TEST
+
+START_TEST (test_pluginproxyfactory_get)
+{
+    GSignondConfig* config = gsignond_config_new();
+    fail_if(config == NULL);
+    
+    GSignondPluginProxyFactory* factory = gsignond_plugin_proxy_factory_new(config);
+    fail_if(factory == NULL);
+    
+    fail_if(gsignond_plugin_proxy_factory_get_plugin(factory, 123, "absentplugin") != NULL);
+
+    GSignondPluginProxy* proxy1 = gsignond_plugin_proxy_factory_get_plugin(
+        factory, 123, "password");
+    GSignondPluginProxy* proxy2 = gsignond_plugin_proxy_factory_get_plugin(
+        factory, 456, "password");
+    GSignondPluginProxy* proxy3 = gsignond_plugin_proxy_factory_get_plugin(
+        factory, 123, "password");
+    fail_if(proxy1 == NULL || proxy2 == NULL || proxy3 == NULL);
+    fail_if(proxy1 != proxy3 || proxy1 == proxy2);
+    check_plugin_proxy(proxy1);
+    check_plugin_proxy(proxy2);
+
+    g_object_unref(factory);
+    g_object_unref(config);
+}
+END_TEST
+
+START_TEST (test_pluginproxyfactory_add)
+{
+    GSignondConfig* config = gsignond_config_new();
+    fail_if(config == NULL);
+    
+    GSignondPluginProxyFactory* factory = gsignond_plugin_proxy_factory_new(config);
+    fail_if(factory == NULL);
+
+    GSignondPluginProxy* proxy = gsignond_plugin_proxy_new(config, "password");
+    fail_if (proxy == NULL);
+    fail_if(gsignond_plugin_proxy_factory_add_plugin(factory, 123, proxy) == FALSE);
+    fail_if(gsignond_plugin_proxy_factory_add_plugin(factory, 123, proxy) == TRUE);
+    fail_if(gsignond_plugin_proxy_factory_get_plugin(factory, 123, "password") != proxy);
+
+    g_object_unref(proxy);
+    g_object_unref(factory);
+    g_object_unref(config);
+}
+END_TEST
+
+
+Suite* pluginproxy_suite (void)
+{
+    Suite *s = suite_create ("Plugin proxy");
+    
+    /* Core test case */
+    TCase *tc_core = tcase_create ("Tests");
+    tcase_add_test (tc_core, test_plugin_loader);
+    tcase_add_test (tc_core, test_pluginproxy_create);
+    tcase_add_test (tc_core, test_pluginproxy_process);
+    tcase_add_test (tc_core, test_pluginproxy_process_cancel);
+    //FIXME: we need an asynchronous or remote testing plugin to really test 
+    // cancellation and queueuing. Password plugin is totally synchronous.
+    tcase_add_test (tc_core, test_pluginproxy_process_queue);
+    tcase_add_test (tc_core, test_pluginproxy_process_queue_cancel);
+    tcase_add_test (tc_core, test_pluginproxyfactory_get);
+    tcase_add_test (tc_core, test_pluginproxyfactory_add);
+    suite_add_tcase (s, tc_core);
+    return s;
+}
+
+int main (void)
+{
+    int number_failed;
+    
+    g_type_init();
+    
+    Suite *s = pluginproxy_suite();
+    SRunner *sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+  
+