~ubuntu-branches/ubuntu/oneiric/libgdata/oneiric-backports

« back to all changes in this revision

Viewing changes to gdata/gdata-authorizer.c

  • Committer: Package Import Robot
  • Author(s): Evan Broder
  • Date: 2011-11-15 21:59:48 UTC
  • mfrom: (4.1.4 experimental)
  • Revision ID: package-import@ubuntu.com-20111115215948-e17s889ocgu5fv4f
Tags: 0.10.1-1~oneiric1
Automated backport upload; no source changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
 
2
/*
 
3
 * GData Client
 
4
 * Copyright (C) Philip Withnall 2011 <philip@tecnocode.co.uk>
 
5
 *
 
6
 * GData Client is free software: you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation, either version 3 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * GData Client is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with GData Client.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
/**
 
21
 * SECTION:gdata-authorizer
 
22
 * @short_description: GData authorization interface
 
23
 * @stability: Unstable
 
24
 * @include: gdata/gdata-authorizer.h
 
25
 *
 
26
 * The #GDataAuthorizer interface provides a uniform way to implement authentication and authorization processes for use by #GDataService<!-- -->s.
 
27
 * Client code will construct a new #GDataAuthorizer instance of their choosing, such as #GDataClientLoginAuthorizer or #GDataOAuth1Authorizer, for
 
28
 * the #GDataService<!-- -->s which will be used by the client, then authenticates and authorizes with the #GDataAuthorizer instead of the
 
29
 * #GDataService. The #GDataService then uses the #GDataAuthorizer to authorize individual network requests using whatever authorization token was
 
30
 * returned to the #GDataAuthorizer by the Google Accounts service.
 
31
 *
 
32
 * All #GDataAuthorizer implementations are expected to operate against a set of #GDataAuthorizationDomain<!-- -->s which are provided to the
 
33
 * authorizer at construction time. These domains specify which data domains the client expects to access using the #GDataService<!-- -->s they
 
34
 * have using the #GDataAuthorizer instance. Following the principle of least privilege, the set of domains should be the minimum such set of domains
 
35
 * which still allows the client to operate normally. Note that implementations of #GDataAuthorizationDomain may display the list of requested
 
36
 * authorization domains to the user for verification before authorization is granted.
 
37
 *
 
38
 * #GDataAuthorizer implementations are provided for some of the standard authorization processes supported by Google for installed applications, as
 
39
 * listed in their <ulink type="http" url="http://code.google.com/apis/accounts/docs/GettingStarted.html">online documentation</ulink>:
 
40
 * <itemizedlist>
 
41
 *  <listitem>#GDataClientLoginAuthorizer for
 
42
 *    <ulink type="http" url="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html">ClientLogin</ulink> (deprecated)</listitem>
 
43
 *  <listitem>#GDataOAuth1Authorizer for
 
44
 *    <ulink type="http" url="http://code.google.com/apis/accounts/docs/OAuthForInstalledApps.html">OAuth 1.0</ulink> (preferred)</listitem>
 
45
 * </itemizedlist>
 
46
 *
 
47
 * It is quite possible for clients to write their own #GDataAuthorizer implementation. For example, if a client uses OAuth 2.0 and handles
 
48
 * authentication itself, it may want to use its own #GDataAuthorizer implementation which simply exposes the client's existing access token to
 
49
 * libgdata and does nothing more.
 
50
 *
 
51
 * It must be noted that all #GDataAuthorizer implementations must be thread safe, as methods such as gdata_authorizer_refresh_authorization() may be
 
52
 * called from any thread (such as the thread performing an asynchronous query operation) at any time.
 
53
 *
 
54
 * Examples of code using #GDataAuthorizer can be found in the documentation for the various implementations of the #GDataAuthorizer interface.
 
55
 *
 
56
 * Since: 0.9.0
 
57
 */
 
58
 
 
59
#include <glib.h>
 
60
 
 
61
#include "gdata-authorizer.h"
 
62
 
 
63
G_DEFINE_INTERFACE (GDataAuthorizer, gdata_authorizer, G_TYPE_OBJECT)
 
64
 
 
65
static void
 
66
gdata_authorizer_default_init (GDataAuthorizerInterface *iface)
 
67
{
 
68
        /* Nothing to see here */
 
69
}
 
70
 
 
71
/**
 
72
 * gdata_authorizer_process_request:
 
73
 * @self: a #GDataAuthorizer
 
74
 * @domain: (allow-none): the #GDataAuthorizationDomain the query falls under, or %NULL
 
75
 * @message: the query to process
 
76
 *
 
77
 * Processes @message, adding all the necessary extra headers and parameters to ensure that it's correctly authenticated and authorized under the
 
78
 * given @domain for the online service. Basically, if a query is not processed by calling this method on it, it will be sent to the online service as
 
79
 * if it's a query from a non-logged-in user. Similarly, if the #GDataAuthorizer isn't authenticated or authorized (for @domain), no changes will
 
80
 * be made to the @message.
 
81
 *
 
82
 * @domain may be %NULL if the request doesn't require authorization.
 
83
 *
 
84
 * This modifies @message in place.
 
85
 *
 
86
 * This method is thread safe.
 
87
 *
 
88
 * Since: 0.9.0
 
89
 */
 
90
void
 
91
gdata_authorizer_process_request (GDataAuthorizer *self, GDataAuthorizationDomain *domain, SoupMessage *message)
 
92
{
 
93
        GDataAuthorizerInterface *iface;
 
94
 
 
95
        g_return_if_fail (GDATA_IS_AUTHORIZER (self));
 
96
        g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
 
97
        g_return_if_fail (SOUP_IS_MESSAGE (message));
 
98
 
 
99
        iface = GDATA_AUTHORIZER_GET_IFACE (self);
 
100
        g_assert (iface->process_request != NULL);
 
101
 
 
102
        iface->process_request (self, domain, message);
 
103
}
 
104
 
 
105
/**
 
106
 * gdata_authorizer_is_authorized_for_domain:
 
107
 * @self: (allow-none): a #GDataAuthorizer, or %NULL
 
108
 * @domain: the #GDataAuthorizationDomain to check against
 
109
 *
 
110
 * Returns whether the #GDataAuthorizer instance believes it's currently authorized to access the given @domain. Note that this will not perform any
 
111
 * network requests, and will just look up the result in the #GDataAuthorizer's local cache of authorizations. This means that the result may be out
 
112
 * of date, as the server may have since invalidated the authorization. If the #GDataAuthorizer class supports timeouts and TTLs on authorizations,
 
113
 * they will not be taken into account; this method effectively returns whether the last successful authorization operation performed on the
 
114
 * #GDataAuthorizer included @domain in the list of requested authorization domains.
 
115
 *
 
116
 * Note that %NULL may be passed as the #GDataAuthorizer, in which case %FALSE will always be returned, regardless of the @domain. This is for
 
117
 * convenience of checking whether a domain is authorized by the #GDataAuthorizer returned by gdata_service_get_authorizer(), which may be %NULL.
 
118
 * For example:
 
119
 * |[
 
120
 * if (gdata_authorizer_is_authorized_for_domain (gdata_service_get_authorizer (my_service), my_domain) == TRUE) {
 
121
 *      /<!-- -->* Code to execute only if we're authorized for the given domain *<!-- -->/
 
122
 * }
 
123
 * ]|
 
124
 *
 
125
 * This method is thread safe.
 
126
 *
 
127
 * Return value: %TRUE if the #GDataAuthorizer has been authorized to access @domain, %FALSE otherwise
 
128
 *
 
129
 * Since: 0.9.0
 
130
 */
 
131
gboolean
 
132
gdata_authorizer_is_authorized_for_domain (GDataAuthorizer *self, GDataAuthorizationDomain *domain)
 
133
{
 
134
        GDataAuthorizerInterface *iface;
 
135
 
 
136
        g_return_val_if_fail (self == NULL || GDATA_IS_AUTHORIZER (self), FALSE);
 
137
        g_return_val_if_fail (GDATA_IS_AUTHORIZATION_DOMAIN (domain), FALSE);
 
138
 
 
139
        if (self == NULL) {
 
140
                return FALSE;
 
141
        }
 
142
 
 
143
        iface = GDATA_AUTHORIZER_GET_IFACE (self);
 
144
        g_assert (iface->is_authorized_for_domain != NULL);
 
145
 
 
146
        return iface->is_authorized_for_domain (self, domain);
 
147
}
 
148
 
 
149
/**
 
150
 * gdata_authorizer_refresh_authorization:
 
151
 * @self: a #GDataAuthorizer
 
152
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
 
153
 * @error: a #GError, or %NULL
 
154
 *
 
155
 * Forces the #GDataAuthorizer to refresh any authorization tokens it holds with the online service. This should typically be called when a
 
156
 * #GDataService query returns %GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED, and is already called transparently by methods such as
 
157
 * gdata_service_query() and gdata_service_insert_entry() (see their documentation for more details).
 
158
 *
 
159
 * If re-authorization is successful, it's guaranteed that by the time this method returns, the properties containing the relevant authorization
 
160
 * tokens on the #GDataAuthorizer instance will have been updated.
 
161
 *
 
162
 * If %FALSE is returned, @error will be set if (and only if) it's due to a refresh being attempted and failing. If a refresh is not attempted, %FALSE
 
163
 * will be returned but @error will not be set.
 
164
 *
 
165
 * If the #GDataAuthorizer has not been previously authenticated or authorized (using the class' specific methods), no authorization will be
 
166
 * attempted, %FALSE will be returned immediately and @error will not be set.
 
167
 *
 
168
 * Some #GDataAuthorizer implementations may not support refreshing authorization tokens at all; for example if doing so requires user interaction.
 
169
 * %FALSE will be returned immediately in that case and @error will not be set.
 
170
 *
 
171
 * This method is thread safe.
 
172
 *
 
173
 * Return value: %TRUE if an authorization refresh was attempted and was successful, %FALSE if a refresh wasn't attempted or was unsuccessful
 
174
 *
 
175
 * Since: 0.9.0
 
176
 */
 
177
gboolean
 
178
gdata_authorizer_refresh_authorization (GDataAuthorizer *self, GCancellable *cancellable, GError **error)
 
179
{
 
180
        GDataAuthorizerInterface *iface;
 
181
 
 
182
        g_return_val_if_fail (GDATA_IS_AUTHORIZER (self), FALSE);
 
183
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
 
184
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
185
 
 
186
        iface = GDATA_AUTHORIZER_GET_IFACE (self);
 
187
 
 
188
        /* Return FALSE with no error if the method isn't implemented */
 
189
        if (iface->refresh_authorization == NULL) {
 
190
                return FALSE;
 
191
        }
 
192
 
 
193
        return iface->refresh_authorization (self, cancellable, error);
 
194
}
 
195
 
 
196
static void
 
197
refresh_authorization_thread (GSimpleAsyncResult *result, GDataAuthorizer *authorizer, GCancellable *cancellable)
 
198
{
 
199
        GError *error = NULL;
 
200
 
 
201
        /* Refresh the authorisation and return */
 
202
        gdata_authorizer_refresh_authorization (authorizer, cancellable, &error);
 
203
 
 
204
        if (error != NULL) {
 
205
                g_simple_async_result_set_from_error (result, error);
 
206
                g_error_free (error);
 
207
        }
 
208
}
 
209
 
 
210
/**
 
211
 * gdata_authorizer_refresh_authorization_async:
 
212
 * @self: a #GDataAuthorizer
 
213
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
 
214
 * @callback: (allow-none) (scope async): a #GAsyncReadyCallback to call when the authorization refresh operation is finished, or %NULL
 
215
 * @user_data: (closure): data to pass to the @callback function
 
216
 *
 
217
 * Forces the #GDataAuthorizer to refresh any authorization tokens it holds with the online service. @self and @cancellable are reffed when this
 
218
 * method is called, so can safely be freed after this method returns.
 
219
 *
 
220
 * For more details, see gdata_authorizer_refresh_authorization(), which is the synchronous version of this method. If the #GDataAuthorizer class
 
221
 * doesn't implement #GDataAuthorizerInterface.refresh_authorization_async but does implement #GDataAuthorizerInterface.refresh_authorization, the
 
222
 * latter will be called from a new thread to make it asynchronous.
 
223
 *
 
224
 * When the authorization refresh operation is finished, @callback will be called. You can then call gdata_authorizer_refresh_authorization_finish()
 
225
 * to get the results of the operation.
 
226
 *
 
227
 * This method is thread safe.
 
228
 *
 
229
 * Since: 0.9.0
 
230
 */
 
231
void
 
232
gdata_authorizer_refresh_authorization_async (GDataAuthorizer *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 
233
{
 
234
        GDataAuthorizerInterface *iface;
 
235
 
 
236
        g_return_if_fail (GDATA_IS_AUTHORIZER (self));
 
237
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
238
 
 
239
        iface = GDATA_AUTHORIZER_GET_IFACE (self);
 
240
 
 
241
        /* Either both _async() and _finish() must be defined, or they must both be undefined. */
 
242
        g_assert ((iface->refresh_authorization_async == NULL && iface->refresh_authorization_finish == NULL) ||
 
243
                  (iface->refresh_authorization_async != NULL && iface->refresh_authorization_finish != NULL));
 
244
 
 
245
        if (iface->refresh_authorization_async != NULL) {
 
246
                /* Call the method */
 
247
                iface->refresh_authorization_async (self, cancellable, callback, user_data);
 
248
        } else if (iface->refresh_authorization != NULL) {
 
249
                /* If the _async() method isn't implemented, fall back to running the sync method in a thread */
 
250
                GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
 
251
                                                                        gdata_authorizer_refresh_authorization_async);
 
252
                g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) refresh_authorization_thread, G_PRIORITY_DEFAULT, cancellable);
 
253
                g_object_unref (result);
 
254
 
 
255
                return;
 
256
        } else {
 
257
                /* If neither are implemented, immediately return FALSE with no error in a callback */
 
258
                GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
 
259
                                                                        gdata_authorizer_refresh_authorization_async);
 
260
                g_simple_async_result_complete_in_idle (result);
 
261
                g_object_unref (result);
 
262
 
 
263
                return;
 
264
        }
 
265
}
 
266
 
 
267
/**
 
268
 * gdata_authorizer_refresh_authorization_finish:
 
269
 * @self: a #GDataAuthorizer
 
270
 * @async_result: a #GAsyncResult
 
271
 * @error: a #GError, or %NULL
 
272
 *
 
273
 * Finishes an asynchronous authorization refresh operation for the #GDataAuthorizer, as started with gdata_authorizer_refresh_authorization_async().
 
274
 *
 
275
 * This method is thread safe.
 
276
 *
 
277
 * Return value: %TRUE if an authorization refresh was attempted and was successful, %FALSE if a refresh wasn't attempted or was unsuccessful
 
278
 *
 
279
 * Since: 0.9.0
 
280
 */
 
281
gboolean
 
282
gdata_authorizer_refresh_authorization_finish (GDataAuthorizer *self, GAsyncResult *async_result, GError **error)
 
283
{
 
284
        GDataAuthorizerInterface *iface;
 
285
 
 
286
        g_return_val_if_fail (GDATA_IS_AUTHORIZER (self), FALSE);
 
287
        g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), FALSE);
 
288
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
289
 
 
290
        iface = GDATA_AUTHORIZER_GET_IFACE (self);
 
291
 
 
292
        /* Either both _async() and _finish() must be defined, or they must both be undefined. */
 
293
        g_assert ((iface->refresh_authorization_async == NULL && iface->refresh_authorization_finish == NULL) ||
 
294
                  (iface->refresh_authorization_async != NULL && iface->refresh_authorization_finish != NULL));
 
295
 
 
296
        if (iface->refresh_authorization_finish != NULL) {
 
297
                /* Call the method */
 
298
                return iface->refresh_authorization_finish (self, async_result, error);
 
299
        } else if (iface->refresh_authorization != NULL) {
 
300
                /* If the _async() method isn't implemented, fall back to finishing off running the sync method in a thread */
 
301
                g_warn_if_fail (g_simple_async_result_is_valid (async_result, G_OBJECT (self), gdata_authorizer_refresh_authorization_async) == TRUE);
 
302
 
 
303
                if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (async_result), error) == TRUE) {
 
304
                        return FALSE;
 
305
                }
 
306
 
 
307
                return TRUE;
 
308
        }
 
309
 
 
310
        /* Fall back to just returning FALSE if none of the methods are implemented */
 
311
        return FALSE;
 
312
}