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