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; | |
asanka
2016/06/14 18:50:48
vabr: This logic doesn't distinguish between http
vabr (Chromium)
2016/06/15 08:50:06
Thanks for the heads-up. I agree with your argumen
| |
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); | |
asanka
2016/06/14 18:50:48
vabr: Note that host_and_port will always contain
vabr (Chromium)
2016/06/15 08:50:06
Acknowledged.
| |
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 != url::Origin(request_url)) { | |
meacer
2016/06/16 01:12:14
Why not use !IsSameOrigin here? It sounds a bit he
asanka
2016/06/16 16:25:47
The addition of the operator follows the style gui
| |
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. | |
vabr (Chromium)
2016/06/15 08:50:06
nit: Could you please make this a TODO(crbug.com/X
asanka
2016/06/16 16:25:47
Added one. The plan is to pretty much fix it immed
vabr (Chromium)
2016/06/16 16:43:21
Acknowledged.
| |
495 *explanation = | |
496 l10n_util::GetStringUTF16(IDS_WEBSITE_SETTINGS_NON_SECURE_TRANSPORT); | |
497 } else { | |
498 explanation->clear(); | |
499 } | |
500 } | |
501 | |
502 // static | |
503 void LoginHandler::ShowLoginPrompt(const GURL& request_url, | |
504 net::AuthChallengeInfo* auth_info, | |
505 LoginHandler* handler) { | |
506 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
507 WebContents* parent_contents = handler->GetWebContentsForLogin(); | |
508 if (!parent_contents) | |
509 return; | |
510 prerender::PrerenderContents* prerender_contents = | |
511 prerender::PrerenderContents::FromWebContents(parent_contents); | |
512 if (prerender_contents) { | |
513 prerender_contents->Destroy(prerender::FINAL_STATUS_AUTH_NEEDED); | |
514 return; | |
515 } | |
516 | |
517 base::string16 authority; | |
518 base::string16 explanation; | |
519 GetDialogStrings(request_url, *auth_info, &authority, &explanation); | |
520 | |
521 password_manager::PasswordManager* password_manager = | |
522 handler->GetPasswordManagerForLogin(); | |
523 | |
524 if (!password_manager) { | |
525 #if defined(ENABLE_EXTENSIONS) | |
526 // A WebContents in a <webview> (a GuestView type) does not have a password | |
527 // manager, but still needs to be able to show login prompts. | |
528 const auto* guest = | |
529 guest_view::GuestViewBase::FromWebContents(parent_contents); | |
530 if (guest && | |
531 extensions::GetViewType(guest->owner_web_contents()) != | |
532 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { | |
533 handler->BuildViewWithoutPasswordManager(authority, explanation); | |
534 return; | |
535 } | |
536 #endif | |
537 handler->CancelAuth(); | |
538 return; | |
539 } | |
540 | |
541 if (password_manager && | |
542 password_manager->client()->GetLogManager()->IsLoggingActive()) { | |
543 password_manager::BrowserSavePasswordProgressLogger logger( | |
544 password_manager->client()->GetLogManager()); | |
545 logger.LogMessage( | |
546 autofill::SavePasswordProgressLogger::STRING_SHOW_LOGIN_PROMPT_METHOD); | |
547 } | |
548 | |
549 PasswordForm observed_form( | |
550 LoginHandler::MakeInputForPasswordManager(request_url, *auth_info)); | |
551 handler->BuildViewWithPasswordManager(authority, explanation, | |
552 password_manager, observed_form); | |
553 } | |
554 | |
555 // static | |
556 void LoginHandler::LoginDialogCallback(const GURL& request_url, | |
557 net::AuthChallengeInfo* auth_info, | |
558 LoginHandler* handler, | |
559 bool is_main_frame) { | |
525 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 560 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
526 WebContents* parent_contents = handler->GetWebContentsForLogin(); | 561 WebContents* parent_contents = handler->GetWebContentsForLogin(); |
527 if (!parent_contents || handler->WasAuthHandled()) { | 562 if (!parent_contents || handler->WasAuthHandled()) { |
528 // The request may have been canceled, or it may be for a renderer not | 563 // 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 | 564 // hosted by a tab (e.g. an extension). Cancel just in case (canceling twice |
530 // is a no-op). | 565 // is a no-op). |
531 handler->CancelAuth(); | 566 handler->CancelAuth(); |
532 return; | 567 return; |
533 } | 568 } |
534 | 569 |
535 // Check if this is a main frame navigation and | 570 // Check if this is a main frame navigation and |
536 // (a) if the request is cross origin or | 571 // (a) if the request is cross origin or |
537 // (b) if an interstitial is already being shown. | 572 // (b) if an interstitial is already being shown. |
538 // | 573 // |
539 // For (a), there are two different ways the navigation can occur: | 574 // For (a), there are two different ways the navigation can occur: |
540 // 1- The user enters the resource URL in the omnibox. | 575 // 1- The user enters the resource URL in the omnibox. |
541 // 2- The page redirects to the resource. | 576 // 2- The page redirects to the resource. |
542 // In both cases, the last committed URL is different than the resource URL, | 577 // In both cases, the last committed URL is different than the resource URL, |
543 // so checking it is sufficient. | 578 // so checking it is sufficient. |
544 // Note that (1) will not be true once site isolation is enabled, as any | 579 // 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. | 580 // navigation could cause a cross-process swap, including link clicks. |
546 // | 581 // |
547 // For (b), the login interstitial should always replace an existing | 582 // For (b), the login interstitial should always replace an existing |
548 // interstitial. This is because |LoginHandler::CloseContentsDeferred| tries | 583 // interstitial. This is because |LoginHandler::CloseContentsDeferred| tries |
549 // to proceed whatever interstitial is being shown when the login dialog is | 584 // to proceed whatever interstitial is being shown when the login dialog is |
550 // closed, so that interstitial should only be a login interstitial. | 585 // closed, so that interstitial should only be a login interstitial. |
551 if (is_main_frame && (parent_contents->ShowingInterstitialPage() || | 586 if (is_main_frame && |
552 parent_contents->GetLastCommittedURL().GetOrigin() != | 587 (parent_contents->ShowingInterstitialPage() || auth_info->is_proxy || |
meacer
2016/06/16 01:12:14
nit: Can you update the comment above and add the
asanka
2016/06/16 16:25:47
Done. Thanks, missed the comment in the first pass
| |
553 request_url.GetOrigin())) { | 588 parent_contents->GetLastCommittedURL().GetOrigin() != |
589 request_url.GetOrigin())) { | |
554 // Show a blank interstitial for main-frame, cross origin requests | 590 // Show a blank interstitial for main-frame, cross origin requests |
555 // so that the correct URL is shown in the omnibox. | 591 // so that the correct URL is shown in the omnibox. |
556 base::Closure callback = | 592 base::Closure callback = |
557 base::Bind(&ShowLoginPrompt, request_url, base::RetainedRef(auth_info), | 593 base::Bind(&LoginHandler::ShowLoginPrompt, request_url, |
558 base::RetainedRef(handler)); | 594 base::RetainedRef(auth_info), base::RetainedRef(handler)); |
559 // The interstitial delegate is owned by the interstitial that it creates. | 595 // The interstitial delegate is owned by the interstitial that it creates. |
560 // This cancels any existing interstitial. | 596 // This cancels any existing interstitial. |
561 handler->SetInterstitialDelegate( | 597 handler->SetInterstitialDelegate( |
562 (new LoginInterstitialDelegate(parent_contents, request_url, callback)) | 598 (new LoginInterstitialDelegate( |
599 parent_contents, auth_info->is_proxy ? GURL() : request_url, | |
600 callback)) | |
563 ->GetWeakPtr()); | 601 ->GetWeakPtr()); |
564 } else { | 602 } else { |
565 ShowLoginPrompt(request_url, auth_info, handler); | 603 ShowLoginPrompt(request_url, auth_info, handler); |
566 } | 604 } |
567 } | 605 } |
568 | 606 |
569 // ---------------------------------------------------------------------------- | 607 // ---------------------------------------------------------------------------- |
570 // Public API | 608 // Public API |
571 | 609 |
572 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info, | 610 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info, |
573 net::URLRequest* request) { | 611 net::URLRequest* request) { |
574 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0; | 612 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0; |
575 LoginHandler* handler = LoginHandler::Create(auth_info, request); | 613 LoginHandler* handler = LoginHandler::Create(auth_info, request); |
576 BrowserThread::PostTask( | 614 BrowserThread::PostTask( |
577 BrowserThread::UI, FROM_HERE, | 615 BrowserThread::UI, FROM_HERE, |
578 base::Bind(&LoginDialogCallback, request->url(), | 616 base::Bind(&LoginHandler::LoginDialogCallback, request->url(), |
579 base::RetainedRef(auth_info), base::RetainedRef(handler), | 617 base::RetainedRef(auth_info), base::RetainedRef(handler), |
580 is_main_frame)); | 618 is_main_frame)); |
581 return handler; | 619 return handler; |
582 } | 620 } |
583 | 621 |
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 |