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 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
27 #include "gsignond/gsignond-plugin-interface.h"
28 #include "gsignond-plugin-enum-types.h"
31 * SECTION:gsignond-plugin-interface
32 * @short_description: an interface for implementing GLib-based authentication plugins
33 * @include: gsignond/gsignond-plugin-interface.h
35 * #GSignondPlugin is an interface for implementing GLib-based authentication plugins.
37 * When creating a plugin, write the #GObject boilerplate code as usual, but
39 * a) declare the type as follows:
41 * |[ G_DEFINE_TYPE_WITH_CODE (GSignondPasswordPlugin, gsignond_password_plugin,
43 * G_IMPLEMENT_INTERFACE (GSIGNOND_TYPE_PLUGIN,
44 * gsignond_plugin_interface_init));
47 * b) implement <function>gsignond_plugin_interface_init</function> as follows:
50 * gsignond_plugin_interface_init (GSignondPluginInterface *iface)
52 * iface->cancel = gsignond_password_plugin_cancel;
53 * iface->request_initial = gsignond_password_plugin_request_initial;
54 * iface->request = gsignond_password_plugin_request;
55 * iface->user_action_finished = gsignond_password_plugin_user_action_finished;
56 * iface->refresh = gsignond_password_plugin_refresh;
60 * where the <function>gsignond_password_plugin_cancel</function> etc. are specific implementations of
61 * plugin interface methods that every plugin must provide (see below for when
62 * and how they're used by the daemon).
64 * c) override #GSignondPlugin:type and #GSignondPlugin:mechanisms property
65 * implementations in the plugin class constructor like this:
68 * gsignond_password_plugin_class_init (GSignondPasswordPluginClass *klass)
70 * GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
72 * gobject_class->set_property = gsignond_password_plugin_set_property;
73 * gobject_class->get_property = gsignond_password_plugin_get_property;
75 * g_object_class_override_property (gobject_class, PROP_TYPE, "type");
76 * g_object_class_override_property (gobject_class, PROP_MECHANISMS,
80 * (naturally, plugin's property setter should ignore attempts to set these properties,
81 * and plugin's property getter should provide their values when asked)
83 * <refsect1><title>The plugin API</title></refsect1>
85 * Plugins implement authentication sessions which are controlled through the
86 * plugin API. Authentication sessions follow one another so there is only one active
89 * The plugin API is a set of methods and signals that should be used in a specific
92 * - successful authentication session begins with gsignond_plugin_request_initial() and ends
93 * with the plugin issuing a #GSignondPlugin::response-final signal
94 * - at any point the application can cancel an active session with
95 * gsignond_plugin_cancel()
96 * - at any point the plugin can cancel an active session by issuing #GSignondPlugin::error
97 * signal, which also provides some details about the cancellation reason.
98 * - if a session is active, and the plugin has an intermediate response or needs
99 * additional information, it issues #GSignondPlugin::response signal, which the
100 * application should respond to with gsignond_plugin_request() method. This can
101 * happen more than once.
102 * - if the plugin needs to launch UI interaction with the user, it's issuing
103 * #GSignondPlugin::user-action-required signal, which the application should
104 * follow up with gsignond_plugin_user_action_finished() method. This can happen
105 * more than once as well.
106 * - if, during an active UI session, the application needs a UI refresh
107 * (for example, to fetch a new captcha image), it's
108 * requested from the plugin with gsignond_plugin_refresh() method, followed
109 * by the plugin's response via #GSignondPlugin::refreshed signal. This can happen
111 * - changes in plugin state are reported through #GSignondPlugin::status-changed signal.
112 * - if the plugin needs to store information in persistent storage, it issues
113 * #GSignondPlugin::store signal. Later, that same information is provided as a
114 * parameter to gsignond_plugin_request_initial().
116 * <refsect1><title>Example plugins</title></refsect1>
118 * See example plugin implementation here:
119 * <ulink url="https://code.google.com/p/accounts-sso/source/browse/?repo=gsignond#git%2Fsrc%2Fplugins">
120 * https://code.google.com/p/accounts-sso/source/browse/?repo=gsignond#git%2Fsrc%2Fplugins</ulink>.
122 * For examples of out of tree plugins, you can have a look at SASL or OAuth plugin
124 * <ulink url="http://code.google.com/p/accounts-sso/source/browse?repo=gsignond-plugin-sasl">
125 * http://code.google.com/p/accounts-sso/source/browse?repo=gsignond-plugin-sasl</ulink>.
127 * <ulink url="http://code.google.com/p/accounts-sso/source/browse?repo=gsignond-plugin-oa">
128 * http://code.google.com/p/accounts-sso/source/browse?repo=gsignond-plugin-oa</ulink>.
134 * GSignondPluginState:
135 * @GSIGNOND_PLUGIN_STATE_NONE: State unknown
136 * @GSIGNOND_PLUGIN_STATE_RESOLVING: Resolving remote server host name
137 * @GSIGNOND_PLUGIN_STATE_CONNECTING: Connecting to remote server
138 * @GSIGNOND_PLUGIN_STATE_SENDING_DATA: Sending data to remote server
139 * @GSIGNOND_PLUGIN_STATE_WAITING: Waiting for reply from remote server
140 * @GSIGNOND_PLUGIN_STATE_USER_PENDING: Waiting for response from user
141 * @GSIGNOND_PLUGIN_STATE_REFRESHING: Refreshing ui request
142 * @GSIGNOND_PLUGIN_STATE_PROCESS_PENDING: Request has been queued
143 * @GSIGNOND_PLUGIN_STATE_STARTED: Request has been dequeued
144 * @GSIGNOND_PLUGIN_STATE_CANCELING: Canceling current process
145 * @GSIGNOND_PLUGIN_STATE_DONE: Process is finished
146 * @GSIGNOND_PLUGIN_STATE_HOLDING: Holding long non-expired token
148 * The plugin provides state updates by emitting #GSignondPlugin::status-changed
149 * signal with this enum and a string describing what happened.
155 * Opaque #GSignondPlugin data structure.
157 G_DEFINE_INTERFACE (GSignondPlugin, gsignond_plugin, 0)
160 * GSignondPluginInterface:
161 * @parent: parent interface type.
162 * @cancel: implementation of gsignond_plugin_cancel()
163 * @request_initial: implementation of gsignond_plugin_request_initial()
164 * @request: implementation of gsignond_plugin_request()
165 * @user_action_finished: implementation of gsignond_plugin_user_action_finished()
166 * @refresh: implementation of gsignond_plugin_refresh()
168 * #GSignondPluginInterface interface containing pointers to methods that all
169 * plugin implementations should provide.
179 USER_ACTION_REQUIRED,
185 static guint signals[LAST_SIGNAL] = { 0 };
187 static void gsignond_plugin_default_init (GSignondPluginInterface *g_class)
190 * GSignondPlugin::response:
191 * @plugin: the plugin which emitted the signal
192 * @session_data: a #GSignondSessionData containing signal parameters
194 * This signal is issued by the plugin when it wants to provide an intermediate
195 * response to the application or needs additional information from the application.
197 * After issuing this signal the plugin expects a gsignond_plugin_response() call.
199 signals[RESPONSE] = g_signal_new ("response", G_TYPE_FROM_CLASS (g_class),
200 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
201 1, GSIGNOND_TYPE_SESSION_DATA);
204 * GSignondPlugin::response-final:
205 * @plugin: the plugin which emitted the signal
206 * @session_data: a #GSignondSessionData containing signal parameters
208 * This signal is issued by the plugin when it has completed the authentication
209 * sequence and is used to provide the final response to the application.
211 * After issuing this signal the plugin is idle and is ready for a new
212 * authentication session.
214 signals[RESPONSE_FINAL] = g_signal_new ("response-final", G_TYPE_FROM_CLASS (g_class),
215 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
216 1, GSIGNOND_TYPE_SESSION_DATA);
219 * GSignondPlugin::store:
220 * @plugin: the plugin which emitted the signal
221 * @data: a #GSignondDictionary containing data to place in persistent storage
223 * This signal is issued by the plugin when it has data to store in persistant
224 * storage. The same data would later be provided to plugin via
225 * gsignond_plugin_request_initial @identity_method_cache parameter.
227 signals[STORE] = g_signal_new ("store", G_TYPE_FROM_CLASS (g_class),
228 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
229 1, GSIGNOND_TYPE_DICTIONARY);
232 * GSignondPlugin::error:
233 * @plugin: the plugin which emitted the signal
234 * @error: the details of the error
236 * This signal is issued by the plugin when an error has occured, or the
237 * plugin otherwise has a reason to cancel the authentication session. The
238 * @error should be specified according to
239 * <link linkend="gsignond-Errors">GSignond errors.</link>
242 signals[ERROR] = g_signal_new ("error", G_TYPE_FROM_CLASS (g_class),
243 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
247 * GSignondPlugin::user-action-required:
248 * @plugin: the plugin which emitted the signal
249 * @ui_data: parameters for UI interaction
251 * This signal is issued by the plugin when it needs a UI interaction with
252 * the user to happen. When the interaction is complete, gsignond_plugin_user_action_finished()
255 signals[USER_ACTION_REQUIRED] = g_signal_new ("user-action-required",
256 G_TYPE_FROM_CLASS (g_class),
257 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
258 1, GSIGNOND_TYPE_SIGNONUI_DATA);
261 * GSignondPlugin::refreshed:
262 * @plugin: the plugin which emitted the signal
263 * @ui_data: parameters for UI refresh
265 * This signal is issued by the plugin when the UI interaction is ongoing
266 * and the UI needs to be refreshed. This can be used for example to update
267 * captcha image in the UI.
269 signals[REFRESHED] = g_signal_new ("refreshed", G_TYPE_FROM_CLASS (g_class),
270 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
271 1, GSIGNOND_TYPE_SIGNONUI_DATA);
274 * GSignondPlugin::status-changed:
275 * @plugin: the plugin which emitted the signal
276 * @state: the plugin state
277 * @message: the message that accompanies the state change
279 * This signal is issued by the plugin when plugin state has changed. This
280 * can be used by applications to report authentication progress.
282 signals[STATUS_CHANGED] = g_signal_new ("status-changed",
283 G_TYPE_FROM_CLASS (g_class),
284 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
285 2, GSIGNOND_TYPE_PLUGIN_STATE, G_TYPE_STRING);
288 * GSignondPlugin:type:
290 * This property holds a plugin type, or authentication method it implements
291 * (for example "oauth" or "sasl").
293 g_object_interface_install_property (g_class, g_param_spec_string ("type",
294 "Type", "Plugin type", "none",
295 G_PARAM_READABLE|G_PARAM_STATIC_STRINGS));
298 * GSignondPlugin:mechanisms:
300 * This property holds a list of authentication mechanisms that the plugin
301 * implements, all specified within the authentication method. For example,
302 * OAuth plugin could implement "oauth1" and "oauth2" mechanisms.
304 g_object_interface_install_property (g_class, g_param_spec_boxed (
305 "mechanisms", "Mechanisms", "List of plugin mechanisms",
306 G_TYPE_STRV, G_PARAM_READABLE|G_PARAM_STATIC_STRINGS));
311 * gsignond_plugin_cancel:
312 * @self: plugin instance
314 * This method cancels an ongoing authentication session. The plugin implementations
315 * should issue a #GSignondPlugin::error signal with #GSIGNOND_ERROR_SESSION_CANCELED
316 * error, and prepare for a new authentication session.
318 void gsignond_plugin_cancel (GSignondPlugin *self)
320 g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
322 GSIGNOND_PLUGIN_GET_INTERFACE (self)->cancel (self);
326 * gsignond_plugin_request_initial:
327 * @self: plugin instance
328 * @session_data: parameters for the session
329 * @identity_method_cache: data from persistent storage, saved previously via
330 * #GSignondPlugin::store signal
331 * @mechanism: mechanism to use for the authentication
333 * This method starts a new authentication session.
335 void gsignond_plugin_request_initial (GSignondPlugin *self,
336 GSignondSessionData *session_data,
337 GSignondDictionary *identity_method_cache,
338 const gchar *mechanism)
340 g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
342 GSIGNOND_PLUGIN_GET_INTERFACE (self)->request_initial (self, session_data,
343 identity_method_cache,
348 * gsignond_plugin_request:
349 * @self: plugin instance
350 * @session_data: additional parameters for the session
352 * This method provides the plugin with additional parameters for the session
353 * after the plugin has asked for it via #GSignondPlugin::response signal.
355 void gsignond_plugin_request (GSignondPlugin *self,
356 GSignondSessionData *session_data)
358 g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
360 GSIGNOND_PLUGIN_GET_INTERFACE (self)->request (self, session_data);
364 * gsignond_plugin_user_action_finished:
365 * @self: plugin instance
366 * @ui_data: results of UI interaction
368 * This method provides the plugin with the results of UI interaction
369 * after the plugin has asked for it via #GSignondPlugin::user-action-required signal.
371 void gsignond_plugin_user_action_finished (GSignondPlugin *self,
372 GSignondSignonuiData *ui_data)
374 g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
376 GSIGNOND_PLUGIN_GET_INTERFACE (self)->user_action_finished (self,
381 * gsignond_plugin_refresh:
382 * @self: plugin instance
383 * @ui_data: UI refresh parameters
385 * This method asks the plugin to refresh the UI. The plugin responds with
386 * #GSignondPlugin::refreshed signal.
388 void gsignond_plugin_refresh (GSignondPlugin *self,
389 GSignondSignonuiData *ui_data)
391 g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
393 GSIGNOND_PLUGIN_GET_INTERFACE (self)->refresh (self, ui_data);
397 * gsignond_plugin_response:
398 * @self: plugin instance
399 * @session_data: session data
401 * Plugin implementations should use this to issue #GSignondPlugin::response
402 * signal. This method should not be used otherwise.
404 void gsignond_plugin_response (GSignondPlugin *self,
405 GSignondSessionData *session_data)
407 g_signal_emit (self, signals[RESPONSE], 0, session_data);
411 * gsignond_plugin_response_final:
412 * @self: plugin instance
413 * @session_data: session data
415 * Plugin implementations should use this to issue #GSignondPlugin::response-final
416 * signal. This method should not be used otherwise.
418 void gsignond_plugin_response_final (GSignondPlugin *self,
419 GSignondSessionData *session_data)
421 g_signal_emit (self, signals[RESPONSE_FINAL], 0, session_data);
425 * gsignond_plugin_store:
426 * @self: plugin instance
427 * @identity_method_cache: data to store
429 * Plugin implementations should use this to issue #GSignondPlugin::store
430 * signal. This method should not be used otherwise.
432 void gsignond_plugin_store (GSignondPlugin *self,
433 GSignondDictionary *identity_method_cache)
435 g_signal_emit (self, signals[STORE], 0, identity_method_cache);
439 * gsignond_plugin_error:
440 * @self: plugin instance
443 * Plugin implementations should use this to issue #GSignondPlugin::error
444 * signal. This method should not be used otherwise.
446 void gsignond_plugin_error (GSignondPlugin *self, GError *error)
448 g_signal_emit (self, signals[ERROR], 0, error);
452 * gsignond_plugin_user_action_required:
453 * @self: plugin instance
456 * Plugin implementations should use this to issue #GSignondPlugin::user-action-required
457 * signal. This method should not be used otherwise.
459 void gsignond_plugin_user_action_required (GSignondPlugin *self,
460 GSignondSignonuiData *ui_data)
462 g_signal_emit (self, signals[USER_ACTION_REQUIRED], 0, ui_data);
466 * gsignond_plugin_refreshed:
467 * @self: plugin instance
470 * Plugin implementations should use this to issue #GSignondPlugin::refreshed
471 * signal. This method should not be used otherwise.
473 void gsignond_plugin_refreshed (GSignondPlugin *self,
474 GSignondSignonuiData *ui_data)
476 g_signal_emit (self, signals[REFRESHED], 0, ui_data);
480 * gsignond_plugin_status_changed:
481 * @self: plugin instance
482 * @state: the new state
483 * @message: the message
485 * Plugin implementations should use this to issue #GSignondPlugin::status-changed
486 * signal. This method should not be used otherwise.
488 void gsignond_plugin_status_changed (GSignondPlugin *self,
489 GSignondPluginState state, const gchar *message)
491 g_signal_emit (self, signals[STATUS_CHANGED], 0, state, message);