1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
4 * This file is part of gsignond
6 * Copyright (C) 2012-2014 Intel Corporation.
8 * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 #include "gsignond/gsignond-error.h"
27 #include "gsignond/gsignond-log.h"
28 #include "gsignond/gsignond-config.h"
29 #include "daemon/gsignond-auth-session.h"
30 #include "gsignond-plugin-proxy.h"
31 #include "gsignond-plugin-remote.h"
33 #define GSIGNOND_PLUGIN_PROXY_PRIV(obj) \
34 G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
35 GSIGNOND_TYPE_PLUGIN_PROXY, \
36 GSignondPluginProxyPrivate)
49 struct _GSignondPluginProxyPrivate
53 GSignondPlugin* plugin;
54 GQueue* session_queue;
55 GSignondAuthSession* active_session;
56 gpointer active_process_userdata;
57 gboolean expecting_request;
61 GSignondAuthSession* auth_session;
62 GSignondSessionData* session_data;
63 GSignondDictionary* identity_method_cache;
66 } GSignondProcessData;
68 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
70 G_DEFINE_TYPE (GSignondPluginProxy, gsignond_plugin_proxy, GSIGNOND_TYPE_DISPOSABLE);
72 static GSignondProcessData*
73 gsignond_process_data_new (
74 GSignondAuthSession* auth_session,
75 GSignondSessionData *session_data,
76 GSignondDictionary *identity_method_cache,
77 const gchar* mechanism,
80 GSignondProcessData* data = g_slice_new0 (GSignondProcessData);
81 data->auth_session = g_object_ref (auth_session);
82 data->session_data = gsignond_dictionary_copy (session_data);
83 if (identity_method_cache)
84 data->identity_method_cache = gsignond_dictionary_copy (identity_method_cache);
85 data->mechanism = g_strdup (mechanism);
86 data->userdata = userdata;
91 gsignond_process_data_free (
92 GSignondProcessData* data)
94 g_object_unref (data->auth_session);
95 gsignond_dictionary_unref (data->session_data);
96 if (data->identity_method_cache)
97 gsignond_dictionary_unref (data->identity_method_cache);
98 g_free (data->mechanism);
99 g_slice_free (GSignondProcessData, data);
103 gsignond_plugin_proxy_process_queue (
104 GSignondPluginProxy *self)
106 g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
108 GSignondPluginProxyPrivate *priv = self->priv;
109 GSignondProcessData* next_data = g_queue_pop_head (priv->session_queue);
111 priv->expecting_request = FALSE;
112 priv->active_process_userdata = next_data->userdata;
113 priv->active_session = next_data->auth_session;
114 g_object_ref (priv->active_session);
115 gsignond_auth_session_notify_state_changed (
116 priv->active_session, GSIGNOND_PLUGIN_STATE_STARTED,
117 "The request is being processed.",
118 priv->active_process_userdata);
119 gsignond_plugin_request_initial (priv->plugin,
120 next_data->session_data,
121 next_data->identity_method_cache,
122 next_data->mechanism);
123 gsignond_process_data_free (next_data);
128 gsignond_plugin_proxy_response_final_callback (
129 GSignondPlugin *plugin,
130 GSignondSessionData *result,
133 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
134 GSignondPluginProxyPrivate *priv = self->priv;
137 if (priv->active_session == NULL) {
138 WARN("Error: plugin %s reported 'response_final', but no active session"
139 " in plugin proxy", priv->plugin_type);
142 gsignond_auth_session_notify_process_result (priv->active_session, result,
143 priv->active_process_userdata);
144 g_object_unref (priv->active_session);
145 priv->active_session = NULL;
146 priv->active_process_userdata = NULL;
147 gsignond_plugin_proxy_process_queue (self);
151 gsignond_plugin_proxy_response_callback(
152 GSignondPlugin *plugin,
153 GSignondSessionData *result,
156 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
157 GSignondPluginProxyPrivate *priv = self->priv;
159 if (priv->active_session == NULL) {
160 WARN("Error: plugin %s reported 'response', but no active session "
161 "in plugin proxy", priv->plugin_type);
164 priv->expecting_request = TRUE;
165 gsignond_auth_session_notify_process_result (priv->active_session,
166 result, priv->active_process_userdata);
170 gsignond_plugin_proxy_store_callback (
171 GSignondPlugin *plugin,
172 GSignondSessionData *result,
175 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
176 GSignondPluginProxyPrivate *priv = self->priv;
178 if (priv->active_session == NULL) {
179 WARN("Error: plugin %s reported 'store', but no active session "
180 "in plugin proxy", priv->plugin_type);
183 gsignond_auth_session_notify_store (priv->active_session, result);
187 gsignond_plugin_proxy_refreshed_callback (
188 GSignondPlugin *plugin,
189 GSignondSignonuiData *ui_result,
192 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
193 GSignondPluginProxyPrivate *priv = self->priv;
195 if (priv->active_session == NULL) {
196 WARN("Error: plugin %s reported 'refreshed', but no active session"
197 " in plugin proxy", priv->plugin_type);
200 gsignond_auth_session_notify_refreshed (priv->active_session, ui_result);
204 gsignond_plugin_proxy_user_action_required_callback (
205 GSignondPlugin *plugin,
206 GSignondSignonuiData *ui_request,
209 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
210 GSignondPluginProxyPrivate *priv = self->priv;
212 if (priv->active_session == NULL) {
213 WARN("Error: plugin %s reported 'user_action_required', but no active"
214 " session in plugin proxy", priv->plugin_type);
217 gsignond_auth_session_notify_user_action_required(
218 priv->active_session, ui_request);
222 gsignond_plugin_proxy_error_callback (
223 GSignondPlugin* plugin,
227 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
228 GSignondPluginProxyPrivate *priv = self->priv;
230 if (priv->active_session == NULL) {
231 WARN("Error: plugin %s reported error %s, but no active session "
232 "in plugin proxy", priv->plugin_type, error->message);
235 gsignond_auth_session_notify_process_error (priv->active_session, error,
236 priv->active_process_userdata);
237 g_object_unref (priv->active_session);
238 priv->active_session = NULL;
239 priv->active_process_userdata = NULL;
240 gsignond_plugin_proxy_process_queue (self);
244 gsignond_plugin_proxy_status_changed_callback (
245 GSignondPlugin *plugin,
246 GSignondPluginState state,
247 const gchar *message,
250 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (user_data);
251 GSignondPluginProxyPrivate *priv = self->priv;
253 if (priv->active_session == NULL) {
254 WARN("Error: plugin %s reported change in state %d with message %s, "
255 "but no active session in plugin proxy", priv->plugin_type,
259 gsignond_auth_session_notify_state_changed (priv->active_session,
260 (gint) state, message,
261 priv->active_process_userdata);
265 _on_remote_plugin_dead (gpointer data, GObject *dead_obj)
268 GSignondPluginProxy *proxy = NULL;
269 if (data && (proxy = GSIGNOND_PLUGIN_PROXY(data))) {
270 proxy->priv->plugin = NULL;
271 g_object_unref (G_OBJECT(data));
277 gsignond_plugin_proxy_constructor (
280 GObjectConstructParam *properties)
284 /* Always chain up to the parent constructor */
285 obj = G_OBJECT_CLASS (gsignond_plugin_proxy_parent_class)->
286 constructor (gtype, n_properties, properties);
288 /* update the object state depending on constructor properties */
289 GSignondPluginProxy* self = GSIGNOND_PLUGIN_PROXY (obj);
290 GSignondPluginProxyPrivate *priv = self->priv;
291 priv->plugin = GSIGNOND_PLUGIN (gsignond_plugin_remote_new (priv->loader_path,
294 if (priv->plugin == NULL) {
295 g_free (priv->plugin_type);
296 priv->plugin_type = NULL;
301 g_signal_connect(priv->plugin, "response", G_CALLBACK(
302 gsignond_plugin_proxy_response_callback), self);
303 g_signal_connect(priv->plugin, "response-final", G_CALLBACK(
304 gsignond_plugin_proxy_response_final_callback), self);
305 g_signal_connect(priv->plugin, "user-action-required", G_CALLBACK(
306 gsignond_plugin_proxy_user_action_required_callback), self);
307 g_signal_connect(priv->plugin, "error", G_CALLBACK(
308 gsignond_plugin_proxy_error_callback), self);
309 g_signal_connect(priv->plugin, "store", G_CALLBACK(
310 gsignond_plugin_proxy_store_callback), self);
311 g_signal_connect(priv->plugin, "refreshed", G_CALLBACK(
312 gsignond_plugin_proxy_refreshed_callback), self);
313 g_signal_connect(priv->plugin, "status-changed", G_CALLBACK(
314 gsignond_plugin_proxy_status_changed_callback), self);
316 g_object_get (priv->plugin, "type", &type, NULL);
317 if (g_strcmp0 (type, priv->plugin_type) != 0) {
318 g_free (priv->plugin_type);
319 priv->plugin_type = NULL;
323 g_object_weak_ref (G_OBJECT(priv->plugin), _on_remote_plugin_dead, obj);
330 gsignond_plugin_proxy_set_property (
336 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (object);
337 GSignondPluginProxyPrivate *priv = self->priv;
342 g_free (self->priv->plugin_type);
343 priv->plugin_type = g_value_dup_string (value);
345 case PROP_LOADERPATH:
346 g_assert (self->priv->loader_path == NULL);
347 priv->loader_path = g_value_dup_string (value);
350 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
356 gsignond_plugin_proxy_get_property (
362 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (object);
363 GSignondPluginProxyPrivate *priv = self->priv;
368 g_value_set_string (value, priv->plugin_type);
370 case PROP_MECHANISMS:
371 g_object_get_property (G_OBJECT(priv->plugin),
372 "mechanisms", value);
374 case PROP_LOADERPATH:
375 g_value_set_string (value, priv->loader_path);
378 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
384 gsignond_plugin_proxy_dispose (
387 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (gobject);
388 GSignondPluginProxyPrivate *priv = self->priv;
390 if (priv->active_session) {
391 g_object_unref (priv->active_session);
392 priv->active_session = NULL;
395 g_object_weak_unref (G_OBJECT(priv->plugin), _on_remote_plugin_dead, self);
396 g_object_unref (priv->plugin);
400 /* Chain up to the parent class */
401 G_OBJECT_CLASS (gsignond_plugin_proxy_parent_class)->dispose (gobject);
405 gsignond_plugin_proxy_finalize (GObject *gobject)
407 GSignondPluginProxy *self = GSIGNOND_PLUGIN_PROXY (gobject);
408 GSignondPluginProxyPrivate *priv = self->priv;
410 if (priv->plugin_type) {
411 g_free (priv->plugin_type);
412 priv->plugin_type = NULL;
414 if (priv->loader_path) {
415 g_free (priv->loader_path);
416 priv->loader_path = NULL;
418 if (priv->session_queue)
420 g_queue_free_full (priv->session_queue,
421 (GDestroyNotify) gsignond_process_data_free);
422 priv->session_queue = NULL;
425 /* Chain up to the parent class */
426 G_OBJECT_CLASS (gsignond_plugin_proxy_parent_class)->finalize (gobject);
431 gsignond_plugin_proxy_class_init (
432 GSignondPluginProxyClass *klass)
434 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
436 g_type_class_add_private (gobject_class,
437 sizeof (GSignondPluginProxyPrivate));
439 gobject_class->constructor = gsignond_plugin_proxy_constructor;
440 gobject_class->set_property = gsignond_plugin_proxy_set_property;
441 gobject_class->get_property = gsignond_plugin_proxy_get_property;
442 gobject_class->dispose = gsignond_plugin_proxy_dispose;
443 gobject_class->finalize = gsignond_plugin_proxy_finalize;
445 obj_properties[PROP_TYPE] =
446 g_param_spec_string ("type",
448 "Set the plugin type for the proxy",
449 "" /* default value */,
450 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
451 G_PARAM_STATIC_STRINGS);
453 obj_properties[PROP_MECHANISMS] = g_param_spec_boxed ("mechanisms",
455 "List of plugin mechanisms",
456 G_TYPE_STRV, G_PARAM_READABLE);
458 obj_properties[PROP_LOADERPATH] = g_param_spec_string ("loaderpath",
460 "Path to plugin loader for this plugin",
461 "" /* default value */,
462 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
463 G_PARAM_STATIC_STRINGS);
465 g_object_class_install_properties (gobject_class,
472 gsignond_plugin_proxy_init (
473 GSignondPluginProxy *self)
475 GSignondPluginProxyPrivate *priv = GSIGNOND_PLUGIN_PROXY_PRIV (self);
478 priv->loader_path = NULL;
479 priv->plugin_type = NULL;
481 priv->session_queue = g_queue_new ();
482 priv->active_session = NULL;
483 priv->active_process_userdata = NULL;
484 priv->expecting_request = FALSE;
488 gsignond_plugin_proxy_get_plugin_type (
489 GSignondPluginProxy *self)
491 g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
493 return self->priv->plugin_type;
497 gsignond_plugin_proxy_new (
498 const gchar *loader_path,
499 const gchar *plugin_type,
502 g_return_val_if_fail (loader_path && plugin_type, NULL);
504 GSignondPluginProxy* proxy = g_object_new (GSIGNOND_TYPE_PLUGIN_PROXY,
505 "loaderpath", loader_path,
507 "auto-dispose", FALSE,
510 if (g_strcmp0 (plugin_type,
511 gsignond_plugin_proxy_get_plugin_type (proxy)) == 0)
514 g_object_unref (proxy);
519 gsignond_plugin_proxy_process (
520 GSignondPluginProxy *self,
521 GSignondAuthSession *session,
522 GSignondSessionData *session_data,
523 GSignondDictionary *identity_method_cache,
524 const gchar *mechanism,
527 g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
528 g_assert (GSIGNOND_IS_AUTH_SESSION (session));
530 GSignondPluginProxyPrivate *priv = self->priv;
532 if (session == priv->active_session && priv->expecting_request == TRUE) {
533 priv->expecting_request = FALSE;
534 // mechanism and identity_method_cache are discarded if this is not an initial request
535 gsignond_plugin_request (priv->plugin, session_data);
539 g_queue_push_tail (priv->session_queue,
540 gsignond_process_data_new (session,
542 identity_method_cache,
543 mechanism, userdata));
544 gsignond_auth_session_notify_state_changed (
545 session, GSIGNOND_PLUGIN_STATE_PROCESS_PENDING,
546 "The request has been queued.", userdata);
547 if (priv->active_session == NULL) {
548 gsignond_plugin_proxy_process_queue (self);
553 gsignond_plugin_proxy_compare_process_data (
554 gconstpointer process_data,
555 gconstpointer auth_session)
557 g_return_val_if_fail (process_data && auth_session, 0);
559 if (auth_session == ((GSignondProcessData*) process_data)->auth_session)
565 static GSignondProcessData*
566 gsignond_plugin_proxy_find_by_session_iface (
567 GSignondPluginProxy *self,
568 GSignondAuthSession *session)
570 g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
572 return (GSignondProcessData*) g_queue_find_custom (
573 self->priv->session_queue,
575 gsignond_plugin_proxy_compare_process_data);
579 gsignond_plugin_proxy_cancel (
580 GSignondPluginProxy *self,
581 GSignondAuthSession *session)
583 g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
584 g_assert (GSIGNOND_IS_AUTH_SESSION (session));
586 GSignondPluginProxyPrivate *priv = self->priv;
588 /* cancel active session */
589 if (session == priv->active_session) {
590 gsignond_plugin_cancel (priv->plugin);
591 } else { /* cancel by de-queue */
592 GSignondProcessData* data =
593 gsignond_plugin_proxy_find_by_session_iface (self, session);
595 GError* error = g_error_new (GSIGNOND_ERROR,
596 GSIGNOND_ERROR_WRONG_STATE,
597 "Canceling an unknown session");
598 gsignond_auth_session_notify_process_error (session, error, NULL);
599 g_error_free (error);
602 g_queue_remove (priv->session_queue, data);
603 gsignond_process_data_free (data);
608 gsignond_plugin_proxy_user_action_finished (
609 GSignondPluginProxy *self,
610 GSignondSignonuiData *ui_data)
612 g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
614 if (self->priv->active_session == NULL) {
615 WARN("Error: 'user_action_finished' requested for plugin %s but no "
616 "active session", self->priv->plugin_type);
619 gsignond_plugin_user_action_finished (self->priv->plugin, ui_data);
623 gsignond_plugin_proxy_refresh (
624 GSignondPluginProxy *self,
625 GSignondSignonuiData *ui_data)
627 g_assert (GSIGNOND_IS_PLUGIN_PROXY (self));
629 if (self->priv->active_session == NULL) {
630 WARN("Error: 'refresh' requested for plugin %s but no active session",
631 self->priv->plugin_type);
634 gsignond_plugin_refresh (self->priv->plugin, ui_data);