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