blob: 4f0903ea74d9b6b00334fa6ab4dee3324d40fda2 [file] [log] [blame]
// Copyright (c) 2013 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 <map>
#include <set>
#include <string>
#include <vector>
#include "ash/shell.h"
#include "ash/system/chromeos/session/logout_confirmation_controller.h"
#include "ash/system/chromeos/session/logout_confirmation_dialog.h"
#include "base/basictypes.h"
#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/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/path_service.h"
#include "base/prefs/pref_change_registrar.h"
#include "base/prefs/pref_service.h"
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h"
#include "chrome/browser/chromeos/extensions/external_cache.h"
#include "chrome/browser/chromeos/input_method/input_method_util.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/chromeos/login/screens/base_screen.h"
#include "chrome/browser/chromeos/login/signin_specifics.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
#include "chrome/browser/chromeos/login/ui/webui_login_view.h"
#include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h"
#include "chrome/browser/chromeos/login/users/avatar/user_image_manager_impl.h"
#include "chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager_impl.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/cloud_external_data_manager_base_test_util.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/chromeos/policy/device_local_account_policy_service.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/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/policy/profile_policy_connector_factory.h"
#include "chrome/browser/policy/test/local_policy_test_server.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/host_desktop.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/chromeos_paths.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "chromeos/ime/extension_ime_util.h"
#include "chromeos/ime/input_method_descriptor.h"
#include "chromeos/ime/input_method_manager.h"
#include "chromeos/login/auth/mock_auth_status_consumer.h"
#include "chromeos/login/auth/user_context.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/cloud_policy_core.h"
#include "components/policy/core/common/cloud/cloud_policy_store.h"
#include "components/policy/core/common/cloud/policy_builder.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/core/common/policy_switches.h"
#include "components/signin/core/common/signin_pref_names.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_type.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "crypto/rsa_private_key.h"
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/browser/app_window/native_app_window.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/management_policy.h"
#include "extensions/browser/notification_types.h"
#include "extensions/common/extension.h"
#include "net/base/url_util.h"
#include "net/http/http_status_code.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/test_url_fetcher_factory.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_status.h"
#include "policy/policy_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/icu/source/common/unicode/locid.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/window_open_disposition.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/widget/widget.h"
#include "url/gurl.h"
namespace em = enterprise_management;
using chromeos::LoginScreenContext;
using testing::InvokeWithoutArgs;
using testing::Return;
using testing::_;
namespace policy {
namespace {
const char kDomain[] = "example.com";
const char kAccountId1[] = "dla1@example.com";
const char kAccountId2[] = "dla2@example.com";
const char kDisplayName1[] = "display name 1";
const char kDisplayName2[] = "display name 2";
const char* const kStartupURLs[] = {
"chrome://policy",
"chrome://about",
};
const char kExistentTermsOfServicePath[] = "chromeos/enterprise/tos.txt";
const char kNonexistentTermsOfServicePath[] = "chromeos/enterprise/tos404.txt";
const char kRelativeUpdateURL[] = "/service/update2/crx";
const char kUpdateManifestHeader[] =
"<?xml version='1.0' encoding='UTF-8'?>\n"
"<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>\n";
const char kUpdateManifestTemplate[] =
" <app appid='%s'>\n"
" <updatecheck codebase='%s' version='%s' />\n"
" </app>\n";
const char kUpdateManifestFooter[] =
"</gupdate>\n";
const char kHostedAppID[] = "kbmnembihfiondgfjekmnmcbddelicoi";
const char kHostedAppCRXPath[] = "extensions/hosted_app.crx";
const char kHostedAppVersion[] = "1.0.0.0";
const char kGoodExtensionID[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
const char kGoodExtensionCRXPath[] = "extensions/good.crx";
const char kGoodExtensionVersion[] = "1.0";
const char kPackagedAppCRXPath[] = "extensions/platform_apps/app_window_2.crx";
const char kShowManagedStorageID[] = "ongnjlefhnoajpbodoldndkbkdgfomlp";
const char kShowManagedStorageCRXPath[] = "extensions/show_managed_storage.crx";
const char kShowManagedStorageVersion[] = "1.0";
const char kExternalData[] = "External data";
const char kExternalDataURL[] = "http://localhost/external_data";
const char* const kSingleRecommendedLocale[] = {
"el",
};
const char* const kRecommendedLocales1[] = {
"pl",
"et",
"en-US",
};
const char* const kRecommendedLocales2[] = {
"fr",
"nl",
};
const char* const kInvalidRecommendedLocale[] = {
"xx",
};
const char kPublicSessionLocale[] = "de";
const char kPublicSessionInputMethodIDTemplate[] = "_comp_ime_%sxkb:de:neo:ger";
// The sequence token used by GetKeyboardLayoutsForLocale() for its background
// tasks.
const char kSequenceToken[] = "chromeos_login_l10n_util";
// Helper that serves extension update manifests to Chrome.
class TestingUpdateManifestProvider {
public:
// Update manifests will be served at |relative_update_url|.
explicit TestingUpdateManifestProvider(
const std::string& relative_update_url);
~TestingUpdateManifestProvider();
// When an update manifest is requested for the given extension |id|, indicate
// that |version| of the extension can be downloaded at |crx_url|.
void AddUpdate(const std::string& id,
const std::string& version,
const GURL& crx_url);
// This method must be registered with the test's EmbeddedTestServer to start
// serving update manifests.
scoped_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request);
private:
struct Update {
public:
Update(const std::string& version, const GURL& crx_url);
Update();
std::string version;
GURL crx_url;
};
typedef std::map<std::string, Update> UpdateMap;
UpdateMap updates_;
const std::string relative_update_url_;
DISALLOW_COPY_AND_ASSIGN(TestingUpdateManifestProvider);
};
// Helper that observes the dictionary |pref| in local state and waits until the
// value stored for |key| matches |expected_value|.
class DictionaryPrefValueWaiter {
public:
DictionaryPrefValueWaiter(const std::string& pref,
const std::string& key,
const std::string& expected_value);
~DictionaryPrefValueWaiter();
void Wait();
private:
void QuitLoopIfExpectedValueFound();
const std::string pref_;
const std::string key_;
const std::string expected_value_;
base::RunLoop run_loop_;
PrefChangeRegistrar pref_change_registrar_;
DISALLOW_COPY_AND_ASSIGN(DictionaryPrefValueWaiter);
};
TestingUpdateManifestProvider::Update::Update(const std::string& version,
const GURL& crx_url)
: version(version),
crx_url(crx_url) {
}
TestingUpdateManifestProvider::Update::Update() {
}
TestingUpdateManifestProvider::TestingUpdateManifestProvider(
const std::string& relative_update_url)
: relative_update_url_(relative_update_url) {
}
TestingUpdateManifestProvider::~TestingUpdateManifestProvider() {
}
void TestingUpdateManifestProvider::AddUpdate(const std::string& id,
const std::string& version,
const GURL& crx_url) {
updates_[id] = Update(version, crx_url);
}
scoped_ptr<net::test_server::HttpResponse>
TestingUpdateManifestProvider::HandleRequest(
const net::test_server::HttpRequest& request) {
const GURL url("http://localhost" + request.relative_url);
if (url.path() != relative_update_url_)
return scoped_ptr<net::test_server::HttpResponse>();
std::string content = kUpdateManifestHeader;
for (net::QueryIterator it(url); !it.IsAtEnd(); it.Advance()) {
if (it.GetKey() != "x")
continue;
// Extract the extension id from the subquery. Since GetValueForKeyInQuery()
// expects a complete URL, dummy scheme and host must be prepended.
std::string id;
net::GetValueForKeyInQuery(GURL("http://dummy?" + it.GetUnescapedValue()),
"id", &id);
UpdateMap::const_iterator entry = updates_.find(id);
if (entry != updates_.end()) {
content += base::StringPrintf(kUpdateManifestTemplate,
id.c_str(),
entry->second.crx_url.spec().c_str(),
entry->second.version.c_str());
}
}
content += kUpdateManifestFooter;
scoped_ptr<net::test_server::BasicHttpResponse>
http_response(new net::test_server::BasicHttpResponse);
http_response->set_code(net::HTTP_OK);
http_response->set_content(content);
http_response->set_content_type("text/xml");
return http_response.Pass();
}
DictionaryPrefValueWaiter::DictionaryPrefValueWaiter(
const std::string& pref,
const std::string& key,
const std::string& expected_value)
: pref_(pref),
key_(key),
expected_value_(expected_value) {
pref_change_registrar_.Init(g_browser_process->local_state());
}
DictionaryPrefValueWaiter::~DictionaryPrefValueWaiter() {
}
void DictionaryPrefValueWaiter::Wait() {
pref_change_registrar_.Add(
pref_.c_str(),
base::Bind(&DictionaryPrefValueWaiter::QuitLoopIfExpectedValueFound,
base::Unretained(this)));
QuitLoopIfExpectedValueFound();
run_loop_.Run();
}
void DictionaryPrefValueWaiter::QuitLoopIfExpectedValueFound() {
const base::DictionaryValue* pref =
pref_change_registrar_.prefs()->GetDictionary(pref_.c_str());
ASSERT_TRUE(pref);
std::string actual_value;
if (pref->GetStringWithoutPathExpansion(key_, &actual_value) &&
actual_value == expected_value_) {
run_loop_.Quit();
}
}
bool DoesInstallSuccessReferToId(const std::string& id,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
return content::Details<const extensions::InstalledExtensionInfo>(details)->
extension->id() == id;
}
bool DoesInstallFailureReferToId(const std::string& id,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
return content::Details<const base::string16>(details)->
find(base::UTF8ToUTF16(id)) != base::string16::npos;
}
scoped_ptr<net::FakeURLFetcher> RunCallbackAndReturnFakeURLFetcher(
scoped_refptr<base::SequencedTaskRunner> task_runner,
const base::Closure& callback,
const GURL& url,
net::URLFetcherDelegate* delegate,
const std::string& response_data,
net::HttpStatusCode response_code,
net::URLRequestStatus::Status status) {
task_runner->PostTask(FROM_HERE, callback);
return make_scoped_ptr(new net::FakeURLFetcher(
url, delegate, response_data, response_code, status));
}
bool IsSessionStarted() {
return user_manager::UserManager::Get()->IsSessionStarted();
}
void PolicyChangedCallback(const base::Closure& callback,
const base::Value* old_value,
const base::Value* new_value) {
callback.Run();
}
} // namespace
class DeviceLocalAccountTest : public DevicePolicyCrosBrowserTest,
public user_manager::UserManager::Observer,
public chrome::BrowserListObserver,
public extensions::AppWindowRegistry::Observer {
protected:
DeviceLocalAccountTest()
: user_id_1_(GenerateDeviceLocalAccountUserId(
kAccountId1, DeviceLocalAccount::TYPE_PUBLIC_SESSION)),
user_id_2_(GenerateDeviceLocalAccountUserId(
kAccountId2, DeviceLocalAccount::TYPE_PUBLIC_SESSION)),
public_session_input_method_id_(base::StringPrintf(
kPublicSessionInputMethodIDTemplate,
chromeos::extension_ime_util::kXkbExtensionId)),
contents_(NULL) {
set_exit_when_last_browser_closes(false);
}
virtual ~DeviceLocalAccountTest() {}
virtual void SetUp() override {
// Configure and start the test server.
scoped_ptr<crypto::RSAPrivateKey> signing_key(
PolicyBuilder::CreateTestSigningKey());
ASSERT_TRUE(test_server_.SetSigningKeyAndSignature(
signing_key.get(), PolicyBuilder::GetTestSigningKeySignature()));
signing_key.reset();
test_server_.RegisterClient(PolicyBuilder::kFakeToken,
PolicyBuilder::kFakeDeviceId);
ASSERT_TRUE(test_server_.Start());
BrowserList::AddObserver(this);
DevicePolicyCrosBrowserTest::SetUp();
}
virtual void SetUpCommandLine(CommandLine* command_line) override {
DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(chromeos::switches::kLoginManager);
command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
command_line->AppendSwitchASCII(policy::switches::kDeviceManagementUrl,
test_server_.GetServiceURL().spec());
}
virtual void SetUpInProcessBrowserTestFixture() override {
DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
// Clear command-line arguments (but keep command-line switches) so the
// startup pages policy takes effect.
CommandLine* command_line = CommandLine::ForCurrentProcess();
CommandLine::StringVector argv(command_line->argv());
argv.erase(argv.begin() + argv.size() - command_line->GetArgs().size(),
argv.end());
command_line->InitFromArgv(argv);
InstallOwnerKey();
MarkAsEnterpriseOwned();
InitializePolicy();
}
virtual void SetUpOnMainThread() override {
DevicePolicyCrosBrowserTest::SetUpOnMainThread();
initial_locale_ = g_browser_process->GetApplicationLocale();
initial_language_ = l10n_util::GetLanguage(initial_locale_);
content::WindowedNotificationObserver(
chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
content::NotificationService::AllSources()).Wait();
chromeos::LoginDisplayHostImpl* host =
reinterpret_cast<chromeos::LoginDisplayHostImpl*>(
chromeos::LoginDisplayHostImpl::default_host());
ASSERT_TRUE(host);
chromeos::WebUILoginView* web_ui_login_view = host->GetWebUILoginView();
ASSERT_TRUE(web_ui_login_view);
content::WebUI* web_ui = web_ui_login_view->GetWebUI();
ASSERT_TRUE(web_ui);
contents_ = web_ui->GetWebContents();
ASSERT_TRUE(contents_);
// Wait for the login UI to be ready.
chromeos::OobeUI* oobe_ui = host->GetOobeUI();
ASSERT_TRUE(oobe_ui);
base::RunLoop run_loop;
const bool oobe_ui_ready = oobe_ui->IsJSReady(run_loop.QuitClosure());
if (!oobe_ui_ready)
run_loop.Run();
// The network selection screen changes the application locale on load and
// once again on blur. Wait for the screen to load and blur it so that any
// locale changes caused by this screen happen now and do not affect any
// subsequent parts of the test.
bool done = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
contents_,
"var languageSelect = document.getElementById('language-select');"
"var blurAndReportSuccess = function() {"
" languageSelect.blur();"
" domAutomationController.send(true);"
"};"
"var screenLoading = document.getElementById('outer-container')"
" .classList.contains('down');"
"if (document.activeElement == languageSelect || !screenLoading)"
" blurAndReportSuccess();"
"else"
" languageSelect.addEventListener('focus', blurAndReportSuccess);",
&done));
// Skip to the login screen.
chromeos::WizardController* wizard_controller =
chromeos::WizardController::default_controller();
ASSERT_TRUE(wizard_controller);
wizard_controller->SkipToLoginForTesting(LoginScreenContext());
}
virtual void TearDownOnMainThread() override {
BrowserList::RemoveObserver(this);
// This shuts down the login UI.
base::MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(&chrome::AttemptExit));
base::RunLoop().RunUntilIdle();
}
virtual void LocalStateChanged(
user_manager::UserManager* user_manager) override {
if (run_loop_)
run_loop_->Quit();
}
virtual void OnBrowserRemoved(Browser* browser) override {
if (run_loop_)
run_loop_->Quit();
}
virtual void OnAppWindowAdded(extensions::AppWindow* app_window) override {
if (run_loop_)
run_loop_->Quit();
}
virtual void OnAppWindowRemoved(extensions::AppWindow* app_window) override {
if (run_loop_)
run_loop_->Quit();
}
void InitializePolicy() {
device_policy()->policy_data().set_public_key_version(1);
em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
proto.mutable_show_user_names()->set_show_user_names(true);
device_local_account_policy_.policy_data().set_policy_type(
dm_protocol::kChromePublicAccountPolicyType);
device_local_account_policy_.policy_data().set_username(kAccountId1);
device_local_account_policy_.policy_data().set_settings_entity_id(
kAccountId1);
device_local_account_policy_.policy_data().set_public_key_version(1);
device_local_account_policy_.payload().mutable_userdisplayname()->set_value(
kDisplayName1);
}
void BuildDeviceLocalAccountPolicy() {
device_local_account_policy_.SetDefaultSigningKey();
device_local_account_policy_.Build();
}
void UploadDeviceLocalAccountPolicy() {
BuildDeviceLocalAccountPolicy();
test_server_.UpdatePolicy(
dm_protocol::kChromePublicAccountPolicyType, kAccountId1,
device_local_account_policy_.payload().SerializeAsString());
}
void UploadAndInstallDeviceLocalAccountPolicy() {
UploadDeviceLocalAccountPolicy();
session_manager_client()->set_device_local_account_policy(
kAccountId1, device_local_account_policy_.GetBlob());
}
void SetRecommendedLocales(const char* const recommended_locales[],
size_t array_size) {
em::StringListPolicyProto* session_locales_proto =
device_local_account_policy_.payload().mutable_sessionlocales();
session_locales_proto->mutable_policy_options()->set_mode(
em::PolicyOptions_PolicyMode_RECOMMENDED);
session_locales_proto->mutable_value()->Clear();
for (size_t i = 0; i < array_size; ++i) {
session_locales_proto->mutable_value()->add_entries(
recommended_locales[i]);
}
}
void AddPublicSessionToDevicePolicy(const std::string& username) {
em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
em::DeviceLocalAccountInfoProto* account =
proto.mutable_device_local_accounts()->add_account();
account->set_account_id(username);
account->set_type(
em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION);
RefreshDevicePolicy();
test_server_.UpdatePolicy(dm_protocol::kChromeDevicePolicyType,
std::string(), proto.SerializeAsString());
}
void EnableAutoLogin() {
em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
em::DeviceLocalAccountsProto* device_local_accounts =
proto.mutable_device_local_accounts();
device_local_accounts->set_auto_login_id(kAccountId1);
device_local_accounts->set_auto_login_delay(0);
RefreshDevicePolicy();
test_server_.UpdatePolicy(dm_protocol::kChromeDevicePolicyType,
std::string(), proto.SerializeAsString());
}
void CheckPublicSessionPresent(const std::string& id) {
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(id);
ASSERT_TRUE(user);
EXPECT_EQ(id, user->email());
EXPECT_EQ(user_manager::USER_TYPE_PUBLIC_ACCOUNT, user->GetType());
}
base::FilePath GetExtensionCacheDirectoryForAccountID(
const std::string& account_id) {
base::FilePath extension_cache_root_dir;
if (!PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS,
&extension_cache_root_dir)) {
ADD_FAILURE();
}
return extension_cache_root_dir.Append(
base::HexEncode(account_id.c_str(), account_id.size()));
}
base::FilePath GetCacheCRXFile(const std::string& account_id,
const std::string& id,
const std::string& version) {
return GetExtensionCacheDirectoryForAccountID(account_id)
.Append(base::StringPrintf("%s-%s.crx", id.c_str(), version.c_str()));
}
// Returns a profile which can be used for testing.
Profile* GetProfileForTest() {
// Any profile can be used here since this test does not test multi profile.
return ProfileManager::GetActiveUserProfile();
}
void WaitForDisplayName(const std::string& user_id,
const std::string& expected_display_name) {
DictionaryPrefValueWaiter("UserDisplayName",
user_id,
expected_display_name).Wait();
}
void WaitForPolicy() {
// Wait for the display name becoming available as that indicates
// device-local account policy is fully loaded, which is a prerequisite for
// successful login.
WaitForDisplayName(user_id_1_, kDisplayName1);
}
void ExpandPublicSessionPod(bool expect_advanced) {
bool advanced = false;
// Click on the pod to expand it.
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
contents_,
base::StringPrintf(
"var pod ="
" document.getElementById('pod-row').getPodWithUsername_('%s');"
"pod.click();"
"domAutomationController.send(pod.classList.contains('advanced'));",
user_id_1_.c_str()),
&advanced));
// Verify that the pod expanded to its basic/advanced form, as expected.
EXPECT_EQ(expect_advanced, advanced);
// Verify that the construction of the pod's language list did not affect
// the current ICU locale.
EXPECT_EQ(initial_language_, icu::Locale::getDefault().getLanguage());
}
// GetKeyboardLayoutsForLocale() posts a task to a background task runner.
// This method flushes that task runner and the current thread's message loop
// to ensure that GetKeyboardLayoutsForLocale() is finished.
void WaitForGetKeyboardLayoutsForLocaleToFinish() {
base::SequencedWorkerPool* worker_pool =
content::BrowserThread::GetBlockingPool();
scoped_refptr<base::SequencedTaskRunner> background_task_runner =
worker_pool->GetSequencedTaskRunner(
worker_pool->GetNamedSequenceToken(kSequenceToken));
base::RunLoop run_loop;
background_task_runner->PostTaskAndReply(FROM_HERE,
base::Bind(&base::DoNothing),
run_loop.QuitClosure());
run_loop.Run();
base::RunLoop().RunUntilIdle();
// Verify that the construction of the keyboard layout list did not affect
// the current ICU locale.
EXPECT_EQ(initial_language_, icu::Locale::getDefault().getLanguage());
}
void StartLogin(const std::string& locale,
const std::string& input_method) {
// Start login into the device-local account.
chromeos::LoginDisplayHostImpl* host =
reinterpret_cast<chromeos::LoginDisplayHostImpl*>(
chromeos::LoginDisplayHostImpl::default_host());
ASSERT_TRUE(host);
host->StartSignInScreen(LoginScreenContext());
chromeos::ExistingUserController* controller =
chromeos::ExistingUserController::current_controller();
ASSERT_TRUE(controller);
chromeos::UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
user_id_1_);
user_context.SetPublicSessionLocale(locale);
user_context.SetPublicSessionInputMethod(input_method);
controller->Login(user_context, chromeos::SigninSpecifics());
}
void WaitForSessionStart() {
if (IsSessionStarted())
return;
content::WindowedNotificationObserver(chrome::NOTIFICATION_SESSION_STARTED,
base::Bind(IsSessionStarted)).Wait();
}
void VerifyKeyboardLayoutMatchesLocale() {
chromeos::input_method::InputMethodManager* input_method_manager =
chromeos::input_method::InputMethodManager::Get();
std::vector<std::string> layouts_from_locale;
input_method_manager->GetInputMethodUtil()->
GetInputMethodIdsFromLanguageCode(
g_browser_process->GetApplicationLocale(),
chromeos::input_method::kKeyboardLayoutsOnly,
&layouts_from_locale);
ASSERT_FALSE(layouts_from_locale.empty());
EXPECT_EQ(layouts_from_locale.front(),
input_method_manager->GetActiveIMEState()
->GetCurrentInputMethod()
.id());
}
const std::string user_id_1_;
const std::string user_id_2_;
const std::string public_session_input_method_id_;
std::string initial_locale_;
std::string initial_language_;
scoped_ptr<base::RunLoop> run_loop_;
UserPolicyBuilder device_local_account_policy_;
LocalPolicyTestServer test_server_;
content::WebContents* contents_;
private:
DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountTest);
};
static bool IsKnownUser(const std::string& account_id) {
return user_manager::UserManager::Get()->IsKnownUser(account_id);
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, LoginScreen) {
AddPublicSessionToDevicePolicy(kAccountId1);
AddPublicSessionToDevicePolicy(kAccountId2);
content::WindowedNotificationObserver(chrome::NOTIFICATION_USER_LIST_CHANGED,
base::Bind(&IsKnownUser, user_id_1_))
.Wait();
EXPECT_TRUE(IsKnownUser(user_id_2_));
CheckPublicSessionPresent(user_id_1_);
CheckPublicSessionPresent(user_id_2_);
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, DisplayName) {
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
// Verify that the display name is shown in the UI.
const std::string get_compact_pod_display_name = base::StringPrintf(
"domAutomationController.send(document.getElementById('pod-row')"
" .getPodWithUsername_('%s').nameElement.textContent);",
user_id_1_.c_str());
std::string display_name;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
contents_,
get_compact_pod_display_name,
&display_name));
EXPECT_EQ(kDisplayName1, display_name);
const std::string get_expanded_pod_display_name = base::StringPrintf(
"domAutomationController.send(document.getElementById('pod-row')"
" .getPodWithUsername_('%s').querySelector('.expanded-pane-name')"
" .textContent);",
user_id_1_.c_str());
display_name.clear();
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
contents_,
get_expanded_pod_display_name,
&display_name));
EXPECT_EQ(kDisplayName1, display_name);
// Click on the pod to expand it.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"document.getElementById('pod-row').getPodWithUsername_('%s')"
" .click();",
user_id_1_.c_str())));
// Change the display name.
device_local_account_policy_.payload().mutable_userdisplayname()->set_value(
kDisplayName2);
UploadAndInstallDeviceLocalAccountPolicy();
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
DeviceLocalAccountPolicyBroker* broker =
connector->GetDeviceLocalAccountPolicyService()->GetBrokerForUser(
user_id_1_);
ASSERT_TRUE(broker);
broker->core()->store()->Load();
WaitForDisplayName(user_id_1_, kDisplayName2);
// Verify that the new display name is shown in the UI.
display_name.clear();
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
contents_,
get_compact_pod_display_name,
&display_name));
EXPECT_EQ(kDisplayName2, display_name);
display_name.clear();
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
contents_,
get_expanded_pod_display_name,
&display_name));
EXPECT_EQ(kDisplayName2, display_name);
// Verify that the pod is still expanded. This indicates that the UI updated
// without reloading and losing state.
bool expanded = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
contents_,
base::StringPrintf(
"domAutomationController.send(document.getElementById('pod-row')"
" .getPodWithUsername_('%s').expanded);",
user_id_1_.c_str()),
&expanded));
EXPECT_TRUE(expanded);
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, PolicyDownload) {
UploadDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
// Sanity check: The policy should be present now.
ASSERT_FALSE(session_manager_client()->device_local_account_policy(
kAccountId1).empty());
}
static bool IsNotKnownUser(const std::string& account_id) {
return !IsKnownUser(account_id);
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, AccountListChange) {
AddPublicSessionToDevicePolicy(kAccountId1);
AddPublicSessionToDevicePolicy(kAccountId2);
content::WindowedNotificationObserver(chrome::NOTIFICATION_USER_LIST_CHANGED,
base::Bind(&IsKnownUser, user_id_1_))
.Wait();
EXPECT_TRUE(IsKnownUser(user_id_2_));
// Update policy to remove kAccountId2.
em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
proto.mutable_device_local_accounts()->clear_account();
AddPublicSessionToDevicePolicy(kAccountId1);
em::ChromeDeviceSettingsProto policy;
policy.mutable_show_user_names()->set_show_user_names(true);
em::DeviceLocalAccountInfoProto* account1 =
policy.mutable_device_local_accounts()->add_account();
account1->set_account_id(kAccountId1);
account1->set_type(
em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION);
test_server_.UpdatePolicy(dm_protocol::kChromeDevicePolicyType, std::string(),
policy.SerializeAsString());
g_browser_process->policy_service()->RefreshPolicies(base::Closure());
// Make sure the second device-local account disappears.
content::WindowedNotificationObserver(chrome::NOTIFICATION_USER_LIST_CHANGED,
base::Bind(&IsNotKnownUser, user_id_2_))
.Wait();
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, StartSession) {
// Specify startup pages.
device_local_account_policy_.payload().mutable_restoreonstartup()->set_value(
SessionStartupPref::kPrefValueURLs);
em::StringListPolicyProto* startup_urls_proto =
device_local_account_policy_.payload().mutable_restoreonstartupurls();
for (size_t i = 0; i < arraysize(kStartupURLs); ++i)
startup_urls_proto->mutable_value()->add_entries(kStartupURLs[i]);
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
ASSERT_NO_FATAL_FAILURE(StartLogin(std::string(), std::string()));
WaitForSessionStart();
// Check that the startup pages specified in policy were opened.
BrowserList* browser_list =
BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
EXPECT_EQ(1U, browser_list->size());
Browser* browser = browser_list->get(0);
ASSERT_TRUE(browser);
TabStripModel* tabs = browser->tab_strip_model();
ASSERT_TRUE(tabs);
int expected_tab_count = static_cast<int>(arraysize(kStartupURLs));
EXPECT_EQ(expected_tab_count, tabs->count());
for (int i = 0; i < expected_tab_count && i < tabs->count(); ++i) {
EXPECT_EQ(GURL(kStartupURLs[i]),
tabs->GetWebContentsAt(i)->GetVisibleURL());
}
// Verify that the session is not considered to be logged in with a GAIA
// account.
Profile* profile = GetProfileForTest();
ASSERT_TRUE(profile);
EXPECT_FALSE(profile->GetPrefs()->HasPrefPath(
prefs::kGoogleServicesUsername));
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, FullscreenDisallowed) {
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
ASSERT_NO_FATAL_FAILURE(StartLogin(std::string(), std::string()));
WaitForSessionStart();
BrowserList* browser_list =
BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
EXPECT_EQ(1U, browser_list->size());
Browser* browser = browser_list->get(0);
ASSERT_TRUE(browser);
BrowserWindow* browser_window = browser->window();
ASSERT_TRUE(browser_window);
// Verify that an attempt to enter fullscreen mode is denied.
EXPECT_FALSE(browser_window->IsFullscreen());
chrome::ToggleFullscreenMode(browser);
EXPECT_FALSE(browser_window->IsFullscreen());
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionsUncached) {
// Make it possible to force-install a hosted app and an extension.
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
TestingUpdateManifestProvider testing_update_manifest_provider(
kRelativeUpdateURL);
testing_update_manifest_provider.AddUpdate(
kHostedAppID,
kHostedAppVersion,
embedded_test_server()->GetURL(std::string("/") + kHostedAppCRXPath));
testing_update_manifest_provider.AddUpdate(
kGoodExtensionID,
kGoodExtensionVersion,
embedded_test_server()->GetURL(std::string("/") + kGoodExtensionCRXPath));
embedded_test_server()->RegisterRequestHandler(
base::Bind(&TestingUpdateManifestProvider::HandleRequest,
base::Unretained(&testing_update_manifest_provider)));
// Specify policy to force-install the hosted app and the extension.
em::StringList* forcelist = device_local_account_policy_.payload()
.mutable_extensioninstallforcelist()->mutable_value();
forcelist->add_entries(base::StringPrintf(
"%s;%s",
kHostedAppID,
embedded_test_server()->GetURL(kRelativeUpdateURL).spec().c_str()));
forcelist->add_entries(base::StringPrintf(
"%s;%s",
kGoodExtensionID,
embedded_test_server()->GetURL(kRelativeUpdateURL).spec().c_str()));
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
// Start listening for app/extension installation results.
content::WindowedNotificationObserver hosted_app_observer(
extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED,
base::Bind(DoesInstallSuccessReferToId, kHostedAppID));
content::WindowedNotificationObserver extension_observer(
extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
base::Bind(DoesInstallFailureReferToId, kGoodExtensionID));
ASSERT_NO_FATAL_FAILURE(StartLogin(std::string(), std::string()));
// Wait for the hosted app installation to succeed and the extension
// installation to fail (because hosted apps are whitelisted for use in
// device-local accounts and extensions are not).
hosted_app_observer.Wait();
extension_observer.Wait();
// Verify that the hosted app was installed.
Profile* profile = GetProfileForTest();
ASSERT_TRUE(profile);
ExtensionService* extension_service =
extensions::ExtensionSystem::Get(profile)->extension_service();
EXPECT_TRUE(extension_service->GetExtensionById(kHostedAppID, true));
// Verify that the extension was not installed.
EXPECT_FALSE(extension_service->GetExtensionById(kGoodExtensionID, true));
// Verify that the app was downloaded to the account's extension cache.
base::FilePath test_dir;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
EXPECT_TRUE(ContentsEqual(
GetCacheCRXFile(kAccountId1, kHostedAppID, kHostedAppVersion),
test_dir.Append(kHostedAppCRXPath)));
// Verify that the extension was removed from the account's extension cache
// after the installation failure.
DeviceLocalAccountPolicyBroker* broker =
g_browser_process->platform_part()->browser_policy_connector_chromeos()->
GetDeviceLocalAccountPolicyService()->GetBrokerForUser(user_id_1_);
ASSERT_TRUE(broker);
chromeos::ExternalCache* cache =
broker->extension_loader()->GetExternalCacheForTesting();
ASSERT_TRUE(cache);
EXPECT_FALSE(cache->GetExtension(kGoodExtensionID, NULL, NULL));
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionsCached) {
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
// Pre-populate the device local account's extension cache with a hosted app
// and an extension.
EXPECT_TRUE(base::CreateDirectory(
GetExtensionCacheDirectoryForAccountID(kAccountId1)));
base::FilePath test_dir;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
const base::FilePath cached_hosted_app =
GetCacheCRXFile(kAccountId1, kHostedAppID, kHostedAppVersion);
EXPECT_TRUE(CopyFile(test_dir.Append(kHostedAppCRXPath),
cached_hosted_app));
EXPECT_TRUE(CopyFile(
test_dir.Append(kGoodExtensionCRXPath),
GetCacheCRXFile(kAccountId1, kGoodExtensionID, kGoodExtensionVersion)));
// Specify policy to force-install the hosted app.
em::StringList* forcelist = device_local_account_policy_.payload()
.mutable_extensioninstallforcelist()->mutable_value();
forcelist->add_entries(base::StringPrintf(
"%s;%s",
kHostedAppID,
embedded_test_server()->GetURL(kRelativeUpdateURL).spec().c_str()));
forcelist->add_entries(base::StringPrintf(
"%s;%s",
kGoodExtensionID,
embedded_test_server()->GetURL(kRelativeUpdateURL).spec().c_str()));
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
// Start listening for app/extension installation results.
content::WindowedNotificationObserver hosted_app_observer(
extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED,
base::Bind(DoesInstallSuccessReferToId, kHostedAppID));
content::WindowedNotificationObserver extension_observer(
extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
base::Bind(DoesInstallFailureReferToId, kGoodExtensionID));
ASSERT_NO_FATAL_FAILURE(StartLogin(std::string(), std::string()));
// Wait for the hosted app installation to succeed and the extension
// installation to fail.
hosted_app_observer.Wait();
extension_observer.Wait();
// Verify that the hosted app was installed.
Profile* profile = GetProfileForTest();
ASSERT_TRUE(profile);
ExtensionService* extension_service =
extensions::ExtensionSystem::Get(profile)->extension_service();
EXPECT_TRUE(extension_service->GetExtensionById(kHostedAppID, true));
// Verify that the extension was not installed.
EXPECT_FALSE(extension_service->GetExtensionById(kGoodExtensionID, true));
// Verify that the app is still in the account's extension cache.
EXPECT_TRUE(PathExists(cached_hosted_app));
// Verify that the extension was removed from the account's extension cache.
DeviceLocalAccountPolicyBroker* broker =
g_browser_process->platform_part()->browser_policy_connector_chromeos()->
GetDeviceLocalAccountPolicyService()->GetBrokerForUser(user_id_1_);
ASSERT_TRUE(broker);
chromeos::ExternalCache* cache =
broker->extension_loader()->GetExternalCacheForTesting();
ASSERT_TRUE(cache);
EXPECT_FALSE(cache->GetExtension(kGoodExtensionID, NULL, NULL));
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExternalData) {
// user_manager::UserManager requests an external data fetch whenever
// the key::kUserAvatarImage policy is set. Since this test wants to
// verify that the underlying policy subsystem will start a fetch
// without this request as well, the user_manager::UserManager must be
// prevented from seeing the policy change.
reinterpret_cast<chromeos::ChromeUserManagerImpl*>(
user_manager::UserManager::Get())->StopPolicyObserverForTesting();
UploadDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
// Start serving external data at |kExternalDataURL|.
scoped_ptr<base::RunLoop> run_loop(new base::RunLoop);
scoped_ptr<net::FakeURLFetcherFactory> fetcher_factory(
new net::FakeURLFetcherFactory(
NULL,
base::Bind(&RunCallbackAndReturnFakeURLFetcher,
base::MessageLoopProxy::current(),
run_loop->QuitClosure())));
fetcher_factory->SetFakeResponse(GURL(kExternalDataURL),
kExternalData,
net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
// Specify an external data reference for the key::kUserAvatarImage policy.
scoped_ptr<base::DictionaryValue> metadata =
test::ConstructExternalDataReference(kExternalDataURL, kExternalData);
std::string policy;
base::JSONWriter::Write(metadata.get(), &policy);
device_local_account_policy_.payload().mutable_useravatarimage()->set_value(
policy);
UploadAndInstallDeviceLocalAccountPolicy();
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
DeviceLocalAccountPolicyBroker* broker =
connector->GetDeviceLocalAccountPolicyService()->GetBrokerForUser(
user_id_1_);
ASSERT_TRUE(broker);
broker->core()->store()->Load();
// The external data should be fetched and cached automatically. Wait for this
// fetch.
run_loop->Run();
// Stop serving external data at |kExternalDataURL|.
fetcher_factory.reset();
const PolicyMap::Entry* policy_entry =
broker->core()->store()->policy_map().Get(key::kUserAvatarImage);
ASSERT_TRUE(policy_entry);
ASSERT_TRUE(policy_entry->external_data_fetcher);
// Retrieve the external data. Although the data is no longer being served at
// |kExternalDataURL|, the retrieval should succeed because the data has been
// cached.
run_loop.reset(new base::RunLoop);
scoped_ptr<std::string> fetched_external_data;
policy_entry->external_data_fetcher->Fetch(base::Bind(
&test::ExternalDataFetchCallback,
&fetched_external_data,
run_loop->QuitClosure()));
run_loop->Run();
ASSERT_TRUE(fetched_external_data);
EXPECT_EQ(kExternalData, *fetched_external_data);
ASSERT_NO_FATAL_FAILURE(StartLogin(std::string(), std::string()));
WaitForSessionStart();
// Verify that the external data reference has propagated to the device-local
// account's ProfilePolicyConnector.
ProfilePolicyConnector* policy_connector =
ProfilePolicyConnectorFactory::GetForProfile(GetProfileForTest());
ASSERT_TRUE(policy_connector);
const PolicyMap& policies = policy_connector->policy_service()->GetPolicies(
PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
policy_entry = policies.Get(key::kUserAvatarImage);
ASSERT_TRUE(policy_entry);
EXPECT_TRUE(base::Value::Equals(metadata.get(), policy_entry->value));
ASSERT_TRUE(policy_entry->external_data_fetcher);
// Retrieve the external data via the ProfilePolicyConnector. The retrieval
// should succeed because the data has been cached.
run_loop.reset(new base::RunLoop);
fetched_external_data.reset();
policy_entry->external_data_fetcher->Fetch(base::Bind(
&test::ExternalDataFetchCallback,
&fetched_external_data,
run_loop->QuitClosure()));
run_loop->Run();
ASSERT_TRUE(fetched_external_data);
EXPECT_EQ(kExternalData, *fetched_external_data);
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, UserAvatarImage) {
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
UploadDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
base::FilePath test_dir;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
std::string image_data;
ASSERT_TRUE(base::ReadFileToString(
test_dir.Append(chromeos::test::kUserAvatarImage1RelativePath),
&image_data));
std::string policy;
base::JSONWriter::Write(test::ConstructExternalDataReference(
embedded_test_server()->GetURL(std::string("/") +
chromeos::test::kUserAvatarImage1RelativePath).spec(),
image_data).get(),
&policy);
device_local_account_policy_.payload().mutable_useravatarimage()->set_value(
policy);
UploadAndInstallDeviceLocalAccountPolicy();
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
DeviceLocalAccountPolicyBroker* broker =
connector->GetDeviceLocalAccountPolicyService()->GetBrokerForUser(
user_id_1_);
ASSERT_TRUE(broker);
run_loop_.reset(new base::RunLoop);
user_manager::UserManager::Get()->AddObserver(this);
broker->core()->store()->Load();
run_loop_->Run();
user_manager::UserManager::Get()->RemoveObserver(this);
scoped_ptr<gfx::ImageSkia> policy_image = chromeos::test::ImageLoader(
test_dir.Append(chromeos::test::kUserAvatarImage1RelativePath)).Load();
ASSERT_TRUE(policy_image);
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(user_id_1_);
ASSERT_TRUE(user);
base::FilePath user_data_dir;
ASSERT_TRUE(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
const base::FilePath saved_image_path =
user_data_dir.Append(user_id_1_).AddExtension("jpg");
EXPECT_FALSE(user->HasDefaultImage());
EXPECT_EQ(user_manager::User::USER_IMAGE_EXTERNAL, user->image_index());
EXPECT_TRUE(chromeos::test::AreImagesEqual(*policy_image, user->GetImage()));
const base::DictionaryValue* images_pref =
g_browser_process->local_state()->GetDictionary("user_image_info");
ASSERT_TRUE(images_pref);
const base::DictionaryValue* image_properties;
ASSERT_TRUE(images_pref->GetDictionaryWithoutPathExpansion(
user_id_1_,
&image_properties));
int image_index;
std::string image_path;
ASSERT_TRUE(image_properties->GetInteger("index", &image_index));
ASSERT_TRUE(image_properties->GetString("path", &image_path));
EXPECT_EQ(user_manager::User::USER_IMAGE_EXTERNAL, image_index);
EXPECT_EQ(saved_image_path.value(), image_path);
scoped_ptr<gfx::ImageSkia> saved_image =
chromeos::test::ImageLoader(saved_image_path).Load();
ASSERT_TRUE(saved_image);
// Check image dimensions. Images can't be compared since JPEG is lossy.
EXPECT_EQ(policy_image->width(), saved_image->width());
EXPECT_EQ(policy_image->height(), saved_image->height());
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, LastWindowClosedLogoutReminder) {
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
ASSERT_NO_FATAL_FAILURE(StartLogin(std::string(), std::string()));
WaitForSessionStart();
Profile* profile = GetProfileForTest();
ASSERT_TRUE(profile);
extensions::AppWindowRegistry* app_window_registry =
extensions::AppWindowRegistry::Get(profile);
app_window_registry->AddObserver(this);
// Verify that the logout confirmation dialog is not showing.
ash::LogoutConfirmationController* logout_confirmation_controller =
ash::Shell::GetInstance()->logout_confirmation_controller();
ASSERT_TRUE(logout_confirmation_controller);
EXPECT_FALSE(logout_confirmation_controller->dialog_for_testing());
// Remove policy that allows only explicitly whitelisted apps to be installed
// in a public session.
extensions::ExtensionSystem* extension_system =
extensions::ExtensionSystem::Get(profile);
ASSERT_TRUE(extension_system);
extension_system->management_policy()->UnregisterAllProviders();
// Install and a platform app.
scoped_refptr<extensions::CrxInstaller> installer =
extensions::CrxInstaller::CreateSilent(
extension_system->extension_service());
installer->set_allow_silent_install(true);
installer->set_install_cause(extension_misc::INSTALL_CAUSE_USER_DOWNLOAD);
installer->set_creation_flags(extensions::Extension::FROM_WEBSTORE);
content::WindowedNotificationObserver app_install_observer(
extensions::NOTIFICATION_CRX_INSTALLER_DONE,
content::NotificationService::AllSources());
base::FilePath test_dir;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
installer->InstallCrx(test_dir.Append(kPackagedAppCRXPath));
app_install_observer.Wait();
const extensions::Extension* app =
content::Details<const extensions::Extension>(
app_install_observer.details()).ptr();
// Start the platform app, causing it to open a window.
run_loop_.reset(new base::RunLoop);
OpenApplication(AppLaunchParams(
profile, app, extensions::LAUNCH_CONTAINER_NONE, NEW_WINDOW));
run_loop_->Run();
EXPECT_EQ(1U, app_window_registry->app_windows().size());
// Close the only open browser window.
BrowserList* browser_list =
BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
EXPECT_EQ(1U, browser_list->size());
Browser* browser = browser_list->get(0);
ASSERT_TRUE(browser);
BrowserWindow* browser_window = browser->window();
ASSERT_TRUE(browser_window);
run_loop_.reset(new base::RunLoop);
browser_window->Close();
browser_window = NULL;
run_loop_->Run();
browser = NULL;
EXPECT_TRUE(browser_list->empty());
// Verify that the logout confirmation dialog is not showing because an app
// window is still open.
EXPECT_FALSE(logout_confirmation_controller->dialog_for_testing());
// Open a browser window.
Browser* first_browser = CreateBrowser(profile);
EXPECT_EQ(1U, browser_list->size());
// Close the app window.
run_loop_.reset(new base::RunLoop);
ASSERT_EQ(1U, app_window_registry->app_windows().size());
app_window_registry->app_windows().front()->GetBaseWindow()->Close();
run_loop_->Run();
EXPECT_TRUE(app_window_registry->app_windows().empty());
// Verify that the logout confirmation dialog is not showing because a browser
// window is still open.
EXPECT_FALSE(logout_confirmation_controller->dialog_for_testing());
// Open a second browser window.
Browser* second_browser = CreateBrowser(profile);
EXPECT_EQ(2U, browser_list->size());
// Close the first browser window.
browser_window = first_browser->window();
ASSERT_TRUE(browser_window);
run_loop_.reset(new base::RunLoop);
browser_window->Close();
browser_window = NULL;
run_loop_->Run();
first_browser = NULL;
EXPECT_EQ(1U, browser_list->size());
// Verify that the logout confirmation dialog is not showing because a browser
// window is still open.
EXPECT_FALSE(logout_confirmation_controller->dialog_for_testing());
// Close the second browser window.
browser_window = second_browser->window();
ASSERT_TRUE(browser_window);
run_loop_.reset(new base::RunLoop);
browser_window->Close();
browser_window = NULL;
run_loop_->Run();
second_browser = NULL;
EXPECT_TRUE(browser_list->empty());
// Verify that the logout confirmation dialog is showing.
ash::LogoutConfirmationDialog* dialog =
logout_confirmation_controller->dialog_for_testing();
ASSERT_TRUE(dialog);
// Deny the logout.
dialog->GetWidget()->Close();
dialog = NULL;
base::RunLoop().RunUntilIdle();
// Verify that the logout confirmation dialog is no longer showing.
EXPECT_FALSE(logout_confirmation_controller->dialog_for_testing());
// Open a browser window.
browser = CreateBrowser(profile);
EXPECT_EQ(1U, browser_list->size());
// Close the browser window.
browser_window = browser->window();
ASSERT_TRUE(browser_window);
run_loop_.reset(new base::RunLoop);
browser_window->Close();
browser_window = NULL;
run_loop_->Run();
browser = NULL;
EXPECT_TRUE(browser_list->empty());
// Verify that the logout confirmation dialog is showing again.
dialog = logout_confirmation_controller->dialog_for_testing();
ASSERT_TRUE(dialog);
// Deny the logout.
dialog->GetWidget()->Close();
dialog = NULL;
base::RunLoop().RunUntilIdle();
app_window_registry->RemoveObserver(this);
};
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, NoRecommendedLocaleNoSwitch) {
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
ExpandPublicSessionPod(false);
// Click the enter button to start the session.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"document.getElementById('pod-row').getPodWithUsername_('%s')"
" .querySelector('.enter-button').click();",
user_id_1_.c_str())));
WaitForSessionStart();
// Verify that the locale has not changed and the first keyboard layout
// applicable to the locale was chosen.
EXPECT_EQ(initial_locale_, g_browser_process->GetApplicationLocale());
EXPECT_EQ(initial_language_, icu::Locale::getDefault().getLanguage());
VerifyKeyboardLayoutMatchesLocale();
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, NoRecommendedLocaleSwitch) {
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
ExpandPublicSessionPod(false);
// Click the link that switches the pod to its advanced form. Verify that the
// pod switches from basic to advanced.
bool advanced = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
contents_,
base::StringPrintf(
"var pod ="
" document.getElementById('pod-row').getPodWithUsername_('%s');"
"pod.querySelector('.language-and-input').click();"
"domAutomationController.send(pod.classList.contains('advanced'));",
user_id_1_.c_str()),
&advanced));
EXPECT_FALSE(advanced);
// Manually select a different locale.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"var languageSelect = document.getElementById('pod-row')"
" .getPodWithUsername_('%s').querySelector('.language-select');"
"languageSelect.value = '%s';"
"var event = document.createEvent('HTMLEvents');"
"event.initEvent('change', false, true);"
"languageSelect.dispatchEvent(event);",
user_id_1_.c_str(),
kPublicSessionLocale)));
// The UI will have requested an updated list of keyboard layouts at this
// point. Wait for the constructions of this list to finish.
WaitForGetKeyboardLayoutsForLocaleToFinish();
// Manually select a different keyboard layout and click the enter button to
// start the session.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"var pod ="
" document.getElementById('pod-row').getPodWithUsername_('%s');"
"pod.querySelector('.keyboard-select').value = '%s';"
"pod.querySelector('.enter-button').click();",
user_id_1_.c_str(),
public_session_input_method_id_.c_str())));
WaitForSessionStart();
// Verify that the locale and keyboard layout have been applied.
EXPECT_EQ(kPublicSessionLocale, g_browser_process->GetApplicationLocale());
EXPECT_EQ(l10n_util::GetLanguage(kPublicSessionLocale),
icu::Locale::getDefault().getLanguage());
EXPECT_EQ(public_session_input_method_id_,
chromeos::input_method::InputMethodManager::Get()
->GetActiveIMEState()
->GetCurrentInputMethod()
.id());
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, OneRecommendedLocale) {
// Specify a recommended locale.
SetRecommendedLocales(kSingleRecommendedLocale,
arraysize(kSingleRecommendedLocale));
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
ExpandPublicSessionPod(false);
// Click the enter button to start the session.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"document.getElementById('pod-row').getPodWithUsername_('%s')"
" .querySelector('.enter-button').click();",
user_id_1_.c_str())));
WaitForSessionStart();
// Verify that the recommended locale has been applied and the first keyboard
// layout applicable to the locale was chosen.
EXPECT_EQ(kSingleRecommendedLocale[0],
g_browser_process->GetApplicationLocale());
EXPECT_EQ(l10n_util::GetLanguage(kSingleRecommendedLocale[0]),
icu::Locale::getDefault().getLanguage());
VerifyKeyboardLayoutMatchesLocale();
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, MultipleRecommendedLocales) {
// Specify recommended locales.
SetRecommendedLocales(kRecommendedLocales1, arraysize(kRecommendedLocales1));
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
AddPublicSessionToDevicePolicy(kAccountId2);
WaitForPolicy();
ExpandPublicSessionPod(true);
// Verify that the pod shows a list of locales beginning with the recommended
// ones, followed by others.
const std::string get_locale_list = base::StringPrintf(
"var languageSelect = document.getElementById('pod-row')"
" .getPodWithUsername_('%s').querySelector('.language-select');"
"var locales = [];"
"for (var i = 0; i < languageSelect.length; ++i)"
" locales.push(languageSelect.options[i].value);"
"domAutomationController.send(JSON.stringify(locales));",
user_id_1_.c_str());
std::string json;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents_,
get_locale_list,
&json));
scoped_ptr<base::Value> value_ptr(base::JSONReader::Read(json));
const base::ListValue* locales = NULL;
ASSERT_TRUE(value_ptr);
ASSERT_TRUE(value_ptr->GetAsList(&locales));
EXPECT_LT(arraysize(kRecommendedLocales1), locales->GetSize());
// Verify that the list starts with the recommended locales, in correct order.
for (size_t i = 0; i < arraysize(kRecommendedLocales1); ++i) {
std::string locale;
EXPECT_TRUE(locales->GetString(i, &locale));
EXPECT_EQ(kRecommendedLocales1[i], locale);
}
// Verify that the recommended locales do not appear again in the remainder of
// the list.
std::set<std::string> recommended_locales;
for (size_t i = 0; i < arraysize(kRecommendedLocales1); ++i)
recommended_locales.insert(kRecommendedLocales1[i]);
for (size_t i = arraysize(kRecommendedLocales1); i < locales->GetSize();
++i) {
std::string locale;
EXPECT_TRUE(locales->GetString(i, &locale));
EXPECT_EQ(recommended_locales.end(), recommended_locales.find(locale));
}
// Verify that the first recommended locale is selected.
const std::string get_selected_locale =
base::StringPrintf(
"domAutomationController.send(document.getElementById('pod-row')"
" .getPodWithUsername_('%s').querySelector('.language-select')"
" .value);",
user_id_1_.c_str());
std::string selected_locale;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents_,
get_selected_locale,
&selected_locale));
EXPECT_EQ(kRecommendedLocales1[0], selected_locale);
// Change the list of recommended locales.
SetRecommendedLocales(kRecommendedLocales2, arraysize(kRecommendedLocales2));
// Also change the display name as it is easy to ensure that policy has been
// updated by waiting for a display name change.
device_local_account_policy_.payload().mutable_userdisplayname()->set_value(
kDisplayName2);
UploadAndInstallDeviceLocalAccountPolicy();
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
DeviceLocalAccountPolicyBroker* broker =
connector->GetDeviceLocalAccountPolicyService()->GetBrokerForUser(
user_id_1_);
ASSERT_TRUE(broker);
broker->core()->store()->Load();
WaitForDisplayName(user_id_1_, kDisplayName2);
// Verify that the new list of locales is shown in the UI.
ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents_,
get_locale_list,
&json));
value_ptr.reset(base::JSONReader::Read(json));
locales = NULL;
ASSERT_TRUE(value_ptr);
ASSERT_TRUE(value_ptr->GetAsList(&locales));
EXPECT_LT(arraysize(kRecommendedLocales2), locales->GetSize());
for (size_t i = 0; i < arraysize(kRecommendedLocales2); ++i) {
std::string locale;
EXPECT_TRUE(locales->GetString(i, &locale));
EXPECT_EQ(kRecommendedLocales2[i], locale);
}
// Verify that the first new recommended locale is selected.
ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents_,
get_selected_locale,
&selected_locale));
EXPECT_EQ(kRecommendedLocales2[0], selected_locale);
// Manually select a different locale.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"var languageSelect = document.getElementById('pod-row')"
" .getPodWithUsername_('%s').querySelector('.language-select');"
"languageSelect.value = '%s';"
"var event = document.createEvent('HTMLEvents');"
"event.initEvent('change', false, true);"
"languageSelect.dispatchEvent(event);",
user_id_1_.c_str(),
kPublicSessionLocale)));
// Change the list of recommended locales.
SetRecommendedLocales(kRecommendedLocales2, arraysize(kRecommendedLocales2));
device_local_account_policy_.payload().mutable_userdisplayname()->set_value(
kDisplayName1);
UploadAndInstallDeviceLocalAccountPolicy();
broker->core()->store()->Load();
WaitForDisplayName(user_id_1_, kDisplayName1);
// Verify that the manually selected locale is still selected.
ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents_,
get_selected_locale,
&selected_locale));
EXPECT_EQ(kPublicSessionLocale, selected_locale);
// The UI will request an updated list of keyboard layouts at this point. Wait
// for the constructions of this list to finish.
WaitForGetKeyboardLayoutsForLocaleToFinish();
// Manually select a different keyboard layout.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"document.getElementById('pod-row').getPodWithUsername_('%s')"
" .querySelector('.keyboard-select').value = '%s';",
user_id_1_.c_str(),
public_session_input_method_id_.c_str())));
// Click on a different pod, causing focus to shift away and the pod to
// contract.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"document.getElementById('pod-row').getPodWithUsername_('%s')"
" .click();",
user_id_2_.c_str())));
// Click on the pod again, causing it to expand again. Verify that the pod has
// kept all its state (the advanced form is being shown, the manually selected
// locale and keyboard layout are selected).
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
contents_,
base::StringPrintf(
"var pod ="
" document.getElementById('pod-row').getPodWithUsername_('%s');"
"pod.click();"
"var state = {};"
"state.advanced = pod.classList.contains('advanced');"
"state.locale = pod.querySelector('.language-select').value;"
"state.keyboardLayout = pod.querySelector('.keyboard-select').value;"
"console.log(JSON.stringify(state));"
"domAutomationController.send(JSON.stringify(state));",
user_id_1_.c_str()),
&json));
LOG(ERROR) << json;
value_ptr.reset(base::JSONReader::Read(json));
const base::DictionaryValue* state = NULL;
ASSERT_TRUE(value_ptr);
ASSERT_TRUE(value_ptr->GetAsDictionary(&state));
bool advanced = false;
EXPECT_TRUE(state->GetBoolean("advanced", &advanced));
EXPECT_TRUE(advanced);
EXPECT_TRUE(state->GetString("locale", &selected_locale));
EXPECT_EQ(kPublicSessionLocale, selected_locale);
std::string selected_keyboard_layout;
EXPECT_TRUE(state->GetString("keyboardLayout", &selected_keyboard_layout));
EXPECT_EQ(public_session_input_method_id_, selected_keyboard_layout);
// Click the enter button to start the session.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"document.getElementById('pod-row').getPodWithUsername_('%s')"
" .querySelector('.enter-button').click();",
user_id_1_.c_str())));
WaitForSessionStart();
// Verify that the locale and keyboard layout have been applied.
EXPECT_EQ(kPublicSessionLocale, g_browser_process->GetApplicationLocale());
EXPECT_EQ(l10n_util::GetLanguage(kPublicSessionLocale),
icu::Locale::getDefault().getLanguage());
EXPECT_EQ(public_session_input_method_id_,
chromeos::input_method::InputMethodManager::Get()
->GetActiveIMEState()
->GetCurrentInputMethod()
.id());
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, InvalidRecommendedLocale) {
// Specify an invalid recommended locale.
SetRecommendedLocales(kInvalidRecommendedLocale,
arraysize(kInvalidRecommendedLocale));
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
// Click on the pod to expand it. Verify that the pod expands to its basic
// form as there is only one recommended locale.
bool advanced = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
contents_,
base::StringPrintf(
"var pod ="
" document.getElementById('pod-row').getPodWithUsername_('%s');"
"pod.click();"
"domAutomationController.send(pod.classList.contains('advanced'));",
user_id_1_.c_str()),
&advanced));
EXPECT_FALSE(advanced);
EXPECT_EQ(l10n_util::GetLanguage(initial_locale_),
icu::Locale::getDefault().getLanguage());
// Click the enter button to start the session.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"document.getElementById('pod-row').getPodWithUsername_('%s')"
" .querySelector('.enter-button').click();",
user_id_1_.c_str())));
WaitForSessionStart();
// Verify that since the recommended locale was invalid, the locale has not
// changed and the first keyboard layout applicable to the locale was chosen.
EXPECT_EQ(initial_locale_, g_browser_process->GetApplicationLocale());
EXPECT_EQ(l10n_util::GetLanguage(initial_locale_),
icu::Locale::getDefault().getLanguage());
VerifyKeyboardLayoutMatchesLocale();
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest,
AutoLoginWithoutRecommendedLocales) {
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
EnableAutoLogin();
WaitForPolicy();
WaitForSessionStart();
// Verify that the locale has not changed and the first keyboard layout
// applicable to the locale was chosen.
EXPECT_EQ(initial_locale_, g_browser_process->GetApplicationLocale());
EXPECT_EQ(initial_language_, icu::Locale::getDefault().getLanguage());
VerifyKeyboardLayoutMatchesLocale();
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest,
AutoLoginWithRecommendedLocales) {
// Specify recommended locales.
SetRecommendedLocales(kRecommendedLocales1, arraysize(kRecommendedLocales1));
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
EnableAutoLogin();
WaitForPolicy();
WaitForSessionStart();
// Verify that the first recommended locale has been applied and the first
// keyboard layout applicable to the locale was chosen.
EXPECT_EQ(kRecommendedLocales1[0], g_browser_process->GetApplicationLocale());
EXPECT_EQ(l10n_util::GetLanguage(kRecommendedLocales1[0]),
icu::Locale::getDefault().getLanguage());
VerifyKeyboardLayoutMatchesLocale();
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, TermsOfServiceWithLocaleSwitch) {
// Specify Terms of Service URL.
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
device_local_account_policy_.payload().mutable_termsofserviceurl()->set_value(
embedded_test_server()->GetURL(
std::string("/") + kExistentTermsOfServicePath).spec());
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
// Select a different locale.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"var languageSelect = document.getElementById('pod-row')"
" .getPodWithUsername_('%s').querySelector('.language-select');"
"languageSelect.value = '%s';"
"var event = document.createEvent('HTMLEvents');"
"event.initEvent('change', false, true);"
"languageSelect.dispatchEvent(event);",
user_id_1_.c_str(),
kPublicSessionLocale)));
// The UI will have requested an updated list of keyboard layouts at this
// point. Wait for the constructions of this list to finish.
WaitForGetKeyboardLayoutsForLocaleToFinish();
// Set up an observer that will quit the message loop when login has succeeded
// and the first wizard screen, if any, is being shown.
base::RunLoop login_wait_run_loop;
chromeos::MockAuthStatusConsumer login_status_consumer;
EXPECT_CALL(login_status_consumer, OnAuthSuccess(_)).Times(1).WillOnce(
InvokeWithoutArgs(&login_wait_run_loop, &base::RunLoop::Quit));
chromeos::ExistingUserController* controller =
chromeos::ExistingUserController::current_controller();
ASSERT_TRUE(controller);
controller->set_login_status_consumer(&login_status_consumer);
// Manually select a different keyboard layout and click the enter button to
// start the session.
ASSERT_TRUE(content::ExecuteScript(
contents_,
base::StringPrintf(
"var pod ="
" document.getElementById('pod-row').getPodWithUsername_('%s');"
"pod.querySelector('.keyboard-select').value = '%s';"
"pod.querySelector('.enter-button').click();",
user_id_1_.c_str(),
public_session_input_method_id_.c_str())));
// Spin the loop until the login observer fires. Then, unregister the
// observer.
login_wait_run_loop.Run();
controller->set_login_status_consumer(NULL);
// Verify that the Terms of Service screen is being shown.
chromeos::WizardController* wizard_controller =
chromeos::WizardController::default_controller();
ASSERT_TRUE(wizard_controller);
ASSERT_TRUE(wizard_controller->current_screen());
EXPECT_EQ(chromeos::WizardController::kTermsOfServiceScreenName,
wizard_controller->current_screen()->GetName());
// Wait for the Terms of Service to finish downloading.
bool done = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(contents_,
"var screenElement = document.getElementById('terms-of-service');"
"function SendReplyIfDownloadDone() {"
" if (screenElement.classList.contains('tos-loading'))"
" return false;"
" domAutomationController.send(true);"
" observer.disconnect();"
" return true;"
"}"
"var observer = new MutationObserver(SendReplyIfDownloadDone);"
"if (!SendReplyIfDownloadDone()) {"
" var options = { attributes: true, attributeFilter: [ 'class' ] };"
" observer.observe(screenElement, options);"
"}",
&done));
// Verify that the locale and keyboard layout have been applied.
EXPECT_EQ(kPublicSessionLocale, g_browser_process->GetApplicationLocale());
EXPECT_EQ(l10n_util::GetLanguage(kPublicSessionLocale),
icu::Locale::getDefault().getLanguage());
EXPECT_EQ(public_session_input_method_id_,
chromeos::input_method::InputMethodManager::Get()
->GetActiveIMEState()
->GetCurrentInputMethod()
.id());
// Click the accept button.
ASSERT_TRUE(content::ExecuteScript(contents_,
"$('tos-accept-button').click();"));
WaitForSessionStart();
// Verify that the locale and keyboard layout are still in force.
EXPECT_EQ(kPublicSessionLocale, g_browser_process->GetApplicationLocale());
EXPECT_EQ(l10n_util::GetLanguage(kPublicSessionLocale),
icu::Locale::getDefault().getLanguage());
EXPECT_EQ(public_session_input_method_id_,
chromeos::input_method::InputMethodManager::Get()
->GetActiveIMEState()
->GetCurrentInputMethod()
.id());
}
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, PolicyForExtensions) {
// Set up a test update server for the Show Managed Storage app.
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
TestingUpdateManifestProvider testing_update_manifest_provider(
kRelativeUpdateURL);
testing_update_manifest_provider.AddUpdate(
kShowManagedStorageID,
kShowManagedStorageVersion,
embedded_test_server()->GetURL(std::string("/") +
kShowManagedStorageCRXPath));
embedded_test_server()->RegisterRequestHandler(
base::Bind(&TestingUpdateManifestProvider::HandleRequest,
base::Unretained(&testing_update_manifest_provider)));
// Force-install the Show Managed Storage app. This app can be installed in
// public sessions because it's whitelisted for testing purposes.
em::StringList* forcelist = device_local_account_policy_.payload()
.mutable_extensioninstallforcelist()->mutable_value();
forcelist->add_entries(base::StringPrintf(
"%s;%s",
kShowManagedStorageID,
embedded_test_server()->GetURL(kRelativeUpdateURL).spec().c_str()));
// Set a policy for the app at the policy testserver.
test_server_.UpdatePolicyData(dm_protocol::kChromeExtensionPolicyType,
kShowManagedStorageID,
"{"
" \"string\": {"
" \"Value\": \"policy test value one\""
" }"
"}");
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
// Observe the app installation after login.
content::WindowedNotificationObserver extension_observer(
extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED,
base::Bind(DoesInstallSuccessReferToId, kShowManagedStorageID));
ASSERT_NO_FATAL_FAILURE(StartLogin(std::string(), std::string()));
WaitForSessionStart();
extension_observer.Wait();
// Verify that the app was installed.
Profile* profile = GetProfileForTest();
ASSERT_TRUE(profile);
ExtensionService* extension_service =
extensions::ExtensionSystem::Get(profile)->extension_service();
EXPECT_TRUE(extension_service->GetExtensionById(kShowManagedStorageID, true));
// Wait for the app policy if it hasn't been fetched yet.
ProfilePolicyConnector* connector =
ProfilePolicyConnectorFactory::GetForProfile(profile);
ASSERT_TRUE(connector);
PolicyService* policy_service = connector->policy_service();
ASSERT_TRUE(policy_service);
const PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, kShowManagedStorageID);
if (policy_service->GetPolicies(ns).empty()) {
PolicyChangeRegistrar policy_registrar(policy_service, ns);
base::RunLoop run_loop;
policy_registrar.Observe(
"string", base::Bind(&PolicyChangedCallback, run_loop.QuitClosure()));
run_loop.Run();
}
// Verify that the app policy was set.
base::StringValue expected_value("policy test value one");
EXPECT_TRUE(base::Value::Equals(
&expected_value,
policy_service->GetPolicies(ns).GetValue("string")));
// Now update the policy at the server.
test_server_.UpdatePolicyData(dm_protocol::kChromeExtensionPolicyType,
kShowManagedStorageID,
"{"
" \"string\": {"
" \"Value\": \"policy test value two\""
" }"
"}");
// And issue a policy refresh.
{
PolicyChangeRegistrar policy_registrar(policy_service, ns);
base::RunLoop run_loop;
policy_registrar.Observe(
"string", base::Bind(&PolicyChangedCallback, run_loop.QuitClosure()));
policy_service->RefreshPolicies(base::Closure());
run_loop.Run();
}
// Verify that the app policy was updated.
base::StringValue expected_new_value("policy test value two");
EXPECT_TRUE(base::Value::Equals(
&expected_new_value,
policy_service->GetPolicies(ns).GetValue("string")));
}
class TermsOfServiceDownloadTest : public DeviceLocalAccountTest,
public testing::WithParamInterface<bool> {
};
IN_PROC_BROWSER_TEST_P(TermsOfServiceDownloadTest, TermsOfServiceScreen) {
// Specify Terms of Service URL.
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
device_local_account_policy_.payload().mutable_termsofserviceurl()->set_value(
embedded_test_server()->GetURL(
std::string("/") +
(GetParam() ? kExistentTermsOfServicePath
: kNonexistentTermsOfServicePath)).spec());
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
ASSERT_NO_FATAL_FAILURE(StartLogin(std::string(), std::string()));
// Set up an observer that will quit the message loop when login has succeeded
// and the first wizard screen, if any, is being shown.
base::RunLoop login_wait_run_loop;
chromeos::MockAuthStatusConsumer login_status_consumer;
EXPECT_CALL(login_status_consumer, OnAuthSuccess(_)).Times(1).WillOnce(
InvokeWithoutArgs(&login_wait_run_loop, &base::RunLoop::Quit));
// Spin the loop until the observer fires. Then, unregister the observer.
chromeos::ExistingUserController* controller =
chromeos::ExistingUserController::current_controller();
ASSERT_TRUE(controller);
controller->set_login_status_consumer(&login_status_consumer);
login_wait_run_loop.Run();
controller->set_login_status_consumer(NULL);
// Verify that the Terms of Service screen is being shown.
chromeos::WizardController* wizard_controller =
chromeos::WizardController::default_controller();
ASSERT_TRUE(wizard_controller);
ASSERT_TRUE(wizard_controller->current_screen());
EXPECT_EQ(chromeos::WizardController::kTermsOfServiceScreenName,
wizard_controller->current_screen()->GetName());
// Wait for the Terms of Service to finish downloading, then get the status of
// the screen's UI elements.
std::string json;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents_,
"var screenElement = document.getElementById('terms-of-service');"
"function SendReplyIfDownloadDone() {"
" if (screenElement.classList.contains('tos-loading'))"
" return false;"
" var status = {};"
" status.heading = document.getElementById('tos-heading').textContent;"
" status.subheading ="
" document.getElementById('tos-subheading').textContent;"
" status.contentHeading ="
" document.getElementById('tos-content-heading').textContent;"
" status.content ="
" document.getElementById('tos-content-main').textContent;"
" status.error = screenElement.classList.contains('error');"
" status.acceptEnabled ="
" !document.getElementById('tos-accept-button').disabled;"
" domAutomationController.send(JSON.stringify(status));"
" observer.disconnect();"
" return true;"
"}"
"var observer = new MutationObserver(SendReplyIfDownloadDone);"
"if (!SendReplyIfDownloadDone()) {"
" var options = { attributes: true, attributeFilter: [ 'class' ] };"
" observer.observe(screenElement, options);"
"}",
&json));
scoped_ptr<base::Value> value_ptr(base::JSONReader::Read(json));
const base::DictionaryValue* status = NULL;
ASSERT_TRUE(value_ptr);
ASSERT_TRUE(value_ptr->GetAsDictionary(&status));
std::string heading;
EXPECT_TRUE(status->GetString("heading", &heading));
std::string subheading;
EXPECT_TRUE(status->GetString("subheading", &subheading));
std::string content_heading;
EXPECT_TRUE(status->GetString("contentHeading", &content_heading));
std::string content;
EXPECT_TRUE(status->GetString("content", &content));
bool error;
EXPECT_TRUE(status->GetBoolean("error", &error));
bool accept_enabled;
EXPECT_TRUE(status->GetBoolean("acceptEnabled", &accept_enabled));
// Verify that the screen's headings have been set correctly.
EXPECT_EQ(
l10n_util::GetStringFUTF8(IDS_TERMS_OF_SERVICE_SCREEN_HEADING,
base::UTF8ToUTF16(kDomain)),
heading);
EXPECT_EQ(
l10n_util::GetStringFUTF8(IDS_TERMS_OF_SERVICE_SCREEN_SUBHEADING,
base::UTF8ToUTF16(kDomain)),
subheading);
EXPECT_EQ(
l10n_util::GetStringFUTF8(IDS_TERMS_OF_SERVICE_SCREEN_CONTENT_HEADING,
base::UTF8ToUTF16(kDomain)),
content_heading);
if (!GetParam()) {
// The Terms of Service URL was invalid. Verify that the screen is showing
// an error and the accept button is disabled.
EXPECT_TRUE(error);
EXPECT_FALSE(accept_enabled);
return;
}
// The Terms of Service URL was valid. Verify that the screen is showing the
// downloaded Terms of Service and the accept button is enabled.
base::FilePath test_dir;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
std::string terms_of_service;
ASSERT_TRUE(base::ReadFileToString(
test_dir.Append(kExistentTermsOfServicePath), &terms_of_service));
EXPECT_EQ(terms_of_service, content);
EXPECT_FALSE(error);
EXPECT_TRUE(accept_enabled);
// Click the accept button.
ASSERT_TRUE(content::ExecuteScript(contents_,
"$('tos-accept-button').click();"));
WaitForSessionStart();
}
INSTANTIATE_TEST_CASE_P(TermsOfServiceDownloadTestInstance,
TermsOfServiceDownloadTest, testing::Bool());
} // namespace policy