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_handler.h" | 5 #include "chrome/browser/ui/login/login_handler.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 14 matching lines...) Expand all Loading... |
25 #include "components/url_formatter/elide_url.h" | 25 #include "components/url_formatter/elide_url.h" |
26 #include "content/public/browser/browser_thread.h" | 26 #include "content/public/browser/browser_thread.h" |
27 #include "content/public/browser/notification_registrar.h" | 27 #include "content/public/browser/notification_registrar.h" |
28 #include "content/public/browser/notification_service.h" | 28 #include "content/public/browser/notification_service.h" |
29 #include "content/public/browser/render_frame_host.h" | 29 #include "content/public/browser/render_frame_host.h" |
30 #include "content/public/browser/resource_dispatcher_host.h" | 30 #include "content/public/browser/resource_dispatcher_host.h" |
31 #include "content/public/browser/resource_request_info.h" | 31 #include "content/public/browser/resource_request_info.h" |
32 #include "content/public/browser/web_contents.h" | 32 #include "content/public/browser/web_contents.h" |
33 #include "content/public/common/origin_util.h" | 33 #include "content/public/common/origin_util.h" |
34 #include "net/base/auth.h" | 34 #include "net/base/auth.h" |
| 35 #include "net/base/host_port_pair.h" |
35 #include "net/base/load_flags.h" | 36 #include "net/base/load_flags.h" |
36 #include "net/http/http_auth_scheme.h" | 37 #include "net/http/http_auth_scheme.h" |
37 #include "net/http/http_transaction_factory.h" | 38 #include "net/http/http_transaction_factory.h" |
38 #include "net/url_request/url_request.h" | 39 #include "net/url_request/url_request.h" |
39 #include "net/url_request/url_request_context.h" | 40 #include "net/url_request/url_request_context.h" |
40 #include "ui/base/l10n/l10n_util.h" | 41 #include "ui/base/l10n/l10n_util.h" |
41 #include "ui/gfx/text_elider.h" | 42 #include "ui/gfx/text_elider.h" |
42 | 43 |
43 #if defined(ENABLE_EXTENSIONS) | 44 #if defined(ENABLE_EXTENSIONS) |
44 #include "components/guest_view/browser/guest_view_base.h" | 45 #include "components/guest_view/browser/guest_view_base.h" |
(...skipping 13 matching lines...) Expand all Loading... |
58 | 59 |
59 namespace { | 60 namespace { |
60 | 61 |
61 // Helper to remove the ref from an net::URLRequest to the LoginHandler. | 62 // Helper to remove the ref from an net::URLRequest to the LoginHandler. |
62 // Should only be called from the IO thread, since it accesses an | 63 // Should only be called from the IO thread, since it accesses an |
63 // net::URLRequest. | 64 // net::URLRequest. |
64 void ResetLoginHandlerForRequest(net::URLRequest* request) { | 65 void ResetLoginHandlerForRequest(net::URLRequest* request) { |
65 ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request); | 66 ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request); |
66 } | 67 } |
67 | 68 |
68 // Helper to create a PasswordForm for PasswordManager to start looking for | |
69 // saved credentials. | |
70 PasswordForm MakeInputForPasswordManager(const GURL& request_url, | |
71 net::AuthChallengeInfo* auth_info) { | |
72 PasswordForm dialog_form; | |
73 if (base::LowerCaseEqualsASCII(auth_info->scheme, net::kBasicAuthScheme)) { | |
74 dialog_form.scheme = PasswordForm::SCHEME_BASIC; | |
75 } else if (base::LowerCaseEqualsASCII(auth_info->scheme, | |
76 net::kDigestAuthScheme)) { | |
77 dialog_form.scheme = PasswordForm::SCHEME_DIGEST; | |
78 } else { | |
79 dialog_form.scheme = PasswordForm::SCHEME_OTHER; | |
80 } | |
81 std::string host_and_port(auth_info->challenger.ToString()); | |
82 if (auth_info->is_proxy) { | |
83 std::string origin = host_and_port; | |
84 // We don't expect this to already start with http:// or https://. | |
85 DCHECK(origin.find("http://") != 0 && origin.find("https://") != 0); | |
86 origin = std::string("http://") + origin; | |
87 dialog_form.origin = GURL(origin); | |
88 } else if (!auth_info->challenger.Equals( | |
89 net::HostPortPair::FromURL(request_url))) { | |
90 dialog_form.origin = GURL(); | |
91 NOTREACHED(); // crbug.com/32718 | |
92 } else { | |
93 dialog_form.origin = GURL(request_url.scheme() + "://" + host_and_port); | |
94 } | |
95 dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, *auth_info); | |
96 return dialog_form; | |
97 } | |
98 | |
99 void ShowLoginPrompt(const GURL& request_url, | |
100 net::AuthChallengeInfo* auth_info, | |
101 LoginHandler* handler) { | |
102 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
103 WebContents* parent_contents = handler->GetWebContentsForLogin(); | |
104 if (!parent_contents) | |
105 return; | |
106 prerender::PrerenderContents* prerender_contents = | |
107 prerender::PrerenderContents::FromWebContents(parent_contents); | |
108 if (prerender_contents) { | |
109 prerender_contents->Destroy(prerender::FINAL_STATUS_AUTH_NEEDED); | |
110 return; | |
111 } | |
112 | |
113 base::string16 authority = l10n_util::GetStringFUTF16( | |
114 auth_info->is_proxy ? IDS_LOGIN_DIALOG_PROXY_AUTHORITY | |
115 : IDS_LOGIN_DIALOG_AUTHORITY, | |
116 url_formatter::FormatUrlForSecurityDisplay(request_url)); | |
117 base::string16 explanation; | |
118 if (!content::IsOriginSecure(request_url)) { | |
119 explanation = | |
120 l10n_util::GetStringUTF16(IDS_WEBSITE_SETTINGS_NON_SECURE_TRANSPORT); | |
121 } | |
122 | |
123 password_manager::PasswordManager* password_manager = | |
124 handler->GetPasswordManagerForLogin(); | |
125 | |
126 if (!password_manager) { | |
127 #if defined(ENABLE_EXTENSIONS) | |
128 // A WebContents in a <webview> (a GuestView type) does not have a password | |
129 // manager, but still needs to be able to show login prompts. | |
130 const auto* guest = | |
131 guest_view::GuestViewBase::FromWebContents(parent_contents); | |
132 if (guest && | |
133 extensions::GetViewType(guest->owner_web_contents()) != | |
134 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { | |
135 handler->BuildViewWithoutPasswordManager(authority, explanation); | |
136 return; | |
137 } | |
138 #endif | |
139 handler->CancelAuth(); | |
140 return; | |
141 } | |
142 | |
143 if (password_manager && | |
144 password_manager->client()->GetLogManager()->IsLoggingActive()) { | |
145 password_manager::BrowserSavePasswordProgressLogger logger( | |
146 password_manager->client()->GetLogManager()); | |
147 logger.LogMessage( | |
148 autofill::SavePasswordProgressLogger::STRING_SHOW_LOGIN_PROMPT_METHOD); | |
149 } | |
150 | |
151 PasswordForm observed_form( | |
152 MakeInputForPasswordManager(request_url, auth_info)); | |
153 handler->BuildViewWithPasswordManager(authority, explanation, | |
154 password_manager, observed_form); | |
155 } | |
156 | |
157 } // namespace | 69 } // namespace |
158 | 70 |
159 // ---------------------------------------------------------------------------- | 71 // ---------------------------------------------------------------------------- |
160 // LoginHandler | 72 // LoginHandler |
161 | 73 |
162 LoginHandler::LoginModelData::LoginModelData( | 74 LoginHandler::LoginModelData::LoginModelData( |
163 password_manager::LoginModel* login_model, | 75 password_manager::LoginModel* login_model, |
164 const autofill::PasswordForm& observed_form) | 76 const autofill::PasswordForm& observed_form) |
165 : model(login_model), form(observed_form) { | 77 : model(login_model), form(observed_form) { |
166 DCHECK(model); | 78 DCHECK(model); |
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
505 } | 417 } |
506 | 418 |
507 // Closes the view_contents from the UI loop. | 419 // Closes the view_contents from the UI loop. |
508 void LoginHandler::CloseContentsDeferred() { | 420 void LoginHandler::CloseContentsDeferred() { |
509 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 421 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
510 CloseDialog(); | 422 CloseDialog(); |
511 if (interstitial_delegate_) | 423 if (interstitial_delegate_) |
512 interstitial_delegate_->Proceed(); | 424 interstitial_delegate_->Proceed(); |
513 } | 425 } |
514 | 426 |
515 // This callback is run on the UI thread and creates a constrained window with | 427 // static |
516 // a LoginView to prompt the user. If the prompt is triggered because of | 428 std::string LoginHandler::GetSignonRealm( |
517 // a cross origin navigation in the main frame, a blank interstitial is first | 429 const GURL& url, |
518 // created which in turn creates the LoginView. Otherwise, a LoginView is | 430 const net::AuthChallengeInfo& auth_info) { |
519 // directly in this callback. In both cases, the response will be sent to | 431 std::string signon_realm; |
520 // LoginHandler, which then routes it to the net::URLRequest on the I/O thread. | 432 if (auth_info.is_proxy) { |
521 void LoginDialogCallback(const GURL& request_url, | 433 // Historically we've been storing the signon realm for proxies using |
522 net::AuthChallengeInfo* auth_info, | 434 // net::HostPortPair::ToString(). |
523 LoginHandler* handler, | 435 net::HostPortPair host_port_pair = |
524 bool is_main_frame) { | 436 net::HostPortPair::FromURL(GURL(auth_info.challenger.Serialize())); |
| 437 signon_realm = host_port_pair.ToString(); |
| 438 signon_realm.append("/"); |
| 439 } else { |
| 440 // Take scheme, host, and port from the url. |
| 441 signon_realm = url.GetOrigin().spec(); |
| 442 // This ends with a "/". |
| 443 } |
| 444 signon_realm.append(auth_info.realm); |
| 445 return signon_realm; |
| 446 } |
| 447 |
| 448 // static |
| 449 PasswordForm LoginHandler::MakeInputForPasswordManager( |
| 450 const GURL& request_url, |
| 451 const net::AuthChallengeInfo& auth_info) { |
| 452 PasswordForm dialog_form; |
| 453 if (base::LowerCaseEqualsASCII(auth_info.scheme, net::kBasicAuthScheme)) { |
| 454 dialog_form.scheme = PasswordForm::SCHEME_BASIC; |
| 455 } else if (base::LowerCaseEqualsASCII(auth_info.scheme, |
| 456 net::kDigestAuthScheme)) { |
| 457 dialog_form.scheme = PasswordForm::SCHEME_DIGEST; |
| 458 } else { |
| 459 dialog_form.scheme = PasswordForm::SCHEME_OTHER; |
| 460 } |
| 461 if (auth_info.is_proxy) { |
| 462 dialog_form.origin = GURL(auth_info.challenger.Serialize()); |
| 463 } else if (!auth_info.challenger.IsSameOriginWith(url::Origin(request_url))) { |
| 464 dialog_form.origin = GURL(); |
| 465 NOTREACHED(); // crbug.com/32718 |
| 466 } else { |
| 467 dialog_form.origin = GURL(auth_info.challenger.Serialize()); |
| 468 } |
| 469 dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, auth_info); |
| 470 return dialog_form; |
| 471 } |
| 472 |
| 473 // static |
| 474 void LoginHandler::GetDialogStrings(const GURL& request_url, |
| 475 const net::AuthChallengeInfo& auth_info, |
| 476 base::string16* authority, |
| 477 base::string16* explanation) { |
| 478 GURL authority_url; |
| 479 |
| 480 if (auth_info.is_proxy) { |
| 481 *authority = l10n_util::GetStringFUTF16( |
| 482 IDS_LOGIN_DIALOG_PROXY_AUTHORITY, |
| 483 url_formatter::FormatOriginForSecurityDisplay( |
| 484 auth_info.challenger, url_formatter::SchemeDisplay::SHOW)); |
| 485 authority_url = GURL(auth_info.challenger.Serialize()); |
| 486 } else { |
| 487 *authority = l10n_util::GetStringFUTF16( |
| 488 IDS_LOGIN_DIALOG_AUTHORITY, |
| 489 url_formatter::FormatUrlForSecurityDisplay(request_url)); |
| 490 authority_url = request_url; |
| 491 } |
| 492 |
| 493 if (!content::IsOriginSecure(authority_url)) { |
| 494 // TODO(asanka): The string should be different for proxies and servers. |
| 495 // http://crbug.com/620756 |
| 496 *explanation = |
| 497 l10n_util::GetStringUTF16(IDS_WEBSITE_SETTINGS_NON_SECURE_TRANSPORT); |
| 498 } else { |
| 499 explanation->clear(); |
| 500 } |
| 501 } |
| 502 |
| 503 // static |
| 504 void LoginHandler::ShowLoginPrompt(const GURL& request_url, |
| 505 net::AuthChallengeInfo* auth_info, |
| 506 LoginHandler* handler) { |
| 507 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 508 WebContents* parent_contents = handler->GetWebContentsForLogin(); |
| 509 if (!parent_contents) |
| 510 return; |
| 511 prerender::PrerenderContents* prerender_contents = |
| 512 prerender::PrerenderContents::FromWebContents(parent_contents); |
| 513 if (prerender_contents) { |
| 514 prerender_contents->Destroy(prerender::FINAL_STATUS_AUTH_NEEDED); |
| 515 return; |
| 516 } |
| 517 |
| 518 base::string16 authority; |
| 519 base::string16 explanation; |
| 520 GetDialogStrings(request_url, *auth_info, &authority, &explanation); |
| 521 |
| 522 password_manager::PasswordManager* password_manager = |
| 523 handler->GetPasswordManagerForLogin(); |
| 524 |
| 525 if (!password_manager) { |
| 526 #if defined(ENABLE_EXTENSIONS) |
| 527 // A WebContents in a <webview> (a GuestView type) does not have a password |
| 528 // manager, but still needs to be able to show login prompts. |
| 529 const auto* guest = |
| 530 guest_view::GuestViewBase::FromWebContents(parent_contents); |
| 531 if (guest && |
| 532 extensions::GetViewType(guest->owner_web_contents()) != |
| 533 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { |
| 534 handler->BuildViewWithoutPasswordManager(authority, explanation); |
| 535 return; |
| 536 } |
| 537 #endif |
| 538 handler->CancelAuth(); |
| 539 return; |
| 540 } |
| 541 |
| 542 if (password_manager && |
| 543 password_manager->client()->GetLogManager()->IsLoggingActive()) { |
| 544 password_manager::BrowserSavePasswordProgressLogger logger( |
| 545 password_manager->client()->GetLogManager()); |
| 546 logger.LogMessage( |
| 547 autofill::SavePasswordProgressLogger::STRING_SHOW_LOGIN_PROMPT_METHOD); |
| 548 } |
| 549 |
| 550 PasswordForm observed_form( |
| 551 LoginHandler::MakeInputForPasswordManager(request_url, *auth_info)); |
| 552 handler->BuildViewWithPasswordManager(authority, explanation, |
| 553 password_manager, observed_form); |
| 554 } |
| 555 |
| 556 // static |
| 557 void LoginHandler::LoginDialogCallback(const GURL& request_url, |
| 558 net::AuthChallengeInfo* auth_info, |
| 559 LoginHandler* handler, |
| 560 bool is_main_frame) { |
525 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 561 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
526 WebContents* parent_contents = handler->GetWebContentsForLogin(); | 562 WebContents* parent_contents = handler->GetWebContentsForLogin(); |
527 if (!parent_contents || handler->WasAuthHandled()) { | 563 if (!parent_contents || handler->WasAuthHandled()) { |
528 // The request may have been canceled, or it may be for a renderer not | 564 // The request may have been canceled, or it may be for a renderer not |
529 // hosted by a tab (e.g. an extension). Cancel just in case (canceling twice | 565 // hosted by a tab (e.g. an extension). Cancel just in case (canceling twice |
530 // is a no-op). | 566 // is a no-op). |
531 handler->CancelAuth(); | 567 handler->CancelAuth(); |
532 return; | 568 return; |
533 } | 569 } |
534 | 570 |
535 // Check if this is a main frame navigation and | 571 // Check if this is a main frame navigation and |
536 // (a) if the request is cross origin or | 572 // (a) if the request is cross origin or |
537 // (b) if an interstitial is already being shown. | 573 // (b) if an interstitial is already being shown or |
| 574 // (c) the prompt is for proxy authentication |
538 // | 575 // |
539 // For (a), there are two different ways the navigation can occur: | 576 // For (a), there are two different ways the navigation can occur: |
540 // 1- The user enters the resource URL in the omnibox. | 577 // 1- The user enters the resource URL in the omnibox. |
541 // 2- The page redirects to the resource. | 578 // 2- The page redirects to the resource. |
542 // In both cases, the last committed URL is different than the resource URL, | 579 // In both cases, the last committed URL is different than the resource URL, |
543 // so checking it is sufficient. | 580 // so checking it is sufficient. |
544 // Note that (1) will not be true once site isolation is enabled, as any | 581 // Note that (1) will not be true once site isolation is enabled, as any |
545 // navigation could cause a cross-process swap, including link clicks. | 582 // navigation could cause a cross-process swap, including link clicks. |
546 // | 583 // |
547 // For (b), the login interstitial should always replace an existing | 584 // For (b), the login interstitial should always replace an existing |
548 // interstitial. This is because |LoginHandler::CloseContentsDeferred| tries | 585 // interstitial. This is because |LoginHandler::CloseContentsDeferred| tries |
549 // to proceed whatever interstitial is being shown when the login dialog is | 586 // to proceed whatever interstitial is being shown when the login dialog is |
550 // closed, so that interstitial should only be a login interstitial. | 587 // closed, so that interstitial should only be a login interstitial. |
551 if (is_main_frame && (parent_contents->ShowingInterstitialPage() || | 588 // |
552 parent_contents->GetLastCommittedURL().GetOrigin() != | 589 // For (c), the authority information in the omnibox will be (and should be) |
553 request_url.GetOrigin())) { | 590 // different from the authority information in the authentication prompt. An |
| 591 // interstitial with an empty URL clears the omnibox and reduces the possible |
| 592 // user confusion that may result from the different authority information |
| 593 // being displayed simultaneously. This is specially important when the proxy |
| 594 // is accessed via an open connection while the target server is considered |
| 595 // secure. |
| 596 if (is_main_frame && |
| 597 (parent_contents->ShowingInterstitialPage() || auth_info->is_proxy || |
| 598 parent_contents->GetLastCommittedURL().GetOrigin() != |
| 599 request_url.GetOrigin())) { |
554 // Show a blank interstitial for main-frame, cross origin requests | 600 // Show a blank interstitial for main-frame, cross origin requests |
555 // so that the correct URL is shown in the omnibox. | 601 // so that the correct URL is shown in the omnibox. |
556 base::Closure callback = | 602 base::Closure callback = |
557 base::Bind(&ShowLoginPrompt, request_url, base::RetainedRef(auth_info), | 603 base::Bind(&LoginHandler::ShowLoginPrompt, request_url, |
558 base::RetainedRef(handler)); | 604 base::RetainedRef(auth_info), base::RetainedRef(handler)); |
559 // The interstitial delegate is owned by the interstitial that it creates. | 605 // The interstitial delegate is owned by the interstitial that it creates. |
560 // This cancels any existing interstitial. | 606 // This cancels any existing interstitial. |
561 handler->SetInterstitialDelegate( | 607 handler->SetInterstitialDelegate( |
562 (new LoginInterstitialDelegate(parent_contents, request_url, callback)) | 608 (new LoginInterstitialDelegate( |
| 609 parent_contents, auth_info->is_proxy ? GURL() : request_url, |
| 610 callback)) |
563 ->GetWeakPtr()); | 611 ->GetWeakPtr()); |
564 } else { | 612 } else { |
565 ShowLoginPrompt(request_url, auth_info, handler); | 613 ShowLoginPrompt(request_url, auth_info, handler); |
566 } | 614 } |
567 } | 615 } |
568 | 616 |
569 // ---------------------------------------------------------------------------- | 617 // ---------------------------------------------------------------------------- |
570 // Public API | 618 // Public API |
571 | 619 |
572 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info, | 620 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info, |
573 net::URLRequest* request) { | 621 net::URLRequest* request) { |
574 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0; | 622 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0; |
575 LoginHandler* handler = LoginHandler::Create(auth_info, request); | 623 LoginHandler* handler = LoginHandler::Create(auth_info, request); |
576 BrowserThread::PostTask( | 624 BrowserThread::PostTask( |
577 BrowserThread::UI, FROM_HERE, | 625 BrowserThread::UI, FROM_HERE, |
578 base::Bind(&LoginDialogCallback, request->url(), | 626 base::Bind(&LoginHandler::LoginDialogCallback, request->url(), |
579 base::RetainedRef(auth_info), base::RetainedRef(handler), | 627 base::RetainedRef(auth_info), base::RetainedRef(handler), |
580 is_main_frame)); | 628 is_main_frame)); |
581 return handler; | 629 return handler; |
582 } | 630 } |
583 | 631 |
584 // Get the signon_realm under which this auth info should be stored. | |
585 // | |
586 // The format of the signon_realm for proxy auth is: | |
587 // proxy-host/auth-realm | |
588 // The format of the signon_realm for server auth is: | |
589 // url-scheme://url-host[:url-port]/auth-realm | |
590 // | |
591 // Be careful when changing this function, since you could make existing | |
592 // saved logins un-retrievable. | |
593 std::string GetSignonRealm(const GURL& url, | |
594 const net::AuthChallengeInfo& auth_info) { | |
595 std::string signon_realm; | |
596 if (auth_info.is_proxy) { | |
597 signon_realm = auth_info.challenger.ToString(); | |
598 signon_realm.append("/"); | |
599 } else { | |
600 // Take scheme, host, and port from the url. | |
601 signon_realm = url.GetOrigin().spec(); | |
602 // This ends with a "/". | |
603 } | |
604 signon_realm.append(auth_info.realm); | |
605 return signon_realm; | |
606 } | |
OLD | NEW |