1/*
2 * Copyright (C) 2013 Samsung Electronics Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "WebKitAuthenticationRequest.h"
22
23#include "AuthenticationChallengeDisposition.h"
24#include "AuthenticationDecisionListener.h"
25#include "WebCredential.h"
26#include "WebKitAuthenticationRequestPrivate.h"
27#include "WebKitCredentialPrivate.h"
28#include "WebProtectionSpace.h"
29#include <WebCore/AuthenticationChallenge.h>
30#include <WebCore/ProtectionSpace.h>
31#include <glib/gi18n-lib.h>
32#include <wtf/glib/WTFGType.h>
33#include <wtf/text/CString.h>
34
35using namespace WebKit;
36using namespace WebCore;
37
38/**
39 * SECTION: WebKitAuthenticationRequest
40 * @Short_description: Represents an authentication request
41 * @Title: WebKitAuthenticationRequest
42 * @See_also: #WebKitWebView
43 *
44 * Whenever a client attempts to load a page protected by HTTP
45 * authentication, credentials will need to be provided to authorize access.
46 * To allow the client to decide how it wishes to handle authentication,
47 * WebKit will fire a #WebKitWebView::authenticate signal with a
48 * WebKitAuthenticationRequest object to provide client side
49 * authentication support. Credentials are exposed through the
50 * #WebKitCredential object.
51 *
52 * In case the client application does not wish
53 * to handle this signal WebKit will provide a default handler. To handle
54 * authentication asynchronously, simply increase the reference count of the
55 * WebKitAuthenticationRequest object.
56 */
57
58enum {
59 CANCELLED,
60
61 LAST_SIGNAL
62};
63
64struct _WebKitAuthenticationRequestPrivate {
65 RefPtr<AuthenticationChallengeProxy> authenticationChallenge;
66 bool privateBrowsingEnabled;
67 bool handledRequest;
68 CString host;
69 CString realm;
70};
71
72static guint signals[LAST_SIGNAL] = { 0, };
73
74WEBKIT_DEFINE_TYPE(WebKitAuthenticationRequest, webkit_authentication_request, G_TYPE_OBJECT)
75
76static inline WebKitAuthenticationScheme toWebKitAuthenticationScheme(WebCore::ProtectionSpaceAuthenticationScheme coreScheme)
77{
78 switch (coreScheme) {
79 case WebCore::ProtectionSpaceAuthenticationSchemeDefault:
80 return WEBKIT_AUTHENTICATION_SCHEME_DEFAULT;
81 case WebCore::ProtectionSpaceAuthenticationSchemeHTTPBasic:
82 return WEBKIT_AUTHENTICATION_SCHEME_HTTP_BASIC;
83 case WebCore::ProtectionSpaceAuthenticationSchemeHTTPDigest:
84 return WEBKIT_AUTHENTICATION_SCHEME_HTTP_DIGEST;
85 case WebCore::ProtectionSpaceAuthenticationSchemeHTMLForm:
86 return WEBKIT_AUTHENTICATION_SCHEME_HTML_FORM;
87 case WebCore::ProtectionSpaceAuthenticationSchemeNTLM:
88 return WEBKIT_AUTHENTICATION_SCHEME_NTLM;
89 case WebCore::ProtectionSpaceAuthenticationSchemeNegotiate:
90 return WEBKIT_AUTHENTICATION_SCHEME_NEGOTIATE;
91 case WebCore::ProtectionSpaceAuthenticationSchemeClientCertificateRequested:
92 return WEBKIT_AUTHENTICATION_SCHEME_CLIENT_CERTIFICATE_REQUESTED;
93 case WebCore::ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested:
94 return WEBKIT_AUTHENTICATION_SCHEME_SERVER_TRUST_EVALUATION_REQUESTED;
95 case WebCore::ProtectionSpaceAuthenticationSchemeUnknown:
96 return WEBKIT_AUTHENTICATION_SCHEME_UNKNOWN;
97 default:
98 ASSERT_NOT_REACHED();
99 return WEBKIT_AUTHENTICATION_SCHEME_DEFAULT;
100 }
101}
102
103static void webkitAuthenticationRequestDispose(GObject* object)
104{
105 WebKitAuthenticationRequest* request = WEBKIT_AUTHENTICATION_REQUEST(object);
106
107 // Make sure the request is always handled before finalizing.
108 if (!request->priv->handledRequest)
109 webkit_authentication_request_cancel(request);
110
111 G_OBJECT_CLASS(webkit_authentication_request_parent_class)->dispose(object);
112}
113
114static void webkit_authentication_request_class_init(WebKitAuthenticationRequestClass* requestClass)
115{
116 GObjectClass* objectClass = G_OBJECT_CLASS(requestClass);
117 objectClass->dispose = webkitAuthenticationRequestDispose;
118
119 /**
120 * WebKitAuthenticationRequest::cancelled:
121 * @request: the #WebKitAuthenticationRequest
122 *
123 * This signal is emitted when the user authentication request is
124 * cancelled. It allows the application to dismiss its authentication
125 * dialog in case of page load failure for example.
126 *
127 * Since: 2.2
128 */
129 signals[CANCELLED] =
130 g_signal_new("cancelled",
131 G_TYPE_FROM_CLASS(objectClass),
132 G_SIGNAL_RUN_LAST,
133 0, 0, 0,
134 g_cclosure_marshal_VOID__VOID,
135 G_TYPE_NONE, 0);
136}
137
138WebKitAuthenticationRequest* webkitAuthenticationRequestCreate(AuthenticationChallengeProxy* authenticationChallenge, bool privateBrowsingEnabled)
139{
140 WebKitAuthenticationRequest* request = WEBKIT_AUTHENTICATION_REQUEST(g_object_new(WEBKIT_TYPE_AUTHENTICATION_REQUEST, NULL));
141 request->priv->authenticationChallenge = authenticationChallenge;
142 request->priv->privateBrowsingEnabled = privateBrowsingEnabled;
143 return request;
144}
145
146AuthenticationChallengeProxy* webkitAuthenticationRequestGetAuthenticationChallenge(WebKitAuthenticationRequest* request)
147{
148 return request->priv->authenticationChallenge.get();
149}
150
151/**
152 * webkit_authentication_request_can_save_credentials:
153 * @request: a #WebKitAuthenticationRequest
154 *
155 * Determine whether the authentication method associated with this
156 * #WebKitAuthenticationRequest should allow the storage of credentials.
157 * This will return %FALSE if WebKit doesn't support credential storing
158 * or if private browsing is enabled.
159 *
160 * Returns: %TRUE if WebKit can store credentials or %FALSE otherwise.
161 *
162 * Since: 2.2
163 */
164gboolean webkit_authentication_request_can_save_credentials(WebKitAuthenticationRequest* request)
165{
166 g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), FALSE);
167
168#if USE(LIBSECRET)
169 return !request->priv->privateBrowsingEnabled;
170#else
171 return FALSE;
172#endif
173}
174
175/**
176 * webkit_authentication_request_get_proposed_credential:
177 * @request: a #WebKitAuthenticationRequest
178 *
179 * Get the #WebKitCredential of the proposed authentication challenge that was
180 * stored from a previous session. The client can use this directly for
181 * authentication or construct their own #WebKitCredential.
182 *
183 * Returns: (transfer full): A #WebKitCredential encapsulating credential details
184 * or %NULL if there is no stored credential.
185 *
186 * Since: 2.2
187 */
188WebKitCredential* webkit_authentication_request_get_proposed_credential(WebKitAuthenticationRequest* request)
189{
190 g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
191
192 const auto& credential = request->priv->authenticationChallenge->core().proposedCredential();
193 if (credential.isEmpty())
194 return 0;
195
196 return webkitCredentialCreate(credential);
197}
198
199/**
200 * webkit_authentication_request_get_host:
201 * @request: a #WebKitAuthenticationRequest
202 *
203 * Get the host that this authentication challenge is applicable to.
204 *
205 * Returns: The host of @request.
206 *
207 * Since: 2.2
208 */
209const gchar* webkit_authentication_request_get_host(WebKitAuthenticationRequest* request)
210{
211 g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
212
213 if (request->priv->host.isNull())
214 request->priv->host = request->priv->authenticationChallenge->core().protectionSpace().host().utf8();
215 return request->priv->host.data();
216}
217
218/**
219 * webkit_authentication_request_get_port:
220 * @request: a #WebKitAuthenticationRequest
221 *
222 * Get the port that this authentication challenge is applicable to.
223 *
224 * Returns: The port of @request.
225 *
226 * Since: 2.2
227 */
228guint webkit_authentication_request_get_port(WebKitAuthenticationRequest* request)
229{
230 g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
231
232 return request->priv->authenticationChallenge->core().protectionSpace().port();
233}
234
235/**
236 * webkit_authentication_request_get_realm:
237 * @request: a #WebKitAuthenticationRequest
238 *
239 * Get the realm that this authentication challenge is applicable to.
240 *
241 * Returns: The realm of @request.
242 *
243 * Since: 2.2
244 */
245const gchar* webkit_authentication_request_get_realm(WebKitAuthenticationRequest* request)
246{
247 g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
248
249 if (request->priv->realm.isNull())
250 request->priv->realm = request->priv->authenticationChallenge->core().protectionSpace().realm().utf8();
251 return request->priv->realm.data();
252}
253
254/**
255 * webkit_authentication_request_get_scheme:
256 * @request: a #WebKitAuthenticationRequest
257 *
258 * Get the authentication scheme of the authentication challenge.
259 *
260 * Returns: The #WebKitAuthenticationScheme of @request.
261 *
262 * Since: 2.2
263 */
264WebKitAuthenticationScheme webkit_authentication_request_get_scheme(WebKitAuthenticationRequest* request)
265{
266 g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), WEBKIT_AUTHENTICATION_SCHEME_UNKNOWN);
267
268 return toWebKitAuthenticationScheme(request->priv->authenticationChallenge->core().protectionSpace().authenticationScheme());
269}
270
271/**
272 * webkit_authentication_request_is_for_proxy:
273 * @request: a #WebKitAuthenticationRequest
274 *
275 * Determine whether the authentication challenge is associated with a proxy server rather than an "origin" server.
276 *
277 * Returns: %TRUE if authentication is for a proxy or %FALSE otherwise.
278 *
279 * Since: 2.2
280 */
281gboolean webkit_authentication_request_is_for_proxy(WebKitAuthenticationRequest* request)
282{
283 g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), FALSE);
284
285 return request->priv->authenticationChallenge->core().protectionSpace().isProxy();
286}
287
288/**
289 * webkit_authentication_request_is_retry:
290 * @request: a #WebKitAuthenticationRequest
291 *
292 * Determine whether this this is a first attempt or a retry for this authentication challenge.
293 *
294 * Returns: %TRUE if authentication attempt is a retry or %FALSE otherwise.
295 *
296 * Since: 2.2
297 */
298gboolean webkit_authentication_request_is_retry(WebKitAuthenticationRequest* request)
299{
300 g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
301
302 return request->priv->authenticationChallenge->core().previousFailureCount() ? TRUE : FALSE;
303}
304
305/**
306 * webkit_authentication_request_authenticate:
307 * @request: a #WebKitAuthenticationRequest
308 * @credential: (transfer none) (allow-none): A #WebKitCredential, or %NULL
309 *
310 * Authenticate the #WebKitAuthenticationRequest using the #WebKitCredential
311 * supplied. To continue without credentials, pass %NULL as @credential.
312 *
313 * Since: 2.2
314 */
315void webkit_authentication_request_authenticate(WebKitAuthenticationRequest* request, WebKitCredential* credential)
316{
317 g_return_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request));
318
319 request->priv->authenticationChallenge->listener().completeChallenge(WebKit::AuthenticationChallengeDisposition::UseCredential, credential ? webkitCredentialGetCredential(credential) : WebCore::Credential());
320
321 request->priv->handledRequest = true;
322}
323
324/**
325 * webkit_authentication_request_cancel:
326 * @request: a #WebKitAuthenticationRequest
327 *
328 * Cancel the authentication challenge. This will also cancel the page loading and result in a
329 * #WebKitWebView::load-failed signal with a #WebKitNetworkError of type %WEBKIT_NETWORK_ERROR_CANCELLED being emitted.
330 *
331 * Since: 2.2
332 */
333void webkit_authentication_request_cancel(WebKitAuthenticationRequest* request)
334{
335 g_return_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request));
336
337 request->priv->authenticationChallenge->listener().completeChallenge(WebKit::AuthenticationChallengeDisposition::Cancel);
338
339 g_signal_emit(request, signals[CANCELLED], 0);
340}
341