blob: 8a5e7c3ecd725e8bcf20a9d3dd464b8967b4faff [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/test/https_forwarder.h"
#include "chrome/browser/chromeos/login/test/oobe_base_test.h"
#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
#include "chrome/browser/chromeos/login/ui/webui_login_display.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/policy/affiliation_test_helper.h"
#include "chrome/browser/chromeos/policy/device_policy_builder.h"
#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/policy/test/local_policy_test_server.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/signin/get_auth_frame.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/features/feature_channel.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/cryptohome/system_salt_getter.h"
#include "chromeos/dbus/cryptohome/key.pb.h"
#include "chromeos/dbus/cryptohome/rpc.pb.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_cryptohome_client.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/dbus/shill_manager_client.h"
#include "chromeos/login/auth/key.h"
#include "chromeos/settings/cros_settings_names.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_switches.h"
#include "components/policy/core/common/policy_types.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "google_apis/gaia/fake_gaia.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_switches.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/base/url_util.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_store.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "policy/policy_constants.h"
#include "policy/proto/device_management_backend.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
namespace em = enterprise_management;
using net::test_server::BasicHttpResponse;
using net::test_server::HttpRequest;
using net::test_server::HttpResponse;
using testing::Return;
using testing::_;
namespace chromeos {
namespace {
const char kGAIASIDCookieName[] = "SID";
const char kGAIALSIDCookieName[] = "LSID";
const char kTestAuthSIDCookie1[] = "fake-auth-SID-cookie-1";
const char kTestAuthSIDCookie2[] = "fake-auth-SID-cookie-2";
const char kTestAuthLSIDCookie1[] = "fake-auth-LSID-cookie-1";
const char kTestAuthLSIDCookie2[] = "fake-auth-LSID-cookie-2";
const char kFirstSAMLUserEmail[] = "bob@example.com";
const char kSecondSAMLUserEmail[] = "alice@example.com";
const char kHTTPSAMLUserEmail[] = "carol@example.com";
const char kNonSAMLUserEmail[] = "dan@example.com";
const char kDifferentDomainSAMLUserEmail[] = "eve@example.test";
const char kIdPHost[] = "login.example.com";
const char kAdditionalIdPHost[] = "login2.example.com";
const char kSAMLIdPCookieName[] = "saml";
const char kSAMLIdPCookieValue1[] = "value-1";
const char kSAMLIdPCookieValue2[] = "value-2";
const char kRelayState[] = "RelayState";
const char kTestUserinfoToken[] = "fake-userinfo-token";
const char kTestRefreshToken[] = "fake-refresh-token";
const char kPolicy[] = "{\"managed_users\": [\"*\"]}";
const char kAffiliationID[] = "some-affiliation-id";
// FakeSamlIdp serves IdP auth form and the form submission. The form is
// served with the template's RelayState placeholder expanded to the real
// RelayState parameter from request. The form submission redirects back to
// FakeGaia with the same RelayState.
class FakeSamlIdp {
public:
FakeSamlIdp();
~FakeSamlIdp();
void SetUp(const std::string& base_path, const GURL& gaia_url);
void SetLoginHTMLTemplate(const std::string& template_file);
void SetLoginAuthHTMLTemplate(const std::string& template_file);
void SetRefreshURL(const GURL& refresh_url);
void SetCookieValue(const std::string& cookie_value);
std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
private:
std::unique_ptr<HttpResponse> BuildHTMLResponse(
const std::string& html_template,
const std::string& relay_state,
const std::string& next_path);
base::FilePath html_template_dir_;
std::string login_path_;
std::string login_auth_path_;
std::string login_html_template_;
std::string login_auth_html_template_;
GURL gaia_assertion_url_;
GURL refresh_url_;
std::string cookie_value_;
DISALLOW_COPY_AND_ASSIGN(FakeSamlIdp);
};
FakeSamlIdp::FakeSamlIdp() {
}
FakeSamlIdp::~FakeSamlIdp() {
}
void FakeSamlIdp::SetUp(const std::string& base_path, const GURL& gaia_url) {
base::FilePath test_data_dir;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
html_template_dir_ = test_data_dir.Append("login");
login_path_ = base_path;
login_auth_path_ = base_path + "Auth";
gaia_assertion_url_ = gaia_url.Resolve("/SSO");
}
void FakeSamlIdp::SetLoginHTMLTemplate(const std::string& template_file) {
EXPECT_TRUE(base::ReadFileToString(
html_template_dir_.Append(template_file),
&login_html_template_));
}
void FakeSamlIdp::SetLoginAuthHTMLTemplate(const std::string& template_file) {
EXPECT_TRUE(base::ReadFileToString(
html_template_dir_.Append(template_file),
&login_auth_html_template_));
}
void FakeSamlIdp::SetRefreshURL(const GURL& refresh_url) {
refresh_url_ = refresh_url;
}
void FakeSamlIdp::SetCookieValue(const std::string& cookie_value) {
cookie_value_ = cookie_value;
}
std::unique_ptr<HttpResponse> FakeSamlIdp::HandleRequest(
const HttpRequest& request) {
// The scheme and host of the URL is actually not important but required to
// get a valid GURL in order to parse |request.relative_url|.
GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
std::string request_path = request_url.path();
if (request_path == login_path_) {
std::string relay_state;
net::GetValueForKeyInQuery(request_url, kRelayState, &relay_state);
return BuildHTMLResponse(login_html_template_,
relay_state,
login_auth_path_);
}
if (request_path != login_auth_path_) {
// Request not understood.
return std::unique_ptr<HttpResponse>();
}
std::string relay_state;
FakeGaia::GetQueryParameter(request.content, kRelayState, &relay_state);
GURL redirect_url = gaia_assertion_url_;
if (!login_auth_html_template_.empty()) {
return BuildHTMLResponse(login_auth_html_template_,
relay_state,
redirect_url.spec());
}
redirect_url = net::AppendQueryParameter(
redirect_url, "SAMLResponse", "fake_response");
redirect_url = net::AppendQueryParameter(
redirect_url, kRelayState, relay_state);
std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
http_response->AddCustomHeader("Location", redirect_url.spec());
http_response->AddCustomHeader(
"Set-cookie",
base::StringPrintf("saml=%s", cookie_value_.c_str()));
return std::move(http_response);
}
std::unique_ptr<HttpResponse> FakeSamlIdp::BuildHTMLResponse(
const std::string& html_template,
const std::string& relay_state,
const std::string& next_path) {
std::string response_html = html_template;
base::ReplaceSubstringsAfterOffset(
&response_html, 0, "$RelayState", relay_state);
base::ReplaceSubstringsAfterOffset(
&response_html, 0, "$Post", next_path);
base::ReplaceSubstringsAfterOffset(
&response_html, 0, "$Refresh", refresh_url_.spec());
std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
http_response->set_code(net::HTTP_OK);
http_response->set_content(response_html);
http_response->set_content_type("text/html");
return std::move(http_response);
}
// A FakeCryptohomeClient that stores the salted and hashed secret passed to
// MountEx().
class SecretInterceptingFakeCryptohomeClient : public FakeCryptohomeClient {
public:
SecretInterceptingFakeCryptohomeClient();
void MountEx(const cryptohome::Identification& id,
const cryptohome::AuthorizationRequest& auth,
const cryptohome::MountRequest& request,
const ProtobufMethodCallback& callback) override;
const std::string& salted_hashed_secret() { return salted_hashed_secret_; }
private:
std::string salted_hashed_secret_;
DISALLOW_COPY_AND_ASSIGN(SecretInterceptingFakeCryptohomeClient);
};
SecretInterceptingFakeCryptohomeClient::
SecretInterceptingFakeCryptohomeClient() {
}
void SecretInterceptingFakeCryptohomeClient::MountEx(
const cryptohome::Identification& id,
const cryptohome::AuthorizationRequest& auth,
const cryptohome::MountRequest& request,
const ProtobufMethodCallback& callback) {
salted_hashed_secret_ = auth.key().secret();
FakeCryptohomeClient::MountEx(id, auth, request, callback);
}
} // namespace
class SamlTest : public OobeBaseTest {
public:
SamlTest() : cryptohome_client_(new SecretInterceptingFakeCryptohomeClient) {
set_initialize_fake_merge_session(false);
}
~SamlTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kOobeSkipPostLogin);
command_line->AppendSwitch(
chromeos::switches::kAllowFailedPolicyFetchForTest);
const GURL gaia_url = gaia_https_forwarder_.GetURLForSSLHost("");
const GURL saml_idp_url = saml_https_forwarder_.GetURLForSSLHost("SAML");
fake_saml_idp_.SetUp(saml_idp_url.path(), gaia_url);
fake_gaia_->RegisterSamlUser(kFirstSAMLUserEmail, saml_idp_url);
fake_gaia_->RegisterSamlUser(kSecondSAMLUserEmail, saml_idp_url);
fake_gaia_->RegisterSamlUser(
kHTTPSAMLUserEmail,
embedded_test_server()->base_url().Resolve("/SAML"));
fake_gaia_->RegisterSamlUser(kDifferentDomainSAMLUserEmail, saml_idp_url);
fake_gaia_->RegisterSamlDomainRedirectUrl("example.com", saml_idp_url);
OobeBaseTest::SetUpCommandLine(command_line);
}
void SetUpInProcessBrowserTestFixture() override {
DBusThreadManager::GetSetterForTesting()->SetCryptohomeClient(
std::unique_ptr<CryptohomeClient>(cryptohome_client_));
OobeBaseTest::SetUpInProcessBrowserTestFixture();
}
void SetUpOnMainThread() override {
fake_gaia_->SetFakeMergeSessionParams(
kFirstSAMLUserEmail, kTestAuthSIDCookie1, kTestAuthLSIDCookie1);
embedded_test_server()->RegisterRequestHandler(base::Bind(
&FakeSamlIdp::HandleRequest, base::Unretained(&fake_saml_idp_)));
OobeBaseTest::SetUpOnMainThread();
}
void SetupAuthFlowChangeListener() {
ASSERT_TRUE(content::ExecuteScript(
GetLoginUI()->GetWebContents(),
"$('gaia-signin').gaiaAuthHost_.addEventListener('authFlowChange',"
"function f() {"
"$('gaia-signin').gaiaAuthHost_.removeEventListener("
"'authFlowChange', f);"
"window.domAutomationController.setAutomationId(0);"
"window.domAutomationController.send("
"$('gaia-signin').isSAML() ? 'SamlLoaded' : 'GaiaLoaded');"
"});"));
}
virtual void StartSamlAndWaitForIdpPageLoad(const std::string& gaia_email) {
WaitForSigninScreen();
SetupAuthFlowChangeListener();
content::DOMMessageQueue message_queue; // Start observe before SAML.
GetLoginDisplay()->ShowSigninScreenForCreds(gaia_email, "");
std::string message;
ASSERT_TRUE(message_queue.WaitForMessage(&message));
EXPECT_EQ("\"SamlLoaded\"", message);
}
void SendConfirmPassword(const std::string& password_to_confirm) {
std::string js =
"$('saml-confirm-password').$.passwordInput.value='$Password';"
"$('saml-confirm-password').$.inputForm.submit();";
base::ReplaceSubstringsAfterOffset(
&js, 0, "$Password", password_to_confirm);
ASSERT_TRUE(content::ExecuteScript(GetLoginUI()->GetWebContents(), js));
}
std::string WaitForAndGetFatalErrorMessage() {
OobeScreenWaiter(OobeScreen::SCREEN_FATAL_ERROR).Wait();
std::string message_element = "$('fatal-error-card')";
std::string error_message;
if (!content::ExecuteScriptAndExtractString(
GetLoginUI()->GetWebContents(),
"window.domAutomationController.send(" + message_element +
".textContent);",
&error_message)) {
ADD_FAILURE();
}
return error_message;
}
FakeSamlIdp* fake_saml_idp() { return &fake_saml_idp_; }
protected:
void InitHttpsForwarders() override {
ASSERT_TRUE(saml_https_forwarder_.Initialize(
kIdPHost, embedded_test_server()->base_url()));
OobeBaseTest::InitHttpsForwarders();
}
HTTPSForwarder saml_https_forwarder_;
SecretInterceptingFakeCryptohomeClient* cryptohome_client_;
private:
FakeSamlIdp fake_saml_idp_;
DISALLOW_COPY_AND_ASSIGN(SamlTest);
};
// Tests that signin frame should have 'saml' class and 'cancel' button is
// visible when SAML IdP page is loaded. And 'cancel' button goes back to
// gaia on clicking.
//
// Times out on CrOS MSAN. https://crbug.com/504141
#if defined(MEMORY_SANITIZER)
#define MAYBE_SamlUI DISABLED_SamlUI
#else
#define MAYBE_SamlUI SamlUI
#endif
IN_PROC_BROWSER_TEST_F(SamlTest, MAYBE_SamlUI) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
// Saml flow UI expectations.
JsExpect("$('gaia-signin').classList.contains('full-width')");
JsExpect("!$('saml-notice-container').hidden");
std::string js = "$('saml-notice-message').textContent.indexOf('$Host') > -1";
base::ReplaceSubstringsAfterOffset(&js, 0, "$Host", kIdPHost);
JsExpect(js);
SetupAuthFlowChangeListener();
// Click on 'cancel'.
content::DOMMessageQueue message_queue; // Observe before 'cancel'.
ASSERT_TRUE(
content::ExecuteScript(GetLoginUI()->GetWebContents(),
"$('gaia-navigation').$.closeButton.click();"));
// Auth flow should change back to Gaia.
std::string message;
do {
ASSERT_TRUE(message_queue.WaitForMessage(&message));
} while (message != "\"GaiaLoaded\"");
// Saml flow is gone.
JsExpect("!$('gaia-signin').classList.contains('full-width')");
}
// Tests the sign-in flow when the credentials passing API is used.
IN_PROC_BROWSER_TEST_F(SamlTest, CredentialPassingAPI) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
fake_saml_idp()->SetLoginAuthHTMLTemplate("saml_api_login_auth.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
content::WindowedNotificationObserver session_start_waiter(
chrome::NOTIFICATION_SESSION_STARTED,
content::NotificationService::AllSources());
// Fill-in the SAML IdP form and submit.
SetSignFormField("Email", "fake_user");
SetSignFormField("Dummy", "not_the_password");
SetSignFormField("Password", "actual_password");
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
// Login should finish login and a session should start.
session_start_waiter.Wait();
// Regression test for http://crbug.com/490737: Verify that the user's actual
// password was used, not the contents of the first type=password input field
// found on the page.
Key key("actual_password");
key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF,
SystemSaltGetter::ConvertRawSaltToHexString(
FakeCryptohomeClient::GetStubSystemSalt()));
EXPECT_EQ(key.GetSecret(), cryptohome_client_->salted_hashed_secret());
}
// Tests the single password scraped flow.
IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedSingle) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
// Fill-in the SAML IdP form and submit.
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
// Scraping a single password should finish the login and start the session.
content::WindowedNotificationObserver session_start_waiter(
chrome::NOTIFICATION_SESSION_STARTED,
content::NotificationService::AllSources());
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
session_start_waiter.Wait();
}
// Tests password scraping from a dynamically created password field.
IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedDynamic) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
ExecuteJsInSigninFrame(
"(function() {"
"var newPassInput = document.createElement('input');"
"newPassInput.id = 'DynamicallyCreatedPassword';"
"newPassInput.type = 'password';"
"newPassInput.name = 'Password';"
"document.forms[0].appendChild(newPassInput);"
"})();");
// Fill-in the SAML IdP form and submit.
SetSignFormField("Email", "fake_user");
SetSignFormField("DynamicallyCreatedPassword", "fake_password");
// Scraping a single password should finish the login and start the session.
content::WindowedNotificationObserver session_start_waiter(
chrome::NOTIFICATION_SESSION_STARTED,
content::NotificationService::AllSources());
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
session_start_waiter.Wait();
}
// Tests the multiple password scraped flow.
IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedMultiple) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login_two_passwords.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
SetSignFormField("Password1", "password1");
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
// Lands on confirm password screen.
OobeScreenWaiter(OobeScreen::SCREEN_CONFIRM_PASSWORD).Wait();
// Entering an unknown password should go back to the confirm password screen.
SendConfirmPassword("wrong_password");
OobeScreenWaiter(OobeScreen::SCREEN_CONFIRM_PASSWORD).Wait();
// Either scraped password should be able to sign-in.
content::WindowedNotificationObserver session_start_waiter(
chrome::NOTIFICATION_SESSION_STARTED,
content::NotificationService::AllSources());
SendConfirmPassword("password1");
session_start_waiter.Wait();
}
// Tests the no password scraped flow.
IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedNone) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login_no_passwords.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
SetSignFormField("Email", "fake_user");
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
EXPECT_EQ(l10n_util::GetStringUTF8(IDS_LOGIN_FATAL_ERROR_NO_PASSWORD),
WaitForAndGetFatalErrorMessage());
}
// Types |bob@example.com| into the GAIA login form but then authenticates as
// |alice@example.com| via SAML. Verifies that the logged-in user is correctly
// identified as Alice.
IN_PROC_BROWSER_TEST_F(SamlTest, UseAutenticatedUserEmailAddress) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
// Type |bob@example.com| into the GAIA login form.
StartSamlAndWaitForIdpPageLoad(kSecondSAMLUserEmail);
// Authenticate as alice@example.com via SAML (the |Email| provided here is
// irrelevant - the authenticated user's e-mail address that FakeGAIA
// reports was set via |SetFakeMergeSessionParams|.
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
content::WindowedNotificationObserver session_start_waiter(
chrome::NOTIFICATION_SESSION_STARTED,
content::NotificationService::AllSources());
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
session_start_waiter.Wait();
const user_manager::User* user =
user_manager::UserManager::Get()->GetActiveUser();
ASSERT_TRUE(user);
EXPECT_EQ(kFirstSAMLUserEmail, user->email());
}
// Verifies that if the authenticated user's e-mail address cannot be retrieved,
// an error message is shown.
IN_PROC_BROWSER_TEST_F(SamlTest, FailToRetrieveAutenticatedUserEmailAddress) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
fake_gaia_->SetFakeMergeSessionParams("", kTestAuthSIDCookie1,
kTestAuthLSIDCookie1);
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
EXPECT_EQ(l10n_util::GetStringUTF8(IDS_LOGIN_FATAL_ERROR_NO_ACCOUNT_DETAILS),
WaitForAndGetFatalErrorMessage());
}
// Tests the password confirm flow when more than one password is scraped: show
// error on the first failure and fatal error on the second failure.
IN_PROC_BROWSER_TEST_F(SamlTest, PasswordConfirmFlow) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login_two_passwords.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
// Fill-in the SAML IdP form and submit.
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
SetSignFormField("Password1", "password1");
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
// Lands on confirm password screen with no error message.
OobeScreenWaiter(OobeScreen::SCREEN_CONFIRM_PASSWORD).Wait();
JsExpect("!$('saml-confirm-password').$.passwordInput.isInvalid");
// Enter an unknown password for the first time should go back to confirm
// password screen with error message.
SendConfirmPassword("wrong_password");
OobeScreenWaiter(OobeScreen::SCREEN_CONFIRM_PASSWORD).Wait();
JsExpect("$('saml-confirm-password').$.passwordInput.isInvalid");
// Enter an unknown password 2nd time should go back fatal error message.
SendConfirmPassword("wrong_password");
EXPECT_EQ(
l10n_util::GetStringUTF8(IDS_LOGIN_FATAL_ERROR_PASSWORD_VERIFICATION),
WaitForAndGetFatalErrorMessage());
}
// Verifies that when the login flow redirects from one host to another, the
// notice shown to the user is updated. This guards against regressions of
// http://crbug.com/447818.
IN_PROC_BROWSER_TEST_F(SamlTest, NoticeUpdatedOnRedirect) {
// Start another https server at |kAdditionalIdPHost|.
HTTPSForwarder saml_https_forwarder_2;
ASSERT_TRUE(saml_https_forwarder_2.Initialize(
kAdditionalIdPHost, embedded_test_server()->base_url()));
// Make the login flow redirect to |kAdditionalIdPHost|.
fake_saml_idp()->SetLoginHTMLTemplate("saml_login_instant_meta_refresh.html");
fake_saml_idp()->SetRefreshURL(
saml_https_forwarder_2.GetURLForSSLHost("simple.html"));
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
// Wait until the notice shown to the user is updated to contain
// |kAdditionalIdPHost|.
std::string js =
"var sendIfHostFound = function() {"
" var found ="
" $('saml-notice-message').textContent.indexOf('$Host') > -1;"
" if (found)"
" window.domAutomationController.send(true);"
" return found;"
"};"
"var processEventsAndSendIfHostFound = function() {"
" window.setTimeout(function() {"
" if (sendIfHostFound()) {"
" $('gaia-signin').gaiaAuthHost_.removeEventListener("
" 'authDomainChange',"
" processEventsAndSendIfHostFound);"
" }"
" }, 0);"
"};"
"if (!sendIfHostFound()) {"
" $('gaia-signin').gaiaAuthHost_.addEventListener("
" 'authDomainChange',"
" processEventsAndSendIfHostFound);"
"}";
base::ReplaceSubstringsAfterOffset(&js, 0, "$Host", kAdditionalIdPHost);
bool dummy;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
GetLoginUI()->GetWebContents(), js, &dummy));
// Verify that the notice is visible.
JsExpect("!$('saml-notice-container').hidden");
}
// Verifies that when GAIA attempts to redirect to a SAML IdP served over http,
// not https, the redirect is blocked and an error message is shown.
IN_PROC_BROWSER_TEST_F(SamlTest, HTTPRedirectDisallowed) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
WaitForSigninScreen();
GetLoginDisplay()->ShowSigninScreenForCreds(kHTTPSAMLUserEmail, "");
const GURL url = embedded_test_server()->base_url().Resolve("/SAML");
EXPECT_EQ(l10n_util::GetStringFUTF8(
IDS_LOGIN_FATAL_ERROR_TEXT_INSECURE_URL,
base::UTF8ToUTF16(url.spec())),
WaitForAndGetFatalErrorMessage());
}
// Verifies that when GAIA attempts to redirect to a page served over http, not
// https, via an HTML meta refresh, the redirect is blocked and an error message
// is shown. This guards against regressions of http://crbug.com/359515.
IN_PROC_BROWSER_TEST_F(SamlTest, MetaRefreshToHTTPDisallowed) {
const GURL url = embedded_test_server()->base_url().Resolve("/SSO");
fake_saml_idp()->SetLoginHTMLTemplate("saml_login_instant_meta_refresh.html");
fake_saml_idp()->SetRefreshURL(url);
WaitForSigninScreen();
GetLoginDisplay()->ShowSigninScreenForCreds(kFirstSAMLUserEmail, "");
EXPECT_EQ(l10n_util::GetStringFUTF8(
IDS_LOGIN_FATAL_ERROR_TEXT_INSECURE_URL,
base::UTF8ToUTF16(url.spec())),
WaitForAndGetFatalErrorMessage());
}
class SAMLEnrollmentTest : public SamlTest,
public content::WebContentsObserver {
public:
SAMLEnrollmentTest();
~SAMLEnrollmentTest() override;
// SamlTest:
void SetUp() override;
void SetUpCommandLine(base::CommandLine* command_line) override;
void SetUpOnMainThread() override;
void StartSamlAndWaitForIdpPageLoad(const std::string& gaia_email) override;
// content::WebContentsObserver:
void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) override;
void WaitForEnrollmentSuccess();
guest_view::TestGuestViewManager* GetGuestViewManager();
content::WebContents* GetEnrollmentContents();
private:
std::unique_ptr<policy::LocalPolicyTestServer> test_server_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<base::RunLoop> run_loop_;
guest_view::TestGuestViewManagerFactory guest_view_manager_factory_;
DISALLOW_COPY_AND_ASSIGN(SAMLEnrollmentTest);
};
SAMLEnrollmentTest::SAMLEnrollmentTest() {
guest_view::GuestViewManager::set_factory_for_testing(
&guest_view_manager_factory_);
gaia_frame_parent_ = "oauth-enroll-auth-view";
}
SAMLEnrollmentTest::~SAMLEnrollmentTest() {
}
void SAMLEnrollmentTest::SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
const base::FilePath policy_file =
temp_dir_.path().AppendASCII("policy.json");
ASSERT_EQ(static_cast<int>(strlen(kPolicy)),
base::WriteFile(policy_file, kPolicy, strlen(kPolicy)));
test_server_.reset(new policy::LocalPolicyTestServer(policy_file));
ASSERT_TRUE(test_server_->Start());
SamlTest::SetUp();
}
void SAMLEnrollmentTest::SetUpCommandLine(base::CommandLine* command_line) {
command_line->AppendSwitchASCII(policy::switches::kDeviceManagementUrl,
test_server_->GetServiceURL().spec());
command_line->AppendSwitch(policy::switches::kDisablePolicyKeyVerification);
SamlTest::SetUpCommandLine(command_line);
}
void SAMLEnrollmentTest::SetUpOnMainThread() {
FakeGaia::AccessTokenInfo token_info;
token_info.token = kTestUserinfoToken;
token_info.scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth);
token_info.scopes.insert(GaiaConstants::kOAuthWrapBridgeUserInfoScope);
token_info.audience = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
token_info.email = kFirstSAMLUserEmail;
fake_gaia_->IssueOAuthToken(kTestRefreshToken, token_info);
SamlTest::SetUpOnMainThread();
}
void SAMLEnrollmentTest::StartSamlAndWaitForIdpPageLoad(
const std::string& gaia_email) {
WaitForSigninScreen();
run_loop_.reset(new base::RunLoop);
ExistingUserController::current_controller()->OnStartEnterpriseEnrollment();
while (!GetEnrollmentContents()) {
GetGuestViewManager()->WaitForNextGuestCreated();
}
Observe(GetEnrollmentContents());
run_loop_->Run();
SetSignFormField("identifier", gaia_email);
run_loop_.reset(new base::RunLoop);
ExecuteJsInSigninFrame("document.getElementById('nextButton').click();");
run_loop_->Run();
}
void SAMLEnrollmentTest::DidFinishLoad(
content::RenderFrameHost* render_frame_host,
const GURL& validated_url) {
const GURL origin = validated_url.GetOrigin();
if (origin != gaia_https_forwarder_.GetURLForSSLHost(std::string()) &&
origin != saml_https_forwarder_.GetURLForSSLHost(std::string())) {
return;
}
// The GAIA or SAML IdP login form finished loading.
if (run_loop_)
run_loop_->Quit();
}
// Waits until the class |oauth-enroll-state-success| becomes set for the
// enrollment screen, indicating enrollment success.
void SAMLEnrollmentTest::WaitForEnrollmentSuccess() {
bool done = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
GetLoginUI()->GetWebContents(),
"var enrollmentScreen = document.getElementById('oauth-enrollment');"
"function SendReplyIfEnrollmentDone() {"
" if (enrollmentScreen.classList.contains("
" 'oauth-enroll-state-attribute-prompt')) {"
" $('oauth-enroll-attribute-prompt-card').fire('submit');"
" return false;"
" }"
" if (!enrollmentScreen.classList.contains("
" 'oauth-enroll-state-success')) {"
" return false;"
" }"
" domAutomationController.send(true);"
" observer.disconnect();"
" return true;"
"}"
"var observer = new MutationObserver(SendReplyIfEnrollmentDone);"
"if (!SendReplyIfEnrollmentDone()) {"
" var options = { attributes: true, attributeFilter: [ 'class' ] };"
" observer.observe(enrollmentScreen, options);"
"}",
&done));
}
guest_view::TestGuestViewManager* SAMLEnrollmentTest::GetGuestViewManager() {
using namespace guest_view;
return static_cast<TestGuestViewManager*>(
TestGuestViewManager::FromBrowserContext(
ProfileHelper::GetSigninProfile()));
}
content::WebContents* SAMLEnrollmentTest::GetEnrollmentContents() {
content::RenderFrameHost* frame_host =
signin::GetAuthFrame(GetLoginUI()->GetWebContents(), gaia_frame_parent_);
if (!frame_host)
return nullptr;
return content::WebContents::FromRenderFrameHost(frame_host);
}
IN_PROC_BROWSER_TEST_F(SAMLEnrollmentTest, WithoutCredentialsPassingAPI) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
// Fill-in the SAML IdP form and submit.
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
WaitForEnrollmentSuccess();
}
IN_PROC_BROWSER_TEST_F(SAMLEnrollmentTest, WithCredentialsPassingAPI) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
fake_saml_idp()->SetLoginAuthHTMLTemplate("saml_api_login_auth.html");
StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
// Fill-in the SAML IdP form and submit.
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
WaitForEnrollmentSuccess();
}
class SAMLPolicyTest : public SamlTest {
public:
SAMLPolicyTest();
~SAMLPolicyTest() override;
// SamlTest:
void SetUpInProcessBrowserTestFixture() override;
void SetUpOnMainThread() override;
void SetSAMLOfflineSigninTimeLimitPolicy(int limit);
void EnableTransferSAMLCookiesPolicy();
void SetLoginBehaviorPolicyToSAMLInterstitial();
void ShowGAIALoginForm();
void ShowSAMLInterstitial();
void ClickNextOnSAMLInterstitialPage();
void ClickChangeAccountOnSAMLInterstitialPage();
void LogInWithSAML(const std::string& user_id,
const std::string& auth_sid_cookie,
const std::string& auth_lsid_cookie);
std::string GetCookieValue(const std::string& name);
void GetCookies();
protected:
void GetCookiesOnIOThread(
const scoped_refptr<net::URLRequestContextGetter>& request_context,
const base::Closure& callback);
void StoreCookieList(const base::Closure& callback,
const net::CookieList& cookie_list);
policy::DevicePolicyCrosTestHelper test_helper_;
// FakeDBusThreadManager uses FakeSessionManagerClient.
FakeSessionManagerClient* fake_session_manager_client_;
policy::DevicePolicyBuilder* device_policy_;
policy::MockConfigurationPolicyProvider provider_;
net::CookieList cookie_list_;
private:
DISALLOW_COPY_AND_ASSIGN(SAMLPolicyTest);
};
SAMLPolicyTest::SAMLPolicyTest()
: fake_session_manager_client_(new FakeSessionManagerClient),
device_policy_(test_helper_.device_policy()) {
}
SAMLPolicyTest::~SAMLPolicyTest() {
}
void SAMLPolicyTest::SetUpInProcessBrowserTestFixture() {
DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
std::unique_ptr<SessionManagerClient>(fake_session_manager_client_));
SamlTest::SetUpInProcessBrowserTestFixture();
// Initialize device policy.
std::set<std::string> device_affiliation_ids;
device_affiliation_ids.insert(kAffiliationID);
policy::affiliation_test_helper::SetDeviceAffiliationID(
&test_helper_, fake_session_manager_client_, device_affiliation_ids);
// Initialize user policy.
EXPECT_CALL(provider_, IsInitializationComplete(_))
.WillRepeatedly(Return(true));
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
}
void SAMLPolicyTest::SetUpOnMainThread() {
SamlTest::SetUpOnMainThread();
// Pretend that the test users' OAuth tokens are valid.
user_manager::UserManager::Get()->SaveUserOAuthStatus(
AccountId::FromUserEmail(kFirstSAMLUserEmail),
user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
user_manager::UserManager::Get()->SaveUserOAuthStatus(
AccountId::FromUserEmail(kNonSAMLUserEmail),
user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
user_manager::UserManager::Get()->SaveUserOAuthStatus(
AccountId::FromUserEmail(kDifferentDomainSAMLUserEmail),
user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
// Give affiliated users appropriate affiliation IDs.
std::set<std::string> user_affiliation_ids;
user_affiliation_ids.insert(kAffiliationID);
chromeos::ChromeUserManager::Get()->SetUserAffiliation(kFirstSAMLUserEmail,
user_affiliation_ids);
chromeos::ChromeUserManager::Get()->SetUserAffiliation(kSecondSAMLUserEmail,
user_affiliation_ids);
chromeos::ChromeUserManager::Get()->SetUserAffiliation(kHTTPSAMLUserEmail,
user_affiliation_ids);
chromeos::ChromeUserManager::Get()->SetUserAffiliation(kNonSAMLUserEmail,
user_affiliation_ids);
// Set up fake networks.
DBusThreadManager::Get()
->GetShillManagerClient()
->GetTestInterface()
->SetupDefaultEnvironment();
}
void SAMLPolicyTest::SetSAMLOfflineSigninTimeLimitPolicy(int limit) {
policy::PolicyMap user_policy;
user_policy.Set(policy::key::kSAMLOfflineSigninTimeLimit,
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::FundamentalValue(limit)), nullptr);
provider_.UpdateChromePolicy(user_policy);
base::RunLoop().RunUntilIdle();
}
void SAMLPolicyTest::EnableTransferSAMLCookiesPolicy() {
em::ChromeDeviceSettingsProto& proto(device_policy_->payload());
proto.mutable_saml_settings()->set_transfer_saml_cookies(true);
base::RunLoop run_loop;
std::unique_ptr<CrosSettings::ObserverSubscription> observer =
CrosSettings::Get()->AddSettingsObserver(kAccountsPrefTransferSAMLCookies,
run_loop.QuitClosure());
device_policy_->SetDefaultSigningKey();
device_policy_->Build();
fake_session_manager_client_->set_device_policy(device_policy_->GetBlob());
fake_session_manager_client_->OnPropertyChangeComplete(true);
run_loop.Run();
}
void SAMLPolicyTest::SetLoginBehaviorPolicyToSAMLInterstitial() {
em::ChromeDeviceSettingsProto& proto(device_policy_->payload());
proto.mutable_login_authentication_behavior()
->set_login_authentication_behavior(
em::LoginAuthenticationBehaviorProto_LoginBehavior_SAML_INTERSTITIAL);
base::RunLoop run_loop;
std::unique_ptr<CrosSettings::ObserverSubscription> observer =
CrosSettings::Get()->AddSettingsObserver(kLoginAuthenticationBehavior,
run_loop.QuitClosure());
device_policy_->SetDefaultSigningKey();
device_policy_->Build();
fake_session_manager_client_->set_device_policy(device_policy_->GetBlob());
fake_session_manager_client_->OnPropertyChangeComplete(true);
run_loop.Run();
}
void SAMLPolicyTest::ShowGAIALoginForm() {
login_screen_load_observer_->Wait();
ASSERT_TRUE(content::ExecuteScript(
GetLoginUI()->GetWebContents(),
"$('gaia-signin').gaiaAuthHost_.addEventListener('ready', function() {"
" window.domAutomationController.setAutomationId(0);"
" window.domAutomationController.send('ready');"
"});"
"$('add-user-button').click();"));
content::DOMMessageQueue message_queue;
std::string message;
ASSERT_TRUE(message_queue.WaitForMessage(&message));
EXPECT_EQ("\"ready\"", message);
}
void SAMLPolicyTest::ShowSAMLInterstitial() {
login_screen_load_observer_->Wait();
ASSERT_TRUE(content::ExecuteScript(
GetLoginUI()->GetWebContents(),
"$('saml-interstitial').addEventListener("
" 'samlInterstitialPageReady',"
" function() {"
" window.domAutomationController.setAutomationId(0);"
" window.domAutomationController.send("
" 'samlInterstitialPageReady');"
" });"
"$('add-user-button').click();"));
content::DOMMessageQueue message_queue;
std::string message;
ASSERT_TRUE(message_queue.WaitForMessage(&message));
EXPECT_EQ("\"samlInterstitialPageReady\"", message);
}
void SAMLPolicyTest::ClickNextOnSAMLInterstitialPage() {
login_screen_load_observer_->Wait();
content::DOMMessageQueue message_queue;
SetupAuthFlowChangeListener();
ASSERT_TRUE(content::ExecuteScript(GetLoginUI()->GetWebContents(),
"$('saml-interstitial').submit();"));
std::string message;
do {
ASSERT_TRUE(message_queue.WaitForMessage(&message));
} while (message != "\"SamlLoaded\"");
EXPECT_EQ("\"SamlLoaded\"", message);
}
void SAMLPolicyTest::ClickChangeAccountOnSAMLInterstitialPage() {
login_screen_load_observer_->Wait();
ASSERT_TRUE(content::ExecuteScript(
GetLoginUI()->GetWebContents(),
"$('gaia-signin').gaiaAuthHost_.addEventListener('ready', function() {"
" window.domAutomationController.setAutomationId(0);"
" window.domAutomationController.send('ready');"
"});"
"$('saml-interstitial').changeAccountLink.click();"));
content::DOMMessageQueue message_queue;
std::string message;
ASSERT_TRUE(message_queue.WaitForMessage(&message));
EXPECT_EQ("\"ready\"", message);
}
void SAMLPolicyTest::LogInWithSAML(const std::string& user_id,
const std::string& auth_sid_cookie,
const std::string& auth_lsid_cookie) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
StartSamlAndWaitForIdpPageLoad(user_id);
fake_gaia_->SetFakeMergeSessionParams(user_id, auth_sid_cookie,
auth_lsid_cookie);
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
// Scraping a single password should finish the login right away.
content::WindowedNotificationObserver session_start_waiter(
chrome::NOTIFICATION_SESSION_STARTED,
content::NotificationService::AllSources());
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
session_start_waiter.Wait();
}
std::string SAMLPolicyTest::GetCookieValue(const std::string& name) {
for (net::CookieList::const_iterator it = cookie_list_.begin();
it != cookie_list_.end(); ++it) {
if (it->Name() == name)
return it->Value();
}
return std::string();
}
void SAMLPolicyTest::GetCookies() {
Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUserUnsafe(
user_manager::UserManager::Get()->GetActiveUser());
ASSERT_TRUE(profile);
base::RunLoop run_loop;
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&SAMLPolicyTest::GetCookiesOnIOThread,
base::Unretained(this),
scoped_refptr<net::URLRequestContextGetter>(
profile->GetRequestContext()),
run_loop.QuitClosure()));
run_loop.Run();
}
void SAMLPolicyTest::GetCookiesOnIOThread(
const scoped_refptr<net::URLRequestContextGetter>& request_context,
const base::Closure& callback) {
request_context->GetURLRequestContext()->cookie_store()->GetAllCookiesAsync(
base::Bind(&SAMLPolicyTest::StoreCookieList, base::Unretained(this),
callback));
}
void SAMLPolicyTest::StoreCookieList(
const base::Closure& callback,
const net::CookieList& cookie_list) {
cookie_list_ = cookie_list;
content::BrowserThread::PostTask(content::BrowserThread::UI,
FROM_HERE,
callback);
}
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_NoSAML) {
// Set the offline login time limit for SAML users to zero.
SetSAMLOfflineSigninTimeLimitPolicy(0);
WaitForSigninScreen();
// Log in without SAML.
GetLoginDisplay()->ShowSigninScreenForCreds(kNonSAMLUserEmail, "password");
content::WindowedNotificationObserver(
chrome::NOTIFICATION_SESSION_STARTED,
content::NotificationService::AllSources()).Wait();
}
// Verifies that the offline login time limit does not affect a user who
// authenticated without SAML.
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, NoSAML) {
login_screen_load_observer_->Wait();
// Verify that offline login is allowed.
JsExpect(
"window.getComputedStyle(document.querySelector("
" '#pod-row .reauth-hint-container')).display == 'none'");
}
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLNoLimit) {
// Remove the offline login time limit for SAML users.
SetSAMLOfflineSigninTimeLimitPolicy(-1);
LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie1, kTestAuthLSIDCookie1);
}
// Verifies that when no offline login time limit is set, a user who
// authenticated with SAML is allowed to log in offline.
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLNoLimit) {
login_screen_load_observer_->Wait();
// Verify that offline login is allowed.
JsExpect(
"window.getComputedStyle(document.querySelector("
" '#pod-row .reauth-hint-container')).display == 'none'");
}
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLZeroLimit) {
// Set the offline login time limit for SAML users to zero.
SetSAMLOfflineSigninTimeLimitPolicy(0);
LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie1, kTestAuthLSIDCookie1);
}
// Verifies that when the offline login time limit is exceeded for a user who
// authenticated via SAML, that user is forced to log in online the next time.
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLZeroLimit) {
login_screen_load_observer_->Wait();
// Verify that offline login is not allowed.
JsExpect(
"window.getComputedStyle(document.querySelector("
" '#pod-row .reauth-hint-container')).display != 'none'");
}
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_PRE_TransferCookiesAffiliated) {
fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue1);
LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie1, kTestAuthLSIDCookie1);
GetCookies();
EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
EXPECT_EQ(kSAMLIdPCookieValue1, GetCookieValue(kSAMLIdPCookieName));
}
// Verifies that when the DeviceTransferSAMLCookies policy is not enabled, SAML
// IdP cookies are not transferred to a user's profile on subsequent login, even
// if the user belongs to the domain that the device is enrolled into. Also
// verifies that GAIA cookies are not transferred.
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_TransferCookiesAffiliated) {
fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue2);
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
ShowGAIALoginForm();
LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie2, kTestAuthLSIDCookie2);
GetCookies();
EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
EXPECT_EQ(kSAMLIdPCookieValue1, GetCookieValue(kSAMLIdPCookieName));
}
// Verifies that when the DeviceTransferSAMLCookies policy is enabled, SAML IdP
// cookies are transferred to a user's profile on subsequent login when the user
// belongs to the domain that the device is enrolled into. Also verifies that
// GAIA cookies are not transferred.
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, TransferCookiesAffiliated) {
fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue2);
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
ShowGAIALoginForm();
EnableTransferSAMLCookiesPolicy();
LogInWithSAML(kFirstSAMLUserEmail, kTestAuthSIDCookie2, kTestAuthLSIDCookie2);
GetCookies();
EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
EXPECT_EQ(kSAMLIdPCookieValue2, GetCookieValue(kSAMLIdPCookieName));
}
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_TransferCookiesUnaffiliated) {
fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue1);
LogInWithSAML(kDifferentDomainSAMLUserEmail,
kTestAuthSIDCookie1,
kTestAuthLSIDCookie1);
GetCookies();
EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
EXPECT_EQ(kSAMLIdPCookieValue1, GetCookieValue(kSAMLIdPCookieName));
}
// Verifies that even if the DeviceTransferSAMLCookies policy is enabled, SAML
// IdP are not transferred to a user's profile on subsequent login if the user
// does not belong to the domain that the device is enrolled into. Also verifies
// that GAIA cookies are not transferred.
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, TransferCookiesUnaffiliated) {
fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue2);
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
ShowGAIALoginForm();
EnableTransferSAMLCookiesPolicy();
LogInWithSAML(kDifferentDomainSAMLUserEmail,
kTestAuthSIDCookie1,
kTestAuthLSIDCookie1);
GetCookies();
EXPECT_EQ(kTestAuthSIDCookie1, GetCookieValue(kGAIASIDCookieName));
EXPECT_EQ(kTestAuthLSIDCookie1, GetCookieValue(kGAIALSIDCookieName));
EXPECT_EQ(kSAMLIdPCookieValue1, GetCookieValue(kSAMLIdPCookieName));
}
// Tests that the SAML interstitial page is loaded when the authentication
// behavior device policy is set to SAML_INTERSTITIAL, and when the user clicks
// the "change account" link, we go back to the default GAIA signin screen.
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLInterstitialChangeAccount) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
SetLoginBehaviorPolicyToSAMLInterstitial();
WaitForSigninScreen();
ShowSAMLInterstitial();
JsExpect("$('signin-frame').hidden == true");
JsExpect("$('offline-gaia').hidden == true");
JsExpect("$('saml-interstitial').hidden == false");
// Click the "change account" link on the SAML interstitial page.
ClickChangeAccountOnSAMLInterstitialPage();
// Expects that only the gaia signin frame is visible and shown.
JsExpect("$('signin-frame').classList.contains('show')");
JsExpect("$('signin-frame').hidden == false");
JsExpect("$('offline-gaia').hidden == true");
JsExpect("$('saml-interstitial').hidden == true");
}
// Tests that clicking "Next" in the SAML interstitial page successfully
// triggers a SAML redirect request, and the SAML IdP authentication page is
// loaded and authenticaing there is successful.
IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLInterstitialNext) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
fake_gaia_->SetFakeMergeSessionParams(
kFirstSAMLUserEmail, kTestAuthSIDCookie1, kTestAuthLSIDCookie1);
SetLoginBehaviorPolicyToSAMLInterstitial();
WaitForSigninScreen();
ShowSAMLInterstitial();
ClickNextOnSAMLInterstitialPage();
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
// Scraping one password should finish login.
content::WindowedNotificationObserver session_start_waiter(
chrome::NOTIFICATION_SESSION_STARTED,
content::NotificationService::AllSources());
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
session_start_waiter.Wait();
}
} // namespace chromeos