1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4
* Copyright (C) Philip Withnall 2011 <philip@tecnocode.co.uk>
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.
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.
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/>.
21
* SECTION:gdata-authorizer
22
* @short_description: GData authorization interface
23
* @stability: Unstable
24
* @include: gdata/gdata-authorizer.h
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.
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.
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>:
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>
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.
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.
54
* Examples of code using #GDataAuthorizer can be found in the documentation for the various implementations of the #GDataAuthorizer interface.
61
#include "gdata-authorizer.h"
63
G_DEFINE_INTERFACE (GDataAuthorizer, gdata_authorizer, G_TYPE_OBJECT)
66
gdata_authorizer_default_init (GDataAuthorizerInterface *iface)
68
/* Nothing to see here */
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
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.
82
* @domain may be %NULL if the request doesn't require authorization.
84
* This modifies @message in place.
86
* This method is thread safe.
91
gdata_authorizer_process_request (GDataAuthorizer *self, GDataAuthorizationDomain *domain, SoupMessage *message)
93
GDataAuthorizerInterface *iface;
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));
99
iface = GDATA_AUTHORIZER_GET_IFACE (self);
100
g_assert (iface->process_request != NULL);
102
iface->process_request (self, domain, message);
106
* gdata_authorizer_is_authorized_for_domain:
107
* @self: (allow-none): a #GDataAuthorizer, or %NULL
108
* @domain: the #GDataAuthorizationDomain to check against
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.
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.
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 *<!-- -->/
125
* This method is thread safe.
127
* Return value: %TRUE if the #GDataAuthorizer has been authorized to access @domain, %FALSE otherwise
132
gdata_authorizer_is_authorized_for_domain (GDataAuthorizer *self, GDataAuthorizationDomain *domain)
134
GDataAuthorizerInterface *iface;
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);
143
iface = GDATA_AUTHORIZER_GET_IFACE (self);
144
g_assert (iface->is_authorized_for_domain != NULL);
146
return iface->is_authorized_for_domain (self, domain);
150
* gdata_authorizer_refresh_authorization:
151
* @self: a #GDataAuthorizer
152
* @cancellable: (allow-none): optional #GCancellable object, or %NULL
153
* @error: a #GError, or %NULL
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).
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.
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.
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.
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.
171
* This method is thread safe.
173
* Return value: %TRUE if an authorization refresh was attempted and was successful, %FALSE if a refresh wasn't attempted or was unsuccessful
178
gdata_authorizer_refresh_authorization (GDataAuthorizer *self, GCancellable *cancellable, GError **error)
180
GDataAuthorizerInterface *iface;
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);
186
iface = GDATA_AUTHORIZER_GET_IFACE (self);
188
/* Return FALSE with no error if the method isn't implemented */
189
if (iface->refresh_authorization == NULL) {
193
return iface->refresh_authorization (self, cancellable, error);
197
refresh_authorization_thread (GSimpleAsyncResult *result, GDataAuthorizer *authorizer, GCancellable *cancellable)
199
GError *error = NULL;
201
/* Refresh the authorisation and return */
202
gdata_authorizer_refresh_authorization (authorizer, cancellable, &error);
205
g_simple_async_result_set_from_error (result, error);
206
g_error_free (error);
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
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.
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.
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.
227
* This method is thread safe.
232
gdata_authorizer_refresh_authorization_async (GDataAuthorizer *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
234
GDataAuthorizerInterface *iface;
236
g_return_if_fail (GDATA_IS_AUTHORIZER (self));
237
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
239
iface = GDATA_AUTHORIZER_GET_IFACE (self);
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));
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);
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);
268
* gdata_authorizer_refresh_authorization_finish:
269
* @self: a #GDataAuthorizer
270
* @async_result: a #GAsyncResult
271
* @error: a #GError, or %NULL
273
* Finishes an asynchronous authorization refresh operation for the #GDataAuthorizer, as started with gdata_authorizer_refresh_authorization_async().
275
* This method is thread safe.
277
* Return value: %TRUE if an authorization refresh was attempted and was successful, %FALSE if a refresh wasn't attempted or was unsuccessful
282
gdata_authorizer_refresh_authorization_finish (GDataAuthorizer *self, GAsyncResult *async_result, GError **error)
284
GDataAuthorizerInterface *iface;
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);
290
iface = GDATA_AUTHORIZER_GET_IFACE (self);
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));
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);
303
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (async_result), error) == TRUE) {
310
/* Fall back to just returning FALSE if none of the methods are implemented */