Chromium Code Reviews| Index: chrome/browser/chromeos/policy/device_local_account_browsertest.cc |
| diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc |
| index c125281f4547b33a63715d117fe8efc9f0af7643..891f5107e95d68d56e0bb619f4a1ecaa85dc5865 100644 |
| --- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc |
| +++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc |
| @@ -4,9 +4,11 @@ |
| #include <map> |
| #include <string> |
| +#include <utility> |
| #include "base/basictypes.h" |
| #include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/command_line.h" |
| #include "base/file_util.h" |
| @@ -17,6 +19,7 @@ |
| #include "base/path_service.h" |
| #include "base/run_loop.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/browser_process.h" |
| @@ -33,6 +36,8 @@ |
| #include "chrome/browser/chromeos/policy/device_local_account.h" |
| #include "chrome/browser/chromeos/policy/device_policy_builder.h" |
| #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h" |
| +#include "chrome/browser/extensions/extension_service.h" |
| +#include "chrome/browser/extensions/extension_system.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/policy/cloud/cloud_policy_constants.h" |
| #include "chrome/browser/policy/cloud/policy_builder.h" |
| @@ -52,12 +57,14 @@ |
| #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| +#include "chrome/common/extensions/extension.h" |
| #include "chromeos/chromeos_switches.h" |
| #include "chromeos/dbus/cryptohome_client.h" |
| #include "chromeos/dbus/dbus_method_call_status.h" |
| #include "chromeos/dbus/fake_cryptohome_client.h" |
| #include "chromeos/dbus/fake_session_manager_client.h" |
| #include "chromeos/dbus/session_manager_client.h" |
| +#include "content/public/browser/notification_details.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_ui.h" |
| #include "content/public/test/browser_test_utils.h" |
| @@ -65,7 +72,10 @@ |
| #include "crypto/rsa_private_key.h" |
| #include "grit/chromium_strings.h" |
| #include "grit/generated_resources.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 "testing/gmock/include/gmock/gmock.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| #include "ui/base/l10n/l10n_util.h" |
| @@ -91,6 +101,160 @@ const char* kStartupURLs[] = { |
| }; |
| 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[] = "0.1"; |
| +const char kGoodExtensionID[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf"; |
| +const char kGoodExtensionPath[] = "extensions/good.crx"; |
| +const char kGoodExtensionVersion[] = "1.0"; |
| + |
| +// Helper that serves extension update manifests to Chrome. The helper registers |
| +// itself with the |test_server|, serving update manifests for all extensions in |
| +// |extension_map| at |relative_update_url|. |
| +// The CRX files for these extensions must be served by the same |test_server|. |
| +// For each extension, the |extension_map| should contain the path to the CRX |
| +// file (relative to the directory from which the |test_server| serves files) |
| +// and its version. |
| +class TestingUpdateManifestProvider { |
| + public: |
| + typedef std::map<std::string, std::pair<std::string, std::string> > |
| + ExtensionMap; |
| + |
| + TestingUpdateManifestProvider( |
| + net::test_server::EmbeddedTestServer* test_server, |
| + const std::string& relative_update_url, |
| + const ExtensionMap& extension_map); |
| + ~TestingUpdateManifestProvider(); |
| + |
| + scoped_ptr<net::test_server::HttpResponse> HandleRequest( |
| + const net::test_server::HttpRequest& request); |
| + |
| + private: |
| + net::test_server::EmbeddedTestServer* test_server_; // Not owned. |
| + const std::string relative_update_url_; |
| + const ExtensionMap extension_map_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestingUpdateManifestProvider); |
| +}; |
| + |
| +class ExtensionInstallSuccessObserver |
| + : public content::WindowedNotificationObserver { |
| + public: |
| + explicit ExtensionInstallSuccessObserver(const std::string& id); |
| + ~ExtensionInstallSuccessObserver(); |
| + |
| + private: |
| + bool CheckNotification(); |
| + |
| + const std::string id_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ExtensionInstallSuccessObserver); |
| +}; |
| + |
| +class ExtensionInstallFailureObserver |
| + : public content::WindowedNotificationObserver { |
| + public: |
| + explicit ExtensionInstallFailureObserver(const std::string& id); |
| + ~ExtensionInstallFailureObserver(); |
| + |
| + private: |
| + bool CheckNotification(); |
| + |
| + const string16 id_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ExtensionInstallFailureObserver); |
| +}; |
| + |
| +ExtensionInstallSuccessObserver::ExtensionInstallSuccessObserver( |
| + const std::string& id) |
| + : content::WindowedNotificationObserver( |
| + chrome::NOTIFICATION_EXTENSION_INSTALLED, |
| + base::Bind(&ExtensionInstallSuccessObserver::CheckNotification, |
| + base::Unretained(this))), |
| + id_(id) { |
| +} |
| + |
| +ExtensionInstallSuccessObserver::~ExtensionInstallSuccessObserver() { |
| +} |
| + |
| +bool ExtensionInstallSuccessObserver::CheckNotification() { |
| + return content::Details<const extensions::InstalledExtensionInfo>( |
| + details())->extension->id() == id_; |
| +} |
| + |
| +ExtensionInstallFailureObserver::ExtensionInstallFailureObserver( |
| + const std::string& id) |
| + : content::WindowedNotificationObserver( |
| + chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, |
| + base::Bind(&ExtensionInstallFailureObserver::CheckNotification, |
| + base::Unretained(this))), |
| + id_(UTF8ToUTF16(id)) { |
| +} |
| + |
| +ExtensionInstallFailureObserver::~ExtensionInstallFailureObserver() { |
| +} |
| + |
| +bool ExtensionInstallFailureObserver::CheckNotification() { |
| + return content::Details<const string16>(details())->find(id_) != |
| + string16::npos; |
| +} |
| + |
| +TestingUpdateManifestProvider::TestingUpdateManifestProvider( |
|
Mattias Nissler (ping if slow)
2013/09/25 12:04:08
Definition order doesn't match declaration order.
bartfab (slow)
2013/09/26 12:39:13
Done.
|
| + net::test_server::EmbeddedTestServer* test_server, |
| + const std::string& relative_update_url, |
| + const ExtensionMap& extension_map) |
|
Mattias Nissler (ping if slow)
2013/09/25 12:04:08
Instead of passing in the map, can we just make an
bartfab (slow)
2013/09/26 12:39:13
Done.
|
| + : test_server_(test_server), |
| + relative_update_url_(relative_update_url), |
| + extension_map_(extension_map) { |
| + test_server_->RegisterRequestHandler( |
|
Mattias Nissler (ping if slow)
2013/09/25 12:04:08
I'd just leave this to the user.
bartfab (slow)
2013/09/26 12:39:13
Done.
|
| + base::Bind(&TestingUpdateManifestProvider::HandleRequest, |
| + base::Unretained(this))); |
| +} |
| + |
| +TestingUpdateManifestProvider::~TestingUpdateManifestProvider() { |
| +} |
| + |
| +scoped_ptr<net::test_server::HttpResponse> |
| + TestingUpdateManifestProvider::HandleRequest( |
| + const net::test_server::HttpRequest& request) { |
| + if (request.relative_url.find(relative_update_url_) != 0) |
| + return scoped_ptr<net::test_server::HttpResponse>(); |
| + |
| + std::string content = kUpdateManifestHeader; |
| + size_t id_pos = 0; |
| + while ((id_pos = request.relative_url.find("id%3D", id_pos)) != |
| + std::string::npos) { |
| + id_pos += 5; |
| + const std::string id = request.relative_url.substr(id_pos, 32); |
| + id_pos += 32; |
| + ExtensionMap::const_iterator entry = extension_map_.find(id); |
| + if (entry != extension_map_.end()) { |
| + content += base::StringPrintf( |
| + kUpdateManifestTemplate, |
| + id.c_str(), |
| + test_server_->GetURL(std::string("/") + entry->second.first) |
| + .spec().c_str(), |
| + entry->second.second.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.PassAs<net::test_server::HttpResponse>(); |
| +} |
| } // namespace |
| @@ -421,6 +585,86 @@ IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, FullscreenDisallowed) { |
| EXPECT_FALSE(browser_window->IsFullscreen()); |
| } |
| +IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionWhitelist) { |
| + // Make it possible to force-install a hosted app and an extension. |
| + TestingUpdateManifestProvider::ExtensionMap extension_map; |
| + extension_map[kHostedAppID] = |
| + std::pair<std::string, std::string>(kHostedAppCRXPath, |
| + kHostedAppVersion); |
| + extension_map[kGoodExtensionID] = |
| + std::pair<std::string, std::string>(kGoodExtensionPath, |
| + kGoodExtensionVersion); |
| + TestingUpdateManifestProvider testing_update_manifest_provider( |
| + embedded_test_server(), |
| + kRelativeUpdateURL, |
| + extension_map); |
| + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
| + |
| + // 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); |
| + |
| + // This observes the display name becoming available as this indicates |
| + // device-local account policy is fully loaded, which is a prerequisite for |
| + // successful login. |
| + content::WindowedNotificationObserver( |
| + chrome::NOTIFICATION_USER_LIST_CHANGED, |
| + base::Bind(&DisplayNameMatches, user_id_1_, kDisplayName)).Wait(); |
| + |
| + // Wait for the login UI to be ready. |
| + chromeos::LoginDisplayHostImpl* host = |
| + reinterpret_cast<chromeos::LoginDisplayHostImpl*>( |
| + chromeos::LoginDisplayHostImpl::default_host()); |
| + ASSERT_TRUE(host); |
| + 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(); |
| + |
| + // Ensure that the browser stays alive, even though no windows are opened |
| + // during session start. |
| + chrome::StartKeepAlive(); |
| + |
| + // Start listening for app/extension installation results. |
| + ExtensionInstallSuccessObserver hosted_app_observer(kHostedAppID); |
| + ExtensionInstallFailureObserver extension_observer(kGoodExtensionID); |
| + |
| + // Start login into the device-local account. |
| + host->StartSignInScreen(); |
| + chromeos::ExistingUserController* controller = |
| + chromeos::ExistingUserController::current_controller(); |
| + ASSERT_TRUE(controller); |
| + controller->LoginAsPublicAccount(user_id_1_); |
| + |
| + // 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 = ProfileManager::GetDefaultProfile(); |
| + 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)); |
| +} |
| + |
| class TermsOfServiceTest : public DeviceLocalAccountTest, |
| public testing::WithParamInterface<bool> { |
| }; |