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 3// found in the LICENSE file. 4 5#include <algorithm> 6#include <list> 7#include <map> 8 9#include "base/metrics/field_trial.h" 10#include "base/strings/utf_string_conversions.h" 11#include "chrome/browser/chrome_notification_types.h" 12#include "chrome/browser/prerender/prerender_manager.h" 13#include "chrome/browser/ui/browser.h" 14#include "chrome/browser/ui/browser_commands.h" 15#include "chrome/browser/ui/login/login_prompt.h" 16#include "chrome/browser/ui/login/login_prompt_test_utils.h" 17#include "chrome/browser/ui/tabs/tab_strip_model.h" 18#include "chrome/test/base/in_process_browser_test.h" 19#include "chrome/test/base/ui_test_utils.h" 20#include "content/public/browser/interstitial_page.h" 21#include "content/public/browser/notification_details.h" 22#include "content/public/browser/notification_source.h" 23#include "content/public/browser/web_contents.h" 24#include "content/public/test/browser_test_utils.h" 25#include "content/public/test/test_navigation_observer.h" 26#include "net/base/auth.h" 27#include "net/dns/mock_host_resolver.h" 28#include "net/test/spawned_test_server/spawned_test_server.h" 29 30using content::NavigationController; 31using content::OpenURLParams; 32using content::Referrer; 33 34namespace { 35 36class LoginPromptBrowserTest : public InProcessBrowserTest { 37 public: 38 LoginPromptBrowserTest() 39 : bad_password_("incorrect"), 40 bad_username_("nouser"), 41 password_("secret"), 42 username_basic_("basicuser"), 43 username_digest_("digestuser") { 44 auth_map_["foo"] = AuthInfo("testuser", "foopassword"); 45 auth_map_["bar"] = AuthInfo("testuser", "barpassword"); 46 auth_map_["testrealm"] = AuthInfo(username_basic_, password_); 47 } 48 49 protected: 50 struct AuthInfo { 51 std::string username_; 52 std::string password_; 53 54 AuthInfo() {} 55 56 AuthInfo(const std::string& username, 57 const std::string& password) 58 : username_(username), password_(password) {} 59 }; 60 61 typedef std::map<std::string, AuthInfo> AuthMap; 62 63 void SetAuthFor(LoginHandler* handler); 64 65 void TestCrossOriginPrompt(const GURL& visit_url, 66 const std::string& landing_host) const; 67 68 AuthMap auth_map_; 69 std::string bad_password_; 70 std::string bad_username_; 71 std::string password_; 72 std::string username_basic_; 73 std::string username_digest_; 74}; 75 76void LoginPromptBrowserTest::SetAuthFor(LoginHandler* handler) { 77 const net::AuthChallengeInfo* challenge = handler->auth_info(); 78 79 ASSERT_TRUE(challenge); 80 AuthMap::iterator i = auth_map_.find(challenge->realm); 81 EXPECT_TRUE(auth_map_.end() != i); 82 if (i != auth_map_.end()) { 83 const AuthInfo& info = i->second; 84 handler->SetAuth(base::UTF8ToUTF16(info.username_), 85 base::UTF8ToUTF16(info.password_)); 86 } 87} 88 89class InterstitialObserver : public content::WebContentsObserver { 90 public: 91 InterstitialObserver(content::WebContents* web_contents, 92 const base::Closure& attach_callback, 93 const base::Closure& detach_callback) 94 : WebContentsObserver(web_contents), 95 attach_callback_(attach_callback), 96 detach_callback_(detach_callback) { 97 } 98 99 virtual void DidAttachInterstitialPage() OVERRIDE { 100 attach_callback_.Run(); 101 } 102 103 virtual void DidDetachInterstitialPage() OVERRIDE { 104 detach_callback_.Run(); 105 } 106 107 private: 108 base::Closure attach_callback_; 109 base::Closure detach_callback_; 110 111 DISALLOW_COPY_AND_ASSIGN(InterstitialObserver); 112}; 113 114void WaitForInterstitialAttach(content::WebContents* web_contents) { 115 scoped_refptr<content::MessageLoopRunner> interstitial_attach_loop_runner( 116 new content::MessageLoopRunner); 117 InterstitialObserver observer( 118 web_contents, 119 interstitial_attach_loop_runner->QuitClosure(), 120 base::Closure()); 121 if (!content::InterstitialPage::GetInterstitialPage(web_contents)) 122 interstitial_attach_loop_runner->Run(); 123} 124 125const char kPrefetchAuthPage[] = "files/login/prefetch.html"; 126 127const char kMultiRealmTestPage[] = "files/login/multi_realm.html"; 128const int kMultiRealmTestRealmCount = 2; 129 130const char kSingleRealmTestPage[] = "files/login/single_realm.html"; 131 132const char* kAuthBasicPage = "auth-basic"; 133const char* kAuthDigestPage = "auth-digest"; 134 135base::string16 ExpectedTitleFromAuth(const base::string16& username, 136 const base::string16& password) { 137 // The TestServer sets the title to username/password on successful login. 138 return username + base::UTF8ToUTF16("/") + password; 139} 140 141// Confirm that <link rel="prefetch"> targetting an auth required 142// resource does not provide a login dialog. These types of requests 143// should instead just cancel the auth. 144 145// Unfortunately, this test doesn't assert on anything for its 146// correctness. Instead, it relies on the auth dialog blocking the 147// browser, and triggering a timeout to cause failure when the 148// prefetch resource requires authorization. 149IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, PrefetchAuthCancels) { 150 ASSERT_TRUE(test_server()->Start()); 151 152 GURL test_page = test_server()->GetURL(kPrefetchAuthPage); 153 154 class SetPrefetchForTest { 155 public: 156 explicit SetPrefetchForTest(bool prefetch) 157 : old_prerender_mode_(prerender::PrerenderManager::GetMode()) { 158 std::string exp_group = prefetch ? "ExperimentYes" : "ExperimentNo"; 159 base::FieldTrialList::CreateFieldTrial("Prefetch", exp_group); 160 // Disable prerender so this is just a prefetch of the top-level page. 161 prerender::PrerenderManager::SetMode( 162 prerender::PrerenderManager::PRERENDER_MODE_DISABLED); 163 } 164 165 ~SetPrefetchForTest() { 166 prerender::PrerenderManager::SetMode(old_prerender_mode_); 167 } 168 169 private: 170 prerender::PrerenderManager::PrerenderManagerMode old_prerender_mode_; 171 } set_prefetch_for_test(true); 172 173 content::WebContents* contents = 174 browser()->tab_strip_model()->GetActiveWebContents(); 175 NavigationController* controller = &contents->GetController(); 176 LoginPromptBrowserTestObserver observer; 177 178 observer.Register(content::Source<NavigationController>(controller)); 179 180 WindowedLoadStopObserver load_stop_waiter(controller, 1); 181 browser()->OpenURL(OpenURLParams( 182 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 183 false)); 184 185 load_stop_waiter.Wait(); 186 EXPECT_TRUE(observer.handlers().empty()); 187 EXPECT_TRUE(test_server()->Stop()); 188} 189 190// Test that "Basic" HTTP authentication works. 191IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestBasicAuth) { 192 ASSERT_TRUE(test_server()->Start()); 193 GURL test_page = test_server()->GetURL(kAuthBasicPage); 194 195 content::WebContents* contents = 196 browser()->tab_strip_model()->GetActiveWebContents(); 197 NavigationController* controller = &contents->GetController(); 198 LoginPromptBrowserTestObserver observer; 199 200 observer.Register(content::Source<NavigationController>(controller)); 201 202 { 203 WindowedAuthNeededObserver auth_needed_waiter(controller); 204 browser()->OpenURL(OpenURLParams( 205 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 206 false)); 207 auth_needed_waiter.Wait(); 208 } 209 210 ASSERT_FALSE(observer.handlers().empty()); 211 { 212 WindowedAuthNeededObserver auth_needed_waiter(controller); 213 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 214 LoginHandler* handler = *observer.handlers().begin(); 215 216 ASSERT_TRUE(handler); 217 handler->SetAuth(base::UTF8ToUTF16(bad_username_), 218 base::UTF8ToUTF16(bad_password_)); 219 auth_supplied_waiter.Wait(); 220 221 // The request should be retried after the incorrect password is 222 // supplied. This should result in a new AUTH_NEEDED notification 223 // for the same realm. 224 auth_needed_waiter.Wait(); 225 } 226 227 ASSERT_EQ(1u, observer.handlers().size()); 228 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 229 LoginHandler* handler = *observer.handlers().begin(); 230 SetAuthFor(handler); 231 auth_supplied_waiter.Wait(); 232 233 base::string16 expected_title = 234 ExpectedTitleFromAuth(base::ASCIIToUTF16("basicuser"), 235 base::ASCIIToUTF16("secret")); 236 content::TitleWatcher title_watcher(contents, expected_title); 237 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); 238} 239 240// Test that "Digest" HTTP authentication works. 241IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestDigestAuth) { 242 ASSERT_TRUE(test_server()->Start()); 243 GURL test_page = test_server()->GetURL(kAuthDigestPage); 244 245 content::WebContents* contents = 246 browser()->tab_strip_model()->GetActiveWebContents(); 247 NavigationController* controller = &contents->GetController(); 248 LoginPromptBrowserTestObserver observer; 249 250 observer.Register(content::Source<NavigationController>(controller)); 251 252 { 253 WindowedAuthNeededObserver auth_needed_waiter(controller); 254 browser()->OpenURL(OpenURLParams( 255 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 256 false)); 257 auth_needed_waiter.Wait(); 258 } 259 260 ASSERT_FALSE(observer.handlers().empty()); 261 { 262 WindowedAuthNeededObserver auth_needed_waiter(controller); 263 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 264 LoginHandler* handler = *observer.handlers().begin(); 265 266 ASSERT_TRUE(handler); 267 handler->SetAuth(base::UTF8ToUTF16(bad_username_), 268 base::UTF8ToUTF16(bad_password_)); 269 auth_supplied_waiter.Wait(); 270 271 // The request should be retried after the incorrect password is 272 // supplied. This should result in a new AUTH_NEEDED notification 273 // for the same realm. 274 auth_needed_waiter.Wait(); 275 } 276 277 ASSERT_EQ(1u, observer.handlers().size()); 278 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 279 LoginHandler* handler = *observer.handlers().begin(); 280 281 base::string16 username(base::UTF8ToUTF16(username_digest_)); 282 base::string16 password(base::UTF8ToUTF16(password_)); 283 handler->SetAuth(username, password); 284 auth_supplied_waiter.Wait(); 285 286 base::string16 expected_title = ExpectedTitleFromAuth(username, password); 287 content::TitleWatcher title_watcher(contents, expected_title); 288 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); 289} 290 291IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestTwoAuths) { 292 ASSERT_TRUE(test_server()->Start()); 293 294 content::WebContents* contents1 = 295 browser()->tab_strip_model()->GetActiveWebContents(); 296 NavigationController* controller1 = &contents1->GetController(); 297 LoginPromptBrowserTestObserver observer; 298 299 observer.Register(content::Source<NavigationController>(controller1)); 300 301 // Open a new tab. 302 ui_test_utils::NavigateToURLWithDisposition( 303 browser(), 304 GURL("about:blank"), 305 NEW_FOREGROUND_TAB, 306 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); 307 308 content::WebContents* contents2 = 309 browser()->tab_strip_model()->GetActiveWebContents(); 310 ASSERT_NE(contents1, contents2); 311 NavigationController* controller2 = &contents2->GetController(); 312 observer.Register(content::Source<NavigationController>(controller2)); 313 314 { 315 WindowedAuthNeededObserver auth_needed_waiter(controller1); 316 contents1->OpenURL(OpenURLParams( 317 test_server()->GetURL(kAuthBasicPage), Referrer(), 318 CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false)); 319 auth_needed_waiter.Wait(); 320 } 321 322 { 323 WindowedAuthNeededObserver auth_needed_waiter(controller2); 324 contents2->OpenURL(OpenURLParams( 325 test_server()->GetURL(kAuthDigestPage), Referrer(), 326 CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false)); 327 auth_needed_waiter.Wait(); 328 } 329 330 ASSERT_EQ(2u, observer.handlers().size()); 331 332 LoginHandler* handler1 = *observer.handlers().begin(); 333 LoginHandler* handler2 = *(++(observer.handlers().begin())); 334 335 base::string16 expected_title1 = ExpectedTitleFromAuth( 336 base::UTF8ToUTF16(username_basic_), base::UTF8ToUTF16(password_)); 337 base::string16 expected_title2 = ExpectedTitleFromAuth( 338 base::UTF8ToUTF16(username_digest_), base::UTF8ToUTF16(password_)); 339 content::TitleWatcher title_watcher1(contents1, expected_title1); 340 content::TitleWatcher title_watcher2(contents2, expected_title2); 341 342 handler1->SetAuth(base::UTF8ToUTF16(username_basic_), 343 base::UTF8ToUTF16(password_)); 344 handler2->SetAuth(base::UTF8ToUTF16(username_digest_), 345 base::UTF8ToUTF16(password_)); 346 347 EXPECT_EQ(expected_title1, title_watcher1.WaitAndGetTitle()); 348 EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle()); 349} 350 351// Test login prompt cancellation. 352IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestCancelAuth) { 353 ASSERT_TRUE(test_server()->Start()); 354 GURL auth_page = test_server()->GetURL(kAuthBasicPage); 355 GURL no_auth_page_1 = test_server()->GetURL("a"); 356 GURL no_auth_page_2 = test_server()->GetURL("b"); 357 GURL no_auth_page_3 = test_server()->GetURL("c"); 358 359 content::WebContents* contents = 360 browser()->tab_strip_model()->GetActiveWebContents(); 361 NavigationController* controller = &contents->GetController(); 362 363 LoginPromptBrowserTestObserver observer; 364 observer.Register(content::Source<NavigationController>(controller)); 365 366 // First navigate to an unauthenticated page so we have something to 367 // go back to. 368 ui_test_utils::NavigateToURL(browser(), no_auth_page_1); 369 370 // Navigating while auth is requested is the same as cancelling. 371 { 372 // We need to wait for two LOAD_STOP events. One for auth_page and one for 373 // no_auth_page_2. 374 WindowedLoadStopObserver load_stop_waiter(controller, 2); 375 WindowedAuthNeededObserver auth_needed_waiter(controller); 376 browser()->OpenURL(OpenURLParams( 377 auth_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 378 false)); 379 auth_needed_waiter.Wait(); 380 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 381 browser()->OpenURL(OpenURLParams( 382 no_auth_page_2, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 383 false)); 384 auth_cancelled_waiter.Wait(); 385 load_stop_waiter.Wait(); 386 EXPECT_TRUE(observer.handlers().empty()); 387 } 388 389 // Try navigating backwards. 390 { 391 // As above, we wait for two LOAD_STOP events; one for each navigation. 392 WindowedLoadStopObserver load_stop_waiter(controller, 2); 393 WindowedAuthNeededObserver auth_needed_waiter(controller); 394 browser()->OpenURL(OpenURLParams( 395 auth_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 396 false)); 397 auth_needed_waiter.Wait(); 398 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 399 ASSERT_TRUE(chrome::CanGoBack(browser())); 400 chrome::GoBack(browser(), CURRENT_TAB); 401 auth_cancelled_waiter.Wait(); 402 load_stop_waiter.Wait(); 403 EXPECT_TRUE(observer.handlers().empty()); 404 } 405 406 // Now add a page and go back, so we have something to go forward to. 407 ui_test_utils::NavigateToURL(browser(), no_auth_page_3); 408 { 409 WindowedLoadStopObserver load_stop_waiter(controller, 1); 410 chrome::GoBack(browser(), CURRENT_TAB); // Should take us to page 1 411 load_stop_waiter.Wait(); 412 } 413 414 { 415 // We wait for two LOAD_STOP events; one for each navigation. 416 WindowedLoadStopObserver load_stop_waiter(controller, 2); 417 WindowedAuthNeededObserver auth_needed_waiter(controller); 418 browser()->OpenURL(OpenURLParams( 419 auth_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 420 false)); 421 auth_needed_waiter.Wait(); 422 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 423 ASSERT_TRUE(chrome::CanGoForward(browser())); 424 chrome::GoForward(browser(), CURRENT_TAB); // Should take us to page 3 425 auth_cancelled_waiter.Wait(); 426 load_stop_waiter.Wait(); 427 EXPECT_TRUE(observer.handlers().empty()); 428 } 429 430 // Now test that cancelling works as expected. 431 { 432 WindowedLoadStopObserver load_stop_waiter(controller, 1); 433 WindowedAuthNeededObserver auth_needed_waiter(controller); 434 browser()->OpenURL(OpenURLParams( 435 auth_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 436 false)); 437 auth_needed_waiter.Wait(); 438 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 439 LoginHandler* handler = *observer.handlers().begin(); 440 ASSERT_TRUE(handler); 441 handler->CancelAuth(); 442 auth_cancelled_waiter.Wait(); 443 load_stop_waiter.Wait(); 444 EXPECT_TRUE(observer.handlers().empty()); 445 } 446} 447 448// Test handling of resources that require authentication even though 449// the page they are included on doesn't. In this case we should only 450// present the minimal number of prompts necessary for successfully 451// displaying the page. First we check whether cancelling works as 452// expected. 453IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmCancellation) { 454 ASSERT_TRUE(test_server()->Start()); 455 GURL test_page = test_server()->GetURL(kMultiRealmTestPage); 456 457 content::WebContents* contents = 458 browser()->tab_strip_model()->GetActiveWebContents(); 459 NavigationController* controller = &contents->GetController(); 460 LoginPromptBrowserTestObserver observer; 461 462 observer.Register(content::Source<NavigationController>(controller)); 463 464 WindowedLoadStopObserver load_stop_waiter(controller, 1); 465 466 { 467 WindowedAuthNeededObserver auth_needed_waiter(controller); 468 browser()->OpenURL(OpenURLParams( 469 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 470 false)); 471 auth_needed_waiter.Wait(); 472 } 473 474 int n_handlers = 0; 475 476 while (n_handlers < kMultiRealmTestRealmCount) { 477 WindowedAuthNeededObserver auth_needed_waiter(controller); 478 479 while (!observer.handlers().empty()) { 480 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 481 LoginHandler* handler = *observer.handlers().begin(); 482 483 ASSERT_TRUE(handler); 484 n_handlers++; 485 handler->CancelAuth(); 486 auth_cancelled_waiter.Wait(); 487 } 488 489 if (n_handlers < kMultiRealmTestRealmCount) 490 auth_needed_waiter.Wait(); 491 } 492 493 load_stop_waiter.Wait(); 494 495 EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers); 496 EXPECT_EQ(0, observer.auth_supplied_count()); 497 EXPECT_LT(0, observer.auth_needed_count()); 498 EXPECT_LT(0, observer.auth_cancelled_count()); 499 EXPECT_TRUE(test_server()->Stop()); 500} 501 502// Similar to the MultipleRealmCancellation test above, but tests 503// whether supplying credentials work as exepcted. 504IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmConfirmation) { 505 ASSERT_TRUE(test_server()->Start()); 506 GURL test_page = test_server()->GetURL(kMultiRealmTestPage); 507 508 content::WebContents* contents = 509 browser()->tab_strip_model()->GetActiveWebContents(); 510 NavigationController* controller = &contents->GetController(); 511 LoginPromptBrowserTestObserver observer; 512 513 observer.Register(content::Source<NavigationController>(controller)); 514 515 WindowedLoadStopObserver load_stop_waiter(controller, 1); 516 int n_handlers = 0; 517 518 { 519 WindowedAuthNeededObserver auth_needed_waiter(controller); 520 521 browser()->OpenURL(OpenURLParams( 522 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 523 false)); 524 auth_needed_waiter.Wait(); 525 } 526 527 while (n_handlers < kMultiRealmTestRealmCount) { 528 WindowedAuthNeededObserver auth_needed_waiter(controller); 529 530 while (!observer.handlers().empty()) { 531 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 532 LoginHandler* handler = *observer.handlers().begin(); 533 534 ASSERT_TRUE(handler); 535 n_handlers++; 536 SetAuthFor(handler); 537 auth_supplied_waiter.Wait(); 538 } 539 540 if (n_handlers < kMultiRealmTestRealmCount) 541 auth_needed_waiter.Wait(); 542 } 543 544 load_stop_waiter.Wait(); 545 546 EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers); 547 EXPECT_LT(0, observer.auth_needed_count()); 548 EXPECT_LT(0, observer.auth_supplied_count()); 549 EXPECT_EQ(0, observer.auth_cancelled_count()); 550 EXPECT_TRUE(test_server()->Stop()); 551} 552 553// Testing for recovery from an incorrect password for the case where 554// there are multiple authenticated resources. 555IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, IncorrectConfirmation) { 556 ASSERT_TRUE(test_server()->Start()); 557 GURL test_page = test_server()->GetURL(kSingleRealmTestPage); 558 559 content::WebContents* contents = 560 browser()->tab_strip_model()->GetActiveWebContents(); 561 NavigationController* controller = &contents->GetController(); 562 LoginPromptBrowserTestObserver observer; 563 564 observer.Register(content::Source<NavigationController>(controller)); 565 566 { 567 WindowedAuthNeededObserver auth_needed_waiter(controller); 568 browser()->OpenURL(OpenURLParams( 569 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 570 false)); 571 auth_needed_waiter.Wait(); 572 } 573 574 EXPECT_FALSE(observer.handlers().empty()); 575 576 if (!observer.handlers().empty()) { 577 WindowedAuthNeededObserver auth_needed_waiter(controller); 578 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 579 LoginHandler* handler = *observer.handlers().begin(); 580 581 ASSERT_TRUE(handler); 582 handler->SetAuth(base::UTF8ToUTF16(bad_username_), 583 base::UTF8ToUTF16(bad_password_)); 584 auth_supplied_waiter.Wait(); 585 586 // The request should be retried after the incorrect password is 587 // supplied. This should result in a new AUTH_NEEDED notification 588 // for the same realm. 589 auth_needed_waiter.Wait(); 590 } 591 592 int n_handlers = 0; 593 594 while (n_handlers < 1) { 595 WindowedAuthNeededObserver auth_needed_waiter(controller); 596 597 while (!observer.handlers().empty()) { 598 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 599 LoginHandler* handler = *observer.handlers().begin(); 600 601 ASSERT_TRUE(handler); 602 n_handlers++; 603 SetAuthFor(handler); 604 auth_supplied_waiter.Wait(); 605 } 606 607 if (n_handlers < 1) 608 auth_needed_waiter.Wait(); 609 } 610 611 // The single realm test has only one realm, and thus only one login 612 // prompt. 613 EXPECT_EQ(1, n_handlers); 614 EXPECT_LT(0, observer.auth_needed_count()); 615 EXPECT_EQ(0, observer.auth_cancelled_count()); 616 EXPECT_EQ(observer.auth_needed_count(), observer.auth_supplied_count()); 617 EXPECT_TRUE(test_server()->Stop()); 618} 619 620// If the favicon is an authenticated resource, we shouldn't prompt 621// for credentials. The same URL, if requested elsewhere should 622// prompt for credentials. 623IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, NoLoginPromptForFavicon) { 624 const char* kFaviconTestPage = "files/login/has_favicon.html"; 625 const char* kFaviconResource = "auth-basic/favicon.gif"; 626 627 ASSERT_TRUE(test_server()->Start()); 628 629 content::WebContents* contents = 630 browser()->tab_strip_model()->GetActiveWebContents(); 631 NavigationController* controller = &contents->GetController(); 632 LoginPromptBrowserTestObserver observer; 633 634 observer.Register(content::Source<NavigationController>(controller)); 635 636 // First load a page that has a favicon that requires 637 // authentication. There should be no login prompt. 638 { 639 GURL test_page = test_server()->GetURL(kFaviconTestPage); 640 WindowedLoadStopObserver load_stop_waiter(controller, 1); 641 browser()->OpenURL(OpenURLParams( 642 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 643 false)); 644 load_stop_waiter.Wait(); 645 } 646 647 // Now request the same favicon, but directly as the document. 648 // There should be one login prompt. 649 { 650 GURL test_page = test_server()->GetURL(kFaviconResource); 651 WindowedLoadStopObserver load_stop_waiter(controller, 1); 652 WindowedAuthNeededObserver auth_needed_waiter(controller); 653 browser()->OpenURL(OpenURLParams( 654 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 655 false)); 656 auth_needed_waiter.Wait(); 657 ASSERT_EQ(1u, observer.handlers().size()); 658 659 while (!observer.handlers().empty()) { 660 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 661 LoginHandler* handler = *observer.handlers().begin(); 662 663 ASSERT_TRUE(handler); 664 handler->CancelAuth(); 665 auth_cancelled_waiter.Wait(); 666 } 667 668 load_stop_waiter.Wait(); 669 } 670 671 EXPECT_EQ(0, observer.auth_supplied_count()); 672 EXPECT_EQ(1, observer.auth_needed_count()); 673 EXPECT_EQ(1, observer.auth_cancelled_count()); 674 EXPECT_TRUE(test_server()->Stop()); 675} 676 677// Block crossdomain image login prompting as a phishing defense. 678IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 679 BlockCrossdomainPromptForSubresources) { 680 const char* kTestPage = "files/login/load_img_from_b.html"; 681 682 host_resolver()->AddRule("www.a.com", "127.0.0.1"); 683 host_resolver()->AddRule("www.b.com", "127.0.0.1"); 684 ASSERT_TRUE(test_server()->Start()); 685 686 content::WebContents* contents = 687 browser()->tab_strip_model()->GetActiveWebContents(); 688 NavigationController* controller = &contents->GetController(); 689 LoginPromptBrowserTestObserver observer; 690 observer.Register(content::Source<NavigationController>(controller)); 691 692 // Load a page that has a cross-domain sub-resource authentication. 693 // There should be no login prompt. 694 { 695 GURL test_page = test_server()->GetURL(kTestPage); 696 ASSERT_EQ("127.0.0.1", test_page.host()); 697 698 // Change the host from 127.0.0.1 to www.a.com so that when the 699 // page tries to load from b, it will be cross-origin. 700 std::string new_host("www.a.com"); 701 GURL::Replacements replacements; 702 replacements.SetHostStr(new_host); 703 test_page = test_page.ReplaceComponents(replacements); 704 705 WindowedLoadStopObserver load_stop_waiter(controller, 1); 706 browser()->OpenURL(OpenURLParams( 707 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 708 false)); 709 load_stop_waiter.Wait(); 710 } 711 712 EXPECT_EQ(0, observer.auth_needed_count()); 713 714 // Now request the same page, but from the same origin. 715 // There should be one login prompt. 716 { 717 GURL test_page = test_server()->GetURL(kTestPage); 718 ASSERT_EQ("127.0.0.1", test_page.host()); 719 720 // Change the host from 127.0.0.1 to www.b.com so that when the 721 // page tries to load from b, it will be same-origin. 722 std::string new_host("www.b.com"); 723 GURL::Replacements replacements; 724 replacements.SetHostStr(new_host); 725 test_page = test_page.ReplaceComponents(replacements); 726 727 WindowedAuthNeededObserver auth_needed_waiter(controller); 728 browser()->OpenURL(OpenURLParams( 729 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 730 false)); 731 auth_needed_waiter.Wait(); 732 ASSERT_EQ(1u, observer.handlers().size()); 733 734 while (!observer.handlers().empty()) { 735 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 736 LoginHandler* handler = *observer.handlers().begin(); 737 738 ASSERT_TRUE(handler); 739 handler->CancelAuth(); 740 auth_cancelled_waiter.Wait(); 741 } 742 } 743 744 EXPECT_EQ(1, observer.auth_needed_count()); 745 EXPECT_TRUE(test_server()->Stop()); 746} 747 748// Allow crossdomain iframe login prompting despite the above. 749IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 750 AllowCrossdomainPromptForSubframes) { 751 const char* kTestPage = "files/login/load_iframe_from_b.html"; 752 753 host_resolver()->AddRule("www.a.com", "127.0.0.1"); 754 host_resolver()->AddRule("www.b.com", "127.0.0.1"); 755 ASSERT_TRUE(test_server()->Start()); 756 757 content::WebContents* contents = 758 browser()->tab_strip_model()->GetActiveWebContents(); 759 NavigationController* controller = &contents->GetController(); 760 LoginPromptBrowserTestObserver observer; 761 observer.Register(content::Source<NavigationController>(controller)); 762 763 // Load a page that has a cross-domain iframe authentication. 764 { 765 GURL test_page = test_server()->GetURL(kTestPage); 766 ASSERT_EQ("127.0.0.1", test_page.host()); 767 768 // Change the host from 127.0.0.1 to www.a.com so that when the 769 // page tries to load from b, it will be cross-origin. 770 std::string new_host("www.a.com"); 771 GURL::Replacements replacements; 772 replacements.SetHostStr(new_host); 773 test_page = test_page.ReplaceComponents(replacements); 774 775 WindowedAuthNeededObserver auth_needed_waiter(controller); 776 browser()->OpenURL(OpenURLParams( 777 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 778 false)); 779 auth_needed_waiter.Wait(); 780 ASSERT_EQ(1u, observer.handlers().size()); 781 782 while (!observer.handlers().empty()) { 783 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 784 LoginHandler* handler = *observer.handlers().begin(); 785 786 ASSERT_TRUE(handler); 787 // When a cross origin iframe displays a login prompt, the blank 788 // interstitial shouldn't be displayed and the omnibox should show the 789 // main frame's url, not the iframe's. 790 EXPECT_EQ(new_host, contents->GetVisibleURL().host()); 791 792 handler->CancelAuth(); 793 auth_cancelled_waiter.Wait(); 794 } 795 } 796 797 // Should stay on the main frame's url once the prompt the iframe is closed. 798 EXPECT_EQ("www.a.com", contents->GetVisibleURL().host()); 799 800 EXPECT_EQ(1, observer.auth_needed_count()); 801 EXPECT_TRUE(test_server()->Stop()); 802} 803 804IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, SupplyRedundantAuths) { 805 ASSERT_TRUE(test_server()->Start()); 806 807 // Get NavigationController for tab 1. 808 content::WebContents* contents_1 = 809 browser()->tab_strip_model()->GetActiveWebContents(); 810 NavigationController* controller_1 = &contents_1->GetController(); 811 812 // Open a new tab. 813 ui_test_utils::NavigateToURLWithDisposition( 814 browser(), 815 GURL("about:blank"), 816 NEW_FOREGROUND_TAB, 817 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); 818 819 // Get NavigationController for tab 2. 820 content::WebContents* contents_2 = 821 browser()->tab_strip_model()->GetActiveWebContents(); 822 ASSERT_NE(contents_1, contents_2); 823 NavigationController* controller_2 = &contents_2->GetController(); 824 825 LoginPromptBrowserTestObserver observer; 826 observer.Register(content::Source<NavigationController>(controller_1)); 827 observer.Register(content::Source<NavigationController>(controller_2)); 828 829 { 830 // Open different auth urls in each tab. 831 WindowedAuthNeededObserver auth_needed_waiter_1(controller_1); 832 WindowedAuthNeededObserver auth_needed_waiter_2(controller_2); 833 contents_1->OpenURL(OpenURLParams( 834 test_server()->GetURL("auth-basic/1"), 835 content::Referrer(), 836 CURRENT_TAB, 837 ui::PAGE_TRANSITION_TYPED, 838 false)); 839 contents_2->OpenURL(OpenURLParams( 840 test_server()->GetURL("auth-basic/2"), 841 content::Referrer(), 842 CURRENT_TAB, 843 ui::PAGE_TRANSITION_TYPED, 844 false)); 845 auth_needed_waiter_1.Wait(); 846 auth_needed_waiter_2.Wait(); 847 848 ASSERT_EQ(2U, observer.handlers().size()); 849 850 // Supply auth in one of the tabs. 851 WindowedAuthSuppliedObserver auth_supplied_waiter_1(controller_1); 852 WindowedAuthSuppliedObserver auth_supplied_waiter_2(controller_2); 853 LoginHandler* handler_1 = *observer.handlers().begin(); 854 ASSERT_TRUE(handler_1); 855 SetAuthFor(handler_1); 856 857 // Both tabs should be authenticated. 858 auth_supplied_waiter_1.Wait(); 859 auth_supplied_waiter_2.Wait(); 860 } 861 862 EXPECT_EQ(2, observer.auth_needed_count()); 863 EXPECT_EQ(2, observer.auth_supplied_count()); 864 EXPECT_EQ(0, observer.auth_cancelled_count()); 865 EXPECT_TRUE(test_server()->Stop()); 866} 867 868IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, CancelRedundantAuths) { 869 ASSERT_TRUE(test_server()->Start()); 870 871 // Get NavigationController for tab 1. 872 content::WebContents* contents_1 = 873 browser()->tab_strip_model()->GetActiveWebContents(); 874 NavigationController* controller_1 = &contents_1->GetController(); 875 876 // Open a new tab. 877 ui_test_utils::NavigateToURLWithDisposition( 878 browser(), 879 GURL("about:blank"), 880 NEW_FOREGROUND_TAB, 881 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); 882 883 // Get NavigationController for tab 2. 884 content::WebContents* contents_2 = 885 browser()->tab_strip_model()->GetActiveWebContents(); 886 ASSERT_NE(contents_1, contents_2); 887 NavigationController* controller_2 = &contents_2->GetController(); 888 889 LoginPromptBrowserTestObserver observer; 890 observer.Register(content::Source<NavigationController>(controller_1)); 891 observer.Register(content::Source<NavigationController>(controller_2)); 892 893 { 894 // Open different auth urls in each tab. 895 WindowedAuthNeededObserver auth_needed_waiter_1(controller_1); 896 WindowedAuthNeededObserver auth_needed_waiter_2(controller_2); 897 contents_1->OpenURL(OpenURLParams( 898 test_server()->GetURL("auth-basic/1"), 899 content::Referrer(), 900 CURRENT_TAB, 901 ui::PAGE_TRANSITION_TYPED, 902 false)); 903 contents_2->OpenURL(OpenURLParams( 904 test_server()->GetURL("auth-basic/2"), 905 content::Referrer(), 906 CURRENT_TAB, 907 ui::PAGE_TRANSITION_TYPED, 908 false)); 909 auth_needed_waiter_1.Wait(); 910 auth_needed_waiter_2.Wait(); 911 912 ASSERT_EQ(2U, observer.handlers().size()); 913 914 // Cancel auth in one of the tabs. 915 WindowedAuthCancelledObserver auth_cancelled_waiter_1(controller_1); 916 WindowedAuthCancelledObserver auth_cancelled_waiter_2(controller_2); 917 LoginHandler* handler_1 = *observer.handlers().begin(); 918 ASSERT_TRUE(handler_1); 919 handler_1->CancelAuth(); 920 921 // Both tabs should cancel auth. 922 auth_cancelled_waiter_1.Wait(); 923 auth_cancelled_waiter_2.Wait(); 924 } 925 926 EXPECT_EQ(2, observer.auth_needed_count()); 927 EXPECT_EQ(0, observer.auth_supplied_count()); 928 EXPECT_EQ(2, observer.auth_cancelled_count()); 929 EXPECT_TRUE(test_server()->Stop()); 930} 931 932IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 933 SupplyRedundantAuthsMultiProfile) { 934 ASSERT_TRUE(test_server()->Start()); 935 936 // Get NavigationController for regular tab. 937 content::WebContents* contents = 938 browser()->tab_strip_model()->GetActiveWebContents(); 939 NavigationController* controller = &contents->GetController(); 940 941 // Open an incognito window. 942 Browser* browser_incognito = CreateIncognitoBrowser(); 943 944 // Get NavigationController for incognito tab. 945 content::WebContents* contents_incognito = 946 browser_incognito->tab_strip_model()->GetActiveWebContents(); 947 ASSERT_NE(contents, contents_incognito); 948 NavigationController* controller_incognito = 949 &contents_incognito->GetController(); 950 951 LoginPromptBrowserTestObserver observer; 952 observer.Register(content::Source<NavigationController>(controller)); 953 LoginPromptBrowserTestObserver observer_incognito; 954 observer_incognito.Register( 955 content::Source<NavigationController>(controller_incognito)); 956 957 { 958 // Open an auth url in each window. 959 WindowedAuthNeededObserver auth_needed_waiter(controller); 960 WindowedAuthNeededObserver auth_needed_waiter_incognito( 961 controller_incognito); 962 contents->OpenURL(OpenURLParams( 963 test_server()->GetURL("auth-basic/1"), 964 content::Referrer(), 965 CURRENT_TAB, 966 ui::PAGE_TRANSITION_TYPED, 967 false)); 968 contents_incognito->OpenURL(OpenURLParams( 969 test_server()->GetURL("auth-basic/2"), 970 content::Referrer(), 971 CURRENT_TAB, 972 ui::PAGE_TRANSITION_TYPED, 973 false)); 974 auth_needed_waiter.Wait(); 975 auth_needed_waiter_incognito.Wait(); 976 977 ASSERT_EQ(1U, observer.handlers().size()); 978 ASSERT_EQ(1U, observer_incognito.handlers().size()); 979 980 // Supply auth in regular tab. 981 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 982 LoginHandler* handler = *observer.handlers().begin(); 983 ASSERT_TRUE(handler); 984 SetAuthFor(handler); 985 986 // Regular tab should be authenticated. 987 auth_supplied_waiter.Wait(); 988 989 // There's not really a way to wait for the incognito window to "do 990 // nothing". Run anything pending in the message loop just to be sure. 991 // (This shouldn't be necessary since notifications are synchronous, but 992 // maybe it will help avoid flake someday in the future..) 993 content::RunAllPendingInMessageLoop(); 994 } 995 996 EXPECT_EQ(1, observer.auth_needed_count()); 997 EXPECT_EQ(1, observer.auth_supplied_count()); 998 EXPECT_EQ(0, observer.auth_cancelled_count()); 999 EXPECT_EQ(1, observer_incognito.auth_needed_count()); 1000 EXPECT_EQ(0, observer_incognito.auth_supplied_count()); 1001 EXPECT_EQ(0, observer_incognito.auth_cancelled_count()); 1002 EXPECT_TRUE(test_server()->Stop()); 1003} 1004 1005// If an XMLHttpRequest is made with incorrect credentials, there should be no 1006// login prompt; instead the 401 status should be returned to the script. 1007IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 1008 NoLoginPromptForXHRWithBadCredentials) { 1009 const char* kXHRTestPage = "files/login/xhr_with_credentials.html#incorrect"; 1010 1011 ASSERT_TRUE(test_server()->Start()); 1012 1013 content::WebContents* contents = 1014 browser()->tab_strip_model()->GetActiveWebContents(); 1015 NavigationController* controller = &contents->GetController(); 1016 LoginPromptBrowserTestObserver observer; 1017 1018 observer.Register(content::Source<NavigationController>(controller)); 1019 1020 // Load a page which makes a synchronous XMLHttpRequest for an authenticated 1021 // resource with the wrong credentials. There should be no login prompt. 1022 { 1023 GURL test_page = test_server()->GetURL(kXHRTestPage); 1024 WindowedLoadStopObserver load_stop_waiter(controller, 1); 1025 browser()->OpenURL(OpenURLParams( 1026 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 1027 false)); 1028 load_stop_waiter.Wait(); 1029 } 1030 1031 base::string16 expected_title(base::UTF8ToUTF16("status=401")); 1032 1033 EXPECT_EQ(expected_title, contents->GetTitle()); 1034 EXPECT_EQ(0, observer.auth_supplied_count()); 1035 EXPECT_EQ(0, observer.auth_needed_count()); 1036 EXPECT_EQ(0, observer.auth_cancelled_count()); 1037 EXPECT_TRUE(test_server()->Stop()); 1038} 1039 1040// If an XMLHttpRequest is made with correct credentials, there should be no 1041// login prompt either. 1042IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 1043 NoLoginPromptForXHRWithGoodCredentials) { 1044 const char* kXHRTestPage = "files/login/xhr_with_credentials.html#secret"; 1045 1046 ASSERT_TRUE(test_server()->Start()); 1047 1048 content::WebContents* contents = 1049 browser()->tab_strip_model()->GetActiveWebContents(); 1050 NavigationController* controller = &contents->GetController(); 1051 LoginPromptBrowserTestObserver observer; 1052 1053 observer.Register(content::Source<NavigationController>(controller)); 1054 1055 // Load a page which makes a synchronous XMLHttpRequest for an authenticated 1056 // resource with the wrong credentials. There should be no login prompt. 1057 { 1058 GURL test_page = test_server()->GetURL(kXHRTestPage); 1059 WindowedLoadStopObserver load_stop_waiter(controller, 1); 1060 browser()->OpenURL(OpenURLParams( 1061 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 1062 false)); 1063 load_stop_waiter.Wait(); 1064 } 1065 1066 base::string16 expected_title(base::UTF8ToUTF16("status=200")); 1067 1068 EXPECT_EQ(expected_title, contents->GetTitle()); 1069 EXPECT_EQ(0, observer.auth_supplied_count()); 1070 EXPECT_EQ(0, observer.auth_needed_count()); 1071 EXPECT_EQ(0, observer.auth_cancelled_count()); 1072 EXPECT_TRUE(test_server()->Stop()); 1073} 1074 1075// If an XMLHttpRequest is made without credentials, there should be a login 1076// prompt. 1077IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 1078 LoginPromptForXHRWithoutCredentials) { 1079 const char* kXHRTestPage = "files/login/xhr_without_credentials.html"; 1080 1081 ASSERT_TRUE(test_server()->Start()); 1082 1083 content::WebContents* contents = 1084 browser()->tab_strip_model()->GetActiveWebContents(); 1085 NavigationController* controller = &contents->GetController(); 1086 LoginPromptBrowserTestObserver observer; 1087 1088 observer.Register(content::Source<NavigationController>(controller)); 1089 1090 // Load a page which makes a synchronous XMLHttpRequest for an authenticated 1091 // resource with the wrong credentials. There should be no login prompt. 1092 { 1093 GURL test_page = test_server()->GetURL(kXHRTestPage); 1094 WindowedAuthNeededObserver auth_needed_waiter(controller); 1095 browser()->OpenURL(OpenURLParams( 1096 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 1097 false)); 1098 auth_needed_waiter.Wait(); 1099 } 1100 1101 ASSERT_FALSE(observer.handlers().empty()); 1102 { 1103 WindowedAuthNeededObserver auth_needed_waiter(controller); 1104 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 1105 LoginHandler* handler = *observer.handlers().begin(); 1106 1107 ASSERT_TRUE(handler); 1108 handler->SetAuth(base::UTF8ToUTF16(bad_username_), 1109 base::UTF8ToUTF16(bad_password_)); 1110 auth_supplied_waiter.Wait(); 1111 1112 // The request should be retried after the incorrect password is 1113 // supplied. This should result in a new AUTH_NEEDED notification 1114 // for the same realm. 1115 auth_needed_waiter.Wait(); 1116 } 1117 1118 ASSERT_EQ(1u, observer.handlers().size()); 1119 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 1120 LoginHandler* handler = *observer.handlers().begin(); 1121 1122 base::string16 username(base::UTF8ToUTF16(username_digest_)); 1123 base::string16 password(base::UTF8ToUTF16(password_)); 1124 handler->SetAuth(username, password); 1125 auth_supplied_waiter.Wait(); 1126 1127 WindowedLoadStopObserver load_stop_waiter(controller, 1); 1128 load_stop_waiter.Wait(); 1129 1130 base::string16 expected_title(base::UTF8ToUTF16("status=200")); 1131 1132 EXPECT_EQ(expected_title, contents->GetTitle()); 1133 EXPECT_EQ(2, observer.auth_supplied_count()); 1134 EXPECT_EQ(2, observer.auth_needed_count()); 1135 EXPECT_EQ(0, observer.auth_cancelled_count()); 1136 EXPECT_TRUE(test_server()->Stop()); 1137} 1138 1139// If an XMLHttpRequest is made without credentials, there should be a login 1140// prompt. If it's cancelled, the script should get a 401 status. 1141IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 1142 LoginPromptForXHRWithoutCredentialsCancelled) { 1143 const char* kXHRTestPage = "files/login/xhr_without_credentials.html"; 1144 1145 ASSERT_TRUE(test_server()->Start()); 1146 1147 content::WebContents* contents = 1148 browser()->tab_strip_model()->GetActiveWebContents(); 1149 NavigationController* controller = &contents->GetController(); 1150 LoginPromptBrowserTestObserver observer; 1151 1152 observer.Register(content::Source<NavigationController>(controller)); 1153 1154 // Load a page which makes a synchronous XMLHttpRequest for an authenticated 1155 // resource with the wrong credentials. There should be no login prompt. 1156 { 1157 GURL test_page = test_server()->GetURL(kXHRTestPage); 1158 WindowedAuthNeededObserver auth_needed_waiter(controller); 1159 browser()->OpenURL(OpenURLParams( 1160 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 1161 false)); 1162 auth_needed_waiter.Wait(); 1163 } 1164 1165 ASSERT_EQ(1u, observer.handlers().size()); 1166 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 1167 LoginHandler* handler = *observer.handlers().begin(); 1168 1169 handler->CancelAuth(); 1170 auth_cancelled_waiter.Wait(); 1171 1172 WindowedLoadStopObserver load_stop_waiter(controller, 1); 1173 load_stop_waiter.Wait(); 1174 1175 base::string16 expected_title(base::UTF8ToUTF16("status=401")); 1176 1177 EXPECT_EQ(expected_title, contents->GetTitle()); 1178 EXPECT_EQ(0, observer.auth_supplied_count()); 1179 EXPECT_EQ(1, observer.auth_needed_count()); 1180 EXPECT_EQ(1, observer.auth_cancelled_count()); 1181 EXPECT_TRUE(test_server()->Stop()); 1182} 1183 1184// If a cross origin navigation triggers a login prompt, the destination URL 1185// should be shown in the omnibox. 1186void LoginPromptBrowserTest::TestCrossOriginPrompt( 1187 const GURL& visit_url, 1188 const std::string& auth_host) const { 1189 content::WebContents* contents = 1190 browser()->tab_strip_model()->GetActiveWebContents(); 1191 NavigationController* controller = &contents->GetController(); 1192 LoginPromptBrowserTestObserver observer; 1193 1194 observer.Register(content::Source<NavigationController>(controller)); 1195 1196 // Load a page which will trigger a login prompt. 1197 { 1198 WindowedAuthNeededObserver auth_needed_waiter(controller); 1199 browser()->OpenURL(OpenURLParams( 1200 visit_url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 1201 false)); 1202 ASSERT_EQ(visit_url.host(), contents->GetVisibleURL().host()); 1203 auth_needed_waiter.Wait(); 1204 ASSERT_EQ(1u, observer.handlers().size()); 1205 WaitForInterstitialAttach(contents); 1206 1207 // The omnibox should show the correct origin for the new page when the 1208 // login prompt is shown. 1209 EXPECT_EQ(auth_host, contents->GetVisibleURL().host()); 1210 EXPECT_TRUE(contents->ShowingInterstitialPage()); 1211 1212 // Cancel and wait for the interstitial to detach. 1213 LoginHandler* handler = *observer.handlers().begin(); 1214 scoped_refptr<content::MessageLoopRunner> loop_runner( 1215 new content::MessageLoopRunner); 1216 InterstitialObserver interstitial_observer(contents, 1217 base::Closure(), 1218 loop_runner->QuitClosure()); 1219 handler->CancelAuth(); 1220 if (content::InterstitialPage::GetInterstitialPage(contents)) 1221 loop_runner->Run(); 1222 EXPECT_EQ(auth_host, contents->GetVisibleURL().host()); 1223 EXPECT_FALSE(contents->ShowingInterstitialPage()); 1224 } 1225} 1226 1227// If a cross origin direct navigation triggers a login prompt, the login 1228// interstitial should be shown. 1229IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 1230 ShowCorrectUrlForCrossOriginMainFrameRequests) { 1231 ASSERT_TRUE(test_server()->Start()); 1232 1233 GURL test_page = test_server()->GetURL(kAuthBasicPage); 1234 ASSERT_EQ("127.0.0.1", test_page.host()); 1235 std::string auth_host("127.0.0.1"); 1236 TestCrossOriginPrompt(test_page, auth_host); 1237} 1238 1239// If a cross origin redirect triggers a login prompt, the destination URL 1240// should be shown in the omnibox when the auth dialog is displayed. 1241IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 1242 ShowCorrectUrlForCrossOriginMainFrameRedirects) { 1243 host_resolver()->AddRule("www.a.com", "127.0.0.1"); 1244 ASSERT_TRUE(test_server()->Start()); 1245 1246 const char* kTestPage = "files/login/cross_origin.html"; 1247 GURL test_page = test_server()->GetURL(kTestPage); 1248 ASSERT_EQ("127.0.0.1", test_page.host()); 1249 std::string auth_host("www.a.com"); 1250 TestCrossOriginPrompt(test_page, auth_host); 1251} 1252 1253IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 1254 LoginInterstitialShouldReplaceExistingInterstitial) { 1255 net::SpawnedTestServer https_server( 1256 net::SpawnedTestServer::TYPE_HTTPS, 1257 net::SpawnedTestServer::SSLOptions( 1258 net::SpawnedTestServer::SSLOptions::CERT_EXPIRED), 1259 base::FilePath()); 1260 ASSERT_TRUE(https_server.Start()); 1261 1262 content::WebContents* contents = 1263 browser()->tab_strip_model()->GetActiveWebContents(); 1264 NavigationController* controller = &contents->GetController(); 1265 LoginPromptBrowserTestObserver observer; 1266 1267 observer.Register(content::Source<NavigationController>(controller)); 1268 1269 // Load a page which triggers an SSL interstitial. Proceeding through it 1270 // should show the login page with the blank interstitial. 1271 { 1272 GURL test_page = https_server.GetURL(kAuthBasicPage); 1273 ASSERT_EQ("127.0.0.1", test_page.host()); 1274 1275 WindowedAuthNeededObserver auth_needed_waiter(controller); 1276 browser()->OpenURL(OpenURLParams( 1277 test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, 1278 false)); 1279 ASSERT_EQ("127.0.0.1", contents->GetURL().host()); 1280 WaitForInterstitialAttach(contents); 1281 1282 // An overrideable SSL interstitial is now being displayed. Proceed through 1283 // the interstitial to see the login prompt. 1284 contents->GetInterstitialPage()->Proceed(); 1285 auth_needed_waiter.Wait(); 1286 ASSERT_EQ(1u, observer.handlers().size()); 1287 WaitForInterstitialAttach(contents); 1288 1289 // The omnibox should show the correct origin while the login prompt is 1290 // being displayed. 1291 EXPECT_EQ("127.0.0.1", contents->GetVisibleURL().host()); 1292 EXPECT_TRUE(contents->ShowingInterstitialPage()); 1293 1294 // Cancelling the login prompt should detach the interstitial while keeping 1295 // the correct origin. 1296 LoginHandler* handler = *observer.handlers().begin(); 1297 scoped_refptr<content::MessageLoopRunner> loop_runner( 1298 new content::MessageLoopRunner); 1299 InterstitialObserver interstitial_observer(contents, 1300 base::Closure(), 1301 loop_runner->QuitClosure()); 1302 handler->CancelAuth(); 1303 if (content::InterstitialPage::GetInterstitialPage(contents)) 1304 loop_runner->Run(); 1305 EXPECT_EQ("127.0.0.1", contents->GetVisibleURL().host()); 1306 EXPECT_FALSE(contents->ShowingInterstitialPage()); 1307 } 1308} 1309 1310} // namespace 1311