Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / saml / saml_browsertest.cc
1 // Copyright 2014 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 "base/command_line.h"
6 #include "base/file_util.h"
7 #include "base/files/file_path.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/path_service.h"
10 #include "base/run_loop.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/chromeos/login/existing_user_controller.h"
16 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
17 #include "chrome/browser/chromeos/login/test/https_forwarder.h"
18 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
19 #include "chrome/browser/chromeos/login/user.h"
20 #include "chrome/browser/chromeos/login/user_manager.h"
21 #include "chrome/browser/chromeos/login/webui_login_display.h"
22 #include "chrome/browser/chromeos/login/wizard_controller.h"
23 #include "chrome/browser/lifetime/application_lifetime.h"
24 #include "chrome/common/chrome_paths.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/test/base/in_process_browser_test.h"
27 #include "chromeos/chromeos_switches.h"
28 #include "components/policy/core/browser/browser_policy_connector.h"
29 #include "components/policy/core/common/mock_configuration_policy_provider.h"
30 #include "components/policy/core/common/policy_map.h"
31 #include "components/policy/core/common/policy_types.h"
32 #include "content/public/browser/render_view_host.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/test/browser_test_utils.h"
35 #include "content/public/test/test_utils.h"
36 #include "google_apis/gaia/fake_gaia.h"
37 #include "google_apis/gaia/gaia_switches.h"
38 #include "net/base/url_util.h"
39 #include "net/dns/mock_host_resolver.h"
40 #include "net/test/embedded_test_server/embedded_test_server.h"
41 #include "net/test/embedded_test_server/http_request.h"
42 #include "net/test/embedded_test_server/http_response.h"
43 #include "policy/policy_constants.h"
44 #include "testing/gmock/include/gmock/gmock.h"
45 #include "testing/gtest/include/gtest/gtest.h"
46 #include "url/gurl.h"
47
48 using net::test_server::BasicHttpResponse;
49 using net::test_server::HttpRequest;
50 using net::test_server::HttpResponse;
51 using testing::_;
52 using testing::Return;
53
54 namespace chromeos {
55
56 namespace {
57
58 const char kTestAuthSIDCookie[] = "fake-auth-SID-cookie";
59 const char kTestAuthLSIDCookie[] = "fake-auth-LSID-cookie";
60 const char kTestAuthCode[] = "fake-auth-code";
61 const char kTestGaiaUberToken[] = "fake-uber-token";
62 const char kTestAuthLoginAccessToken[] = "fake-access-token";
63 const char kTestRefreshToken[] = "fake-refresh-token";
64 const char kTestSessionSIDCookie[] = "fake-session-SID-cookie";
65 const char kTestSessionLSIDCookie[] = "fake-session-LSID-cookie";
66
67 const char kFirstSAMLUserEmail[] = "bob@example.com";
68 const char kSecondSAMLUserEmail[] = "alice@example.com";
69 const char kHTTPSAMLUserEmail[] = "carol@example.com";
70 const char kNonSAMLUserEmail[] = "dan@example.com";
71
72 const char kRelayState[] = "RelayState";
73
74 // FakeSamlIdp serves IdP auth form and the form submission. The form is
75 // served with the template's RelayState placeholder expanded to the real
76 // RelayState parameter from request. The form submission redirects back to
77 // FakeGaia with the same RelayState.
78 class FakeSamlIdp {
79  public:
80   FakeSamlIdp();
81   ~FakeSamlIdp();
82
83   void SetUp(const std::string& base_path, const GURL& gaia_url);
84
85   void SetLoginHTMLTemplate(const std::string& template_file);
86   void SetLoginAuthHTMLTemplate(const std::string& template_file);
87
88   scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
89
90  private:
91   scoped_ptr<HttpResponse> BuildHTMLResponse(const std::string& html_template,
92                                              const std::string& relay_state,
93                                              const std::string& next_path);
94
95   base::FilePath html_template_dir_;
96
97   std::string login_path_;
98   std::string login_auth_path_;
99
100   std::string login_html_template_;
101   std::string login_auth_html_template_;
102   GURL gaia_assertion_url_;
103
104   DISALLOW_COPY_AND_ASSIGN(FakeSamlIdp);
105 };
106
107 FakeSamlIdp::FakeSamlIdp() {
108 }
109
110 FakeSamlIdp::~FakeSamlIdp() {
111 }
112
113 void FakeSamlIdp::SetUp(const std::string& base_path, const GURL& gaia_url) {
114   base::FilePath test_data_dir;
115   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
116   html_template_dir_ = test_data_dir.Append("login");
117
118   login_path_= base_path;
119   login_auth_path_ = base_path + "Auth";
120   gaia_assertion_url_ = gaia_url.Resolve("/SSO");
121 }
122
123 void FakeSamlIdp::SetLoginHTMLTemplate(const std::string& template_file) {
124   EXPECT_TRUE(base::ReadFileToString(
125       html_template_dir_.Append(template_file),
126       &login_html_template_));
127 }
128
129 void FakeSamlIdp::SetLoginAuthHTMLTemplate(const std::string& template_file) {
130   EXPECT_TRUE(base::ReadFileToString(
131       html_template_dir_.Append(template_file),
132       &login_auth_html_template_));
133 }
134
135 scoped_ptr<HttpResponse> FakeSamlIdp::HandleRequest(
136     const HttpRequest& request) {
137   // The scheme and host of the URL is actually not important but required to
138   // get a valid GURL in order to parse |request.relative_url|.
139   GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
140   std::string request_path = request_url.path();
141
142   if (request_path == login_path_) {
143     std::string relay_state;
144     net::GetValueForKeyInQuery(request_url, kRelayState, &relay_state);
145     return BuildHTMLResponse(login_html_template_,
146                              relay_state,
147                              login_auth_path_);
148   }
149
150   if (request_path != login_auth_path_) {
151     // Request not understood.
152     return scoped_ptr<HttpResponse>();
153   }
154
155   std::string relay_state;
156   FakeGaia::GetQueryParameter(request.content, kRelayState, &relay_state);
157   GURL redirect_url = gaia_assertion_url_;
158
159   if (!login_auth_html_template_.empty()) {
160     return BuildHTMLResponse(login_auth_html_template_,
161                              relay_state,
162                              redirect_url.spec());
163   }
164
165   redirect_url = net::AppendQueryParameter(
166       redirect_url, "SAMLResponse", "fake_response");
167   redirect_url = net::AppendQueryParameter(
168       redirect_url, kRelayState, relay_state);
169
170   scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
171   http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
172   http_response->AddCustomHeader("Location", redirect_url.spec());
173   return http_response.PassAs<HttpResponse>();
174 }
175
176 scoped_ptr<HttpResponse> FakeSamlIdp::BuildHTMLResponse(
177     const std::string& html_template,
178     const std::string& relay_state,
179     const std::string& next_path) {
180   std::string response_html = html_template;
181   ReplaceSubstringsAfterOffset(&response_html, 0, "$RelayState", relay_state);
182   ReplaceSubstringsAfterOffset(&response_html, 0, "$Post", next_path);
183
184   scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
185   http_response->set_code(net::HTTP_OK);
186   http_response->set_content(response_html);
187   http_response->set_content_type("text/html");
188
189   return http_response.PassAs<HttpResponse>();
190 }
191
192 }  // namespace
193
194 class SamlTest : public InProcessBrowserTest {
195  public:
196   SamlTest() : saml_load_injected_(false) {}
197   virtual ~SamlTest() {}
198
199   virtual void SetUp() OVERRIDE {
200     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
201
202     // Start the GAIA https wrapper here so that the GAIA URLs can be pointed at
203     // it in SetUpCommandLine().
204     gaia_https_forwarder_.reset(
205         new HTTPSForwarder(embedded_test_server()->base_url()));
206     ASSERT_TRUE(gaia_https_forwarder_->Start());
207
208     // Start the SAML IdP https wrapper here so that GAIA can be pointed at it
209     // in SetUpCommandLine().
210     saml_https_forwarder_.reset(
211         new HTTPSForwarder(embedded_test_server()->base_url()));
212     ASSERT_TRUE(saml_https_forwarder_->Start());
213
214     // Stop IO thread here because no threads are allowed while
215     // spawning sandbox host process. See crbug.com/322732.
216     embedded_test_server()->StopThread();
217
218     InProcessBrowserTest::SetUp();
219   }
220
221   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
222     host_resolver()->AddRule("*", "127.0.0.1");
223   }
224
225   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
226     command_line->AppendSwitch(switches::kLoginManager);
227     command_line->AppendSwitch(switches::kForceLoginManagerInTests);
228     command_line->AppendSwitch(::switches::kDisableBackgroundNetworking);
229     command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
230
231     const GURL gaia_url = gaia_https_forwarder_->GetURL("");
232     command_line->AppendSwitchASCII(::switches::kGaiaUrl, gaia_url.spec());
233     command_line->AppendSwitchASCII(::switches::kLsoUrl, gaia_url.spec());
234     command_line->AppendSwitchASCII(::switches::kGoogleApisUrl,
235                                     gaia_url.spec());
236
237     const GURL saml_idp_url = saml_https_forwarder_->GetURL("SAML");
238     fake_saml_idp_.SetUp(saml_idp_url.path(), gaia_url);
239     fake_gaia_.RegisterSamlUser(kFirstSAMLUserEmail, saml_idp_url);
240     fake_gaia_.RegisterSamlUser(kSecondSAMLUserEmail, saml_idp_url);
241     fake_gaia_.RegisterSamlUser(
242         kHTTPSAMLUserEmail,
243         embedded_test_server()->base_url().Resolve("/SAML"));
244
245     fake_gaia_.Initialize();
246   }
247
248   virtual void SetUpOnMainThread() OVERRIDE {
249     FakeGaia::MergeSessionParams params;
250     params.auth_sid_cookie = kTestAuthSIDCookie;
251     params.auth_lsid_cookie = kTestAuthLSIDCookie;
252     params.auth_code = kTestAuthCode;
253     params.refresh_token = kTestRefreshToken;
254     params.access_token = kTestAuthLoginAccessToken;
255     params.gaia_uber_token = kTestGaiaUberToken;
256     params.session_sid_cookie = kTestSessionSIDCookie;
257     params.session_lsid_cookie = kTestSessionLSIDCookie;
258     params.email = kFirstSAMLUserEmail;
259     fake_gaia_.SetMergeSessionParams(params);
260
261     embedded_test_server()->RegisterRequestHandler(
262         base::Bind(&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_)));
263     embedded_test_server()->RegisterRequestHandler(base::Bind(
264         &FakeSamlIdp::HandleRequest, base::Unretained(&fake_saml_idp_)));
265
266     // Restart the thread as the sandbox host process has already been spawned.
267     embedded_test_server()->RestartThreadAndListen();
268
269     login_screen_load_observer_.reset(new content::WindowedNotificationObserver(
270         chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
271         content::NotificationService::AllSources()));
272   }
273
274   virtual void CleanUpOnMainThread() OVERRIDE {
275     // If the login display is still showing, exit gracefully.
276     if (LoginDisplayHostImpl::default_host()) {
277       base::MessageLoop::current()->PostTask(FROM_HERE,
278                                              base::Bind(&chrome::AttemptExit));
279       content::RunMessageLoop();
280     }
281   }
282
283   WebUILoginDisplay* GetLoginDisplay() {
284     ExistingUserController* controller =
285         ExistingUserController::current_controller();
286     CHECK(controller);
287     return static_cast<WebUILoginDisplay*>(controller->login_display());
288   }
289
290   void WaitForSigninScreen() {
291     WizardController::SkipPostLoginScreensForTesting();
292     WizardController* wizard_controller =
293         chromeos::WizardController::default_controller();
294     CHECK(wizard_controller);
295     wizard_controller->SkipToLoginForTesting(LoginScreenContext());
296
297     login_screen_load_observer_->Wait();
298   }
299
300   void StartSamlAndWaitForIdpPageLoad(const std::string& gaia_email) {
301     WaitForSigninScreen();
302
303     if (!saml_load_injected_) {
304       saml_load_injected_ = true;
305
306       ASSERT_TRUE(content::ExecuteScript(
307           GetLoginUI()->GetWebContents(),
308           "$('gaia-signin').gaiaAuthHost_.addEventListener('authFlowChange',"
309               "function() {"
310                 "window.domAutomationController.setAutomationId(0);"
311                 "window.domAutomationController.send("
312                     "$('gaia-signin').isSAML() ? 'SamlLoaded' : 'GaiaLoaded');"
313               "});"));
314     }
315
316     content::DOMMessageQueue message_queue;  // Start observe before SAML.
317     GetLoginDisplay()->ShowSigninScreenForCreds(gaia_email, "");
318
319     std::string message;
320     ASSERT_TRUE(message_queue.WaitForMessage(&message));
321     EXPECT_EQ("\"SamlLoaded\"", message);
322   }
323
324   void SetSignFormField(const std::string& field_id,
325                         const std::string& field_value) {
326     std::string js =
327         "(function(){"
328           "document.getElementById('$FieldId').value = '$FieldValue';"
329           "var e = new Event('input');"
330           "document.getElementById('$FieldId').dispatchEvent(e);"
331         "})();";
332     ReplaceSubstringsAfterOffset(&js, 0, "$FieldId", field_id);
333     ReplaceSubstringsAfterOffset(&js, 0, "$FieldValue", field_value);
334     ExecuteJsInSigninFrame(js);
335   }
336
337   void SendConfirmPassword(const std::string& password_to_confirm) {
338     std::string js =
339         "$('confirm-password-input').value='$Password';"
340         "$('confirm-password').onConfirmPassword_();";
341     ReplaceSubstringsAfterOffset(&js, 0, "$Password", password_to_confirm);
342     ASSERT_TRUE(content::ExecuteScript(GetLoginUI()->GetWebContents(), js));
343   }
344
345   void JsExpect(const std::string& js) {
346     bool result;
347     EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
348         GetLoginUI()->GetWebContents(),
349         "window.domAutomationController.send(!!(" + js + "));",
350         &result));
351     EXPECT_TRUE(result) << js;
352   }
353
354   content::WebUI* GetLoginUI() {
355     return static_cast<chromeos::LoginDisplayHostImpl*>(
356         chromeos::LoginDisplayHostImpl::default_host())->GetOobeUI()->web_ui();
357   }
358
359   // Executes Js code in the auth iframe hosted by gaia_auth extension.
360   void ExecuteJsInSigninFrame(const std::string& js) {
361     ASSERT_TRUE(content::ExecuteScriptInFrame(
362         GetLoginUI()->GetWebContents(),
363         "//iframe[@id='signin-frame']\n//iframe",
364         js));
365   }
366
367   FakeSamlIdp* fake_saml_idp() { return &fake_saml_idp_; }
368
369  protected:
370   scoped_ptr<content::WindowedNotificationObserver> login_screen_load_observer_;
371
372  private:
373   FakeGaia fake_gaia_;
374   FakeSamlIdp fake_saml_idp_;
375   scoped_ptr<HTTPSForwarder> gaia_https_forwarder_;
376   scoped_ptr<HTTPSForwarder> saml_https_forwarder_;
377
378   bool saml_load_injected_;
379
380   DISALLOW_COPY_AND_ASSIGN(SamlTest);
381 };
382
383 // Tests that signin frame should have 'saml' class and 'cancel' button is
384 // visible when SAML IdP page is loaded. And 'cancel' button goes back to
385 // gaia on clicking.
386 IN_PROC_BROWSER_TEST_F(SamlTest, SamlUI) {
387   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
388   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
389
390   // Saml flow UI expectations.
391   JsExpect("$('gaia-signin').classList.contains('saml')");
392   JsExpect("!$('cancel-add-user-button').hidden");
393
394   // Click on 'cancel'.
395   content::DOMMessageQueue message_queue;  // Observe before 'cancel'.
396   ASSERT_TRUE(content::ExecuteScript(
397       GetLoginUI()->GetWebContents(),
398       "$('cancel-add-user-button').click();"));
399
400   // Auth flow should change back to Gaia.
401   std::string message;
402   do {
403     ASSERT_TRUE(message_queue.WaitForMessage(&message));
404   } while (message != "\"GaiaLoaded\"");
405
406   // Saml flow is gone.
407   JsExpect("!$('gaia-signin').classList.contains('saml')");
408 }
409
410 // Tests the sign-in flow when the credentials passing API is used.
411 IN_PROC_BROWSER_TEST_F(SamlTest, CredentialPassingAPI) {
412   fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
413   fake_saml_idp()->SetLoginAuthHTMLTemplate("saml_api_login_auth.html");
414   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
415
416   // Fill-in the SAML IdP form and submit.
417   SetSignFormField("Email", "fake_user");
418   SetSignFormField("Password", "fake_password");
419   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
420
421   // Login should finish login and a session should start.
422   content::WindowedNotificationObserver(
423       chrome::NOTIFICATION_SESSION_STARTED,
424       content::NotificationService::AllSources()).Wait();
425 }
426
427 // Tests the single password scraped flow.
428 IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedSingle) {
429   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
430   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
431
432   // Fill-in the SAML IdP form and submit.
433   SetSignFormField("Email", "fake_user");
434   SetSignFormField("Password", "fake_password");
435   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
436
437   // Lands on confirm password screen.
438   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
439
440   // Enter an unknown password should go back to confirm password screen.
441   SendConfirmPassword("wrong_password");
442   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
443
444   // Enter a known password should finish login and start session.
445   SendConfirmPassword("fake_password");
446   content::WindowedNotificationObserver(
447       chrome::NOTIFICATION_SESSION_STARTED,
448       content::NotificationService::AllSources()).Wait();
449 }
450
451 // Tests the multiple password scraped flow.
452 IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedMultiple) {
453   fake_saml_idp()->SetLoginHTMLTemplate("saml_login_two_passwords.html");
454
455   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
456
457   SetSignFormField("Email", "fake_user");
458   SetSignFormField("Password", "fake_password");
459   SetSignFormField("Password1", "password1");
460   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
461
462   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
463
464   // Either scraped password should be able to sign-in.
465   SendConfirmPassword("password1");
466   content::WindowedNotificationObserver(
467       chrome::NOTIFICATION_SESSION_STARTED,
468       content::NotificationService::AllSources()).Wait();
469 }
470
471 // Tests the no password scraped flow.
472 IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedNone) {
473   fake_saml_idp()->SetLoginHTMLTemplate("saml_login_no_passwords.html");
474
475   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
476
477   SetSignFormField("Email", "fake_user");
478   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
479
480   OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
481 }
482
483 // Types |bob@example.com| into the GAIA login form but then authenticates as
484 // |alice@example.com| via SAML. Verifies that the logged-in user is correctly
485 // identified as Alice.
486 IN_PROC_BROWSER_TEST_F(SamlTest, UseAutenticatedUserEmailAddress) {
487   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
488   // Type |bob@example.com| into the GAIA login form.
489   StartSamlAndWaitForIdpPageLoad(kSecondSAMLUserEmail);
490
491   // Authenticate as alice@example.com via SAML (the |Email| provided here is
492   // irrelevant - the authenticated user's e-mail address that FakeGAIA
493   // reports was set via SetMergeSessionParams()).
494   SetSignFormField("Email", "fake_user");
495   SetSignFormField("Password", "fake_password");
496   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
497
498   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
499
500   SendConfirmPassword("fake_password");
501   content::WindowedNotificationObserver(
502       chrome::NOTIFICATION_SESSION_STARTED,
503       content::NotificationService::AllSources()).Wait();
504   const User* user = UserManager::Get()->GetActiveUser();
505   ASSERT_TRUE(user);
506   EXPECT_EQ(kFirstSAMLUserEmail, user->email());
507 }
508
509 // Tests the password confirm flow: show error on the first failure and
510 // fatal error on the second failure.
511 IN_PROC_BROWSER_TEST_F(SamlTest, PasswordConfirmFlow) {
512   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
513   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
514
515   // Fill-in the SAML IdP form and submit.
516   SetSignFormField("Email", "fake_user");
517   SetSignFormField("Password", "fake_password");
518   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
519
520   // Lands on confirm password screen with no error message.
521   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
522   JsExpect("!$('confirm-password').classList.contains('error')");
523
524   // Enter an unknown password for the first time should go back to confirm
525   // password screen with error message.
526   SendConfirmPassword("wrong_password");
527   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
528   JsExpect("$('confirm-password').classList.contains('error')");
529
530   // Enter an unknown password 2nd time should go back to confirm password
531   // screen.
532   SendConfirmPassword("wrong_password");
533   OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
534 }
535
536 // Verifies that when GAIA attempts to redirect to a SAML IdP served over http,
537 // not https, the redirect is blocked by CSP and an error message is shown.
538 IN_PROC_BROWSER_TEST_F(SamlTest, HTTPRedirectDisallowed) {
539   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
540
541   WaitForSigninScreen();
542   GetLoginDisplay()->ShowSigninScreenForCreds(kHTTPSAMLUserEmail, "");
543
544   OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
545 }
546
547 class SAMLPolicyTest : public SamlTest {
548  public:
549   SAMLPolicyTest();
550   virtual ~SAMLPolicyTest();
551
552   // SamlTest:
553   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
554   virtual void SetUpOnMainThread() OVERRIDE;
555
556   void SetSAMLOfflineSigninTimeLimitPolicy(int limit);
557
558  protected:
559   policy::MockConfigurationPolicyProvider provider_;
560
561  private:
562   DISALLOW_COPY_AND_ASSIGN(SAMLPolicyTest);
563 };
564
565 SAMLPolicyTest::SAMLPolicyTest() {
566 }
567
568 SAMLPolicyTest::~SAMLPolicyTest() {
569 }
570
571 void SAMLPolicyTest::SetUpInProcessBrowserTestFixture() {
572   SamlTest::SetUpInProcessBrowserTestFixture();
573
574   EXPECT_CALL(provider_, IsInitializationComplete(_))
575       .WillRepeatedly(Return(true));
576   policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
577 }
578
579 void SAMLPolicyTest::SetUpOnMainThread() {
580   SamlTest::SetUpOnMainThread();
581
582   // Pretend that the test users' OAuth tokens are valid.
583   UserManager::Get()->SaveUserOAuthStatus(kFirstSAMLUserEmail,
584                                           User::OAUTH2_TOKEN_STATUS_VALID);
585   UserManager::Get()->SaveUserOAuthStatus(kNonSAMLUserEmail,
586                                           User::OAUTH2_TOKEN_STATUS_VALID);
587 }
588
589 void SAMLPolicyTest::SetSAMLOfflineSigninTimeLimitPolicy(int limit) {
590   policy::PolicyMap policy;
591   policy.Set(policy::key::kSAMLOfflineSigninTimeLimit,
592              policy::POLICY_LEVEL_MANDATORY,
593              policy::POLICY_SCOPE_USER,
594              new base::FundamentalValue(limit),
595              NULL);
596   provider_.UpdateChromePolicy(policy);
597   base::RunLoop().RunUntilIdle();
598 }
599
600 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_NoSAML) {
601   // Set the offline login time limit for SAML users to zero.
602   SetSAMLOfflineSigninTimeLimitPolicy(0);
603
604   WaitForSigninScreen();
605
606   // Log in without SAML.
607   GetLoginDisplay()->ShowSigninScreenForCreds(kNonSAMLUserEmail, "password");
608
609   content::WindowedNotificationObserver(
610     chrome::NOTIFICATION_SESSION_STARTED,
611     content::NotificationService::AllSources()).Wait();
612 }
613
614 // Verifies that the offline login time limit does not affect a user who
615 // authenticated without SAML.
616 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, NoSAML) {
617   login_screen_load_observer_->Wait();
618   // Verify that offline login is allowed.
619   JsExpect("document.querySelector('#pod-row .signin-button').hidden");
620 }
621
622 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLNoLimit) {
623   // Remove the offline login time limit for SAML users.
624   SetSAMLOfflineSigninTimeLimitPolicy(-1);
625
626   // Log in with SAML.
627   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
628   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
629
630   SetSignFormField("Email", "fake_user");
631   SetSignFormField("Password", "fake_password");
632   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
633
634   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
635
636   SendConfirmPassword("fake_password");
637   content::WindowedNotificationObserver(
638       chrome::NOTIFICATION_SESSION_STARTED,
639       content::NotificationService::AllSources()).Wait();
640 }
641
642 // Verifies that when no offline login time limit is set, a user who
643 // authenticated with SAML is allowed to log in offline.
644 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLNoLimit) {
645   login_screen_load_observer_->Wait();
646   // Verify that offline login is allowed.
647   JsExpect("document.querySelector('#pod-row .signin-button').hidden");
648 }
649
650 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLZeroLimit) {
651   // Set the offline login time limit for SAML users to zero.
652   SetSAMLOfflineSigninTimeLimitPolicy(0);
653
654   // Log in with SAML.
655   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
656   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
657
658   SetSignFormField("Email", "fake_user");
659   SetSignFormField("Password", "fake_password");
660   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
661
662   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
663
664   SendConfirmPassword("fake_password");
665   content::WindowedNotificationObserver(
666       chrome::NOTIFICATION_SESSION_STARTED,
667       content::NotificationService::AllSources()).Wait();
668 }
669
670 // Verifies that when the offline login time limit is exceeded for a user who
671 // authenticated via SAML, that user is forced to log in online the next time.
672 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLZeroLimit) {
673   login_screen_load_observer_->Wait();
674   // Verify that offline login is not allowed.
675   JsExpect("!document.querySelector('#pod-row .signin-button').hidden");
676 }
677
678 }  // namespace chromeos