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/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/chromeos/login/existing_user_controller.h"
17 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
18 #include "chrome/browser/chromeos/login/test/https_forwarder.h"
19 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
20 #include "chrome/browser/chromeos/login/user.h"
21 #include "chrome/browser/chromeos/login/user_manager.h"
22 #include "chrome/browser/chromeos/login/webui_login_display.h"
23 #include "chrome/browser/chromeos/login/wizard_controller.h"
24 #include "chrome/browser/lifetime/application_lifetime.h"
25 #include "chrome/common/chrome_paths.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/test/base/in_process_browser_test.h"
28 #include "chromeos/chromeos_switches.h"
29 #include "components/policy/core/browser/browser_policy_connector.h"
30 #include "components/policy/core/common/mock_configuration_policy_provider.h"
31 #include "components/policy/core/common/policy_map.h"
32 #include "components/policy/core/common/policy_types.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);
87 void SetRefreshURL(const GURL& refresh_url);
89 scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
92 scoped_ptr<HttpResponse> BuildHTMLResponse(const std::string& html_template,
93 const std::string& relay_state,
94 const std::string& next_path);
96 base::FilePath html_template_dir_;
98 std::string login_path_;
99 std::string login_auth_path_;
101 std::string login_html_template_;
102 std::string login_auth_html_template_;
103 GURL gaia_assertion_url_;
106 DISALLOW_COPY_AND_ASSIGN(FakeSamlIdp);
109 FakeSamlIdp::FakeSamlIdp() {
112 FakeSamlIdp::~FakeSamlIdp() {
115 void FakeSamlIdp::SetUp(const std::string& base_path, const GURL& gaia_url) {
116 base::FilePath test_data_dir;
117 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
118 html_template_dir_ = test_data_dir.Append("login");
120 login_path_= base_path;
121 login_auth_path_ = base_path + "Auth";
122 gaia_assertion_url_ = gaia_url.Resolve("/SSO");
125 void FakeSamlIdp::SetLoginHTMLTemplate(const std::string& template_file) {
126 EXPECT_TRUE(base::ReadFileToString(
127 html_template_dir_.Append(template_file),
128 &login_html_template_));
131 void FakeSamlIdp::SetLoginAuthHTMLTemplate(const std::string& template_file) {
132 EXPECT_TRUE(base::ReadFileToString(
133 html_template_dir_.Append(template_file),
134 &login_auth_html_template_));
137 void FakeSamlIdp::SetRefreshURL(const GURL& refresh_url) {
138 refresh_url_ = refresh_url;
141 scoped_ptr<HttpResponse> FakeSamlIdp::HandleRequest(
142 const HttpRequest& request) {
143 // The scheme and host of the URL is actually not important but required to
144 // get a valid GURL in order to parse |request.relative_url|.
145 GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
146 std::string request_path = request_url.path();
148 if (request_path == login_path_) {
149 std::string relay_state;
150 net::GetValueForKeyInQuery(request_url, kRelayState, &relay_state);
151 return BuildHTMLResponse(login_html_template_,
156 if (request_path != login_auth_path_) {
157 // Request not understood.
158 return scoped_ptr<HttpResponse>();
161 std::string relay_state;
162 FakeGaia::GetQueryParameter(request.content, kRelayState, &relay_state);
163 GURL redirect_url = gaia_assertion_url_;
165 if (!login_auth_html_template_.empty()) {
166 return BuildHTMLResponse(login_auth_html_template_,
168 redirect_url.spec());
171 redirect_url = net::AppendQueryParameter(
172 redirect_url, "SAMLResponse", "fake_response");
173 redirect_url = net::AppendQueryParameter(
174 redirect_url, kRelayState, relay_state);
176 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
177 http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
178 http_response->AddCustomHeader("Location", redirect_url.spec());
179 return http_response.PassAs<HttpResponse>();
182 scoped_ptr<HttpResponse> FakeSamlIdp::BuildHTMLResponse(
183 const std::string& html_template,
184 const std::string& relay_state,
185 const std::string& next_path) {
186 std::string response_html = html_template;
187 ReplaceSubstringsAfterOffset(&response_html, 0, "$RelayState", relay_state);
188 ReplaceSubstringsAfterOffset(&response_html, 0, "$Post", next_path);
189 ReplaceSubstringsAfterOffset(
190 &response_html, 0, "$Refresh", refresh_url_.spec());
192 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
193 http_response->set_code(net::HTTP_OK);
194 http_response->set_content(response_html);
195 http_response->set_content_type("text/html");
197 return http_response.PassAs<HttpResponse>();
202 class SamlTest : public InProcessBrowserTest {
204 SamlTest() : saml_load_injected_(false) {}
205 virtual ~SamlTest() {}
207 virtual void SetUp() OVERRIDE {
208 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
210 // Start the GAIA https wrapper here so that the GAIA URLs can be pointed at
211 // it in SetUpCommandLine().
212 gaia_https_forwarder_.reset(
213 new HTTPSForwarder(embedded_test_server()->base_url()));
214 ASSERT_TRUE(gaia_https_forwarder_->Start());
216 // Start the SAML IdP https wrapper here so that GAIA can be pointed at it
217 // in SetUpCommandLine().
218 saml_https_forwarder_.reset(
219 new HTTPSForwarder(embedded_test_server()->base_url()));
220 ASSERT_TRUE(saml_https_forwarder_->Start());
222 // Stop IO thread here because no threads are allowed while
223 // spawning sandbox host process. See crbug.com/322732.
224 embedded_test_server()->StopThread();
226 InProcessBrowserTest::SetUp();
229 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
230 host_resolver()->AddRule("*", "127.0.0.1");
233 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
234 command_line->AppendSwitch(switches::kLoginManager);
235 command_line->AppendSwitch(switches::kForceLoginManagerInTests);
236 command_line->AppendSwitch(::switches::kDisableBackgroundNetworking);
237 command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
239 const GURL gaia_url = gaia_https_forwarder_->GetURL("");
240 command_line->AppendSwitchASCII(::switches::kGaiaUrl, gaia_url.spec());
241 command_line->AppendSwitchASCII(::switches::kLsoUrl, gaia_url.spec());
242 command_line->AppendSwitchASCII(::switches::kGoogleApisUrl,
245 const GURL saml_idp_url = saml_https_forwarder_->GetURL("SAML");
246 fake_saml_idp_.SetUp(saml_idp_url.path(), gaia_url);
247 fake_gaia_.RegisterSamlUser(kFirstSAMLUserEmail, saml_idp_url);
248 fake_gaia_.RegisterSamlUser(kSecondSAMLUserEmail, saml_idp_url);
249 fake_gaia_.RegisterSamlUser(
251 embedded_test_server()->base_url().Resolve("/SAML"));
253 fake_gaia_.Initialize();
256 virtual void SetUpOnMainThread() OVERRIDE {
257 SetMergeSessionParams(kFirstSAMLUserEmail);
259 embedded_test_server()->RegisterRequestHandler(
260 base::Bind(&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_)));
261 embedded_test_server()->RegisterRequestHandler(base::Bind(
262 &FakeSamlIdp::HandleRequest, base::Unretained(&fake_saml_idp_)));
264 // Restart the thread as the sandbox host process has already been spawned.
265 embedded_test_server()->RestartThreadAndListen();
267 login_screen_load_observer_.reset(new content::WindowedNotificationObserver(
268 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
269 content::NotificationService::AllSources()));
272 virtual void CleanUpOnMainThread() OVERRIDE {
273 // If the login display is still showing, exit gracefully.
274 if (LoginDisplayHostImpl::default_host()) {
275 base::MessageLoop::current()->PostTask(FROM_HERE,
276 base::Bind(&chrome::AttemptExit));
277 content::RunMessageLoop();
281 void SetMergeSessionParams(const std::string& email) {
282 FakeGaia::MergeSessionParams params;
283 params.auth_sid_cookie = kTestAuthSIDCookie;
284 params.auth_lsid_cookie = kTestAuthLSIDCookie;
285 params.auth_code = kTestAuthCode;
286 params.refresh_token = kTestRefreshToken;
287 params.access_token = kTestAuthLoginAccessToken;
288 params.gaia_uber_token = kTestGaiaUberToken;
289 params.session_sid_cookie = kTestSessionSIDCookie;
290 params.session_lsid_cookie = kTestSessionLSIDCookie;
291 params.email = email;
292 fake_gaia_.SetMergeSessionParams(params);
295 WebUILoginDisplay* GetLoginDisplay() {
296 ExistingUserController* controller =
297 ExistingUserController::current_controller();
299 return static_cast<WebUILoginDisplay*>(controller->login_display());
302 void WaitForSigninScreen() {
303 WizardController::SkipPostLoginScreensForTesting();
304 WizardController* wizard_controller =
305 chromeos::WizardController::default_controller();
306 CHECK(wizard_controller);
307 wizard_controller->SkipToLoginForTesting(LoginScreenContext());
309 login_screen_load_observer_->Wait();
312 void StartSamlAndWaitForIdpPageLoad(const std::string& gaia_email) {
313 WaitForSigninScreen();
315 if (!saml_load_injected_) {
316 saml_load_injected_ = true;
318 ASSERT_TRUE(content::ExecuteScript(
319 GetLoginUI()->GetWebContents(),
320 "$('gaia-signin').gaiaAuthHost_.addEventListener('authFlowChange',"
322 "window.domAutomationController.setAutomationId(0);"
323 "window.domAutomationController.send("
324 "$('gaia-signin').isSAML() ? 'SamlLoaded' : 'GaiaLoaded');"
328 content::DOMMessageQueue message_queue; // Start observe before SAML.
329 GetLoginDisplay()->ShowSigninScreenForCreds(gaia_email, "");
332 ASSERT_TRUE(message_queue.WaitForMessage(&message));
333 EXPECT_EQ("\"SamlLoaded\"", message);
336 void SetSignFormField(const std::string& field_id,
337 const std::string& field_value) {
340 "document.getElementById('$FieldId').value = '$FieldValue';"
341 "var e = new Event('input');"
342 "document.getElementById('$FieldId').dispatchEvent(e);"
344 ReplaceSubstringsAfterOffset(&js, 0, "$FieldId", field_id);
345 ReplaceSubstringsAfterOffset(&js, 0, "$FieldValue", field_value);
346 ExecuteJsInSigninFrame(js);
349 void SendConfirmPassword(const std::string& password_to_confirm) {
351 "$('confirm-password-input').value='$Password';"
352 "$('confirm-password').onConfirmPassword_();";
353 ReplaceSubstringsAfterOffset(&js, 0, "$Password", password_to_confirm);
354 ASSERT_TRUE(content::ExecuteScript(GetLoginUI()->GetWebContents(), js));
357 void JsExpect(const std::string& js) {
359 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
360 GetLoginUI()->GetWebContents(),
361 "window.domAutomationController.send(!!(" + js + "));",
363 EXPECT_TRUE(result) << js;
366 content::WebUI* GetLoginUI() {
367 return static_cast<LoginDisplayHostImpl*>(
368 LoginDisplayHostImpl::default_host())->GetOobeUI()->web_ui();
371 // Executes JavaScript code in the auth iframe hosted by gaia_auth extension.
372 void ExecuteJsInSigninFrame(const std::string& js) {
373 content::RenderFrameHost* frame =
374 LoginDisplayHostImpl::GetGaiaAuthIframe(GetLoginUI()->GetWebContents());
375 ASSERT_TRUE(content::ExecuteScript(frame, js));
378 FakeSamlIdp* fake_saml_idp() { return &fake_saml_idp_; }
381 scoped_ptr<content::WindowedNotificationObserver> login_screen_load_observer_;
385 FakeSamlIdp fake_saml_idp_;
386 scoped_ptr<HTTPSForwarder> gaia_https_forwarder_;
387 scoped_ptr<HTTPSForwarder> saml_https_forwarder_;
389 bool saml_load_injected_;
391 DISALLOW_COPY_AND_ASSIGN(SamlTest);
394 // Tests that signin frame should have 'saml' class and 'cancel' button is
395 // visible when SAML IdP page is loaded. And 'cancel' button goes back to
397 IN_PROC_BROWSER_TEST_F(SamlTest, SamlUI) {
398 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
399 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
401 // Saml flow UI expectations.
402 JsExpect("$('gaia-signin').classList.contains('saml')");
403 JsExpect("!$('cancel-add-user-button').hidden");
405 // Click on 'cancel'.
406 content::DOMMessageQueue message_queue; // Observe before 'cancel'.
407 ASSERT_TRUE(content::ExecuteScript(
408 GetLoginUI()->GetWebContents(),
409 "$('cancel-add-user-button').click();"));
411 // Auth flow should change back to Gaia.
414 ASSERT_TRUE(message_queue.WaitForMessage(&message));
415 } while (message != "\"GaiaLoaded\"");
417 // Saml flow is gone.
418 JsExpect("!$('gaia-signin').classList.contains('saml')");
421 // Tests the sign-in flow when the credentials passing API is used.
422 IN_PROC_BROWSER_TEST_F(SamlTest, CredentialPassingAPI) {
423 fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
424 fake_saml_idp()->SetLoginAuthHTMLTemplate("saml_api_login_auth.html");
425 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
427 // Fill-in the SAML IdP form and submit.
428 SetSignFormField("Email", "fake_user");
429 SetSignFormField("Password", "fake_password");
430 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
432 // Login should finish login and a session should start.
433 content::WindowedNotificationObserver(
434 chrome::NOTIFICATION_SESSION_STARTED,
435 content::NotificationService::AllSources()).Wait();
438 // Tests the single password scraped flow.
439 IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedSingle) {
440 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
441 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
443 // Fill-in the SAML IdP form and submit.
444 SetSignFormField("Email", "fake_user");
445 SetSignFormField("Password", "fake_password");
446 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
448 // Lands on confirm password screen.
449 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
451 // Enter an unknown password should go back to confirm password screen.
452 SendConfirmPassword("wrong_password");
453 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
455 // Enter a known password should finish login and start session.
456 SendConfirmPassword("fake_password");
457 content::WindowedNotificationObserver(
458 chrome::NOTIFICATION_SESSION_STARTED,
459 content::NotificationService::AllSources()).Wait();
462 // Tests the multiple password scraped flow.
463 IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedMultiple) {
464 fake_saml_idp()->SetLoginHTMLTemplate("saml_login_two_passwords.html");
466 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
468 SetSignFormField("Email", "fake_user");
469 SetSignFormField("Password", "fake_password");
470 SetSignFormField("Password1", "password1");
471 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
473 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
475 // Either scraped password should be able to sign-in.
476 SendConfirmPassword("password1");
477 content::WindowedNotificationObserver(
478 chrome::NOTIFICATION_SESSION_STARTED,
479 content::NotificationService::AllSources()).Wait();
482 // Tests the no password scraped flow.
483 IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedNone) {
484 fake_saml_idp()->SetLoginHTMLTemplate("saml_login_no_passwords.html");
486 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
488 SetSignFormField("Email", "fake_user");
489 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
491 OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
494 // Types |bob@example.com| into the GAIA login form but then authenticates as
495 // |alice@example.com| via SAML. Verifies that the logged-in user is correctly
496 // identified as Alice.
497 IN_PROC_BROWSER_TEST_F(SamlTest, UseAutenticatedUserEmailAddress) {
498 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
499 // Type |bob@example.com| into the GAIA login form.
500 StartSamlAndWaitForIdpPageLoad(kSecondSAMLUserEmail);
502 // Authenticate as alice@example.com via SAML (the |Email| provided here is
503 // irrelevant - the authenticated user's e-mail address that FakeGAIA
504 // reports was set via SetMergeSessionParams()).
505 SetSignFormField("Email", "fake_user");
506 SetSignFormField("Password", "fake_password");
507 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
509 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
511 SendConfirmPassword("fake_password");
512 content::WindowedNotificationObserver(
513 chrome::NOTIFICATION_SESSION_STARTED,
514 content::NotificationService::AllSources()).Wait();
515 const User* user = UserManager::Get()->GetActiveUser();
517 EXPECT_EQ(kFirstSAMLUserEmail, user->email());
520 // Verifies that if the authenticated user's e-mail address cannot be retrieved,
521 // an error message is shown.
522 IN_PROC_BROWSER_TEST_F(SamlTest, FailToRetrieveAutenticatedUserEmailAddress) {
523 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
524 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
526 SetMergeSessionParams("");
527 SetSignFormField("Email", "fake_user");
528 SetSignFormField("Password", "fake_password");
529 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
531 OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
534 // Tests the password confirm flow: show error on the first failure and
535 // fatal error on the second failure.
536 IN_PROC_BROWSER_TEST_F(SamlTest, PasswordConfirmFlow) {
537 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
538 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
540 // Fill-in the SAML IdP form and submit.
541 SetSignFormField("Email", "fake_user");
542 SetSignFormField("Password", "fake_password");
543 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
545 // Lands on confirm password screen with no error message.
546 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
547 JsExpect("!$('confirm-password').classList.contains('error')");
549 // Enter an unknown password for the first time should go back to confirm
550 // password screen with error message.
551 SendConfirmPassword("wrong_password");
552 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
553 JsExpect("$('confirm-password').classList.contains('error')");
555 // Enter an unknown password 2nd time should go back to confirm password
557 SendConfirmPassword("wrong_password");
558 OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
561 // Verifies that when GAIA attempts to redirect to a SAML IdP served over http,
562 // not https, the redirect is blocked and an error message is shown.
563 IN_PROC_BROWSER_TEST_F(SamlTest, HTTPRedirectDisallowed) {
564 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
566 WaitForSigninScreen();
567 GetLoginDisplay()->ShowSigninScreenForCreds(kHTTPSAMLUserEmail, "");
569 OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
570 JsExpect(base::StringPrintf(
571 "$('fatal-error-message').textContent.indexOf('%s') != -1",
572 embedded_test_server()->base_url().Resolve("/SAML").spec().c_str()));
575 // Verifies that when GAIA attempts to redirect to a page served over http, not
576 // https, via an HTML meta refresh, the redirect is blocked and an error message
577 // is shown. This guards against regressions of http://crbug.com/359515.
578 IN_PROC_BROWSER_TEST_F(SamlTest, MetaRefreshToHTTPDisallowed) {
579 fake_saml_idp()->SetLoginHTMLTemplate("saml_login_instant_meta_refresh.html");
580 fake_saml_idp()->SetRefreshURL(
581 embedded_test_server()->base_url().Resolve("/SSO"));
583 WaitForSigninScreen();
584 GetLoginDisplay()->ShowSigninScreenForCreds(kFirstSAMLUserEmail, "");
586 OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
589 class SAMLPolicyTest : public SamlTest {
592 virtual ~SAMLPolicyTest();
595 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
596 virtual void SetUpOnMainThread() OVERRIDE;
598 void SetSAMLOfflineSigninTimeLimitPolicy(int limit);
601 policy::MockConfigurationPolicyProvider provider_;
604 DISALLOW_COPY_AND_ASSIGN(SAMLPolicyTest);
607 SAMLPolicyTest::SAMLPolicyTest() {
610 SAMLPolicyTest::~SAMLPolicyTest() {
613 void SAMLPolicyTest::SetUpInProcessBrowserTestFixture() {
614 SamlTest::SetUpInProcessBrowserTestFixture();
616 EXPECT_CALL(provider_, IsInitializationComplete(_))
617 .WillRepeatedly(Return(true));
618 policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
621 void SAMLPolicyTest::SetUpOnMainThread() {
622 SamlTest::SetUpOnMainThread();
624 // Pretend that the test users' OAuth tokens are valid.
625 UserManager::Get()->SaveUserOAuthStatus(kFirstSAMLUserEmail,
626 User::OAUTH2_TOKEN_STATUS_VALID);
627 UserManager::Get()->SaveUserOAuthStatus(kNonSAMLUserEmail,
628 User::OAUTH2_TOKEN_STATUS_VALID);
631 void SAMLPolicyTest::SetSAMLOfflineSigninTimeLimitPolicy(int limit) {
632 policy::PolicyMap policy;
633 policy.Set(policy::key::kSAMLOfflineSigninTimeLimit,
634 policy::POLICY_LEVEL_MANDATORY,
635 policy::POLICY_SCOPE_USER,
636 new base::FundamentalValue(limit),
638 provider_.UpdateChromePolicy(policy);
639 base::RunLoop().RunUntilIdle();
642 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_NoSAML) {
643 // Set the offline login time limit for SAML users to zero.
644 SetSAMLOfflineSigninTimeLimitPolicy(0);
646 WaitForSigninScreen();
648 // Log in without SAML.
649 GetLoginDisplay()->ShowSigninScreenForCreds(kNonSAMLUserEmail, "password");
651 content::WindowedNotificationObserver(
652 chrome::NOTIFICATION_SESSION_STARTED,
653 content::NotificationService::AllSources()).Wait();
656 // Verifies that the offline login time limit does not affect a user who
657 // authenticated without SAML.
658 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, NoSAML) {
659 login_screen_load_observer_->Wait();
660 // Verify that offline login is allowed.
661 JsExpect("document.querySelector('#pod-row .signin-button').hidden");
664 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLNoLimit) {
665 // Remove the offline login time limit for SAML users.
666 SetSAMLOfflineSigninTimeLimitPolicy(-1);
669 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
670 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
672 SetSignFormField("Email", "fake_user");
673 SetSignFormField("Password", "fake_password");
674 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
676 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
678 SendConfirmPassword("fake_password");
679 content::WindowedNotificationObserver(
680 chrome::NOTIFICATION_SESSION_STARTED,
681 content::NotificationService::AllSources()).Wait();
684 // Verifies that when no offline login time limit is set, a user who
685 // authenticated with SAML is allowed to log in offline.
686 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLNoLimit) {
687 login_screen_load_observer_->Wait();
688 // Verify that offline login is allowed.
689 JsExpect("document.querySelector('#pod-row .signin-button').hidden");
692 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLZeroLimit) {
693 // Set the offline login time limit for SAML users to zero.
694 SetSAMLOfflineSigninTimeLimitPolicy(0);
697 fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
698 StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
700 SetSignFormField("Email", "fake_user");
701 SetSignFormField("Password", "fake_password");
702 ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
704 OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
706 SendConfirmPassword("fake_password");
707 content::WindowedNotificationObserver(
708 chrome::NOTIFICATION_SESSION_STARTED,
709 content::NotificationService::AllSources()).Wait();
712 // Verifies that when the offline login time limit is exceeded for a user who
713 // authenticated via SAML, that user is forced to log in online the next time.
714 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLZeroLimit) {
715 login_screen_load_observer_->Wait();
716 // Verify that offline login is not allowed.
717 JsExpect("!document.querySelector('#pod-row .signin-button').hidden");
720 } // namespace chromeos