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