Home | History | Annotate | Download | only in saml
      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/bind.h"
      6 #include "base/bind_helpers.h"
      7 #include "base/callback.h"
      8 #include "base/command_line.h"
      9 #include "base/files/file_path.h"
     10 #include "base/files/file_util.h"
     11 #include "base/location.h"
     12 #include "base/memory/ref_counted.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/path_service.h"
     15 #include "base/run_loop.h"
     16 #include "base/strings/string16.h"
     17 #include "base/strings/string_util.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "base/values.h"
     21 #include "chrome/browser/chrome_notification_types.h"
     22 #include "chrome/browser/chromeos/login/existing_user_controller.h"
     23 #include "chrome/browser/chromeos/login/test/https_forwarder.h"
     24 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
     25 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
     26 #include "chrome/browser/chromeos/login/ui/webui_login_display.h"
     27 #include "chrome/browser/chromeos/login/wizard_controller.h"
     28 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
     29 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
     30 #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
     31 #include "chrome/browser/chromeos/profiles/profile_helper.h"
     32 #include "chrome/browser/chromeos/settings/cros_settings.h"
     33 #include "chrome/browser/lifetime/application_lifetime.h"
     34 #include "chrome/browser/profiles/profile.h"
     35 #include "chrome/browser/ui/webui/signin/inline_login_ui.h"
     36 #include "chrome/common/chrome_paths.h"
     37 #include "chrome/common/chrome_switches.h"
     38 #include "chrome/grit/generated_resources.h"
     39 #include "chrome/test/base/in_process_browser_test.h"
     40 #include "chromeos/chromeos_switches.h"
     41 #include "chromeos/dbus/dbus_thread_manager.h"
     42 #include "chromeos/dbus/fake_session_manager_client.h"
     43 #include "chromeos/dbus/session_manager_client.h"
     44 #include "chromeos/settings/cros_settings_names.h"
     45 #include "components/policy/core/browser/browser_policy_connector.h"
     46 #include "components/policy/core/common/mock_configuration_policy_provider.h"
     47 #include "components/policy/core/common/policy_map.h"
     48 #include "components/policy/core/common/policy_types.h"
     49 #include "components/user_manager/user.h"
     50 #include "components/user_manager/user_manager.h"
     51 #include "content/public/browser/browser_thread.h"
     52 #include "content/public/browser/web_contents.h"
     53 #include "content/public/test/browser_test_utils.h"
     54 #include "content/public/test/test_utils.h"
     55 #include "google_apis/gaia/fake_gaia.h"
     56 #include "google_apis/gaia/gaia_switches.h"
     57 #include "net/base/url_util.h"
     58 #include "net/cookies/canonical_cookie.h"
     59 #include "net/cookies/cookie_monster.h"
     60 #include "net/cookies/cookie_store.h"
     61 #include "net/dns/mock_host_resolver.h"
     62 #include "net/test/embedded_test_server/embedded_test_server.h"
     63 #include "net/test/embedded_test_server/http_request.h"
     64 #include "net/test/embedded_test_server/http_response.h"
     65 #include "net/url_request/url_request_context.h"
     66 #include "net/url_request/url_request_context_getter.h"
     67 #include "policy/policy_constants.h"
     68 #include "policy/proto/device_management_backend.pb.h"
     69 #include "testing/gmock/include/gmock/gmock.h"
     70 #include "testing/gtest/include/gtest/gtest.h"
     71 #include "ui/base/l10n/l10n_util.h"
     72 #include "url/gurl.h"
     73 
     74 namespace em = enterprise_management;
     75 
     76 using net::test_server::BasicHttpResponse;
     77 using net::test_server::HttpRequest;
     78 using net::test_server::HttpResponse;
     79 using testing::_;
     80 using testing::Return;
     81 
     82 namespace chromeos {
     83 
     84 namespace {
     85 
     86 const char kGAIASIDCookieName[] = "SID";
     87 const char kGAIALSIDCookieName[] = "LSID";
     88 
     89 const char kTestAuthSIDCookie1[] = "fake-auth-SID-cookie-1";
     90 const char kTestAuthSIDCookie2[] = "fake-auth-SID-cookie-2";
     91 const char kTestAuthLSIDCookie1[] = "fake-auth-LSID-cookie-1";
     92 const char kTestAuthLSIDCookie2[] = "fake-auth-LSID-cookie-2";
     93 
     94 const char kFirstSAMLUserEmail[] = "bob (at) example.com";
     95 const char kSecondSAMLUserEmail[] = "alice (at) example.com";
     96 const char kHTTPSAMLUserEmail[] = "carol (at) example.com";
     97 const char kNonSAMLUserEmail[] = "dan (at) example.com";
     98 const char kDifferentDomainSAMLUserEmail[] = "eve (at) example.test";
     99 
    100 const char kSAMLIdPCookieName[] = "saml";
    101 const char kSAMLIdPCookieValue1[] = "value-1";
    102 const char kSAMLIdPCookieValue2[] = "value-2";
    103 
    104 const char kRelayState[] = "RelayState";
    105 
    106 // FakeSamlIdp serves IdP auth form and the form submission. The form is
    107 // served with the template's RelayState placeholder expanded to the real
    108 // RelayState parameter from request. The form submission redirects back to
    109 // FakeGaia with the same RelayState.
    110 class FakeSamlIdp {
    111  public:
    112   FakeSamlIdp();
    113   ~FakeSamlIdp();
    114 
    115   void SetUp(const std::string& base_path, const GURL& gaia_url);
    116 
    117   void SetLoginHTMLTemplate(const std::string& template_file);
    118   void SetLoginAuthHTMLTemplate(const std::string& template_file);
    119   void SetRefreshURL(const GURL& refresh_url);
    120   void SetCookieValue(const std::string& cookie_value);
    121 
    122   scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
    123 
    124  private:
    125   scoped_ptr<HttpResponse> BuildHTMLResponse(const std::string& html_template,
    126                                              const std::string& relay_state,
    127                                              const std::string& next_path);
    128 
    129   base::FilePath html_template_dir_;
    130 
    131   std::string login_path_;
    132   std::string login_auth_path_;
    133 
    134   std::string login_html_template_;
    135   std::string login_auth_html_template_;
    136   GURL gaia_assertion_url_;
    137   GURL refresh_url_;
    138   std::string cookie_value_;
    139 
    140   DISALLOW_COPY_AND_ASSIGN(FakeSamlIdp);
    141 };
    142 
    143 FakeSamlIdp::FakeSamlIdp() {
    144 }
    145 
    146 FakeSamlIdp::~FakeSamlIdp() {
    147 }
    148 
    149 void FakeSamlIdp::SetUp(const std::string& base_path, const GURL& gaia_url) {
    150   base::FilePath test_data_dir;
    151   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
    152   html_template_dir_ = test_data_dir.Append("login");
    153 
    154   login_path_= base_path;
    155   login_auth_path_ = base_path + "Auth";
    156   gaia_assertion_url_ = gaia_url.Resolve("/SSO");
    157 }
    158 
    159 void FakeSamlIdp::SetLoginHTMLTemplate(const std::string& template_file) {
    160   EXPECT_TRUE(base::ReadFileToString(
    161       html_template_dir_.Append(template_file),
    162       &login_html_template_));
    163 }
    164 
    165 void FakeSamlIdp::SetLoginAuthHTMLTemplate(const std::string& template_file) {
    166   EXPECT_TRUE(base::ReadFileToString(
    167       html_template_dir_.Append(template_file),
    168       &login_auth_html_template_));
    169 }
    170 
    171 void FakeSamlIdp::SetRefreshURL(const GURL& refresh_url) {
    172   refresh_url_ = refresh_url;
    173 }
    174 
    175 void FakeSamlIdp::SetCookieValue(const std::string& cookie_value) {
    176   cookie_value_ = cookie_value;
    177 }
    178 
    179 scoped_ptr<HttpResponse> FakeSamlIdp::HandleRequest(
    180     const HttpRequest& request) {
    181   // The scheme and host of the URL is actually not important but required to
    182   // get a valid GURL in order to parse |request.relative_url|.
    183   GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
    184   std::string request_path = request_url.path();
    185 
    186   if (request_path == login_path_) {
    187     std::string relay_state;
    188     net::GetValueForKeyInQuery(request_url, kRelayState, &relay_state);
    189     return BuildHTMLResponse(login_html_template_,
    190                              relay_state,
    191                              login_auth_path_);
    192   }
    193 
    194   if (request_path != login_auth_path_) {
    195     // Request not understood.
    196     return scoped_ptr<HttpResponse>();
    197   }
    198 
    199   std::string relay_state;
    200   FakeGaia::GetQueryParameter(request.content, kRelayState, &relay_state);
    201   GURL redirect_url = gaia_assertion_url_;
    202 
    203   if (!login_auth_html_template_.empty()) {
    204     return BuildHTMLResponse(login_auth_html_template_,
    205                              relay_state,
    206                              redirect_url.spec());
    207   }
    208 
    209   redirect_url = net::AppendQueryParameter(
    210       redirect_url, "SAMLResponse", "fake_response");
    211   redirect_url = net::AppendQueryParameter(
    212       redirect_url, kRelayState, relay_state);
    213 
    214   scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
    215   http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
    216   http_response->AddCustomHeader("Location", redirect_url.spec());
    217   http_response->AddCustomHeader(
    218       "Set-cookie",
    219       base::StringPrintf("saml=%s", cookie_value_.c_str()));
    220   return http_response.PassAs<HttpResponse>();
    221 }
    222 
    223 scoped_ptr<HttpResponse> FakeSamlIdp::BuildHTMLResponse(
    224     const std::string& html_template,
    225     const std::string& relay_state,
    226     const std::string& next_path) {
    227   std::string response_html = html_template;
    228   ReplaceSubstringsAfterOffset(&response_html, 0, "$RelayState", relay_state);
    229   ReplaceSubstringsAfterOffset(&response_html, 0, "$Post", next_path);
    230   ReplaceSubstringsAfterOffset(
    231       &response_html, 0, "$Refresh", refresh_url_.spec());
    232 
    233   scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
    234   http_response->set_code(net::HTTP_OK);
    235   http_response->set_content(response_html);
    236   http_response->set_content_type("text/html");
    237 
    238   return http_response.PassAs<HttpResponse>();
    239 }
    240 
    241 }  // namespace
    242 
    243 class SamlTest : public InProcessBrowserTest {
    244  public:
    245   SamlTest() : saml_load_injected_(false) {}
    246   virtual ~SamlTest() {}
    247 
    248   virtual void SetUp() OVERRIDE {
    249     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    250 
    251     // Start the GAIA https wrapper here so that the GAIA URLs can be pointed at
    252     // it in SetUpCommandLine().
    253     gaia_https_forwarder_.reset(
    254         new HTTPSForwarder(embedded_test_server()->base_url()));
    255     ASSERT_TRUE(gaia_https_forwarder_->Start());
    256 
    257     // Start the SAML IdP https wrapper here so that GAIA can be pointed at it
    258     // in SetUpCommandLine().
    259     saml_https_forwarder_.reset(
    260         new HTTPSForwarder(embedded_test_server()->base_url()));
    261     ASSERT_TRUE(saml_https_forwarder_->Start());
    262 
    263     // Stop IO thread here because no threads are allowed while
    264     // spawning sandbox host process. See crbug.com/322732.
    265     embedded_test_server()->StopThread();
    266 
    267     InProcessBrowserTest::SetUp();
    268   }
    269 
    270   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
    271     host_resolver()->AddRule("*", "127.0.0.1");
    272   }
    273 
    274   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    275     command_line->AppendSwitch(switches::kLoginManager);
    276     command_line->AppendSwitch(switches::kForceLoginManagerInTests);
    277     command_line->AppendSwitch(::switches::kDisableBackgroundNetworking);
    278     command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
    279 
    280     const GURL gaia_url = gaia_https_forwarder_->GetURL("");
    281     command_line->AppendSwitchASCII(::switches::kGaiaUrl, gaia_url.spec());
    282     command_line->AppendSwitchASCII(::switches::kLsoUrl, gaia_url.spec());
    283     command_line->AppendSwitchASCII(::switches::kGoogleApisUrl,
    284                                     gaia_url.spec());
    285 
    286     const GURL saml_idp_url = saml_https_forwarder_->GetURL("SAML");
    287     fake_saml_idp_.SetUp(saml_idp_url.path(), gaia_url);
    288     fake_gaia_.RegisterSamlUser(kFirstSAMLUserEmail, saml_idp_url);
    289     fake_gaia_.RegisterSamlUser(kSecondSAMLUserEmail, saml_idp_url);
    290     fake_gaia_.RegisterSamlUser(
    291         kHTTPSAMLUserEmail,
    292         embedded_test_server()->base_url().Resolve("/SAML"));
    293     fake_gaia_.RegisterSamlUser(kDifferentDomainSAMLUserEmail, saml_idp_url);
    294 
    295     fake_gaia_.Initialize();
    296   }
    297 
    298   virtual void SetUpOnMainThread() OVERRIDE {
    299     fake_gaia_.SetFakeMergeSessionParams(kFirstSAMLUserEmail,
    300                                          kTestAuthSIDCookie1,
    301                                          kTestAuthLSIDCookie1);
    302 
    303     embedded_test_server()->RegisterRequestHandler(
    304         base::Bind(&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_)));
    305     embedded_test_server()->RegisterRequestHandler(base::Bind(
    306         &FakeSamlIdp::HandleRequest, base::Unretained(&fake_saml_idp_)));
    307 
    308     // Restart the thread as the sandbox host process has already been spawned.
    309     embedded_test_server()->RestartThreadAndListen();
    310 
    311     login_screen_load_observer_.reset(new content::WindowedNotificationObserver(
    312         chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
    313         content::NotificationService::AllSources()));
    314   }
    315 
    316   virtual void TearDownOnMainThread() OVERRIDE {
    317     // If the login display is still showing, exit gracefully.
    318     if (LoginDisplayHostImpl::default_host()) {
    319       base::MessageLoop::current()->PostTask(FROM_HERE,
    320                                              base::Bind(&chrome::AttemptExit));
    321       content::RunMessageLoop();
    322     }
    323   }
    324 
    325   WebUILoginDisplay* GetLoginDisplay() {
    326     ExistingUserController* controller =
    327         ExistingUserController::current_controller();
    328     CHECK(controller);
    329     return static_cast<WebUILoginDisplay*>(controller->login_display());
    330   }
    331 
    332   void WaitForSigninScreen() {
    333     WizardController* wizard_controller =
    334         WizardController::default_controller();
    335     if (wizard_controller) {
    336       WizardController::SkipPostLoginScreensForTesting();
    337       wizard_controller->SkipToLoginForTesting(LoginScreenContext());
    338     }
    339 
    340     login_screen_load_observer_->Wait();
    341   }
    342 
    343   void StartSamlAndWaitForIdpPageLoad(const std::string& gaia_email) {
    344     WaitForSigninScreen();
    345 
    346     if (!saml_load_injected_) {
    347       saml_load_injected_ = true;
    348 
    349       ASSERT_TRUE(content::ExecuteScript(
    350           GetLoginUI()->GetWebContents(),
    351           "$('gaia-signin').gaiaAuthHost_.addEventListener('authFlowChange',"
    352               "function() {"
    353                 "window.domAutomationController.setAutomationId(0);"
    354                 "window.domAutomationController.send("
    355                     "$('gaia-signin').isSAML() ? 'SamlLoaded' : 'GaiaLoaded');"
    356               "});"));
    357     }
    358 
    359     content::DOMMessageQueue message_queue;  // Start observe before SAML.
    360     GetLoginDisplay()->ShowSigninScreenForCreds(gaia_email, "");
    361 
    362     std::string message;
    363     ASSERT_TRUE(message_queue.WaitForMessage(&message));
    364     EXPECT_EQ("\"SamlLoaded\"", message);
    365   }
    366 
    367   void SetSignFormField(const std::string& field_id,
    368                         const std::string& field_value) {
    369     std::string js =
    370         "(function(){"
    371           "document.getElementById('$FieldId').value = '$FieldValue';"
    372           "var e = new Event('input');"
    373           "document.getElementById('$FieldId').dispatchEvent(e);"
    374         "})();";
    375     ReplaceSubstringsAfterOffset(&js, 0, "$FieldId", field_id);
    376     ReplaceSubstringsAfterOffset(&js, 0, "$FieldValue", field_value);
    377     ExecuteJsInSigninFrame(js);
    378   }
    379 
    380   void SendConfirmPassword(const std::string& password_to_confirm) {
    381     std::string js =
    382         "$('confirm-password-input').value='$Password';"
    383         "$('confirm-password').onConfirmPassword_();";
    384     ReplaceSubstringsAfterOffset(&js, 0, "$Password", password_to_confirm);
    385     ASSERT_TRUE(content::ExecuteScript(GetLoginUI()->GetWebContents(), js));
    386   }
    387 
    388   void JsExpect(const std::string& js) {
    389     bool result;
    390     EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
    391         GetLoginUI()->GetWebContents(),
    392         "window.domAutomationController.send(!!(" + js + "));",
    393         &result));
    394     EXPECT_TRUE(result) << js;
    395   }
    396 
    397   std::string WaitForAndGetFatalErrorMessage() {
    398     OobeScreenWaiter(OobeDisplay::SCREEN_FATAL_ERROR).Wait();
    399     std::string error_message;
    400     if (!content::ExecuteScriptAndExtractString(
    401           GetLoginUI()->GetWebContents(),
    402           "window.domAutomationController.send("
    403               "$('fatal-error-message').textContent);",
    404           &error_message)) {
    405       ADD_FAILURE();
    406     }
    407     return error_message;
    408   }
    409 
    410   content::WebUI* GetLoginUI() {
    411     return static_cast<LoginDisplayHostImpl*>(
    412         LoginDisplayHostImpl::default_host())->GetOobeUI()->web_ui();
    413   }
    414 
    415   // Executes JavaScript code in the auth iframe hosted by gaia_auth extension.
    416   void ExecuteJsInSigninFrame(const std::string& js) {
    417     content::RenderFrameHost* frame = InlineLoginUI::GetAuthIframe(
    418         GetLoginUI()->GetWebContents(), GURL(), "signin-frame");
    419     ASSERT_TRUE(content::ExecuteScript(frame, js));
    420   }
    421 
    422   FakeSamlIdp* fake_saml_idp() { return &fake_saml_idp_; }
    423 
    424  protected:
    425   scoped_ptr<content::WindowedNotificationObserver> login_screen_load_observer_;
    426   FakeGaia fake_gaia_;
    427 
    428  private:
    429   FakeSamlIdp fake_saml_idp_;
    430   scoped_ptr<HTTPSForwarder> gaia_https_forwarder_;
    431   scoped_ptr<HTTPSForwarder> saml_https_forwarder_;
    432 
    433   bool saml_load_injected_;
    434 
    435   DISALLOW_COPY_AND_ASSIGN(SamlTest);
    436 };
    437 
    438 // Tests that signin frame should have 'saml' class and 'cancel' button is
    439 // visible when SAML IdP page is loaded. And 'cancel' button goes back to
    440 // gaia on clicking.
    441 IN_PROC_BROWSER_TEST_F(SamlTest, SamlUI) {
    442   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    443   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
    444 
    445   // Saml flow UI expectations.
    446   JsExpect("$('gaia-signin').classList.contains('saml')");
    447   JsExpect("!$('cancel-add-user-button').hidden");
    448 
    449   // Click on 'cancel'.
    450   content::DOMMessageQueue message_queue;  // Observe before 'cancel'.
    451   ASSERT_TRUE(content::ExecuteScript(
    452       GetLoginUI()->GetWebContents(),
    453       "$('cancel-add-user-button').click();"));
    454 
    455   // Auth flow should change back to Gaia.
    456   std::string message;
    457   do {
    458     ASSERT_TRUE(message_queue.WaitForMessage(&message));
    459   } while (message != "\"GaiaLoaded\"");
    460 
    461   // Saml flow is gone.
    462   JsExpect("!$('gaia-signin').classList.contains('saml')");
    463 }
    464 
    465 // Tests the sign-in flow when the credentials passing API is used.
    466 IN_PROC_BROWSER_TEST_F(SamlTest, CredentialPassingAPI) {
    467   fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
    468   fake_saml_idp()->SetLoginAuthHTMLTemplate("saml_api_login_auth.html");
    469   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
    470 
    471   // Fill-in the SAML IdP form and submit.
    472   SetSignFormField("Email", "fake_user");
    473   SetSignFormField("Password", "fake_password");
    474   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
    475 
    476   // Login should finish login and a session should start.
    477   content::WindowedNotificationObserver(
    478       chrome::NOTIFICATION_SESSION_STARTED,
    479       content::NotificationService::AllSources()).Wait();
    480 }
    481 
    482 // Tests the single password scraped flow.
    483 IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedSingle) {
    484   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    485   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
    486 
    487   // Fill-in the SAML IdP form and submit.
    488   SetSignFormField("Email", "fake_user");
    489   SetSignFormField("Password", "fake_password");
    490   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
    491 
    492   // Lands on confirm password screen.
    493   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
    494 
    495   // Enter an unknown password should go back to confirm password screen.
    496   SendConfirmPassword("wrong_password");
    497   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
    498 
    499   // Enter a known password should finish login and start session.
    500   SendConfirmPassword("fake_password");
    501   content::WindowedNotificationObserver(
    502       chrome::NOTIFICATION_SESSION_STARTED,
    503       content::NotificationService::AllSources()).Wait();
    504 }
    505 
    506 // Tests the multiple password scraped flow.
    507 IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedMultiple) {
    508   fake_saml_idp()->SetLoginHTMLTemplate("saml_login_two_passwords.html");
    509 
    510   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
    511 
    512   SetSignFormField("Email", "fake_user");
    513   SetSignFormField("Password", "fake_password");
    514   SetSignFormField("Password1", "password1");
    515   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
    516 
    517   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
    518 
    519   // Either scraped password should be able to sign-in.
    520   SendConfirmPassword("password1");
    521   content::WindowedNotificationObserver(
    522       chrome::NOTIFICATION_SESSION_STARTED,
    523       content::NotificationService::AllSources()).Wait();
    524 }
    525 
    526 // Tests the no password scraped flow.
    527 IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedNone) {
    528   fake_saml_idp()->SetLoginHTMLTemplate("saml_login_no_passwords.html");
    529 
    530   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
    531 
    532   SetSignFormField("Email", "fake_user");
    533   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
    534 
    535   EXPECT_EQ(l10n_util::GetStringUTF8(IDS_LOGIN_FATAL_ERROR_NO_PASSWORD),
    536             WaitForAndGetFatalErrorMessage());
    537 }
    538 
    539 // Types |bob (at) example.com| into the GAIA login form but then authenticates as
    540 // |alice (at) example.com| via SAML. Verifies that the logged-in user is correctly
    541 // identified as Alice.
    542 IN_PROC_BROWSER_TEST_F(SamlTest, UseAutenticatedUserEmailAddress) {
    543   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    544   // Type |bob (at) example.com| into the GAIA login form.
    545   StartSamlAndWaitForIdpPageLoad(kSecondSAMLUserEmail);
    546 
    547   // Authenticate as alice (at) example.com via SAML (the |Email| provided here is
    548   // irrelevant - the authenticated user's e-mail address that FakeGAIA
    549   // reports was set via |SetFakeMergeSessionParams|.
    550   SetSignFormField("Email", "fake_user");
    551   SetSignFormField("Password", "fake_password");
    552   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
    553 
    554   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
    555 
    556   SendConfirmPassword("fake_password");
    557   content::WindowedNotificationObserver(
    558       chrome::NOTIFICATION_SESSION_STARTED,
    559       content::NotificationService::AllSources()).Wait();
    560   const user_manager::User* user =
    561       user_manager::UserManager::Get()->GetActiveUser();
    562   ASSERT_TRUE(user);
    563   EXPECT_EQ(kFirstSAMLUserEmail, user->email());
    564 }
    565 
    566 // Verifies that if the authenticated user's e-mail address cannot be retrieved,
    567 // an error message is shown.
    568 IN_PROC_BROWSER_TEST_F(SamlTest, FailToRetrieveAutenticatedUserEmailAddress) {
    569   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    570   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
    571 
    572   fake_gaia_.SetFakeMergeSessionParams(
    573       "", kTestAuthSIDCookie1, kTestAuthLSIDCookie1);
    574   SetSignFormField("Email", "fake_user");
    575   SetSignFormField("Password", "fake_password");
    576   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
    577 
    578   EXPECT_EQ(l10n_util::GetStringUTF8(IDS_LOGIN_FATAL_ERROR_NO_EMAIL),
    579             WaitForAndGetFatalErrorMessage());
    580 }
    581 
    582 // Tests the password confirm flow: show error on the first failure and
    583 // fatal error on the second failure.
    584 IN_PROC_BROWSER_TEST_F(SamlTest, PasswordConfirmFlow) {
    585   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    586   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
    587 
    588   // Fill-in the SAML IdP form and submit.
    589   SetSignFormField("Email", "fake_user");
    590   SetSignFormField("Password", "fake_password");
    591   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
    592 
    593   // Lands on confirm password screen with no error message.
    594   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
    595   JsExpect("!$('confirm-password').classList.contains('error')");
    596 
    597   // Enter an unknown password for the first time should go back to confirm
    598   // password screen with error message.
    599   SendConfirmPassword("wrong_password");
    600   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
    601   JsExpect("$('confirm-password').classList.contains('error')");
    602 
    603   // Enter an unknown password 2nd time should go back fatal error message.
    604   SendConfirmPassword("wrong_password");
    605   EXPECT_EQ(
    606       l10n_util::GetStringUTF8(IDS_LOGIN_FATAL_ERROR_PASSWORD_VERIFICATION),
    607       WaitForAndGetFatalErrorMessage());
    608 }
    609 
    610 // Verifies that when GAIA attempts to redirect to a SAML IdP served over http,
    611 // not https, the redirect is blocked and an error message is shown.
    612 IN_PROC_BROWSER_TEST_F(SamlTest, HTTPRedirectDisallowed) {
    613   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    614 
    615   WaitForSigninScreen();
    616   GetLoginDisplay()->ShowSigninScreenForCreds(kHTTPSAMLUserEmail, "");
    617 
    618   const GURL url = embedded_test_server()->base_url().Resolve("/SAML");
    619   EXPECT_EQ(l10n_util::GetStringFUTF8(
    620                 IDS_LOGIN_FATAL_ERROR_TEXT_INSECURE_URL,
    621                 base::UTF8ToUTF16(url.spec())),
    622             WaitForAndGetFatalErrorMessage());
    623 }
    624 
    625 // Verifies that when GAIA attempts to redirect to a page served over http, not
    626 // https, via an HTML meta refresh, the redirect is blocked and an error message
    627 // is shown. This guards against regressions of http://crbug.com/359515.
    628 IN_PROC_BROWSER_TEST_F(SamlTest, MetaRefreshToHTTPDisallowed) {
    629   const GURL url = embedded_test_server()->base_url().Resolve("/SSO");
    630   fake_saml_idp()->SetLoginHTMLTemplate("saml_login_instant_meta_refresh.html");
    631   fake_saml_idp()->SetRefreshURL(url);
    632 
    633   WaitForSigninScreen();
    634   GetLoginDisplay()->ShowSigninScreenForCreds(kFirstSAMLUserEmail, "");
    635 
    636   EXPECT_EQ(l10n_util::GetStringFUTF8(
    637                 IDS_LOGIN_FATAL_ERROR_TEXT_INSECURE_URL,
    638                 base::UTF8ToUTF16(url.spec())),
    639             WaitForAndGetFatalErrorMessage());
    640 }
    641 
    642 class SAMLPolicyTest : public SamlTest {
    643  public:
    644   SAMLPolicyTest();
    645   virtual ~SAMLPolicyTest();
    646 
    647   // SamlTest:
    648   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
    649   virtual void SetUpOnMainThread() OVERRIDE;
    650 
    651   void SetSAMLOfflineSigninTimeLimitPolicy(int limit);
    652   void EnableTransferSAMLCookiesPolicy();
    653 
    654   void ShowGAIALoginForm();
    655   void LogInWithSAML(const std::string& user_id,
    656                      const std::string& auth_sid_cookie,
    657                      const std::string& auth_lsid_cookie);
    658 
    659   std::string GetCookieValue(const std::string& name);
    660 
    661   void GetCookies();
    662 
    663  protected:
    664   void GetCookiesOnIOThread(
    665       const scoped_refptr<net::URLRequestContextGetter>& request_context,
    666       const base::Closure& callback);
    667   void StoreCookieList(const base::Closure& callback,
    668                        const net::CookieList& cookie_list);
    669 
    670   policy::DevicePolicyCrosTestHelper test_helper_;
    671 
    672   // FakeDBusThreadManager uses FakeSessionManagerClient.
    673   FakeSessionManagerClient* fake_session_manager_client_;
    674   policy::DevicePolicyBuilder* device_policy_;
    675 
    676   policy::MockConfigurationPolicyProvider provider_;
    677 
    678   net::CookieList cookie_list_;
    679 
    680  private:
    681   DISALLOW_COPY_AND_ASSIGN(SAMLPolicyTest);
    682 };
    683 
    684 SAMLPolicyTest::SAMLPolicyTest()
    685     : fake_session_manager_client_(new FakeSessionManagerClient),
    686       device_policy_(test_helper_.device_policy()) {
    687 }
    688 
    689 SAMLPolicyTest::~SAMLPolicyTest() {
    690 }
    691 
    692 void SAMLPolicyTest::SetUpInProcessBrowserTestFixture() {
    693   DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
    694       scoped_ptr<SessionManagerClient>(fake_session_manager_client_));
    695 
    696   SamlTest::SetUpInProcessBrowserTestFixture();
    697 
    698   // Initialize device policy.
    699   test_helper_.InstallOwnerKey();
    700   test_helper_.MarkAsEnterpriseOwned();
    701   device_policy_->SetDefaultSigningKey();
    702   device_policy_->Build();
    703   fake_session_manager_client_->set_device_policy(device_policy_->GetBlob());
    704   fake_session_manager_client_->OnPropertyChangeComplete(true);
    705 
    706   // Initialize user policy.
    707   EXPECT_CALL(provider_, IsInitializationComplete(_))
    708       .WillRepeatedly(Return(true));
    709   policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
    710 }
    711 
    712 void SAMLPolicyTest::SetUpOnMainThread() {
    713   SamlTest::SetUpOnMainThread();
    714 
    715   // Pretend that the test users' OAuth tokens are valid.
    716   user_manager::UserManager::Get()->SaveUserOAuthStatus(
    717       kFirstSAMLUserEmail, user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
    718   user_manager::UserManager::Get()->SaveUserOAuthStatus(
    719       kNonSAMLUserEmail, user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
    720   user_manager::UserManager::Get()->SaveUserOAuthStatus(
    721       kDifferentDomainSAMLUserEmail,
    722       user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
    723 }
    724 
    725 void SAMLPolicyTest::SetSAMLOfflineSigninTimeLimitPolicy(int limit) {
    726   policy::PolicyMap user_policy;
    727   user_policy.Set(policy::key::kSAMLOfflineSigninTimeLimit,
    728                   policy::POLICY_LEVEL_MANDATORY,
    729                   policy::POLICY_SCOPE_USER,
    730                   new base::FundamentalValue(limit),
    731                   NULL);
    732   provider_.UpdateChromePolicy(user_policy);
    733   base::RunLoop().RunUntilIdle();
    734 }
    735 
    736 void SAMLPolicyTest::EnableTransferSAMLCookiesPolicy() {
    737   em::ChromeDeviceSettingsProto& proto(device_policy_->payload());
    738   proto.mutable_saml_settings()->set_transfer_saml_cookies(true);
    739 
    740   base::RunLoop run_loop;
    741   scoped_ptr<CrosSettings::ObserverSubscription> observer =
    742       CrosSettings::Get()->AddSettingsObserver(
    743            kAccountsPrefTransferSAMLCookies,
    744            run_loop.QuitClosure());
    745   device_policy_->SetDefaultSigningKey();
    746   device_policy_->Build();
    747   fake_session_manager_client_->set_device_policy(device_policy_->GetBlob());
    748   fake_session_manager_client_->OnPropertyChangeComplete(true);
    749   run_loop.Run();
    750 }
    751 
    752 void SAMLPolicyTest::ShowGAIALoginForm() {
    753   login_screen_load_observer_->Wait();
    754   ASSERT_TRUE(content::ExecuteScript(
    755       GetLoginUI()->GetWebContents(),
    756       "$('gaia-signin').gaiaAuthHost_.addEventListener('ready', function() {"
    757       "  window.domAutomationController.setAutomationId(0);"
    758       "  window.domAutomationController.send('ready');"
    759       "});"
    760       "$('add-user-button').click();"));
    761   content::DOMMessageQueue message_queue;
    762   std::string message;
    763   ASSERT_TRUE(message_queue.WaitForMessage(&message));
    764   EXPECT_EQ("\"ready\"", message);
    765 }
    766 
    767 void SAMLPolicyTest::LogInWithSAML(const std::string& user_id,
    768                                    const std::string& auth_sid_cookie,
    769                                    const std::string& auth_lsid_cookie) {
    770   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    771   StartSamlAndWaitForIdpPageLoad(user_id);
    772 
    773   fake_gaia_.SetFakeMergeSessionParams(
    774       user_id, auth_sid_cookie, auth_lsid_cookie);
    775   SetSignFormField("Email", "fake_user");
    776   SetSignFormField("Password", "fake_password");
    777   ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
    778 
    779   OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
    780 
    781   SendConfirmPassword("fake_password");
    782   content::WindowedNotificationObserver(
    783       chrome::NOTIFICATION_SESSION_STARTED,
    784       content::NotificationService::AllSources()).Wait();
    785 }
    786 
    787 std::string SAMLPolicyTest::GetCookieValue(const std::string& name) {
    788   for (net::CookieList::const_iterator it = cookie_list_.begin();
    789        it != cookie_list_.end(); ++it) {
    790     if (it->Name() == name)
    791       return it->Value();
    792   }
    793   return std::string();
    794 }
    795 
    796 void SAMLPolicyTest::GetCookies() {
    797   Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUserUnsafe(
    798       user_manager::UserManager::Get()->GetActiveUser());
    799   ASSERT_TRUE(profile);
    800   base::RunLoop run_loop;
    801   content::BrowserThread::PostTask(
    802       content::BrowserThread::IO,
    803       FROM_HERE,
    804       base::Bind(&SAMLPolicyTest::GetCookiesOnIOThread,
    805                  base::Unretained(this),
    806                  scoped_refptr<net::URLRequestContextGetter>(
    807                      profile->GetRequestContext()),
    808                  run_loop.QuitClosure()));
    809   run_loop.Run();
    810 }
    811 
    812 void SAMLPolicyTest::GetCookiesOnIOThread(
    813     const scoped_refptr<net::URLRequestContextGetter>& request_context,
    814     const base::Closure& callback) {
    815   request_context->GetURLRequestContext()->cookie_store()->
    816       GetCookieMonster()->GetAllCookiesAsync(base::Bind(
    817           &SAMLPolicyTest::StoreCookieList,
    818           base::Unretained(this),
    819           callback));
    820 }
    821 
    822 void SAMLPolicyTest::StoreCookieList(
    823     const base::Closure& callback,
    824     const net::CookieList& cookie_list) {
    825   cookie_list_ = cookie_list;
    826   content::BrowserThread::PostTask(content::BrowserThread::UI,
    827                                    FROM_HERE,
    828                                    callback);
    829 }
    830 
    831 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_NoSAML) {
    832   // Set the offline login time limit for SAML users to zero.
    833   SetSAMLOfflineSigninTimeLimitPolicy(0);
    834 
    835   WaitForSigninScreen();
    836 
    837   // Log in without SAML.
    838   GetLoginDisplay()->ShowSigninScreenForCreds(kNonSAMLUserEmail, "password");
    839 
    840   content::WindowedNotificationObserver(
    841     chrome::NOTIFICATION_SESSION_STARTED,
    842     content::NotificationService::AllSources()).Wait();
    843 }
    844 
    845 // Verifies that the offline login time limit does not affect a user who
    846 // authenticated without SAML.
    847 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, NoSAML) {
    848   login_screen_load_observer_->Wait();
    849   // Verify that offline login is allowed.
    850   JsExpect("window.getComputedStyle(document.querySelector("
    851            "    '#pod-row .signin-button-container')).display == 'none'");
    852 }
    853 
    854 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLNoLimit) {
    855   // Remove the offline login time limit for SAML users.
    856   SetSAMLOfflineSigninTimeLimitPolicy(-1);
    857 
    858   LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie1, kTestAuthLSIDCookie1);
    859 }
    860 
    861 // Verifies that when no offline login time limit is set, a user who
    862 // authenticated with SAML is allowed to log in offline.
    863 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLNoLimit) {
    864   login_screen_load_observer_->Wait();
    865   // Verify that offline login is allowed.
    866   JsExpect("window.getComputedStyle(document.querySelector("
    867            "    '#pod-row .signin-button-container')).display == 'none'");
    868 }
    869 
    870 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLZeroLimit) {
    871   // Set the offline login time limit for SAML users to zero.
    872   SetSAMLOfflineSigninTimeLimitPolicy(0);
    873 
    874   LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie1, kTestAuthLSIDCookie1);
    875 }
    876 
    877 // Verifies that when the offline login time limit is exceeded for a user who
    878 // authenticated via SAML, that user is forced to log in online the next time.
    879 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLZeroLimit) {
    880   login_screen_load_observer_->Wait();
    881   // Verify that offline login is not allowed.
    882   JsExpect("window.getComputedStyle(document.querySelector("
    883            "    '#pod-row .signin-button-container')).display != 'none'");
    884 }
    885 
    886 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_PRE_TransferCookiesAffiliated) {
    887   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue1);
    888   LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie1, kTestAuthLSIDCookie1);
    889 
    890   GetCookies();
    891   EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
    892   EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
    893   EXPECT_EQ(kSAMLIdPCookieValue1, GetCookieValue(kSAMLIdPCookieName));
    894 }
    895 
    896 // Verifies that when the DeviceTransferSAMLCookies policy is not enabled, SAML
    897 // IdP cookies are not transferred to a user's profile on subsequent login, even
    898 // if the user belongs to the domain that the device is enrolled into. Also
    899 // verifies that GAIA cookies are not transferred.
    900 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_TransferCookiesAffiliated) {
    901   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue2);
    902   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    903   ShowGAIALoginForm();
    904   LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie2, kTestAuthLSIDCookie2);
    905 
    906   GetCookies();
    907   EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
    908   EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
    909   EXPECT_EQ(kSAMLIdPCookieValue1, GetCookieValue(kSAMLIdPCookieName));
    910 }
    911 
    912 // Verifies that when the DeviceTransferSAMLCookies policy is enabled, SAML IdP
    913 // cookies are transferred to a user's profile on subsequent login when the user
    914 // belongs to the domain that the device is enrolled into. Also verifies that
    915 // GAIA cookies are not transferred.
    916 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, TransferCookiesAffiliated) {
    917   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue2);
    918   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    919   ShowGAIALoginForm();
    920 
    921   EnableTransferSAMLCookiesPolicy();
    922   LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie2, kTestAuthLSIDCookie2);
    923 
    924   GetCookies();
    925   EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
    926   EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
    927   EXPECT_EQ(kSAMLIdPCookieValue2, GetCookieValue(kSAMLIdPCookieName));
    928 }
    929 
    930 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_TransferCookiesUnaffiliated) {
    931   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue1);
    932   LogInWithSAML(kDifferentDomainSAMLUserEmail,
    933                 kTestAuthSIDCookie1,
    934                 kTestAuthLSIDCookie1);
    935 
    936   GetCookies();
    937   EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
    938   EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
    939   EXPECT_EQ(kSAMLIdPCookieValue1, GetCookieValue(kSAMLIdPCookieName));
    940 }
    941 
    942 // Verifies that even if the DeviceTransferSAMLCookies policy is enabled, SAML
    943 // IdP are not transferred to a user's profile on subsequent login if the user
    944 // does not belong to the domain that the device is enrolled into. Also verifies
    945 // that GAIA cookies are not transferred.
    946 IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, TransferCookiesUnaffiliated) {
    947   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue2);
    948   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
    949   ShowGAIALoginForm();
    950 
    951   EnableTransferSAMLCookiesPolicy();
    952   LogInWithSAML(kDifferentDomainSAMLUserEmail,
    953                 kTestAuthSIDCookie1,
    954                 kTestAuthLSIDCookie1);
    955 
    956   GetCookies();
    957   EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
    958   EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
    959   EXPECT_EQ(kSAMLIdPCookieValue1, GetCookieValue(kSAMLIdPCookieName));
    960 }
    961 
    962 }  // namespace chromeos
    963