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/tabs/tab_strip_model.h" 17 #include "chrome/test/base/in_process_browser_test.h" 18 #include "chrome/test/base/ui_test_utils.h" 19 #include "content/public/browser/notification_details.h" 20 #include "content/public/browser/notification_source.h" 21 #include "content/public/browser/web_contents.h" 22 #include "content/public/test/browser_test_utils.h" 23 #include "net/base/auth.h" 24 #include "net/dns/mock_host_resolver.h" 25 26 using content::NavigationController; 27 using content::OpenURLParams; 28 using content::Referrer; 29 30 namespace { 31 32 class LoginPromptBrowserTest : public InProcessBrowserTest { 33 public: 34 LoginPromptBrowserTest() 35 : bad_password_("incorrect"), 36 bad_username_("nouser"), 37 password_("secret"), 38 username_basic_("basicuser"), 39 username_digest_("digestuser") { 40 auth_map_["foo"] = AuthInfo("testuser", "foopassword"); 41 auth_map_["bar"] = AuthInfo("testuser", "barpassword"); 42 auth_map_["testrealm"] = AuthInfo(username_basic_, password_); 43 } 44 45 protected: 46 struct AuthInfo { 47 std::string username_; 48 std::string password_; 49 50 AuthInfo() {} 51 52 AuthInfo(const std::string& username, 53 const std::string& password) 54 : username_(username), password_(password) {} 55 }; 56 57 typedef std::map<std::string, AuthInfo> AuthMap; 58 59 void SetAuthFor(LoginHandler* handler); 60 61 AuthMap auth_map_; 62 std::string bad_password_; 63 std::string bad_username_; 64 std::string password_; 65 std::string username_basic_; 66 std::string username_digest_; 67 }; 68 69 void LoginPromptBrowserTest::SetAuthFor(LoginHandler* handler) { 70 const net::AuthChallengeInfo* challenge = handler->auth_info(); 71 72 ASSERT_TRUE(challenge); 73 AuthMap::iterator i = auth_map_.find(challenge->realm); 74 EXPECT_TRUE(auth_map_.end() != i); 75 if (i != auth_map_.end()) { 76 const AuthInfo& info = i->second; 77 handler->SetAuth(UTF8ToUTF16(info.username_), 78 UTF8ToUTF16(info.password_)); 79 } 80 } 81 82 // Maintains a set of LoginHandlers that are currently active and 83 // keeps a count of the notifications that were observed. 84 class LoginPromptBrowserTestObserver : public content::NotificationObserver { 85 public: 86 LoginPromptBrowserTestObserver() 87 : auth_needed_count_(0), 88 auth_supplied_count_(0), 89 auth_cancelled_count_(0) {} 90 91 virtual void Observe(int type, 92 const content::NotificationSource& source, 93 const content::NotificationDetails& details) OVERRIDE; 94 95 void AddHandler(LoginHandler* handler); 96 97 void RemoveHandler(LoginHandler* handler); 98 99 void Register(const content::NotificationSource& source); 100 101 std::list<LoginHandler*> handlers_; 102 103 // The exact number of notifications we receive is depedent on the 104 // number of requests that were dispatched and is subject to a 105 // number of factors that we don't directly control here. The 106 // values below should only be used qualitatively. 107 int auth_needed_count_; 108 int auth_supplied_count_; 109 int auth_cancelled_count_; 110 111 private: 112 content::NotificationRegistrar registrar_; 113 114 DISALLOW_COPY_AND_ASSIGN(LoginPromptBrowserTestObserver); 115 }; 116 117 void LoginPromptBrowserTestObserver::Observe( 118 int type, 119 const content::NotificationSource& source, 120 const content::NotificationDetails& details) { 121 if (type == chrome::NOTIFICATION_AUTH_NEEDED) { 122 LoginNotificationDetails* login_details = 123 content::Details<LoginNotificationDetails>(details).ptr(); 124 AddHandler(login_details->handler()); 125 auth_needed_count_++; 126 } else if (type == chrome::NOTIFICATION_AUTH_SUPPLIED) { 127 AuthSuppliedLoginNotificationDetails* login_details = 128 content::Details<AuthSuppliedLoginNotificationDetails>(details).ptr(); 129 RemoveHandler(login_details->handler()); 130 auth_supplied_count_++; 131 } else if (type == chrome::NOTIFICATION_AUTH_CANCELLED) { 132 LoginNotificationDetails* login_details = 133 content::Details<LoginNotificationDetails>(details).ptr(); 134 RemoveHandler(login_details->handler()); 135 auth_cancelled_count_++; 136 } 137 } 138 139 void LoginPromptBrowserTestObserver::AddHandler(LoginHandler* handler) { 140 std::list<LoginHandler*>::iterator i = std::find(handlers_.begin(), 141 handlers_.end(), 142 handler); 143 EXPECT_TRUE(i == handlers_.end()); 144 if (i == handlers_.end()) 145 handlers_.push_back(handler); 146 } 147 148 void LoginPromptBrowserTestObserver::RemoveHandler(LoginHandler* handler) { 149 std::list<LoginHandler*>::iterator i = std::find(handlers_.begin(), 150 handlers_.end(), 151 handler); 152 EXPECT_TRUE(i != handlers_.end()); 153 if (i != handlers_.end()) 154 handlers_.erase(i); 155 } 156 157 void LoginPromptBrowserTestObserver::Register( 158 const content::NotificationSource& source) { 159 registrar_.Add(this, chrome::NOTIFICATION_AUTH_NEEDED, source); 160 registrar_.Add(this, chrome::NOTIFICATION_AUTH_SUPPLIED, source); 161 registrar_.Add(this, chrome::NOTIFICATION_AUTH_CANCELLED, source); 162 } 163 164 template <int T> 165 class WindowedNavigationObserver 166 : public content::WindowedNotificationObserver { 167 public: 168 explicit WindowedNavigationObserver(NavigationController* controller) 169 : content::WindowedNotificationObserver( 170 T, content::Source<NavigationController>(controller)) {} 171 }; 172 173 // LOAD_STOP observer is special since we want to be able to wait for 174 // multiple LOAD_STOP events. 175 class WindowedLoadStopObserver 176 : public WindowedNavigationObserver<content::NOTIFICATION_LOAD_STOP> { 177 public: 178 WindowedLoadStopObserver(NavigationController* controller, 179 int notification_count) 180 : WindowedNavigationObserver<content::NOTIFICATION_LOAD_STOP>(controller), 181 remaining_notification_count_(notification_count) {} 182 protected: 183 virtual void Observe(int type, 184 const content::NotificationSource& source, 185 const content::NotificationDetails& details) OVERRIDE; 186 private: 187 int remaining_notification_count_; // Number of notifications remaining. 188 }; 189 190 void WindowedLoadStopObserver::Observe( 191 int type, 192 const content::NotificationSource& source, 193 const content::NotificationDetails& details) { 194 if (--remaining_notification_count_ == 0) 195 WindowedNotificationObserver::Observe(type, source, details); 196 } 197 198 typedef WindowedNavigationObserver<chrome::NOTIFICATION_AUTH_NEEDED> 199 WindowedAuthNeededObserver; 200 201 typedef WindowedNavigationObserver<chrome::NOTIFICATION_AUTH_CANCELLED> 202 WindowedAuthCancelledObserver; 203 204 typedef WindowedNavigationObserver<chrome::NOTIFICATION_AUTH_SUPPLIED> 205 WindowedAuthSuppliedObserver; 206 207 const char kPrefetchAuthPage[] = "files/login/prefetch.html"; 208 209 const char kMultiRealmTestPage[] = "files/login/multi_realm.html"; 210 const int kMultiRealmTestRealmCount = 2; 211 212 const char kSingleRealmTestPage[] = "files/login/single_realm.html"; 213 214 const char* kAuthBasicPage = "auth-basic"; 215 const char* kAuthDigestPage = "auth-digest"; 216 217 base::string16 ExpectedTitleFromAuth(const base::string16& username, 218 const base::string16& password) { 219 // The TestServer sets the title to username/password on successful login. 220 return username + UTF8ToUTF16("/") + password; 221 } 222 223 // Confirm that <link rel="prefetch"> targetting an auth required 224 // resource does not provide a login dialog. These types of requests 225 // should instead just cancel the auth. 226 227 // Unfortunately, this test doesn't assert on anything for its 228 // correctness. Instead, it relies on the auth dialog blocking the 229 // browser, and triggering a timeout to cause failure when the 230 // prefetch resource requires authorization. 231 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, PrefetchAuthCancels) { 232 ASSERT_TRUE(test_server()->Start()); 233 234 GURL test_page = test_server()->GetURL(kPrefetchAuthPage); 235 236 class SetPrefetchForTest { 237 public: 238 explicit SetPrefetchForTest(bool prefetch) 239 : old_prerender_mode_(prerender::PrerenderManager::GetMode()) { 240 std::string exp_group = prefetch ? "ExperimentYes" : "ExperimentNo"; 241 base::FieldTrialList::CreateFieldTrial("Prefetch", exp_group); 242 // Disable prerender so this is just a prefetch of the top-level page. 243 prerender::PrerenderManager::SetMode( 244 prerender::PrerenderManager::PRERENDER_MODE_DISABLED); 245 } 246 247 ~SetPrefetchForTest() { 248 prerender::PrerenderManager::SetMode(old_prerender_mode_); 249 } 250 251 private: 252 prerender::PrerenderManager::PrerenderManagerMode old_prerender_mode_; 253 } set_prefetch_for_test(true); 254 255 content::WebContents* contents = 256 browser()->tab_strip_model()->GetActiveWebContents(); 257 NavigationController* controller = &contents->GetController(); 258 LoginPromptBrowserTestObserver observer; 259 260 observer.Register(content::Source<NavigationController>(controller)); 261 262 WindowedLoadStopObserver load_stop_waiter(controller, 1); 263 browser()->OpenURL(OpenURLParams( 264 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 265 false)); 266 267 load_stop_waiter.Wait(); 268 EXPECT_TRUE(observer.handlers_.empty()); 269 EXPECT_TRUE(test_server()->Stop()); 270 } 271 272 // Test that "Basic" HTTP authentication works. 273 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestBasicAuth) { 274 ASSERT_TRUE(test_server()->Start()); 275 GURL test_page = test_server()->GetURL(kAuthBasicPage); 276 277 content::WebContents* contents = 278 browser()->tab_strip_model()->GetActiveWebContents(); 279 NavigationController* controller = &contents->GetController(); 280 LoginPromptBrowserTestObserver observer; 281 282 observer.Register(content::Source<NavigationController>(controller)); 283 284 { 285 WindowedAuthNeededObserver auth_needed_waiter(controller); 286 browser()->OpenURL(OpenURLParams( 287 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 288 false)); 289 auth_needed_waiter.Wait(); 290 } 291 292 ASSERT_FALSE(observer.handlers_.empty()); 293 { 294 WindowedAuthNeededObserver auth_needed_waiter(controller); 295 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 296 LoginHandler* handler = *observer.handlers_.begin(); 297 298 ASSERT_TRUE(handler); 299 handler->SetAuth(UTF8ToUTF16(bad_username_), UTF8ToUTF16(bad_password_)); 300 auth_supplied_waiter.Wait(); 301 302 // The request should be retried after the incorrect password is 303 // supplied. This should result in a new AUTH_NEEDED notification 304 // for the same realm. 305 auth_needed_waiter.Wait(); 306 } 307 308 ASSERT_EQ(1u, observer.handlers_.size()); 309 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 310 LoginHandler* handler = *observer.handlers_.begin(); 311 SetAuthFor(handler); 312 auth_supplied_waiter.Wait(); 313 314 base::string16 expected_title = 315 ExpectedTitleFromAuth(ASCIIToUTF16("basicuser"), ASCIIToUTF16("secret")); 316 content::TitleWatcher title_watcher(contents, expected_title); 317 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); 318 } 319 320 // Test that "Digest" HTTP authentication works. 321 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestDigestAuth) { 322 ASSERT_TRUE(test_server()->Start()); 323 GURL test_page = test_server()->GetURL(kAuthDigestPage); 324 325 content::WebContents* contents = 326 browser()->tab_strip_model()->GetActiveWebContents(); 327 NavigationController* controller = &contents->GetController(); 328 LoginPromptBrowserTestObserver observer; 329 330 observer.Register(content::Source<NavigationController>(controller)); 331 332 { 333 WindowedAuthNeededObserver auth_needed_waiter(controller); 334 browser()->OpenURL(OpenURLParams( 335 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 336 false)); 337 auth_needed_waiter.Wait(); 338 } 339 340 ASSERT_FALSE(observer.handlers_.empty()); 341 { 342 WindowedAuthNeededObserver auth_needed_waiter(controller); 343 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 344 LoginHandler* handler = *observer.handlers_.begin(); 345 346 ASSERT_TRUE(handler); 347 handler->SetAuth(UTF8ToUTF16(bad_username_), UTF8ToUTF16(bad_password_)); 348 auth_supplied_waiter.Wait(); 349 350 // The request should be retried after the incorrect password is 351 // supplied. This should result in a new AUTH_NEEDED notification 352 // for the same realm. 353 auth_needed_waiter.Wait(); 354 } 355 356 ASSERT_EQ(1u, observer.handlers_.size()); 357 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 358 LoginHandler* handler = *observer.handlers_.begin(); 359 360 base::string16 username(UTF8ToUTF16(username_digest_)); 361 base::string16 password(UTF8ToUTF16(password_)); 362 handler->SetAuth(username, password); 363 auth_supplied_waiter.Wait(); 364 365 base::string16 expected_title = ExpectedTitleFromAuth(username, password); 366 content::TitleWatcher title_watcher(contents, expected_title); 367 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); 368 } 369 370 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestTwoAuths) { 371 ASSERT_TRUE(test_server()->Start()); 372 373 content::WebContents* contents1 = 374 browser()->tab_strip_model()->GetActiveWebContents(); 375 NavigationController* controller1 = &contents1->GetController(); 376 LoginPromptBrowserTestObserver observer; 377 378 observer.Register(content::Source<NavigationController>(controller1)); 379 380 // Open a new tab. 381 ui_test_utils::NavigateToURLWithDisposition( 382 browser(), 383 GURL("about:blank"), 384 NEW_FOREGROUND_TAB, 385 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); 386 387 content::WebContents* contents2 = 388 browser()->tab_strip_model()->GetActiveWebContents(); 389 ASSERT_NE(contents1, contents2); 390 NavigationController* controller2 = &contents2->GetController(); 391 observer.Register(content::Source<NavigationController>(controller2)); 392 393 { 394 WindowedAuthNeededObserver auth_needed_waiter(controller1); 395 contents1->OpenURL(OpenURLParams( 396 test_server()->GetURL(kAuthBasicPage), Referrer(), 397 CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false)); 398 auth_needed_waiter.Wait(); 399 } 400 401 { 402 WindowedAuthNeededObserver auth_needed_waiter(controller2); 403 contents2->OpenURL(OpenURLParams( 404 test_server()->GetURL(kAuthDigestPage), Referrer(), 405 CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false)); 406 auth_needed_waiter.Wait(); 407 } 408 409 ASSERT_EQ(2u, observer.handlers_.size()); 410 411 LoginHandler* handler1 = *observer.handlers_.begin(); 412 LoginHandler* handler2 = *(++(observer.handlers_.begin())); 413 414 base::string16 expected_title1 = ExpectedTitleFromAuth( 415 UTF8ToUTF16(username_basic_), UTF8ToUTF16(password_)); 416 base::string16 expected_title2 = ExpectedTitleFromAuth( 417 UTF8ToUTF16(username_digest_), UTF8ToUTF16(password_)); 418 content::TitleWatcher title_watcher1(contents1, expected_title1); 419 content::TitleWatcher title_watcher2(contents2, expected_title2); 420 421 handler1->SetAuth(UTF8ToUTF16(username_basic_), UTF8ToUTF16(password_)); 422 handler2->SetAuth(UTF8ToUTF16(username_digest_), UTF8ToUTF16(password_)); 423 424 EXPECT_EQ(expected_title1, title_watcher1.WaitAndGetTitle()); 425 EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle()); 426 } 427 428 // Test login prompt cancellation. 429 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestCancelAuth) { 430 ASSERT_TRUE(test_server()->Start()); 431 GURL auth_page = test_server()->GetURL(kAuthBasicPage); 432 GURL no_auth_page_1 = test_server()->GetURL("a"); 433 GURL no_auth_page_2 = test_server()->GetURL("b"); 434 GURL no_auth_page_3 = test_server()->GetURL("c"); 435 436 content::WebContents* contents = 437 browser()->tab_strip_model()->GetActiveWebContents(); 438 NavigationController* controller = &contents->GetController(); 439 440 LoginPromptBrowserTestObserver observer; 441 observer.Register(content::Source<NavigationController>(controller)); 442 443 // First navigate to an unauthenticated page so we have something to 444 // go back to. 445 ui_test_utils::NavigateToURL(browser(), no_auth_page_1); 446 447 // Navigating while auth is requested is the same as cancelling. 448 { 449 // We need to wait for two LOAD_STOP events. One for auth_page and one for 450 // no_auth_page_2. 451 WindowedLoadStopObserver load_stop_waiter(controller, 2); 452 WindowedAuthNeededObserver auth_needed_waiter(controller); 453 browser()->OpenURL(OpenURLParams( 454 auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 455 false)); 456 auth_needed_waiter.Wait(); 457 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 458 browser()->OpenURL(OpenURLParams( 459 no_auth_page_2, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 460 false)); 461 auth_cancelled_waiter.Wait(); 462 load_stop_waiter.Wait(); 463 EXPECT_TRUE(observer.handlers_.empty()); 464 } 465 466 // Try navigating backwards. 467 { 468 // As above, we wait for two LOAD_STOP events; one for each navigation. 469 WindowedLoadStopObserver load_stop_waiter(controller, 2); 470 WindowedAuthNeededObserver auth_needed_waiter(controller); 471 browser()->OpenURL(OpenURLParams( 472 auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 473 false)); 474 auth_needed_waiter.Wait(); 475 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 476 ASSERT_TRUE(chrome::CanGoBack(browser())); 477 chrome::GoBack(browser(), CURRENT_TAB); 478 auth_cancelled_waiter.Wait(); 479 load_stop_waiter.Wait(); 480 EXPECT_TRUE(observer.handlers_.empty()); 481 } 482 483 // Now add a page and go back, so we have something to go forward to. 484 ui_test_utils::NavigateToURL(browser(), no_auth_page_3); 485 { 486 WindowedLoadStopObserver load_stop_waiter(controller, 1); 487 chrome::GoBack(browser(), CURRENT_TAB); // Should take us to page 1 488 load_stop_waiter.Wait(); 489 } 490 491 { 492 // We wait for two LOAD_STOP events; one for each navigation. 493 WindowedLoadStopObserver load_stop_waiter(controller, 2); 494 WindowedAuthNeededObserver auth_needed_waiter(controller); 495 browser()->OpenURL(OpenURLParams( 496 auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 497 false)); 498 auth_needed_waiter.Wait(); 499 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 500 ASSERT_TRUE(chrome::CanGoForward(browser())); 501 chrome::GoForward(browser(), CURRENT_TAB); // Should take us to page 3 502 auth_cancelled_waiter.Wait(); 503 load_stop_waiter.Wait(); 504 EXPECT_TRUE(observer.handlers_.empty()); 505 } 506 507 // Now test that cancelling works as expected. 508 { 509 WindowedLoadStopObserver load_stop_waiter(controller, 1); 510 WindowedAuthNeededObserver auth_needed_waiter(controller); 511 browser()->OpenURL(OpenURLParams( 512 auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 513 false)); 514 auth_needed_waiter.Wait(); 515 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 516 LoginHandler* handler = *observer.handlers_.begin(); 517 ASSERT_TRUE(handler); 518 handler->CancelAuth(); 519 auth_cancelled_waiter.Wait(); 520 load_stop_waiter.Wait(); 521 EXPECT_TRUE(observer.handlers_.empty()); 522 } 523 } 524 525 // Test handling of resources that require authentication even though 526 // the page they are included on doesn't. In this case we should only 527 // present the minimal number of prompts necessary for successfully 528 // displaying the page. First we check whether cancelling works as 529 // expected. 530 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmCancellation) { 531 ASSERT_TRUE(test_server()->Start()); 532 GURL test_page = test_server()->GetURL(kMultiRealmTestPage); 533 534 content::WebContents* contents = 535 browser()->tab_strip_model()->GetActiveWebContents(); 536 NavigationController* controller = &contents->GetController(); 537 LoginPromptBrowserTestObserver observer; 538 539 observer.Register(content::Source<NavigationController>(controller)); 540 541 WindowedLoadStopObserver load_stop_waiter(controller, 1); 542 543 { 544 WindowedAuthNeededObserver auth_needed_waiter(controller); 545 browser()->OpenURL(OpenURLParams( 546 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 547 false)); 548 auth_needed_waiter.Wait(); 549 } 550 551 int n_handlers = 0; 552 553 while (n_handlers < kMultiRealmTestRealmCount) { 554 WindowedAuthNeededObserver auth_needed_waiter(controller); 555 556 while (!observer.handlers_.empty()) { 557 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 558 LoginHandler* handler = *observer.handlers_.begin(); 559 560 ASSERT_TRUE(handler); 561 n_handlers++; 562 handler->CancelAuth(); 563 auth_cancelled_waiter.Wait(); 564 } 565 566 if (n_handlers < kMultiRealmTestRealmCount) 567 auth_needed_waiter.Wait(); 568 } 569 570 load_stop_waiter.Wait(); 571 572 EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers); 573 EXPECT_EQ(0, observer.auth_supplied_count_); 574 EXPECT_LT(0, observer.auth_needed_count_); 575 EXPECT_LT(0, observer.auth_cancelled_count_); 576 EXPECT_TRUE(test_server()->Stop()); 577 } 578 579 // Similar to the MultipleRealmCancellation test above, but tests 580 // whether supplying credentials work as exepcted. 581 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmConfirmation) { 582 ASSERT_TRUE(test_server()->Start()); 583 GURL test_page = test_server()->GetURL(kMultiRealmTestPage); 584 585 content::WebContents* contents = 586 browser()->tab_strip_model()->GetActiveWebContents(); 587 NavigationController* controller = &contents->GetController(); 588 LoginPromptBrowserTestObserver observer; 589 590 observer.Register(content::Source<NavigationController>(controller)); 591 592 WindowedLoadStopObserver load_stop_waiter(controller, 1); 593 int n_handlers = 0; 594 595 { 596 WindowedAuthNeededObserver auth_needed_waiter(controller); 597 598 browser()->OpenURL(OpenURLParams( 599 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 600 false)); 601 auth_needed_waiter.Wait(); 602 } 603 604 while (n_handlers < kMultiRealmTestRealmCount) { 605 WindowedAuthNeededObserver auth_needed_waiter(controller); 606 607 while (!observer.handlers_.empty()) { 608 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 609 LoginHandler* handler = *observer.handlers_.begin(); 610 611 ASSERT_TRUE(handler); 612 n_handlers++; 613 SetAuthFor(handler); 614 auth_supplied_waiter.Wait(); 615 } 616 617 if (n_handlers < kMultiRealmTestRealmCount) 618 auth_needed_waiter.Wait(); 619 } 620 621 load_stop_waiter.Wait(); 622 623 EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers); 624 EXPECT_LT(0, observer.auth_needed_count_); 625 EXPECT_LT(0, observer.auth_supplied_count_); 626 EXPECT_EQ(0, observer.auth_cancelled_count_); 627 EXPECT_TRUE(test_server()->Stop()); 628 } 629 630 // Testing for recovery from an incorrect password for the case where 631 // there are multiple authenticated resources. 632 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, IncorrectConfirmation) { 633 ASSERT_TRUE(test_server()->Start()); 634 GURL test_page = test_server()->GetURL(kSingleRealmTestPage); 635 636 content::WebContents* contents = 637 browser()->tab_strip_model()->GetActiveWebContents(); 638 NavigationController* controller = &contents->GetController(); 639 LoginPromptBrowserTestObserver observer; 640 641 observer.Register(content::Source<NavigationController>(controller)); 642 643 { 644 WindowedAuthNeededObserver auth_needed_waiter(controller); 645 browser()->OpenURL(OpenURLParams( 646 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 647 false)); 648 auth_needed_waiter.Wait(); 649 } 650 651 EXPECT_FALSE(observer.handlers_.empty()); 652 653 if (!observer.handlers_.empty()) { 654 WindowedAuthNeededObserver auth_needed_waiter(controller); 655 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 656 LoginHandler* handler = *observer.handlers_.begin(); 657 658 ASSERT_TRUE(handler); 659 handler->SetAuth(UTF8ToUTF16(bad_username_), 660 UTF8ToUTF16(bad_password_)); 661 auth_supplied_waiter.Wait(); 662 663 // The request should be retried after the incorrect password is 664 // supplied. This should result in a new AUTH_NEEDED notification 665 // for the same realm. 666 auth_needed_waiter.Wait(); 667 } 668 669 int n_handlers = 0; 670 671 while (n_handlers < 1) { 672 WindowedAuthNeededObserver auth_needed_waiter(controller); 673 674 while (!observer.handlers_.empty()) { 675 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 676 LoginHandler* handler = *observer.handlers_.begin(); 677 678 ASSERT_TRUE(handler); 679 n_handlers++; 680 SetAuthFor(handler); 681 auth_supplied_waiter.Wait(); 682 } 683 684 if (n_handlers < 1) 685 auth_needed_waiter.Wait(); 686 } 687 688 // The single realm test has only one realm, and thus only one login 689 // prompt. 690 EXPECT_EQ(1, n_handlers); 691 EXPECT_LT(0, observer.auth_needed_count_); 692 EXPECT_EQ(0, observer.auth_cancelled_count_); 693 EXPECT_EQ(observer.auth_needed_count_, observer.auth_supplied_count_); 694 EXPECT_TRUE(test_server()->Stop()); 695 } 696 697 // If the favicon is an authenticated resource, we shouldn't prompt 698 // for credentials. The same URL, if requested elsewhere should 699 // prompt for credentials. 700 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, NoLoginPromptForFavicon) { 701 const char* kFaviconTestPage = "files/login/has_favicon.html"; 702 const char* kFaviconResource = "auth-basic/favicon.gif"; 703 704 ASSERT_TRUE(test_server()->Start()); 705 706 content::WebContents* contents = 707 browser()->tab_strip_model()->GetActiveWebContents(); 708 NavigationController* controller = &contents->GetController(); 709 LoginPromptBrowserTestObserver observer; 710 711 observer.Register(content::Source<NavigationController>(controller)); 712 713 // First load a page that has a favicon that requires 714 // authentication. There should be no login prompt. 715 { 716 GURL test_page = test_server()->GetURL(kFaviconTestPage); 717 WindowedLoadStopObserver load_stop_waiter(controller, 1); 718 browser()->OpenURL(OpenURLParams( 719 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 720 false)); 721 load_stop_waiter.Wait(); 722 } 723 724 // Now request the same favicon, but directly as the document. 725 // There should be one login prompt. 726 { 727 GURL test_page = test_server()->GetURL(kFaviconResource); 728 WindowedLoadStopObserver load_stop_waiter(controller, 1); 729 WindowedAuthNeededObserver auth_needed_waiter(controller); 730 browser()->OpenURL(OpenURLParams( 731 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 732 false)); 733 auth_needed_waiter.Wait(); 734 ASSERT_EQ(1u, observer.handlers_.size()); 735 736 while (!observer.handlers_.empty()) { 737 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 738 LoginHandler* handler = *observer.handlers_.begin(); 739 740 ASSERT_TRUE(handler); 741 handler->CancelAuth(); 742 auth_cancelled_waiter.Wait(); 743 } 744 745 load_stop_waiter.Wait(); 746 } 747 748 EXPECT_EQ(0, observer.auth_supplied_count_); 749 EXPECT_EQ(1, observer.auth_needed_count_); 750 EXPECT_EQ(1, observer.auth_cancelled_count_); 751 EXPECT_TRUE(test_server()->Stop()); 752 } 753 754 // Block crossdomain image login prompting as a phishing defense. 755 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 756 BlockCrossdomainPrompt) { 757 const char* kTestPage = "files/login/load_img_from_b.html"; 758 759 host_resolver()->AddRule("www.a.com", "127.0.0.1"); 760 host_resolver()->AddRule("www.b.com", "127.0.0.1"); 761 ASSERT_TRUE(test_server()->Start()); 762 763 content::WebContents* contents = 764 browser()->tab_strip_model()->GetActiveWebContents(); 765 NavigationController* controller = &contents->GetController(); 766 LoginPromptBrowserTestObserver observer; 767 observer.Register(content::Source<NavigationController>(controller)); 768 769 // Load a page that has a cross-domain sub-resource authentication. 770 // There should be no login prompt. 771 { 772 GURL test_page = test_server()->GetURL(kTestPage); 773 ASSERT_EQ("127.0.0.1", test_page.host()); 774 775 // Change the host from 127.0.0.1 to www.a.com so that when the 776 // page tries to load from b, it will be cross-origin. 777 std::string new_host("www.a.com"); 778 GURL::Replacements replacements; 779 replacements.SetHostStr(new_host); 780 test_page = test_page.ReplaceComponents(replacements); 781 782 WindowedLoadStopObserver load_stop_waiter(controller, 1); 783 browser()->OpenURL(OpenURLParams( 784 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 785 false)); 786 load_stop_waiter.Wait(); 787 } 788 789 EXPECT_EQ(0, observer.auth_needed_count_); 790 791 // Now request the same page, but from the same origin. 792 // There should be one login prompt. 793 { 794 GURL test_page = test_server()->GetURL(kTestPage); 795 ASSERT_EQ("127.0.0.1", test_page.host()); 796 797 // Change the host from 127.0.0.1 to www.b.com so that when the 798 // page tries to load from b, it will be same-origin. 799 std::string new_host("www.b.com"); 800 GURL::Replacements replacements; 801 replacements.SetHostStr(new_host); 802 test_page = test_page.ReplaceComponents(replacements); 803 804 WindowedAuthNeededObserver auth_needed_waiter(controller); 805 browser()->OpenURL(OpenURLParams( 806 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 807 false)); 808 auth_needed_waiter.Wait(); 809 ASSERT_EQ(1u, observer.handlers_.size()); 810 811 while (!observer.handlers_.empty()) { 812 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 813 LoginHandler* handler = *observer.handlers_.begin(); 814 815 ASSERT_TRUE(handler); 816 handler->CancelAuth(); 817 auth_cancelled_waiter.Wait(); 818 } 819 } 820 821 EXPECT_EQ(1, observer.auth_needed_count_); 822 EXPECT_TRUE(test_server()->Stop()); 823 } 824 825 // Allow crossdomain iframe login prompting despite the above. 826 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 827 AllowCrossdomainPrompt) { 828 const char* kTestPage = "files/login/load_iframe_from_b.html"; 829 830 host_resolver()->AddRule("www.a.com", "127.0.0.1"); 831 host_resolver()->AddRule("www.b.com", "127.0.0.1"); 832 ASSERT_TRUE(test_server()->Start()); 833 834 content::WebContents* contents = 835 browser()->tab_strip_model()->GetActiveWebContents(); 836 NavigationController* controller = &contents->GetController(); 837 LoginPromptBrowserTestObserver observer; 838 observer.Register(content::Source<NavigationController>(controller)); 839 840 // Load a page that has a cross-domain iframe authentication. 841 { 842 GURL test_page = test_server()->GetURL(kTestPage); 843 ASSERT_EQ("127.0.0.1", test_page.host()); 844 845 // Change the host from 127.0.0.1 to www.a.com so that when the 846 // page tries to load from b, it will be cross-origin. 847 std::string new_host("www.a.com"); 848 GURL::Replacements replacements; 849 replacements.SetHostStr(new_host); 850 test_page = test_page.ReplaceComponents(replacements); 851 852 WindowedAuthNeededObserver auth_needed_waiter(controller); 853 browser()->OpenURL(OpenURLParams( 854 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 855 false)); 856 auth_needed_waiter.Wait(); 857 ASSERT_EQ(1u, observer.handlers_.size()); 858 859 while (!observer.handlers_.empty()) { 860 WindowedAuthCancelledObserver auth_cancelled_waiter(controller); 861 LoginHandler* handler = *observer.handlers_.begin(); 862 863 ASSERT_TRUE(handler); 864 handler->CancelAuth(); 865 auth_cancelled_waiter.Wait(); 866 } 867 } 868 869 EXPECT_EQ(1, observer.auth_needed_count_); 870 EXPECT_TRUE(test_server()->Stop()); 871 } 872 873 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, SupplyRedundantAuths) { 874 ASSERT_TRUE(test_server()->Start()); 875 876 // Get NavigationController for tab 1. 877 content::WebContents* contents_1 = 878 browser()->tab_strip_model()->GetActiveWebContents(); 879 NavigationController* controller_1 = &contents_1->GetController(); 880 881 // Open a new tab. 882 ui_test_utils::NavigateToURLWithDisposition( 883 browser(), 884 GURL("about:blank"), 885 NEW_FOREGROUND_TAB, 886 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); 887 888 // Get NavigationController for tab 2. 889 content::WebContents* contents_2 = 890 browser()->tab_strip_model()->GetActiveWebContents(); 891 ASSERT_NE(contents_1, contents_2); 892 NavigationController* controller_2 = &contents_2->GetController(); 893 894 LoginPromptBrowserTestObserver observer; 895 observer.Register(content::Source<NavigationController>(controller_1)); 896 observer.Register(content::Source<NavigationController>(controller_2)); 897 898 { 899 // Open different auth urls in each tab. 900 WindowedAuthNeededObserver auth_needed_waiter_1(controller_1); 901 WindowedAuthNeededObserver auth_needed_waiter_2(controller_2); 902 contents_1->OpenURL(OpenURLParams( 903 test_server()->GetURL("auth-basic/1"), 904 content::Referrer(), 905 CURRENT_TAB, 906 content::PAGE_TRANSITION_TYPED, 907 false)); 908 contents_2->OpenURL(OpenURLParams( 909 test_server()->GetURL("auth-basic/2"), 910 content::Referrer(), 911 CURRENT_TAB, 912 content::PAGE_TRANSITION_TYPED, 913 false)); 914 auth_needed_waiter_1.Wait(); 915 auth_needed_waiter_2.Wait(); 916 917 ASSERT_EQ(2U, observer.handlers_.size()); 918 919 // Supply auth in one of the tabs. 920 WindowedAuthSuppliedObserver auth_supplied_waiter_1(controller_1); 921 WindowedAuthSuppliedObserver auth_supplied_waiter_2(controller_2); 922 LoginHandler* handler_1 = *observer.handlers_.begin(); 923 ASSERT_TRUE(handler_1); 924 SetAuthFor(handler_1); 925 926 // Both tabs should be authenticated. 927 auth_supplied_waiter_1.Wait(); 928 auth_supplied_waiter_2.Wait(); 929 } 930 931 EXPECT_EQ(2, observer.auth_needed_count_); 932 EXPECT_EQ(2, observer.auth_supplied_count_); 933 EXPECT_EQ(0, observer.auth_cancelled_count_); 934 EXPECT_TRUE(test_server()->Stop()); 935 } 936 937 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, CancelRedundantAuths) { 938 ASSERT_TRUE(test_server()->Start()); 939 940 // Get NavigationController for tab 1. 941 content::WebContents* contents_1 = 942 browser()->tab_strip_model()->GetActiveWebContents(); 943 NavigationController* controller_1 = &contents_1->GetController(); 944 945 // Open a new tab. 946 ui_test_utils::NavigateToURLWithDisposition( 947 browser(), 948 GURL("about:blank"), 949 NEW_FOREGROUND_TAB, 950 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); 951 952 // Get NavigationController for tab 2. 953 content::WebContents* contents_2 = 954 browser()->tab_strip_model()->GetActiveWebContents(); 955 ASSERT_NE(contents_1, contents_2); 956 NavigationController* controller_2 = &contents_2->GetController(); 957 958 LoginPromptBrowserTestObserver observer; 959 observer.Register(content::Source<NavigationController>(controller_1)); 960 observer.Register(content::Source<NavigationController>(controller_2)); 961 962 { 963 // Open different auth urls in each tab. 964 WindowedAuthNeededObserver auth_needed_waiter_1(controller_1); 965 WindowedAuthNeededObserver auth_needed_waiter_2(controller_2); 966 contents_1->OpenURL(OpenURLParams( 967 test_server()->GetURL("auth-basic/1"), 968 content::Referrer(), 969 CURRENT_TAB, 970 content::PAGE_TRANSITION_TYPED, 971 false)); 972 contents_2->OpenURL(OpenURLParams( 973 test_server()->GetURL("auth-basic/2"), 974 content::Referrer(), 975 CURRENT_TAB, 976 content::PAGE_TRANSITION_TYPED, 977 false)); 978 auth_needed_waiter_1.Wait(); 979 auth_needed_waiter_2.Wait(); 980 981 ASSERT_EQ(2U, observer.handlers_.size()); 982 983 // Cancel auth in one of the tabs. 984 WindowedAuthCancelledObserver auth_cancelled_waiter_1(controller_1); 985 WindowedAuthCancelledObserver auth_cancelled_waiter_2(controller_2); 986 LoginHandler* handler_1 = *observer.handlers_.begin(); 987 ASSERT_TRUE(handler_1); 988 handler_1->CancelAuth(); 989 990 // Both tabs should cancel auth. 991 auth_cancelled_waiter_1.Wait(); 992 auth_cancelled_waiter_2.Wait(); 993 } 994 995 EXPECT_EQ(2, observer.auth_needed_count_); 996 EXPECT_EQ(0, observer.auth_supplied_count_); 997 EXPECT_EQ(2, observer.auth_cancelled_count_); 998 EXPECT_TRUE(test_server()->Stop()); 999 } 1000 1001 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, 1002 SupplyRedundantAuthsMultiProfile) { 1003 ASSERT_TRUE(test_server()->Start()); 1004 1005 // Get NavigationController for regular tab. 1006 content::WebContents* contents = 1007 browser()->tab_strip_model()->GetActiveWebContents(); 1008 NavigationController* controller = &contents->GetController(); 1009 1010 // Open an incognito window. 1011 Browser* browser_incognito = CreateIncognitoBrowser(); 1012 1013 // Get NavigationController for incognito tab. 1014 content::WebContents* contents_incognito = 1015 browser_incognito->tab_strip_model()->GetActiveWebContents(); 1016 ASSERT_NE(contents, contents_incognito); 1017 NavigationController* controller_incognito = 1018 &contents_incognito->GetController(); 1019 1020 LoginPromptBrowserTestObserver observer; 1021 observer.Register(content::Source<NavigationController>(controller)); 1022 LoginPromptBrowserTestObserver observer_incognito; 1023 observer_incognito.Register( 1024 content::Source<NavigationController>(controller_incognito)); 1025 1026 { 1027 // Open an auth url in each window. 1028 WindowedAuthNeededObserver auth_needed_waiter(controller); 1029 WindowedAuthNeededObserver auth_needed_waiter_incognito( 1030 controller_incognito); 1031 contents->OpenURL(OpenURLParams( 1032 test_server()->GetURL("auth-basic/1"), 1033 content::Referrer(), 1034 CURRENT_TAB, 1035 content::PAGE_TRANSITION_TYPED, 1036 false)); 1037 contents_incognito->OpenURL(OpenURLParams( 1038 test_server()->GetURL("auth-basic/2"), 1039 content::Referrer(), 1040 CURRENT_TAB, 1041 content::PAGE_TRANSITION_TYPED, 1042 false)); 1043 auth_needed_waiter.Wait(); 1044 auth_needed_waiter_incognito.Wait(); 1045 1046 ASSERT_EQ(1U, observer.handlers_.size()); 1047 ASSERT_EQ(1U, observer_incognito.handlers_.size()); 1048 1049 // Supply auth in regular tab. 1050 WindowedAuthSuppliedObserver auth_supplied_waiter(controller); 1051 LoginHandler* handler = *observer.handlers_.begin(); 1052 ASSERT_TRUE(handler); 1053 SetAuthFor(handler); 1054 1055 // Regular tab should be authenticated. 1056 auth_supplied_waiter.Wait(); 1057 1058 // There's not really a way to wait for the incognito window to "do 1059 // nothing". Run anything pending in the message loop just to be sure. 1060 // (This shouldn't be necessary since notifications are synchronous, but 1061 // maybe it will help avoid flake someday in the future..) 1062 content::RunAllPendingInMessageLoop(); 1063 } 1064 1065 EXPECT_EQ(1, observer.auth_needed_count_); 1066 EXPECT_EQ(1, observer.auth_supplied_count_); 1067 EXPECT_EQ(0, observer.auth_cancelled_count_); 1068 EXPECT_EQ(1, observer_incognito.auth_needed_count_); 1069 EXPECT_EQ(0, observer_incognito.auth_supplied_count_); 1070 EXPECT_EQ(0, observer_incognito.auth_cancelled_count_); 1071 EXPECT_TRUE(test_server()->Stop()); 1072 } 1073 1074 } // namespace 1075