Update to upstream 1.0.1
[profile/ivi/gsignond.git] / src / common / gsignond-plugin-interface.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of gsignond
5  *
6  * Copyright (C) 2012 Intel Corporation.
7  *
8  * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
9  *
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.
14  *
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.
19  *
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
23  * 02110-1301 USA
24  */
25
26
27 #include "gsignond/gsignond-plugin-interface.h"
28 #include "gsignond-plugin-enum-types.h"
29
30 /**
31  * SECTION:gsignond-plugin-interface
32  * @short_description: an interface for implementing GLib-based authentication plugins
33  * @include: gsignond/gsignond-plugin-interface.h
34  *
35  * #GSignondPlugin is an interface for implementing GLib-based authentication plugins.
36  * 
37  * When creating a plugin, write the #GObject boilerplate code as usual, but
38  * 
39  * a) declare the type as follows:
40  * 
41  * |[    G_DEFINE_TYPE_WITH_CODE (GSignondPasswordPlugin, gsignond_password_plugin, 
42  *                         G_TYPE_OBJECT,
43  *                         G_IMPLEMENT_INTERFACE (GSIGNOND_TYPE_PLUGIN,
44  *                                                gsignond_plugin_interface_init));
45  * ]| 
46  * 
47  * b) implement <function>gsignond_plugin_interface_init</function> as follows:
48  * 
49  * |[ static void
50  * gsignond_plugin_interface_init (GSignondPluginInterface *iface)
51  * {
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;
57  * }
58  * ]|
59  * 
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).
63  * 
64  * c) override #GSignondPlugin:type and #GSignondPlugin:mechanisms property 
65  * implementations in the plugin class constructor like this:
66  * 
67  * |[static void
68  * gsignond_password_plugin_class_init (GSignondPasswordPluginClass *klass)
69  * {
70  *     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
71  *     
72  *     gobject_class->set_property = gsignond_password_plugin_set_property;
73  *     gobject_class->get_property = gsignond_password_plugin_get_property;
74  *     
75  *     g_object_class_override_property (gobject_class, PROP_TYPE, "type");
76  *     g_object_class_override_property (gobject_class, PROP_MECHANISMS, 
77  *                                       "mechanisms");
78  * }
79  * ]|
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)
82  * 
83  * <refsect1><title>The plugin API</title></refsect1>
84  * 
85  * Plugins implement authentication sessions which are controlled through the
86  * plugin API. Authentication sessions follow one another so there is only one active
87  * session at a time.
88  * 
89  * The plugin API is a set of methods and signals that should be used in a specific
90  * sequence:
91  * 
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
110  * more than once.
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().
115  * 
116  * <refsect1><title>Example plugins</title></refsect1>
117  * 
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>.
121  * 
122  * For examples of out of tree plugins, you can have a look at SASL or OAuth plugin
123  * implementations:
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>.
126  * 
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>.
129  * 
130  */
131
132
133 /**
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
147  * 
148  * The plugin provides state updates by emitting #GSignondPlugin::status-changed
149  * signal with this enum and a string describing what happened.
150  */
151
152 /**
153  * GSignondPlugin:
154  *
155  * Opaque #GSignondPlugin data structure.
156  */
157 G_DEFINE_INTERFACE (GSignondPlugin, gsignond_plugin, 0)
158
159 /**
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()
167  * 
168  * #GSignondPluginInterface interface containing pointers to methods that all
169  * plugin implementations should provide.
170  */
171
172 /* signals */
173 enum
174 {
175     RESPONSE,
176     RESPONSE_FINAL,
177     STORE,
178     ERROR,
179     USER_ACTION_REQUIRED,
180     REFRESHED,
181     STATUS_CHANGED,
182     LAST_SIGNAL
183 };
184
185 static guint signals[LAST_SIGNAL] = { 0 };
186
187 static void gsignond_plugin_default_init (GSignondPluginInterface *g_class)
188 {
189     /**
190      * GSignondPlugin::response:
191      * @plugin: the plugin which emitted the signal
192      * @session_data: a #GSignondSessionData containing signal parameters
193      * 
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.
196      * 
197      * After issuing this signal the plugin expects a gsignond_plugin_response() call.
198      */
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);
202
203     /**
204      * GSignondPlugin::response-final:
205      * @plugin: the plugin which emitted the signal
206      * @session_data: a #GSignondSessionData containing signal parameters
207      * 
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.
210      * 
211      * After issuing this signal the plugin is idle and is ready for a new
212      * authentication session.
213      */
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);
217
218     /**
219      * GSignondPlugin::store:
220      * @plugin: the plugin which emitted the signal
221      * @data: a #GSignondDictionary containing data to place in persistent storage
222      * 
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.
226      */
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);
230
231     /**
232      * GSignondPlugin::error:
233      * @plugin: the plugin which emitted the signal
234      * @error: the details of the error
235      * 
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>
240      * 
241      */
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,
244         1, G_TYPE_ERROR);
245     
246     /**
247      * GSignondPlugin::user-action-required:
248      * @plugin: the plugin which emitted the signal
249      * @ui_data: parameters for UI interaction
250      * 
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()
253      * should be issued.
254      */
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);
259
260     /**
261      * GSignondPlugin::refreshed:
262      * @plugin: the plugin which emitted the signal
263      * @ui_data: parameters for UI refresh
264      * 
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.
268      */
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);
272
273     /**
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
278      * 
279      * This signal is issued by the plugin when plugin state has changed. This
280      * can be used by applications to report authentication progress.
281      */
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);
286
287     /**
288      * GSignondPlugin:type:
289      * 
290      * This property holds a plugin type, or authentication method it implements
291      * (for example "oauth" or "sasl").
292      */
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));
296
297     /**
298      * GSignondPlugin:mechanisms:
299      * 
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.
303      */
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));
307     
308 }
309
310 /**
311  * gsignond_plugin_cancel:
312  * @self: plugin instance
313  * 
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.
317  */
318 void gsignond_plugin_cancel (GSignondPlugin *self)
319 {
320     g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
321     
322     GSIGNOND_PLUGIN_GET_INTERFACE (self)->cancel (self);
323 }
324
325 /**
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
332  * 
333  * This method starts a new authentication session.
334  */
335 void gsignond_plugin_request_initial (GSignondPlugin *self, 
336                               GSignondSessionData *session_data, 
337                               GSignondDictionary *identity_method_cache,
338                               const gchar *mechanism)
339 {
340     g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
341     
342     GSIGNOND_PLUGIN_GET_INTERFACE (self)->request_initial (self, session_data,
343             identity_method_cache,
344             mechanism);
345 }
346
347 /**
348  * gsignond_plugin_request:
349  * @self: plugin instance 
350  * @session_data: additional parameters for the session
351  * 
352  * This method provides the plugin with additional parameters for the session
353  * after the plugin has asked for it via #GSignondPlugin::response signal.
354  */
355 void gsignond_plugin_request (GSignondPlugin *self, 
356                               GSignondSessionData *session_data)
357 {
358     g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
359     
360     GSIGNOND_PLUGIN_GET_INTERFACE (self)->request (self, session_data);
361 }
362
363 /**
364  * gsignond_plugin_user_action_finished:
365  * @self: plugin instance 
366  * @ui_data: results of UI interaction
367  * 
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.
370  */
371 void gsignond_plugin_user_action_finished (GSignondPlugin *self, 
372                                            GSignondSignonuiData *ui_data)
373 {
374     g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
375     
376     GSIGNOND_PLUGIN_GET_INTERFACE (self)->user_action_finished (self, 
377                                                                 ui_data);
378 }
379
380 /**
381  * gsignond_plugin_refresh:
382  * @self: plugin instance 
383  * @ui_data: UI refresh parameters
384  * 
385  * This method asks the plugin to refresh the UI. The plugin responds with
386  * #GSignondPlugin::refreshed signal.
387  */
388 void gsignond_plugin_refresh (GSignondPlugin *self, 
389                               GSignondSignonuiData *ui_data)
390 {
391     g_return_if_fail (GSIGNOND_IS_PLUGIN (self));
392     
393     GSIGNOND_PLUGIN_GET_INTERFACE (self)->refresh (self, ui_data);
394 }
395
396 /**
397  * gsignond_plugin_response:
398  * @self: plugin instance
399  * @session_data: session data
400  * 
401  * Plugin implementations should use this to issue #GSignondPlugin::response
402  * signal. This method should not be used otherwise.
403  */
404 void gsignond_plugin_response (GSignondPlugin *self, 
405                              GSignondSessionData *session_data)
406 {
407     g_signal_emit (self, signals[RESPONSE], 0, session_data);
408 }
409
410 /**
411  * gsignond_plugin_response_final:
412  * @self: plugin instance
413  * @session_data: session data
414  * 
415  * Plugin implementations should use this to issue #GSignondPlugin::response-final
416  * signal. This method should not be used otherwise.
417  */
418 void gsignond_plugin_response_final (GSignondPlugin *self, 
419                              GSignondSessionData *session_data)
420 {
421     g_signal_emit (self, signals[RESPONSE_FINAL], 0, session_data);
422 }
423
424 /**
425  * gsignond_plugin_store:
426  * @self: plugin instance
427  * @identity_method_cache: data to store
428  * 
429  * Plugin implementations should use this to issue #GSignondPlugin::store
430  * signal. This method should not be used otherwise.
431  */
432 void gsignond_plugin_store (GSignondPlugin *self, 
433                             GSignondDictionary *identity_method_cache)
434 {
435     g_signal_emit (self, signals[STORE], 0, identity_method_cache);
436 }
437
438 /**
439  * gsignond_plugin_error:
440  * @self: plugin instance
441  * @error: the error
442  * 
443  * Plugin implementations should use this to issue #GSignondPlugin::error
444  * signal. This method should not be used otherwise.
445  */
446 void gsignond_plugin_error (GSignondPlugin *self, GError *error)
447 {
448     g_signal_emit (self, signals[ERROR], 0, error);
449 }
450
451 /**
452  * gsignond_plugin_user_action_required:
453  * @self: plugin instance
454  * @ui_data: UI data
455  * 
456  * Plugin implementations should use this to issue #GSignondPlugin::user-action-required
457  * signal. This method should not be used otherwise.
458  */
459 void gsignond_plugin_user_action_required (GSignondPlugin *self, 
460                                            GSignondSignonuiData *ui_data)
461 {
462     g_signal_emit (self, signals[USER_ACTION_REQUIRED], 0, ui_data);
463 }
464
465 /**
466  * gsignond_plugin_refreshed:
467  * @self: plugin instance
468  * @ui_data: UI data
469  * 
470  * Plugin implementations should use this to issue #GSignondPlugin::refreshed
471  * signal. This method should not be used otherwise.
472  */
473 void gsignond_plugin_refreshed (GSignondPlugin *self, 
474                                 GSignondSignonuiData *ui_data)
475 {
476     g_signal_emit (self, signals[REFRESHED], 0, ui_data);
477 }
478
479 /**
480  * gsignond_plugin_status_changed:
481  * @self: plugin instance
482  * @state: the new state
483  * @message: the message
484  * 
485  * Plugin implementations should use this to issue #GSignondPlugin::status-changed
486  * signal. This method should not be used otherwise.
487  */
488 void gsignond_plugin_status_changed (GSignondPlugin *self,
489         GSignondPluginState state, const gchar *message)
490 {
491     g_signal_emit (self, signals[STATUS_CHANGED], 0, state, message);
492 }
493