| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/login_prompt.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "app/l10n_util.h" | |
| 10 #include "base/command_line.h" | |
| 11 #include "base/lock.h" | |
| 12 #include "base/utf_string_conversions.h" | |
| 13 #include "chrome/browser/browser_thread.h" | |
| 14 #include "chrome/browser/password_manager/password_manager.h" | |
| 15 #include "chrome/browser/renderer_host/render_process_host.h" | |
| 16 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" | |
| 17 #include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" | |
| 18 #include "chrome/browser/tab_contents/constrained_window.h" | |
| 19 #include "chrome/browser/tab_contents/tab_contents.h" | |
| 20 #include "chrome/browser/tab_contents/tab_util.h" | |
| 21 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | |
| 22 #include "chrome/common/chrome_switches.h" | |
| 23 #include "chrome/common/notification_service.h" | |
| 24 #include "grit/generated_resources.h" | |
| 25 #include "net/base/auth.h" | |
| 26 #include "net/base/net_util.h" | |
| 27 #include "net/url_request/url_request.h" | |
| 28 | |
| 29 using webkit_glue::PasswordForm; | |
| 30 | |
| 31 class LoginHandlerImpl; | |
| 32 | |
| 33 // Helper to remove the ref from an net::URLRequest to the LoginHandler. | |
| 34 // Should only be called from the IO thread, since it accesses an | |
| 35 // net::URLRequest. | |
| 36 void ResetLoginHandlerForRequest(net::URLRequest* request) { | |
| 37 ResourceDispatcherHostRequestInfo* info = | |
| 38 ResourceDispatcherHost::InfoForRequest(request); | |
| 39 if (!info) | |
| 40 return; | |
| 41 | |
| 42 info->set_login_handler(NULL); | |
| 43 } | |
| 44 | |
| 45 // Get the signon_realm under which this auth info should be stored. | |
| 46 // | |
| 47 // The format of the signon_realm for proxy auth is: | |
| 48 // proxy-host/auth-realm | |
| 49 // The format of the signon_realm for server auth is: | |
| 50 // url-scheme://url-host[:url-port]/auth-realm | |
| 51 // | |
| 52 // Be careful when changing this function, since you could make existing | |
| 53 // saved logins un-retrievable. | |
| 54 std::string GetSignonRealm(const GURL& url, | |
| 55 const net::AuthChallengeInfo& auth_info) { | |
| 56 std::string signon_realm; | |
| 57 if (auth_info.is_proxy) { | |
| 58 signon_realm = WideToASCII(auth_info.host_and_port); | |
| 59 signon_realm.append("/"); | |
| 60 } else { | |
| 61 // Take scheme, host, and port from the url. | |
| 62 signon_realm = url.GetOrigin().spec(); | |
| 63 // This ends with a "/". | |
| 64 } | |
| 65 signon_realm.append(WideToUTF8(auth_info.realm)); | |
| 66 return signon_realm; | |
| 67 } | |
| 68 | |
| 69 // ---------------------------------------------------------------------------- | |
| 70 // LoginHandler | |
| 71 | |
| 72 LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info, | |
| 73 net::URLRequest* request) | |
| 74 : handled_auth_(false), | |
| 75 dialog_(NULL), | |
| 76 auth_info_(auth_info), | |
| 77 request_(request), | |
| 78 password_manager_(NULL), | |
| 79 login_model_(NULL) { | |
| 80 // This constructor is called on the I/O thread, so we cannot load the nib | |
| 81 // here. BuildViewForPasswordManager() will be invoked on the UI thread | |
| 82 // later, so wait with loading the nib until then. | |
| 83 DCHECK(request_) << "LoginHandler constructed with NULL request"; | |
| 84 DCHECK(auth_info_) << "LoginHandler constructed with NULL auth info"; | |
| 85 | |
| 86 AddRef(); // matched by LoginHandler::ReleaseSoon(). | |
| 87 | |
| 88 BrowserThread::PostTask( | |
| 89 BrowserThread::UI, FROM_HERE, | |
| 90 NewRunnableMethod(this, &LoginHandler::AddObservers)); | |
| 91 | |
| 92 if (!ResourceDispatcherHost::RenderViewForRequest( | |
| 93 request_, &render_process_host_id_, &tab_contents_id_)) { | |
| 94 NOTREACHED(); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 LoginHandler::~LoginHandler() { | |
| 99 SetModel(NULL); | |
| 100 } | |
| 101 | |
| 102 void LoginHandler::SetPasswordForm(const webkit_glue::PasswordForm& form) { | |
| 103 password_form_ = form; | |
| 104 } | |
| 105 | |
| 106 void LoginHandler::SetPasswordManager(PasswordManager* password_manager) { | |
| 107 password_manager_ = password_manager; | |
| 108 } | |
| 109 | |
| 110 TabContents* LoginHandler::GetTabContentsForLogin() const { | |
| 111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 112 | |
| 113 return tab_util::GetTabContentsByID(render_process_host_id_, | |
| 114 tab_contents_id_); | |
| 115 } | |
| 116 | |
| 117 void LoginHandler::SetAuth(const std::wstring& username, | |
| 118 const std::wstring& password) { | |
| 119 if (WasAuthHandled(true)) | |
| 120 return; | |
| 121 | |
| 122 // Tell the password manager the credentials were submitted / accepted. | |
| 123 if (password_manager_) { | |
| 124 password_form_.username_value = WideToUTF16Hack(username); | |
| 125 password_form_.password_value = WideToUTF16Hack(password); | |
| 126 password_manager_->ProvisionallySavePassword(password_form_); | |
| 127 } | |
| 128 | |
| 129 BrowserThread::PostTask( | |
| 130 BrowserThread::UI, FROM_HERE, | |
| 131 NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred)); | |
| 132 BrowserThread::PostTask( | |
| 133 BrowserThread::UI, FROM_HERE, | |
| 134 NewRunnableMethod( | |
| 135 this, &LoginHandler::NotifyAuthSupplied, username, password)); | |
| 136 BrowserThread::PostTask( | |
| 137 BrowserThread::IO, FROM_HERE, | |
| 138 NewRunnableMethod( | |
| 139 this, &LoginHandler::SetAuthDeferred, username, password)); | |
| 140 } | |
| 141 | |
| 142 void LoginHandler::CancelAuth() { | |
| 143 if (WasAuthHandled(true)) | |
| 144 return; | |
| 145 | |
| 146 BrowserThread::PostTask( | |
| 147 BrowserThread::UI, FROM_HERE, | |
| 148 NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred)); | |
| 149 BrowserThread::PostTask( | |
| 150 BrowserThread::UI, FROM_HERE, | |
| 151 NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled)); | |
| 152 BrowserThread::PostTask( | |
| 153 BrowserThread::IO, FROM_HERE, | |
| 154 NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred)); | |
| 155 } | |
| 156 | |
| 157 void LoginHandler::OnRequestCancelled() { | |
| 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) << | |
| 159 "Why is OnRequestCancelled called from the UI thread?"; | |
| 160 | |
| 161 // Reference is no longer valid. | |
| 162 request_ = NULL; | |
| 163 | |
| 164 // Give up on auth if the request was cancelled. | |
| 165 CancelAuth(); | |
| 166 } | |
| 167 | |
| 168 void LoginHandler::AddObservers() { | |
| 169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 170 | |
| 171 registrar_.Add(this, NotificationType::AUTH_SUPPLIED, | |
| 172 NotificationService::AllSources()); | |
| 173 registrar_.Add(this, NotificationType::AUTH_CANCELLED, | |
| 174 NotificationService::AllSources()); | |
| 175 } | |
| 176 | |
| 177 void LoginHandler::RemoveObservers() { | |
| 178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 179 | |
| 180 registrar_.Remove(this, NotificationType::AUTH_SUPPLIED, | |
| 181 NotificationService::AllSources()); | |
| 182 registrar_.Remove(this, NotificationType::AUTH_CANCELLED, | |
| 183 NotificationService::AllSources()); | |
| 184 | |
| 185 DCHECK(registrar_.IsEmpty()); | |
| 186 } | |
| 187 | |
| 188 void LoginHandler::Observe(NotificationType type, | |
| 189 const NotificationSource& source, | |
| 190 const NotificationDetails& details) { | |
| 191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 192 DCHECK(type == NotificationType::AUTH_SUPPLIED || | |
| 193 type == NotificationType::AUTH_CANCELLED); | |
| 194 | |
| 195 TabContents* requesting_contents = GetTabContentsForLogin(); | |
| 196 if (!requesting_contents) | |
| 197 return; | |
| 198 | |
| 199 NavigationController* this_controller = &requesting_contents->controller(); | |
| 200 NavigationController* that_controller = | |
| 201 Source<NavigationController>(source).ptr(); | |
| 202 | |
| 203 // Only handle notifications from other handlers. | |
| 204 if (this_controller == that_controller) | |
| 205 return; | |
| 206 | |
| 207 LoginNotificationDetails* login_details = | |
| 208 Details<LoginNotificationDetails>(details).ptr(); | |
| 209 | |
| 210 // Only handle notification for the identical auth info. | |
| 211 if (*login_details->handler()->auth_info() != *auth_info()) | |
| 212 return; | |
| 213 | |
| 214 // Set or cancel the auth in this handler. | |
| 215 if (type == NotificationType::AUTH_SUPPLIED) { | |
| 216 AuthSuppliedLoginNotificationDetails* supplied_details = | |
| 217 Details<AuthSuppliedLoginNotificationDetails>(details).ptr(); | |
| 218 SetAuth(supplied_details->username(), supplied_details->password()); | |
| 219 } else { | |
| 220 DCHECK(type == NotificationType::AUTH_CANCELLED); | |
| 221 CancelAuth(); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 void LoginHandler::SetModel(LoginModel* model) { | |
| 226 if (login_model_) | |
| 227 login_model_->SetObserver(NULL); | |
| 228 login_model_ = model; | |
| 229 if (login_model_) | |
| 230 login_model_->SetObserver(this); | |
| 231 } | |
| 232 | |
| 233 void LoginHandler::SetDialog(ConstrainedWindow* dialog) { | |
| 234 dialog_ = dialog; | |
| 235 } | |
| 236 | |
| 237 void LoginHandler::NotifyAuthNeeded() { | |
| 238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 239 if (WasAuthHandled(false)) | |
| 240 return; | |
| 241 | |
| 242 TabContents* requesting_contents = GetTabContentsForLogin(); | |
| 243 if (!requesting_contents) | |
| 244 return; | |
| 245 | |
| 246 NotificationService* service = NotificationService::current(); | |
| 247 NavigationController* controller = &requesting_contents->controller(); | |
| 248 LoginNotificationDetails details(this); | |
| 249 | |
| 250 service->Notify(NotificationType::AUTH_NEEDED, | |
| 251 Source<NavigationController>(controller), | |
| 252 Details<LoginNotificationDetails>(&details)); | |
| 253 } | |
| 254 | |
| 255 void LoginHandler::NotifyAuthCancelled() { | |
| 256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 257 DCHECK(WasAuthHandled(false)); | |
| 258 | |
| 259 TabContents* requesting_contents = GetTabContentsForLogin(); | |
| 260 if (!requesting_contents) | |
| 261 return; | |
| 262 | |
| 263 NotificationService* service = NotificationService::current(); | |
| 264 NavigationController* controller = &requesting_contents->controller(); | |
| 265 LoginNotificationDetails details(this); | |
| 266 | |
| 267 service->Notify(NotificationType::AUTH_CANCELLED, | |
| 268 Source<NavigationController>(controller), | |
| 269 Details<LoginNotificationDetails>(&details)); | |
| 270 } | |
| 271 | |
| 272 void LoginHandler::NotifyAuthSupplied(const std::wstring& username, | |
| 273 const std::wstring& password) { | |
| 274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 275 DCHECK(WasAuthHandled(false)); | |
| 276 | |
| 277 TabContents* requesting_contents = GetTabContentsForLogin(); | |
| 278 if (!requesting_contents) | |
| 279 return; | |
| 280 | |
| 281 NotificationService* service = NotificationService::current(); | |
| 282 NavigationController* controller = &requesting_contents->controller(); | |
| 283 AuthSuppliedLoginNotificationDetails details(this, username, password); | |
| 284 | |
| 285 service->Notify(NotificationType::AUTH_SUPPLIED, | |
| 286 Source<NavigationController>(controller), | |
| 287 Details<AuthSuppliedLoginNotificationDetails>(&details)); | |
| 288 } | |
| 289 | |
| 290 void LoginHandler::ReleaseSoon() { | |
| 291 if (!WasAuthHandled(true)) { | |
| 292 BrowserThread::PostTask( | |
| 293 BrowserThread::IO, FROM_HERE, | |
| 294 NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred)); | |
| 295 BrowserThread::PostTask( | |
| 296 BrowserThread::UI, FROM_HERE, | |
| 297 NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled)); | |
| 298 } | |
| 299 | |
| 300 BrowserThread::PostTask( | |
| 301 BrowserThread::UI, FROM_HERE, | |
| 302 NewRunnableMethod(this, &LoginHandler::RemoveObservers)); | |
| 303 | |
| 304 // Delete this object once all InvokeLaters have been called. | |
| 305 BrowserThread::ReleaseSoon(BrowserThread::IO, FROM_HERE, this); | |
| 306 } | |
| 307 | |
| 308 // Returns whether authentication had been handled (SetAuth or CancelAuth). | |
| 309 // If |set_handled| is true, it will mark authentication as handled. | |
| 310 bool LoginHandler::WasAuthHandled(bool set_handled) { | |
| 311 AutoLock lock(handled_auth_lock_); | |
| 312 bool was_handled = handled_auth_; | |
| 313 if (set_handled) | |
| 314 handled_auth_ = true; | |
| 315 return was_handled; | |
| 316 } | |
| 317 | |
| 318 // Calls SetAuth from the IO loop. | |
| 319 void LoginHandler::SetAuthDeferred(const std::wstring& username, | |
| 320 const std::wstring& password) { | |
| 321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 322 | |
| 323 if (request_) { | |
| 324 request_->SetAuth(WideToUTF16Hack(username), WideToUTF16Hack(password)); | |
| 325 ResetLoginHandlerForRequest(request_); | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 // Calls CancelAuth from the IO loop. | |
| 330 void LoginHandler::CancelAuthDeferred() { | |
| 331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 332 | |
| 333 if (request_) { | |
| 334 request_->CancelAuth(); | |
| 335 // Verify that CancelAuth doesn't destroy the request via our delegate. | |
| 336 DCHECK(request_ != NULL); | |
| 337 ResetLoginHandlerForRequest(request_); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 // Closes the view_contents from the UI loop. | |
| 342 void LoginHandler::CloseContentsDeferred() { | |
| 343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 344 | |
| 345 // The hosting ConstrainedWindow may have been freed. | |
| 346 if (dialog_) | |
| 347 dialog_->CloseConstrainedWindow(); | |
| 348 } | |
| 349 | |
| 350 // ---------------------------------------------------------------------------- | |
| 351 // LoginDialogTask | |
| 352 | |
| 353 // This task is run on the UI thread and creates a constrained window with | |
| 354 // a LoginView to prompt the user. The response will be sent to LoginHandler, | |
| 355 // which then routes it to the net::URLRequest on the I/O thread. | |
| 356 class LoginDialogTask : public Task { | |
| 357 public: | |
| 358 LoginDialogTask(const GURL& request_url, | |
| 359 net::AuthChallengeInfo* auth_info, | |
| 360 LoginHandler* handler) | |
| 361 : request_url_(request_url), auth_info_(auth_info), handler_(handler) { | |
| 362 } | |
| 363 virtual ~LoginDialogTask() { | |
| 364 } | |
| 365 | |
| 366 void Run() { | |
| 367 TabContents* parent_contents = handler_->GetTabContentsForLogin(); | |
| 368 if (!parent_contents) { | |
| 369 // The request may have been cancelled, or it may be for a renderer | |
| 370 // not hosted by a tab (e.g. an extension). Cancel just in case | |
| 371 // (cancelling twice is a no-op). | |
| 372 handler_->CancelAuth(); | |
| 373 return; | |
| 374 } | |
| 375 | |
| 376 // Tell the password manager to look for saved passwords. | |
| 377 TabContentsWrapper** wrapper = | |
| 378 TabContentsWrapper::property_accessor()->GetProperty( | |
| 379 parent_contents->property_bag()); | |
| 380 if (!wrapper) | |
| 381 return; | |
| 382 PasswordManager* password_manager = (*wrapper)->GetPasswordManager(); | |
| 383 std::vector<PasswordForm> v; | |
| 384 MakeInputForPasswordManager(&v); | |
| 385 password_manager->PasswordFormsFound(v); | |
| 386 handler_->SetPasswordManager(password_manager); | |
| 387 | |
| 388 std::wstring explanation = auth_info_->realm.empty() ? | |
| 389 l10n_util::GetStringF(IDS_LOGIN_DIALOG_DESCRIPTION_NO_REALM, | |
| 390 auth_info_->host_and_port) : | |
| 391 l10n_util::GetStringF(IDS_LOGIN_DIALOG_DESCRIPTION, | |
| 392 auth_info_->host_and_port, | |
| 393 auth_info_->realm); | |
| 394 handler_->BuildViewForPasswordManager(password_manager, | |
| 395 explanation); | |
| 396 } | |
| 397 | |
| 398 private: | |
| 399 // Helper to create a PasswordForm and stuff it into a vector as input | |
| 400 // for PasswordManager::PasswordFormsFound, the hook into PasswordManager. | |
| 401 void MakeInputForPasswordManager( | |
| 402 std::vector<PasswordForm>* password_manager_input) { | |
| 403 PasswordForm dialog_form; | |
| 404 if (LowerCaseEqualsASCII(auth_info_->scheme, "basic")) { | |
| 405 dialog_form.scheme = PasswordForm::SCHEME_BASIC; | |
| 406 } else if (LowerCaseEqualsASCII(auth_info_->scheme, "digest")) { | |
| 407 dialog_form.scheme = PasswordForm::SCHEME_DIGEST; | |
| 408 } else { | |
| 409 dialog_form.scheme = PasswordForm::SCHEME_OTHER; | |
| 410 } | |
| 411 std::string host_and_port(WideToASCII(auth_info_->host_and_port)); | |
| 412 if (auth_info_->is_proxy) { | |
| 413 std::string origin = host_and_port; | |
| 414 // We don't expect this to already start with http:// or https://. | |
| 415 DCHECK(origin.find("http://") != 0 && origin.find("https://") != 0); | |
| 416 origin = std::string("http://") + origin; | |
| 417 dialog_form.origin = GURL(origin); | |
| 418 } else if (net::GetHostAndPort(request_url_) != host_and_port) { | |
| 419 dialog_form.origin = GURL(); | |
| 420 NOTREACHED(); // crbug.com/32718 | |
| 421 } else { | |
| 422 dialog_form.origin = GURL(request_url_.scheme() + "://" + host_and_port); | |
| 423 } | |
| 424 dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, *auth_info_); | |
| 425 password_manager_input->push_back(dialog_form); | |
| 426 // Set the password form for the handler (by copy). | |
| 427 handler_->SetPasswordForm(dialog_form); | |
| 428 } | |
| 429 | |
| 430 // The url from the net::URLRequest initiating the auth challenge. | |
| 431 GURL request_url_; | |
| 432 | |
| 433 // Info about who/where/what is asking for authentication. | |
| 434 scoped_refptr<net::AuthChallengeInfo> auth_info_; | |
| 435 | |
| 436 // Where to send the authentication when obtained. | |
| 437 // This is owned by the ResourceDispatcherHost that invoked us. | |
| 438 LoginHandler* handler_; | |
| 439 | |
| 440 DISALLOW_COPY_AND_ASSIGN(LoginDialogTask); | |
| 441 }; | |
| 442 | |
| 443 // ---------------------------------------------------------------------------- | |
| 444 // Public API | |
| 445 | |
| 446 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info, | |
| 447 net::URLRequest* request) { | |
| 448 LoginHandler* handler = LoginHandler::Create(auth_info, request); | |
| 449 BrowserThread::PostTask( | |
| 450 BrowserThread::UI, FROM_HERE, new LoginDialogTask( | |
| 451 request->url(), auth_info, handler)); | |
| 452 return handler; | |
| 453 } | |
| OLD | NEW |