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.
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"
48 using net::test_server::BasicHttpResponse;
49 using net::test_server::HttpRequest;
50 using net::test_server::HttpResponse;
52 using testing::Return;
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";
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";
72 const char kRelayState[] = "RelayState";
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.
83 void SetUp(const std::string& base_path, const GURL& gaia_url);
85 void SetLoginHTMLTemplate(const std::string& template_file);
86 void SetLoginAuthHTMLTemplate(const std::string& template_file);
88 scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
91 scoped_ptr<HttpResponse> BuildHTMLResponse(const std::string& html_template,
92 const std::string& relay_state,
93 const std::string& next_path);
95 base::FilePath html_template_dir_;
97 std::string login_path_;
98 std::string login_auth_path_;
100 std::string login_html_template_;
101 std::string login_auth_html_template_;
102 GURL gaia_assertion_url_;
104 DISALLOW_COPY_AND_ASSIGN(FakeSamlIdp);
107 FakeSamlIdp::FakeSamlIdp() {
110 FakeSamlIdp::~FakeSamlIdp() {
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");
118 login_path_= base_path;
119 login_auth_path_ = base_path + "Auth";
120 gaia_assertion_url_ = gaia_url.Resolve("/SSO");
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_));
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_));
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();
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_,
150 if (request_path != login_auth_path_) {
151 // Request not understood.
152 return scoped_ptr<HttpResponse>();
155 std::string relay_state;
156 FakeGaia::GetQueryParameter(request.content, kRelayState, &relay_state);
157 GURL redirect_url = gaia_assertion_url_;
159 if (!login_auth_html_template_.empty()) {
160 return BuildHTMLResponse(login_auth_html_template_,
162 redirect_url.spec());
165 redirect_url = net::AppendQueryParameter(
166 redirect_url, "SAMLResponse", "fake_response");
167 redirect_url = net::AppendQueryParameter(
168 redirect_url, kRelayState, relay_state);
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>();
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);
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");
189 return http_response.PassAs<HttpResponse>();
194 class SamlTest : public InProcessBrowserTest {
196 SamlTest() : saml_load_injected_(false) {}
197 virtual ~SamlTest() {}
199 virtual void SetUp() OVERRIDE {
200 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
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());
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());
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();
218 InProcessBrowserTest::SetUp();
221 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
222 host_resolver()->AddRule("*", "127.0.0.1");
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");
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,
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(
243 embedded_test_server()->base_url().Resolve("/SAML"));
245 fake_gaia_.Initialize();
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);
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_)));
266 // Restart the thread as the sandbox host process has already been spawned.
267 embedded_test_server()->RestartThreadAndListen();
269 login_screen_load_observer_.reset(new content::WindowedNotificationObserver(
270 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
271 content::NotificationService::AllSources()));
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();
283 WebUILoginDisplay* GetLoginDisplay() {
284 ExistingUserController* controller =
285 ExistingUserController::current_controller();
287 return static_cast<WebUILoginDisplay*>(controller->login_display());
290 void WaitForSigninScreen() {
291 WizardController::SkipPostLoginScreensForTesting();
292 WizardController* wizard_controller =
293 chromeos::WizardController::default_controller();
294 CHECK(wizard_controller);
295 wizard_controller->SkipToLoginForTesting(LoginScreenContext());
297 login_screen_load_observer_->Wait();
300 void StartSamlAndWaitForIdpPageLoad(const std::string& gaia_email) {
301 WaitForSigninScreen();
303 if (!saml_load_injected_) {
304 saml_load_injected_ = true;
306 ASSERT_TRUE(content::ExecuteScript(
307 GetLoginUI()->GetWebContents(),
308 "$('gaia-signin').gaiaAuthHost_.addEventListener('authFlowChange',"
310 "window.domAutomationController.setAutomationId(0);"
311 "window.domAutomationController.send("
312 "$('gaia-signin').isSAML() ? 'SamlLoaded' : 'GaiaLoaded');"
316 content::DOMMessageQueue message_queue; // Start observe before SAML.
317 GetLoginDisplay()->ShowSigninScreenForCreds(gaia_email, "");
320 ASSERT_TRUE(message_queue.WaitForMessage(&message));
321 EXPECT_EQ("\"SamlLoaded\"", message);
324 void SetSignFormField(const std::string& field_id,
325 const std::string& field_value) {
328 "document.getElementById('$FieldId').value = '$FieldValue';"
329 "var e = new Event('input');"
330 "document.getElementById('$FieldId').dispatchEvent(e);"
332 ReplaceSubstringsAfterOffset(&js, 0, "$FieldId", field_id);
333 ReplaceSubstringsAfterOffset(&js, 0, "$FieldValue", field_value);
334 ExecuteJsInSigninFrame(js);
337 void SendConfirmPassword(const std::string& password_to_confirm) {
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));
345 void JsExpect(const std::string& js) {
347 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
348 GetLoginUI()->GetWebContents(),
349 "window.domAutomationController.send(!!(" + js + "));",
351 EXPECT_TRUE(result) << js;
354 content::WebUI* GetLoginUI() {
355 return static_cast<chromeos::LoginDisplayHostImpl*>(
356 chromeos::LoginDisplayHostImpl::default_host())->GetOobeUI()->web_ui();
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",
367 FakeSamlIdp* fake_saml_idp() { return &fake_saml_idp_; }
370 scoped_ptr<content::WindowedNotificationObserver> login_screen_load_observer_;
374 FakeSamlIdp fake_saml_idp_;
375 scoped_ptr<HTTPSForwarder> gaia_https_forwarder_;
376 scoped_ptr<HTTPSForwarder> saml_https_forwarder_;
378 bool saml_load_injected_;
380 DISALLOW_COPY_AND_ASSIGN(SamlTest);
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
386 IN_PROC_BROWSER_TEST_F(SamlTest, SamlUI) {
387 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
388 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
390 // Saml flow UI expectations.
391 JsExpect("$('gaia-signin').classList.contains('saml')");
392 JsExpect("!$('cancel-add-user-button').hidden");
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();"));
400 // Auth flow should change back to Gaia.
403 ASSERT_TRUE(message_queue.WaitForMessage(&message));
404 } while (message != "\"GaiaLoaded\"");
406 // Saml flow is gone.
407 JsExpect("!$('gaia-signin').classList.contains('saml')");
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);
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();");
421 // Login should finish login and a session should start.
422 content::WindowedNotificationObserver(
423 chrome::NOTIFICATION_SESSION_STARTED,
424 content::NotificationService::AllSources()).Wait();
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);
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();");
437 // Lands on confirm password screen.
438 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
440 // Enter an unknown password should go back to confirm password screen.
441 SendConfirmPassword("wrong_password");
442 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
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();
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");
455 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
457 SetSignFormField("Email", "fake_user");
458 SetSignFormField("Password", "fake_password");
459 SetSignFormField("Password1", "password1");
460 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
462 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
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();
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");
475 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
477 SetSignFormField("Email", "fake_user");
478 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
480 OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
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);
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();");
498 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
500 SendConfirmPassword("fake_password");
501 content::WindowedNotificationObserver(
502 chrome::NOTIFICATION_SESSION_STARTED,
503 content::NotificationService::AllSources()).Wait();
504 const User* user = UserManager::Get()->GetActiveUser();
506 EXPECT_EQ(kFirstSAMLUserEmail, user->email());
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);
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();");
520 // Lands on confirm password screen with no error message.
521 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
522 JsExpect("!$('confirm-password').classList.contains('error')");
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')");
530 // Enter an unknown password 2nd time should go back to confirm password
532 SendConfirmPassword("wrong_password");
533 OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
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");
541 WaitForSigninScreen();
542 GetLoginDisplay()->ShowSigninScreenForCreds(kHTTPSAMLUserEmail, "");
544 OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
547 class SAMLPolicyTest : public SamlTest {
550 virtual ~SAMLPolicyTest();
553 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
554 virtual void SetUpOnMainThread() OVERRIDE;
556 void SetSAMLOfflineSigninTimeLimitPolicy(int limit);
559 policy::MockConfigurationPolicyProvider provider_;
562 DISALLOW_COPY_AND_ASSIGN(SAMLPolicyTest);
565 SAMLPolicyTest::SAMLPolicyTest() {
568 SAMLPolicyTest::~SAMLPolicyTest() {
571 void SAMLPolicyTest::SetUpInProcessBrowserTestFixture() {
572 SamlTest::SetUpInProcessBrowserTestFixture();
574 EXPECT_CALL(provider_, IsInitializationComplete(_))
575 .WillRepeatedly(Return(true));
576 policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
579 void SAMLPolicyTest::SetUpOnMainThread() {
580 SamlTest::SetUpOnMainThread();
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);
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),
596 provider_.UpdateChromePolicy(policy);
597 base::RunLoop().RunUntilIdle();
600 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_NoSAML) {
601 // Set the offline login time limit for SAML users to zero.
602 SetSAMLOfflineSigninTimeLimitPolicy(0);
604 WaitForSigninScreen();
606 // Log in without SAML.
607 GetLoginDisplay()->ShowSigninScreenForCreds(kNonSAMLUserEmail, "password");
609 content::WindowedNotificationObserver(
610 chrome::NOTIFICATION_SESSION_STARTED,
611 content::NotificationService::AllSources()).Wait();
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");
622 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLNoLimit) {
623 // Remove the offline login time limit for SAML users.
624 SetSAMLOfflineSigninTimeLimitPolicy(-1);
627 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
628 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
630 SetSignFormField("Email", "fake_user");
631 SetSignFormField("Password", "fake_password");
632 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
634 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
636 SendConfirmPassword("fake_password");
637 content::WindowedNotificationObserver(
638 chrome::NOTIFICATION_SESSION_STARTED,
639 content::NotificationService::AllSources()).Wait();
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");
650 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLZeroLimit) {
651 // Set the offline login time limit for SAML users to zero.
652 SetSAMLOfflineSigninTimeLimitPolicy(0);
655 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
656 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
658 SetSignFormField("Email", "fake_user");
659 SetSignFormField("Password", "fake_password");
660 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
662 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
664 SendConfirmPassword("fake_password");
665 content::WindowedNotificationObserver(
666 chrome::NOTIFICATION_SESSION_STARTED,
667 content::NotificationService::AllSources()).Wait();
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");
678 } // namespace chromeos