OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/login/login_prompt.h" | 5 #include "chrome/browser/ui/login/login_prompt.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 using content::BrowserThread; | 48 using content::BrowserThread; |
49 using content::NavigationController; | 49 using content::NavigationController; |
50 using content::RenderViewHost; | 50 using content::RenderViewHost; |
51 using content::RenderViewHostDelegate; | 51 using content::RenderViewHostDelegate; |
52 using content::ResourceDispatcherHost; | 52 using content::ResourceDispatcherHost; |
53 using content::ResourceRequestInfo; | 53 using content::ResourceRequestInfo; |
54 using content::WebContents; | 54 using content::WebContents; |
55 | 55 |
56 class LoginHandlerImpl; | 56 class LoginHandlerImpl; |
57 | 57 |
| 58 namespace { |
| 59 |
58 // Helper to remove the ref from an net::URLRequest to the LoginHandler. | 60 // Helper to remove the ref from an net::URLRequest to the LoginHandler. |
59 // Should only be called from the IO thread, since it accesses an | 61 // Should only be called from the IO thread, since it accesses an |
60 // net::URLRequest. | 62 // net::URLRequest. |
61 void ResetLoginHandlerForRequest(net::URLRequest* request) { | 63 void ResetLoginHandlerForRequest(net::URLRequest* request) { |
62 ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request); | 64 ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request); |
63 } | 65 } |
64 | 66 |
65 // Get the signon_realm under which this auth info should be stored. | 67 // Helper to create a PasswordForm for PasswordManager to start looking for |
66 // | 68 // saved credentials. |
67 // The format of the signon_realm for proxy auth is: | 69 PasswordForm MakeInputForPasswordManager(const GURL& request_url, |
68 // proxy-host/auth-realm | 70 net::AuthChallengeInfo* auth_info) { |
69 // The format of the signon_realm for server auth is: | 71 PasswordForm dialog_form; |
70 // url-scheme://url-host[:url-port]/auth-realm | 72 if (base::LowerCaseEqualsASCII(auth_info->scheme, "basic")) { |
71 // | 73 dialog_form.scheme = PasswordForm::SCHEME_BASIC; |
72 // Be careful when changing this function, since you could make existing | 74 } else if (base::LowerCaseEqualsASCII(auth_info->scheme, "digest")) { |
73 // saved logins un-retrievable. | 75 dialog_form.scheme = PasswordForm::SCHEME_DIGEST; |
74 std::string GetSignonRealm(const GURL& url, | |
75 const net::AuthChallengeInfo& auth_info) { | |
76 std::string signon_realm; | |
77 if (auth_info.is_proxy) { | |
78 signon_realm = auth_info.challenger.ToString(); | |
79 signon_realm.append("/"); | |
80 } else { | 76 } else { |
81 // Take scheme, host, and port from the url. | 77 dialog_form.scheme = PasswordForm::SCHEME_OTHER; |
82 signon_realm = url.GetOrigin().spec(); | |
83 // This ends with a "/". | |
84 } | 78 } |
85 signon_realm.append(auth_info.realm); | 79 std::string host_and_port(auth_info->challenger.ToString()); |
86 return signon_realm; | 80 if (auth_info->is_proxy) { |
| 81 std::string origin = host_and_port; |
| 82 // We don't expect this to already start with http:// or https://. |
| 83 DCHECK(origin.find("http://") != 0 && origin.find("https://") != 0); |
| 84 origin = std::string("http://") + origin; |
| 85 dialog_form.origin = GURL(origin); |
| 86 } else if (!auth_info->challenger.Equals( |
| 87 net::HostPortPair::FromURL(request_url))) { |
| 88 dialog_form.origin = GURL(); |
| 89 NOTREACHED(); // crbug.com/32718 |
| 90 } else { |
| 91 dialog_form.origin = GURL(request_url.scheme() + "://" + host_and_port); |
| 92 } |
| 93 dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, *auth_info); |
| 94 return dialog_form; |
87 } | 95 } |
88 | 96 |
| 97 void ShowLoginPrompt(const GURL& request_url, |
| 98 net::AuthChallengeInfo* auth_info, |
| 99 LoginHandler* handler) { |
| 100 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 101 WebContents* parent_contents = handler->GetWebContentsForLogin(); |
| 102 if (!parent_contents) |
| 103 return; |
| 104 prerender::PrerenderContents* prerender_contents = |
| 105 prerender::PrerenderContents::FromWebContents(parent_contents); |
| 106 if (prerender_contents) { |
| 107 prerender_contents->Destroy(prerender::FINAL_STATUS_AUTH_NEEDED); |
| 108 return; |
| 109 } |
| 110 |
| 111 // The realm is controlled by the remote server, so there is no reason |
| 112 // to believe it is of a reasonable length. |
| 113 base::string16 elided_realm; |
| 114 gfx::ElideString(base::UTF8ToUTF16(auth_info->realm), 120, &elided_realm); |
| 115 |
| 116 std::string languages; |
| 117 content::WebContents* web_contents = handler->GetWebContentsForLogin(); |
| 118 if (web_contents) { |
| 119 Profile* profile = |
| 120 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| 121 if (profile) |
| 122 languages = profile->GetPrefs()->GetString(prefs::kAcceptLanguages); |
| 123 } |
| 124 |
| 125 base::string16 authority = |
| 126 url_formatter::FormatUrlForSecurityDisplay(request_url, languages); |
| 127 base::string16 explanation = |
| 128 elided_realm.empty() |
| 129 ? l10n_util::GetStringFUTF16(IDS_LOGIN_DIALOG_DESCRIPTION_NO_REALM, |
| 130 authority) |
| 131 : l10n_util::GetStringFUTF16(IDS_LOGIN_DIALOG_DESCRIPTION, authority, |
| 132 elided_realm); |
| 133 |
| 134 password_manager::PasswordManager* password_manager = |
| 135 handler->GetPasswordManagerForLogin(); |
| 136 |
| 137 if (!password_manager) { |
| 138 #if defined(ENABLE_EXTENSIONS) |
| 139 // A WebContents in a <webview> (a GuestView type) does not have a password |
| 140 // manager, but still needs to be able to show login prompts. |
| 141 if (guest_view::GuestViewBase::FromWebContents(parent_contents)) { |
| 142 handler->BuildViewWithoutPasswordManager(explanation); |
| 143 return; |
| 144 } |
| 145 #endif |
| 146 handler->CancelAuth(); |
| 147 return; |
| 148 } |
| 149 |
| 150 if (password_manager && password_manager->client()->IsLoggingActive()) { |
| 151 password_manager::BrowserSavePasswordProgressLogger logger( |
| 152 password_manager->client()); |
| 153 logger.LogMessage( |
| 154 autofill::SavePasswordProgressLogger::STRING_SHOW_LOGIN_PROMPT_METHOD); |
| 155 } |
| 156 |
| 157 PasswordForm observed_form( |
| 158 MakeInputForPasswordManager(request_url, auth_info)); |
| 159 handler->BuildViewWithPasswordManager(explanation, password_manager, |
| 160 observed_form); |
| 161 } |
| 162 |
| 163 } // namespace |
| 164 |
89 // ---------------------------------------------------------------------------- | 165 // ---------------------------------------------------------------------------- |
90 // LoginHandler | 166 // LoginHandler |
91 | 167 |
92 LoginHandler::LoginModelData::LoginModelData( | 168 LoginHandler::LoginModelData::LoginModelData( |
93 password_manager::LoginModel* login_model, | 169 password_manager::LoginModel* login_model, |
94 const autofill::PasswordForm& observed_form) | 170 const autofill::PasswordForm& observed_form) |
95 : model(login_model), form(observed_form) { | 171 : model(login_model), form(observed_form) { |
96 DCHECK(model); | 172 DCHECK(model); |
97 } | 173 } |
98 | 174 |
99 LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info, | 175 LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info, |
100 net::URLRequest* request) | 176 net::URLRequest* request) |
101 : handled_auth_(false), | 177 : handled_auth_(false), |
102 auth_info_(auth_info), | 178 auth_info_(auth_info), |
103 request_(request), | 179 request_(request), |
104 http_network_session_( | 180 http_network_session_( |
105 request_->context()->http_transaction_factory()->GetSession()), | 181 request_->context()->http_transaction_factory()->GetSession()), |
106 password_manager_(NULL), | 182 password_manager_(NULL), |
107 login_model_(NULL) { | 183 login_model_(NULL) { |
108 // This constructor is called on the I/O thread, so we cannot load the nib | 184 // This constructor is called on the I/O thread, so we cannot load the nib |
109 // here. BuildView() will be invoked on the UI thread later, so wait with | 185 // here. BuildViewImpl() will be invoked on the UI thread later, so wait with |
110 // loading the nib until then. | 186 // loading the nib until then. |
111 DCHECK(request_) << "LoginHandler constructed with NULL request"; | 187 DCHECK(request_) << "LoginHandler constructed with NULL request"; |
112 DCHECK(auth_info_.get()) << "LoginHandler constructed with NULL auth info"; | 188 DCHECK(auth_info_.get()) << "LoginHandler constructed with NULL auth info"; |
113 | 189 |
114 AddRef(); // matched by LoginHandler::ReleaseSoon(). | 190 AddRef(); // matched by LoginHandler::ReleaseSoon(). |
115 | 191 |
116 BrowserThread::PostTask( | 192 BrowserThread::PostTask( |
117 BrowserThread::UI, FROM_HERE, | 193 BrowserThread::UI, FROM_HERE, |
118 base::Bind(&LoginHandler::AddObservers, this)); | 194 base::Bind(&LoginHandler::AddObservers, this)); |
119 | 195 |
120 if (!ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame( | 196 if (!ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame( |
121 &render_process_host_id_, &render_frame_id_)) { | 197 &render_process_host_id_, &render_frame_id_)) { |
122 NOTREACHED(); | 198 NOTREACHED(); |
123 } | 199 } |
124 } | 200 } |
125 | 201 |
126 void LoginHandler::OnRequestCancelled() { | 202 void LoginHandler::OnRequestCancelled() { |
127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) << | 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) << |
128 "Why is OnRequestCancelled called from the UI thread?"; | 204 "Why is OnRequestCancelled called from the UI thread?"; |
129 | 205 |
130 // Reference is no longer valid. | 206 // Reference is no longer valid. |
131 request_ = NULL; | 207 request_ = NULL; |
132 | 208 |
133 // Give up on auth if the request was cancelled. | 209 // Give up on auth if the request was cancelled. |
134 CancelAuth(); | 210 CancelAuth(); |
135 } | 211 } |
136 | 212 |
137 void LoginHandler::SetPasswordForm(const autofill::PasswordForm& form) { | 213 void LoginHandler::BuildViewWithPasswordManager( |
138 password_form_ = form; | 214 const base::string16& explanation, |
| 215 password_manager::PasswordManager* password_manager, |
| 216 const autofill::PasswordForm& observed_form) { |
| 217 password_manager_ = password_manager; |
| 218 password_form_ = observed_form; |
| 219 LoginHandler::LoginModelData model_data(password_manager, observed_form); |
| 220 BuildViewImpl(explanation, &model_data); |
139 } | 221 } |
140 | 222 |
141 void LoginHandler::SetPasswordManager( | 223 void LoginHandler::BuildViewWithoutPasswordManager( |
142 password_manager::PasswordManager* password_manager) { | 224 const base::string16& explanation) { |
143 password_manager_ = password_manager; | 225 BuildViewImpl(explanation, nullptr); |
144 } | 226 } |
145 | 227 |
146 WebContents* LoginHandler::GetWebContentsForLogin() const { | 228 WebContents* LoginHandler::GetWebContentsForLogin() const { |
147 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 229 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
148 | 230 |
149 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID( | 231 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID( |
150 render_process_host_id_, render_frame_id_); | 232 render_process_host_id_, render_frame_id_); |
151 return WebContents::FromRenderFrameHost(rfh); | 233 return WebContents::FromRenderFrameHost(rfh); |
152 } | 234 } |
153 | 235 |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 base::AutoLock lock(handled_auth_lock_); | 357 base::AutoLock lock(handled_auth_lock_); |
276 bool was_handled = handled_auth_; | 358 bool was_handled = handled_auth_; |
277 return was_handled; | 359 return was_handled; |
278 } | 360 } |
279 | 361 |
280 LoginHandler::~LoginHandler() { | 362 LoginHandler::~LoginHandler() { |
281 ResetModel(); | 363 ResetModel(); |
282 } | 364 } |
283 | 365 |
284 void LoginHandler::SetModel(LoginModelData model_data) { | 366 void LoginHandler::SetModel(LoginModelData model_data) { |
285 if (login_model_) | 367 ResetModel(); |
286 login_model_->RemoveObserver(this); | |
287 login_model_ = model_data.model; | 368 login_model_ = model_data.model; |
288 login_model_->AddObserverAndDeliverCredentials(this, model_data.form); | 369 login_model_->AddObserverAndDeliverCredentials(this, model_data.form); |
289 } | 370 } |
290 | 371 |
291 void LoginHandler::ResetModel() { | 372 void LoginHandler::ResetModel() { |
292 if (login_model_) | 373 if (login_model_) |
293 login_model_->RemoveObserver(this); | 374 login_model_->RemoveObserver(this); |
294 login_model_ = nullptr; | 375 login_model_ = nullptr; |
295 } | 376 } |
296 | 377 |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 if (!requesting_contents) | 512 if (!requesting_contents) |
432 return; | 513 return; |
433 // If a (blank) login interstitial was displayed, proceed so that the | 514 // If a (blank) login interstitial was displayed, proceed so that the |
434 // navigation is committed. | 515 // navigation is committed. |
435 content::InterstitialPage* interstitial_page = | 516 content::InterstitialPage* interstitial_page = |
436 requesting_contents->GetInterstitialPage(); | 517 requesting_contents->GetInterstitialPage(); |
437 if (interstitial_page) | 518 if (interstitial_page) |
438 interstitial_page->Proceed(); | 519 interstitial_page->Proceed(); |
439 } | 520 } |
440 | 521 |
441 // Helper to create a PasswordForm for PasswordManager to start looking for | |
442 // saved credentials. | |
443 PasswordForm MakeInputForPasswordManager(const GURL& request_url, | |
444 net::AuthChallengeInfo* auth_info, | |
445 LoginHandler* handler) { | |
446 PasswordForm dialog_form; | |
447 if (base::LowerCaseEqualsASCII(auth_info->scheme, "basic")) { | |
448 dialog_form.scheme = PasswordForm::SCHEME_BASIC; | |
449 } else if (base::LowerCaseEqualsASCII(auth_info->scheme, "digest")) { | |
450 dialog_form.scheme = PasswordForm::SCHEME_DIGEST; | |
451 } else { | |
452 dialog_form.scheme = PasswordForm::SCHEME_OTHER; | |
453 } | |
454 std::string host_and_port(auth_info->challenger.ToString()); | |
455 if (auth_info->is_proxy) { | |
456 std::string origin = host_and_port; | |
457 // We don't expect this to already start with http:// or https://. | |
458 DCHECK(origin.find("http://") != 0 && origin.find("https://") != 0); | |
459 origin = std::string("http://") + origin; | |
460 dialog_form.origin = GURL(origin); | |
461 } else if (!auth_info->challenger.Equals( | |
462 net::HostPortPair::FromURL(request_url))) { | |
463 dialog_form.origin = GURL(); | |
464 NOTREACHED(); // crbug.com/32718 | |
465 } else { | |
466 dialog_form.origin = GURL(request_url.scheme() + "://" + host_and_port); | |
467 } | |
468 dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, *auth_info); | |
469 // Set the password form for the handler (by copy). | |
470 handler->SetPasswordForm(dialog_form); | |
471 return dialog_form; | |
472 } | |
473 | |
474 void ShowLoginPrompt(const GURL& request_url, | |
475 net::AuthChallengeInfo* auth_info, | |
476 LoginHandler* handler) { | |
477 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
478 WebContents* parent_contents = handler->GetWebContentsForLogin(); | |
479 if (!parent_contents) | |
480 return; | |
481 prerender::PrerenderContents* prerender_contents = | |
482 prerender::PrerenderContents::FromWebContents(parent_contents); | |
483 if (prerender_contents) { | |
484 prerender_contents->Destroy(prerender::FINAL_STATUS_AUTH_NEEDED); | |
485 return; | |
486 } | |
487 | |
488 // The realm is controlled by the remote server, so there is no reason | |
489 // to believe it is of a reasonable length. | |
490 base::string16 elided_realm; | |
491 gfx::ElideString(base::UTF8ToUTF16(auth_info->realm), 120, &elided_realm); | |
492 | |
493 std::string languages; | |
494 content::WebContents* web_contents = handler->GetWebContentsForLogin(); | |
495 if (web_contents) { | |
496 Profile* profile = | |
497 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
498 if (profile) | |
499 languages = profile->GetPrefs()->GetString(prefs::kAcceptLanguages); | |
500 } | |
501 | |
502 base::string16 authority = | |
503 url_formatter::FormatUrlForSecurityDisplay(request_url, languages); | |
504 base::string16 explanation = | |
505 elided_realm.empty() | |
506 ? l10n_util::GetStringFUTF16(IDS_LOGIN_DIALOG_DESCRIPTION_NO_REALM, | |
507 authority) | |
508 : l10n_util::GetStringFUTF16(IDS_LOGIN_DIALOG_DESCRIPTION, authority, | |
509 elided_realm); | |
510 | |
511 password_manager::PasswordManager* password_manager = | |
512 handler->GetPasswordManagerForLogin(); | |
513 | |
514 if (!password_manager) { | |
515 #if defined(ENABLE_EXTENSIONS) | |
516 // A WebContents in a <webview> (a GuestView type) does not have a password | |
517 // manager, but still needs to be able to show login prompts. | |
518 if (guest_view::GuestViewBase::FromWebContents(parent_contents)) { | |
519 handler->BuildView(explanation, nullptr); | |
520 return; | |
521 } | |
522 #endif | |
523 handler->CancelAuth(); | |
524 return; | |
525 } | |
526 | |
527 if (password_manager && password_manager->client()->IsLoggingActive()) { | |
528 password_manager::BrowserSavePasswordProgressLogger logger( | |
529 password_manager->client()); | |
530 logger.LogMessage( | |
531 autofill::SavePasswordProgressLogger::STRING_SHOW_LOGIN_PROMPT_METHOD); | |
532 } | |
533 | |
534 handler->SetPasswordManager(password_manager); | |
535 | |
536 PasswordForm observed_form( | |
537 MakeInputForPasswordManager(request_url, auth_info, handler)); | |
538 LoginHandler::LoginModelData model_data(password_manager, observed_form); | |
539 handler->BuildView(explanation, &model_data); | |
540 } | |
541 | |
542 // This callback is run on the UI thread and creates a constrained window with | 522 // This callback is run on the UI thread and creates a constrained window with |
543 // a LoginView to prompt the user. If the prompt is triggered because of | 523 // a LoginView to prompt the user. If the prompt is triggered because of |
544 // a cross origin navigation in the main frame, a blank interstitial is first | 524 // a cross origin navigation in the main frame, a blank interstitial is first |
545 // created which in turn creates the LoginView. Otherwise, a LoginView is | 525 // created which in turn creates the LoginView. Otherwise, a LoginView is |
546 // directly in this callback. In both cases, the response will be sent to | 526 // directly in this callback. In both cases, the response will be sent to |
547 // LoginHandler, which then routes it to the net::URLRequest on the I/O thread. | 527 // LoginHandler, which then routes it to the net::URLRequest on the I/O thread. |
548 void LoginDialogCallback(const GURL& request_url, | 528 void LoginDialogCallback(const GURL& request_url, |
549 net::AuthChallengeInfo* auth_info, | 529 net::AuthChallengeInfo* auth_info, |
550 LoginHandler* handler, | 530 LoginHandler* handler, |
551 bool is_main_frame) { | 531 bool is_main_frame) { |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
603 net::URLRequest* request) { | 583 net::URLRequest* request) { |
604 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0; | 584 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0; |
605 LoginHandler* handler = LoginHandler::Create(auth_info, request); | 585 LoginHandler* handler = LoginHandler::Create(auth_info, request); |
606 BrowserThread::PostTask( | 586 BrowserThread::PostTask( |
607 BrowserThread::UI, FROM_HERE, | 587 BrowserThread::UI, FROM_HERE, |
608 base::Bind(&LoginDialogCallback, request->url(), | 588 base::Bind(&LoginDialogCallback, request->url(), |
609 make_scoped_refptr(auth_info), make_scoped_refptr(handler), | 589 make_scoped_refptr(auth_info), make_scoped_refptr(handler), |
610 is_main_frame)); | 590 is_main_frame)); |
611 return handler; | 591 return handler; |
612 } | 592 } |
| 593 |
| 594 // Get the signon_realm under which this auth info should be stored. |
| 595 // |
| 596 // The format of the signon_realm for proxy auth is: |
| 597 // proxy-host/auth-realm |
| 598 // The format of the signon_realm for server auth is: |
| 599 // url-scheme://url-host[:url-port]/auth-realm |
| 600 // |
| 601 // Be careful when changing this function, since you could make existing |
| 602 // saved logins un-retrievable. |
| 603 std::string GetSignonRealm(const GURL& url, |
| 604 const net::AuthChallengeInfo& auth_info) { |
| 605 std::string signon_realm; |
| 606 if (auth_info.is_proxy) { |
| 607 signon_realm = auth_info.challenger.ToString(); |
| 608 signon_realm.append("/"); |
| 609 } else { |
| 610 // Take scheme, host, and port from the url. |
| 611 signon_realm = url.GetOrigin().spec(); |
| 612 // This ends with a "/". |
| 613 } |
| 614 signon_realm.append(auth_info.realm); |
| 615 return signon_realm; |
| 616 } |
OLD | NEW |