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 | |
35 | using namespace WebKit; |
36 | using 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 | |
58 | enum { |
59 | CANCELLED, |
60 | |
61 | LAST_SIGNAL |
62 | }; |
63 | |
64 | struct _WebKitAuthenticationRequestPrivate { |
65 | RefPtr<AuthenticationChallengeProxy> authenticationChallenge; |
66 | bool privateBrowsingEnabled; |
67 | bool handledRequest; |
68 | CString host; |
69 | CString realm; |
70 | }; |
71 | |
72 | static guint signals[LAST_SIGNAL] = { 0, }; |
73 | |
74 | WEBKIT_DEFINE_TYPE(WebKitAuthenticationRequest, webkit_authentication_request, G_TYPE_OBJECT) |
75 | |
76 | static 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 | |
103 | static 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 | |
114 | static 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 | |
138 | WebKitAuthenticationRequest* 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 | |
146 | AuthenticationChallengeProxy* 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 | */ |
164 | gboolean 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 | */ |
188 | WebKitCredential* 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 | */ |
209 | const 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 | */ |
228 | guint 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 | */ |
245 | const 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 | */ |
264 | WebKitAuthenticationScheme 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 | */ |
281 | gboolean 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 | */ |
298 | gboolean 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 | */ |
315 | void 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 | */ |
333 | void 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 | |