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 59eb85be77a6a69c85f3d90b8e037f88261c3301..aae8978aa6fd4d78bbbd39ae373eb297629a5557 100644 |
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc |
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc |
@@ -7,6 +7,7 @@ |
#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 +18,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 +35,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,11 +56,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_session_manager_client.h" |
#include "chromeos/dbus/session_manager_client.h" |
+#include "content/public/browser/notification_details.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" |
@@ -64,7 +71,11 @@ |
#include "crypto/rsa_private_key.h" |
#include "grit/chromium_strings.h" |
#include "grit/generated_resources.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 "testing/gmock/include/gmock/gmock.h" |
#include "third_party/cros_system_api/dbus/service_constants.h" |
#include "ui/base/l10n/l10n_util.h" |
@@ -90,6 +101,129 @@ 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. |
+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); |
+}; |
+ |
+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.PassAs<net::test_server::HttpResponse>(); |
+} |
+ |
+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 string16>(details)->find(UTF8ToUTF16(id)) != |
+ string16::npos; |
+} |
} // namespace |
@@ -420,6 +554,92 @@ 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. |
+ 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("/") + kGoodExtensionPath)); |
+ 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); |
+ |
+ // 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. |
+ content::WindowedNotificationObserver hosted_app_observer( |
+ chrome::NOTIFICATION_EXTENSION_INSTALLED, |
+ base::Bind(DoesInstallSuccessReferToId, kHostedAppID)); |
+ content::WindowedNotificationObserver extension_observer( |
+ chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, |
+ base::Bind(DoesInstallFailureReferToId, 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> { |
}; |