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> { |
}; |