summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chromeos
diff options
context:
space:
mode:
authorbartfab@chromium.org <bartfab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-21 20:10:21 +0000
committerbartfab@chromium.org <bartfab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-21 20:10:21 +0000
commit535380a3d4c99727bd0cd197ad9d9525c2f3c983 (patch)
tree3362bb78a3afde95fc28d59230b7eef2cd9b4ee5 /chrome/browser/chromeos
parentc124bdbd7e9d0420046984828cc121a209d44da5 (diff)
downloadchromium_src-535380a3d4c99727bd0cd197ad9d9525c2f3c983.zip
chromium_src-535380a3d4c99727bd0cd197ad9d9525c2f3c983.tar.gz
chromium_src-535380a3d4c99727bd0cd197ad9d9525c2f3c983.tar.bz2
Cache force-installed apps/extensions in device-local accounts
This CL adds the DeviceLocalAccountExternalPolicyLoader, a replacement for the ExternalPolicyLoader that caches force-installed apps/extensions, allowing them to be installed offline. The caches for individual accounts are managed by the DeviceLocalAccountPolicyService which makes sure to remove obsolete cache directories. BUG=287802 TEST=Full coverage with new browser and unit tests Review URL: https://codereview.chromium.org/27548004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@229896 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos')
-rw-r--r--chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc114
-rw-r--r--chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h78
-rw-r--r--chrome/browser/chromeos/extensions/device_local_account_external_policy_loader_unittest.cc309
-rw-r--r--chrome/browser/chromeos/extensions/external_pref_cache_loader.h5
-rw-r--r--chrome/browser/chromeos/policy/device_local_account.h16
-rw-r--r--chrome/browser/chromeos/policy/device_local_account_browsertest.cc137
-rw-r--r--chrome/browser/chromeos/policy/device_local_account_policy_provider.cc1
-rw-r--r--chrome/browser/chromeos/policy/device_local_account_policy_service.cc358
-rw-r--r--chrome/browser/chromeos/policy/device_local_account_policy_service.h101
-rw-r--r--chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc853
-rw-r--r--chrome/browser/chromeos/settings/device_settings_test_helper.cc1
11 files changed, 1607 insertions, 366 deletions
diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc
new file mode 100644
index 0000000..affd4d2
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc
@@ -0,0 +1,114 @@
+// Copyright 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 "chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/prefs/pref_value_map.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/policy/configuration_policy_handler.h"
+#include "chrome/browser/policy/policy_map.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace chromeos {
+
+namespace {
+
+// A dummy pref name under which policy::ExtensionInstallForcelistPolicyHandler
+// can store the parsed list of force-installed extensions.
+const char kExtensionInstallForcelist[] = "forcelist";
+
+} // namespace
+
+DeviceLocalAccountExternalPolicyLoader::
+ DeviceLocalAccountExternalPolicyLoader(policy::CloudPolicyStore* store,
+ const base::FilePath& cache_dir)
+ : store_(store),
+ cache_dir_(cache_dir) {
+}
+
+bool DeviceLocalAccountExternalPolicyLoader::IsCacheRunning() const {
+ return external_cache_;
+}
+
+void DeviceLocalAccountExternalPolicyLoader::StartCache(
+ const scoped_refptr<base::SequencedTaskRunner>& cache_task_runner) {
+ DCHECK(!external_cache_);
+ store_->AddObserver(this);
+ external_cache_.reset(new ExternalCache(
+ cache_dir_,
+ g_browser_process->system_request_context(),
+ cache_task_runner,
+ this,
+ true /* always_check_updates */,
+ false /* wait_for_cache_initialization */));
+
+ if (store_->is_initialized())
+ UpdateExtensionListFromStore();
+}
+
+void DeviceLocalAccountExternalPolicyLoader::StopCache(
+ const base::Closure& callback) {
+ if (external_cache_) {
+ external_cache_->Shutdown(callback);
+ external_cache_.reset();
+ store_->RemoveObserver(this);
+ }
+
+ base::DictionaryValue empty_prefs;
+ OnExtensionListsUpdated(&empty_prefs);
+}
+
+void DeviceLocalAccountExternalPolicyLoader::StartLoading() {
+ if (prefs_)
+ LoadFinished();
+}
+
+void DeviceLocalAccountExternalPolicyLoader::OnStoreLoaded(
+ policy::CloudPolicyStore* store) {
+ DCHECK(external_cache_);
+ DCHECK_EQ(store_, store);
+ UpdateExtensionListFromStore();
+}
+
+void DeviceLocalAccountExternalPolicyLoader::OnStoreError(
+ policy::CloudPolicyStore* store) {
+ DCHECK(external_cache_);
+ DCHECK_EQ(store_, store);
+}
+
+void DeviceLocalAccountExternalPolicyLoader::OnExtensionListsUpdated(
+ const base::DictionaryValue* prefs) {
+ DCHECK(external_cache_ || prefs->empty());
+ prefs_.reset(prefs->DeepCopy());
+ LoadFinished();
+}
+
+DeviceLocalAccountExternalPolicyLoader::
+ ~DeviceLocalAccountExternalPolicyLoader() {
+ DCHECK(!external_cache_);
+}
+
+void DeviceLocalAccountExternalPolicyLoader::UpdateExtensionListFromStore() {
+ scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
+ const policy::PolicyMap& policy_map = store_->policy_map();
+ policy::ExtensionInstallForcelistPolicyHandler
+ policy_handler(kExtensionInstallForcelist);
+ if (policy_handler.CheckPolicySettings(policy_map, NULL)) {
+ PrefValueMap pref_value_map;
+ policy_handler.ApplyPolicySettings(policy_map, &pref_value_map);
+ const base::Value* value = NULL;
+ const base::DictionaryValue* dict = NULL;
+ if (pref_value_map.GetValue(kExtensionInstallForcelist, &value) &&
+ value->GetAsDictionary(&dict)) {
+ prefs.reset(dict->DeepCopy());
+ }
+ }
+
+ external_cache_->UpdateExtensionsList(prefs.Pass());
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h
new file mode 100644
index 0000000..c9abf5d
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h
@@ -0,0 +1,78 @@
+// Copyright 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEVICE_LOCAL_ACCOUNT_EXTERNAL_POLICY_LOADER_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEVICE_LOCAL_ACCOUNT_EXTERNAL_POLICY_LOADER_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "chrome/browser/chromeos/extensions/external_cache.h"
+#include "chrome/browser/extensions/external_loader.h"
+#include "chrome/browser/policy/cloud/cloud_policy_store.h"
+
+namespace chromeos {
+
+// A specialization of the ExternalLoader that serves external extensions from
+// the enterprise policy force-install list. This class is used for device-local
+// accounts in place of the ExternalPolicyLoader. The difference is that while
+// the ExternalPolicyLoader requires extensions to be downloaded on-the-fly,
+// this class caches them, allowing for offline installation.
+class DeviceLocalAccountExternalPolicyLoader
+ : public extensions::ExternalLoader,
+ public policy::CloudPolicyStore::Observer,
+ public ExternalCache::Delegate {
+ public:
+ // The list of force-installed extensions will be read from |store| and the
+ // extensions will be cached in the |cache_dir_|.
+ DeviceLocalAccountExternalPolicyLoader(policy::CloudPolicyStore* store,
+ const base::FilePath& cache_dir);
+
+ // While running, the cache requires exclusive write access to the
+ // |cache_dir_|.
+ bool IsCacheRunning() const;
+
+ // Start the cache. This method must only be invoked when there are no pending
+ // write operations to |cache_dir_| on any thread and none will be initiated
+ // while the cache is running.
+ void StartCache(
+ const scoped_refptr<base::SequencedTaskRunner>& cache_task_runner);
+
+ // Stop the cache. The |callback| will be invoked when the cache has shut down
+ // completely and write access to the |cache_dir_| is permitted again.
+ void StopCache(const base::Closure& callback);
+
+ // extensions::ExternalLoader:
+ virtual void StartLoading() OVERRIDE;
+
+ // policy::CloudPolicyStore::Observer:
+ virtual void OnStoreLoaded(policy::CloudPolicyStore* store) OVERRIDE;
+ virtual void OnStoreError(policy::CloudPolicyStore* store) OVERRIDE;
+
+ // ExternalCache::Delegate:
+ virtual void OnExtensionListsUpdated(
+ const base::DictionaryValue* prefs) OVERRIDE;
+
+ private:
+ // If the cache was started, it must be stopped before |this| is destroyed.
+ virtual ~DeviceLocalAccountExternalPolicyLoader();
+
+ // Pass the current list of force-installed extensions from the |store_| to
+ // the |external_cache_|.
+ void UpdateExtensionListFromStore();
+
+ policy::CloudPolicyStore* store_;
+ const base::FilePath cache_dir_;
+ scoped_ptr<ExternalCache> external_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountExternalPolicyLoader);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEVICE_LOCAL_ACCOUNT_EXTERNAL_POLICY_LOADER_H_
diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader_unittest.cc b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader_unittest.cc
new file mode 100644
index 0000000..27a70cd
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader_unittest.cc
@@ -0,0 +1,309 @@
+// Copyright 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 "chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h"
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/external_provider_impl.h"
+#include "chrome/browser/extensions/external_provider_interface.h"
+#include "chrome/browser/extensions/updater/extension_downloader.h"
+#include "chrome/browser/policy/cloud/mock_cloud_policy_store.h"
+#include "chrome/browser/policy/policy_map.h"
+#include "chrome/browser/policy/policy_types.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/common/manifest.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_test_util.h"
+#include "policy/policy_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using ::testing::InvokeWithoutArgs;
+using ::testing::Mock;
+using ::testing::_;
+
+namespace chromeos {
+
+namespace {
+
+const char kCacheDir[] = "cache";
+const char kExtensionId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
+const char kExtensionUpdateManifest[] =
+ "extensions/good_v1_update_manifest.xml";
+const char kExtensionCRXSourceDir[] = "extensions";
+const char kExtensionCRXFile[] = "good.crx";
+const char kExtensionCRXVersion[] = "1.0.0.0";
+
+class MockExternalPolicyProviderVisitor
+ : public extensions::ExternalProviderInterface::VisitorInterface {
+ public:
+ MockExternalPolicyProviderVisitor();
+ virtual ~MockExternalPolicyProviderVisitor();
+
+ MOCK_METHOD6(OnExternalExtensionFileFound,
+ bool(const std::string&,
+ const base::Version*,
+ const base::FilePath&,
+ extensions::Manifest::Location,
+ int,
+ bool));
+ MOCK_METHOD5(OnExternalExtensionUpdateUrlFound,
+ bool(const std::string&,
+ const GURL&,
+ extensions::Manifest::Location,
+ int,
+ bool));
+ MOCK_METHOD1(OnExternalProviderReady,
+ void(const extensions::ExternalProviderInterface* provider));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockExternalPolicyProviderVisitor);
+};
+
+MockExternalPolicyProviderVisitor::MockExternalPolicyProviderVisitor() {
+}
+
+MockExternalPolicyProviderVisitor::~MockExternalPolicyProviderVisitor() {
+}
+
+} // namespace
+
+class DeviceLocalAccountExternalPolicyLoaderTest : public testing::Test {
+ protected:
+ DeviceLocalAccountExternalPolicyLoaderTest();
+ virtual ~DeviceLocalAccountExternalPolicyLoaderTest();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ void VerifyAndResetVisitorCallExpectations();
+ void SetForceInstallListPolicy();
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ base::ScopedTempDir temp_dir_;
+ base::FilePath cache_dir_;
+ policy::MockCloudPolicyStore store_;
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ base::FilePath test_dir_;
+
+ scoped_refptr<DeviceLocalAccountExternalPolicyLoader> loader_;
+ MockExternalPolicyProviderVisitor visitor_;
+ scoped_ptr<extensions::ExternalProviderImpl> provider_;
+};
+
+DeviceLocalAccountExternalPolicyLoaderTest::
+ DeviceLocalAccountExternalPolicyLoaderTest()
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
+}
+
+DeviceLocalAccountExternalPolicyLoaderTest::
+ ~DeviceLocalAccountExternalPolicyLoaderTest() {
+}
+
+void DeviceLocalAccountExternalPolicyLoaderTest::SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ cache_dir_ = temp_dir_.path().Append(kCacheDir);
+ ASSERT_TRUE(file_util::CreateDirectoryAndGetError(cache_dir_, NULL));
+ request_context_getter_ =
+ new net::TestURLRequestContextGetter(base::MessageLoopProxy::current());
+ TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(
+ request_context_getter_.get());
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir_));
+
+ loader_ = new DeviceLocalAccountExternalPolicyLoader(&store_, cache_dir_);
+ provider_.reset(new extensions::ExternalProviderImpl(
+ &visitor_,
+ loader_,
+ NULL,
+ extensions::Manifest::EXTERNAL_POLICY,
+ extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD,
+ extensions::Extension::NO_FLAGS));
+
+ content::RenderProcessHost::SetRunRendererInProcess(true);
+
+ VerifyAndResetVisitorCallExpectations();
+}
+
+void DeviceLocalAccountExternalPolicyLoaderTest::TearDown() {
+ content::RenderProcessHost::SetRunRendererInProcess(false);
+ TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(NULL);
+}
+
+void DeviceLocalAccountExternalPolicyLoaderTest::
+ VerifyAndResetVisitorCallExpectations() {
+ Mock::VerifyAndClearExpectations(&visitor_);
+ EXPECT_CALL(visitor_, OnExternalExtensionFileFound(_, _, _, _, _, _))
+ .Times(0);
+ EXPECT_CALL(visitor_, OnExternalExtensionUpdateUrlFound(_, _, _, _, _))
+ .Times(0);
+ EXPECT_CALL(visitor_, OnExternalProviderReady(_))
+ .Times(0);
+}
+
+void DeviceLocalAccountExternalPolicyLoaderTest::SetForceInstallListPolicy() {
+ scoped_ptr<base::ListValue> forcelist(new base::ListValue);
+ forcelist->AppendString("invalid");
+ forcelist->AppendString(base::StringPrintf(
+ "%s;%s",
+ kExtensionId,
+ extension_urls::GetWebstoreUpdateUrl().spec().c_str()));
+ store_.policy_map_.Set(policy::key::kExtensionInstallForcelist,
+ policy::POLICY_LEVEL_MANDATORY,
+ policy::POLICY_SCOPE_USER,
+ forcelist.release(),
+ NULL);
+ store_.NotifyStoreLoaded();
+}
+
+// Verifies that when the cache is not explicitly started, the loader does not
+// serve any extensions, even if the force-install list policy is set or a load
+// is manually requested.
+TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, CacheNotStarted) {
+ // Set the force-install list policy.
+ SetForceInstallListPolicy();
+
+ // Manually request a load.
+ loader_->StartLoading();
+
+ EXPECT_FALSE(loader_->IsCacheRunning());
+ EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
+}
+
+// Verifies that the cache can be started and stopped correctly.
+TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, ForceInstallListEmpty) {
+ // Set an empty force-install list policy.
+ store_.NotifyStoreLoaded();
+
+ // Start the cache. Verify that the loader announces an empty extension list.
+ EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
+ .Times(1);
+ loader_->StartCache(base::MessageLoopProxy::current());
+ VerifyAndResetVisitorCallExpectations();
+
+ // Stop the cache. Verify that the loader announces an empty extension list.
+ EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
+ .Times(1);
+ base::RunLoop run_loop;
+ loader_->StopCache(run_loop.QuitClosure());
+ VerifyAndResetVisitorCallExpectations();
+
+ // Spin the loop until the cache shutdown callback is invoked. Verify that at
+ // that point, no further file I/O tasks are pending.
+ run_loop.Run();
+ EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
+}
+
+// Verifies that when a force-install list policy referencing an extension is
+// set and the cache is started, the loader downloads, caches and serves the
+// extension.
+TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, ForceInstallListSet) {
+ // Set a force-install list policy that contains an invalid entry (which
+ // should be ignored) and a valid reference to an extension.
+ SetForceInstallListPolicy();
+
+ // Start the cache.
+ loader_->StartCache(base::MessageLoopProxy::current());
+
+ // Spin the loop, allowing the loader to process the force-install list.
+ // Verify that the loader announces an empty extension list.
+ net::TestURLFetcherFactory factory;
+ EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
+ .Times(1);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Verify that a downloader has started and is attempting to download an
+ // update manifest.
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(
+ extensions::ExtensionDownloader::kManifestFetcherId);
+ ASSERT_TRUE(fetcher);
+ ASSERT_TRUE(fetcher->delegate());
+
+ // Return a manifest to the downloader.
+ std::string manifest;
+ EXPECT_TRUE(base::ReadFileToString(test_dir_.Append(kExtensionUpdateManifest),
+ &manifest));
+ fetcher->set_response_code(200);
+ fetcher->SetResponseString(manifest);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+
+ // Wait for the manifest to be parsed.
+ content::WindowedNotificationObserver(
+ chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
+ content::NotificationService::AllSources()).Wait();
+
+ // Verify that the downloader is attempting to download a CRX file.
+ fetcher = factory.GetFetcherByID(
+ extensions::ExtensionDownloader::kExtensionFetcherId);
+ ASSERT_TRUE(fetcher);
+ ASSERT_TRUE(fetcher->delegate());
+
+ // Create a temporary CRX file and return its path to the downloader.
+ EXPECT_TRUE(base::CopyFile(
+ test_dir_.Append(kExtensionCRXSourceDir).Append(kExtensionCRXFile),
+ temp_dir_.path().Append(kExtensionCRXFile)));
+ fetcher->set_response_code(200);
+ fetcher->SetResponseFilePath(temp_dir_.path().Append(kExtensionCRXFile));
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+
+ // Spin the loop. Verify that the loader announces the presence of a new CRX
+ // file, served from the cache directory.
+ const base::FilePath cached_crx_path = cache_dir_.Append(base::StringPrintf(
+ "%s-%s.crx", kExtensionId, kExtensionCRXVersion));
+ base::RunLoop cache_run_loop;
+ EXPECT_CALL(visitor_, OnExternalExtensionFileFound(
+ kExtensionId,
+ _,
+ cached_crx_path,
+ extensions::Manifest::EXTERNAL_POLICY,
+ _,
+ _));
+ EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(&cache_run_loop, &base::RunLoop::Quit));
+ cache_run_loop.Run();
+ VerifyAndResetVisitorCallExpectations();
+
+ // Verify that the CRX file actually exists in the cache directory and its
+ // contents matches the file returned to the downloader.
+ EXPECT_TRUE(base::ContentsEqual(
+ test_dir_.Append(kExtensionCRXSourceDir).Append(kExtensionCRXFile),
+ cached_crx_path));
+
+ // Stop the cache. Verify that the loader announces an empty extension list.
+ EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
+ .Times(1);
+ base::RunLoop shutdown_run_loop;
+ loader_->StopCache(shutdown_run_loop.QuitClosure());
+ VerifyAndResetVisitorCallExpectations();
+
+ // Spin the loop until the cache shutdown callback is invoked. Verify that at
+ // that point, no further file I/O tasks are pending.
+ shutdown_run_loop.Run();
+ EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/external_pref_cache_loader.h b/chrome/browser/chromeos/extensions/external_pref_cache_loader.h
index 16a08ff..e448182 100644
--- a/chrome/browser/chromeos/extensions/external_pref_cache_loader.h
+++ b/chrome/browser/chromeos/extensions/external_pref_cache_loader.h
@@ -9,9 +9,8 @@
namespace chromeos {
-// A specialization of the ExternalPrefCacheLoader that caches crx files for
-// external extensions with update URL in common place for all users on the
-// machine.
+// A specialization of the ExternalPrefLoader that caches crx files for external
+// extensions with update URL in a common place for all users on the machine.
class ExternalPrefCacheLoader : public extensions::ExternalPrefLoader {
public:
// All instances of ExternalPrefCacheLoader use the same cache so
diff --git a/chrome/browser/chromeos/policy/device_local_account.h b/chrome/browser/chromeos/policy/device_local_account.h
index ff83461..d10b228 100644
--- a/chrome/browser/chromeos/policy/device_local_account.h
+++ b/chrome/browser/chromeos/policy/device_local_account.h
@@ -32,6 +32,22 @@ struct DeviceLocalAccount {
~DeviceLocalAccount();
Type type;
+ // A device-local account has two identifiers:
+ // * The |account_id| is chosen by the entity that defines the device-local
+ // account. The only constraints are that the |account_id| be unique and,
+ // for legacy reasons, it contain an @ symbol.
+ // * The |user_id| is a synthesized identifier that is guaranteed to be
+ // unique, contain an @ symbol, not collide with the |user_id| of any other
+ // user on the device (such as regular users or supervised users) and be
+ // identifiable as belonging to a device-local account by.
+ // The |account_id| is primarily used by policy code: If device policy defines
+ // a device-local account with a certain |account_id|, the user policy for
+ // that account has to be fetched by referencing the same |account_id|.
+ // The |user_id| is passed to the chromeos::UserManager where it becomes part
+ // of the global user list on the device. The |account_id| would not be safe
+ // to use here as it is a free-form identifier that could conflict with
+ // another |user_id| on the device and cannot be easily identified as
+ // belonging to a device-local account.
std::string account_id;
std::string user_id;
std::string kiosk_app_id;
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index 12e5622..9934608 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -12,14 +12,17 @@
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
#include "base/json/json_reader.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/run_loop.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/test/scoped_path_override.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
@@ -57,6 +60,7 @@
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
+#include "chromeos/chromeos_paths.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_method_call_status.h"
@@ -113,9 +117,9 @@ const char kUpdateManifestFooter[] =
"</gupdate>\n";
const char kHostedAppID[] = "kbmnembihfiondgfjekmnmcbddelicoi";
const char kHostedAppCRXPath[] = "extensions/hosted_app.crx";
-const char kHostedAppVersion[] = "0.1";
+const char kHostedAppVersion[] = "1.0.0.0";
const char kGoodExtensionID[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
-const char kGoodExtensionPath[] = "extensions/good.crx";
+const char kGoodExtensionCRXPath[] = "extensions/good.crx";
const char kGoodExtensionVersion[] = "1.0";
// Helper that serves extension update manifests to Chrome.
@@ -247,6 +251,11 @@ class DeviceLocalAccountTest : public DevicePolicyCrosBrowserTest {
PolicyBuilder::kFakeDeviceId);
ASSERT_TRUE(test_server_.Start());
+ ASSERT_TRUE(extension_cache_root_dir_.CreateUniqueTempDir());
+ extension_cache_root_dir_override_.reset(new base::ScopedPathOverride(
+ chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE,
+ extension_cache_root_dir_.path()));
+
DevicePolicyCrosBrowserTest::SetUp();
}
@@ -336,9 +345,24 @@ class DeviceLocalAccountTest : public DevicePolicyCrosBrowserTest {
EXPECT_EQ(chromeos::User::USER_TYPE_PUBLIC_ACCOUNT, user->GetType());
}
+ base::FilePath GetCacheDirectoryForAccountID(const std::string& account_id) {
+ return extension_cache_root_dir_.path()
+ .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 GetCacheDirectoryForAccountID(account_id)
+ .Append(base::StringPrintf("%s-%s.crx", id.c_str(), version.c_str()));
+ }
+
const std::string user_id_1_;
const std::string user_id_2_;
+ base::ScopedTempDir extension_cache_root_dir_;
+ scoped_ptr<base::ScopedPathOverride> extension_cache_root_dir_override_;
+
UserPolicyBuilder device_local_account_policy_;
LocalPolicyTestServer test_server_;
};
@@ -554,7 +578,7 @@ IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, FullscreenDisallowed) {
EXPECT_FALSE(browser_window->IsFullscreen());
}
-IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionWhitelist) {
+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(
@@ -566,7 +590,7 @@ IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionWhitelist) {
testing_update_manifest_provider.AddUpdate(
kGoodExtensionID,
kGoodExtensionVersion,
- embedded_test_server()->GetURL(std::string("/") + kGoodExtensionPath));
+ embedded_test_server()->GetURL(std::string("/") + kGoodExtensionCRXPath));
embedded_test_server()->RegisterRequestHandler(
base::Bind(&TestingUpdateManifestProvider::HandleRequest,
base::Unretained(&testing_update_manifest_provider)));
@@ -625,6 +649,105 @@ IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionWhitelist) {
controller->LoginAsPublicAccount(user_id_1_);
// 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 = 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));
+
+ // Verify that the app was copied 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 not copied to the account's extension cache.
+ EXPECT_FALSE(PathExists(GetCacheCRXFile(
+ kAccountId1, kGoodExtensionID, kGoodExtensionVersion)));
+}
+
+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(file_util::CreateDirectory(
+ GetCacheDirectoryForAccountID(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));
+ const base::FilePath cached_extension =
+ GetCacheCRXFile(kAccountId1, kGoodExtensionID, kGoodExtensionVersion);
+ EXPECT_TRUE(CopyFile(test_dir.Append(kGoodExtensionCRXPath),
+ cached_extension));
+
+ // 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);
+
+ // 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();
@@ -638,6 +761,12 @@ IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, ExtensionWhitelist) {
// 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.
+ EXPECT_FALSE(PathExists(cached_extension));
}
class TermsOfServiceTest : public DeviceLocalAccountTest,
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_provider.cc b/chrome/browser/chromeos/policy/device_local_account_policy_provider.cc
index 6b0535e..4381ce4 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_provider.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_provider.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/policy/device_local_account_policy_provider.h"
#include "base/bind.h"
+#include "chrome/browser/policy/cloud/cloud_policy_core.h"
#include "chrome/browser/policy/cloud/cloud_policy_service.h"
#include "chrome/browser/policy/policy_bundle.h"
#include "chrome/browser/policy/policy_service.h"
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
index 825d39c..3579bec 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
@@ -7,18 +7,24 @@
#include <vector>
#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/path_service.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/chromeos/policy/device_local_account_policy_store.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
#include "chrome/browser/policy/cloud/cloud_policy_client.h"
#include "chrome/browser/policy/cloud/cloud_policy_constants.h"
#include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h"
#include "chrome/browser/policy/cloud/device_management_service.h"
#include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
+#include "chromeos/chromeos_paths.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/settings/cros_settings_provider.h"
@@ -53,23 +59,86 @@ scoped_ptr<CloudPolicyClient> CreateClient(
return client.Pass();
}
+// Get the subdirectory of the cache directory in which force-installed
+// extensions are cached for |account_id|.
+std::string GetCacheSubdirectoryForAccountID(const std::string& account_id) {
+ return base::HexEncode(account_id.c_str(), account_id.size());
+}
+
+// Cleans up the cache directory by removing subdirectories that are not found
+// in |subdirectories_to_keep|. Only caches whose cache directory is found in
+// |subdirectories_to_keep| may be running while the clean-up is in progress.
+void DeleteOrphanedExtensionCaches(
+ const std::set<std::string>& subdirectories_to_keep) {
+ base::FilePath cache_root_dir;
+ CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE,
+ &cache_root_dir));
+ base::FileEnumerator enumerator(cache_root_dir,
+ false,
+ base::FileEnumerator::DIRECTORIES);
+ for (base::FilePath path = enumerator.Next(); !path.empty();
+ path = enumerator.Next()) {
+ const std::string subdirectory(path.BaseName().MaybeAsASCII());
+ if (subdirectories_to_keep.find(subdirectory) ==
+ subdirectories_to_keep.end()) {
+ base::DeleteFile(path, true);
+ }
+ }
+}
+
+// Removes the subdirectory belonging to |account_id_to_delete| from the cache
+// directory. No cache belonging to |account_id_to_delete| may be running while
+// the removal is in progress.
+void DeleteObsoleteExtensionCache(const std::string& account_id_to_delete) {
+ base::FilePath cache_root_dir;
+ CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE,
+ &cache_root_dir));
+ const base::FilePath path = cache_root_dir
+ .Append(GetCacheSubdirectoryForAccountID(account_id_to_delete));
+ if (base::DirectoryExists(path))
+ base::DeleteFile(path, true);
+}
+
} // namespace
DeviceLocalAccountPolicyBroker::DeviceLocalAccountPolicyBroker(
- const std::string& user_id,
+ const DeviceLocalAccount& account,
scoped_ptr<DeviceLocalAccountPolicyStore> store,
const scoped_refptr<base::SequencedTaskRunner>& task_runner)
- : user_id_(user_id),
+ : account_id_(account.account_id),
+ user_id_(account.user_id),
store_(store.Pass()),
core_(PolicyNamespaceKey(dm_protocol::kChromePublicAccountPolicyType,
store_->account_id()),
store_.get(),
- task_runner) {}
+ task_runner) {
+ base::FilePath cache_root_dir;
+ CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE,
+ &cache_root_dir));
+ extension_loader_ = new chromeos::DeviceLocalAccountExternalPolicyLoader(
+ store_.get(),
+ cache_root_dir.Append(
+ GetCacheSubdirectoryForAccountID(account.account_id)));
+}
-DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() {}
+DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() {
+}
+
+void DeviceLocalAccountPolicyBroker::Initialize() {
+ store_->Load();
+}
+
+void DeviceLocalAccountPolicyBroker::ConnectIfPossible(
+ chromeos::DeviceSettingsService* device_settings_service,
+ DeviceManagementService* device_management_service) {
+ if (core_.client())
+ return;
+
+ scoped_ptr<CloudPolicyClient> client(CreateClient(device_settings_service,
+ device_management_service));
+ if (!client)
+ return;
-void DeviceLocalAccountPolicyBroker::Connect(
- scoped_ptr<CloudPolicyClient> client) {
core_.Connect(client.Pass());
core_.StartRefreshScheduler();
UpdateRefreshDelay();
@@ -98,66 +167,26 @@ std::string DeviceLocalAccountPolicyBroker::GetDisplayName() const {
return display_name;
}
-DeviceLocalAccountPolicyService::PolicyBrokerWrapper::PolicyBrokerWrapper()
- : parent(NULL), broker(NULL) {}
-
-DeviceLocalAccountPolicyService::PolicyBrokerWrapper::~PolicyBrokerWrapper() {}
-
-DeviceLocalAccountPolicyBroker*
- DeviceLocalAccountPolicyService::PolicyBrokerWrapper::GetBroker() {
- if (!broker) {
- scoped_ptr<DeviceLocalAccountPolicyStore> store(
- new DeviceLocalAccountPolicyStore(account_id,
- parent->session_manager_client_,
- parent->device_settings_service_,
- parent->background_task_runner_));
- broker = new DeviceLocalAccountPolicyBroker(
- user_id, store.Pass(), base::MessageLoopProxy::current());
- broker->core()->store()->AddObserver(parent);
- broker->core()->store()->Load();
- }
- return broker;
-}
-
-void DeviceLocalAccountPolicyService::PolicyBrokerWrapper::ConnectIfPossible() {
- if (broker && broker->core()->client())
- return;
- scoped_ptr<CloudPolicyClient> client(CreateClient(
- parent->device_settings_service_,
- parent->device_management_service_));
- if (client)
- GetBroker()->Connect(client.Pass());
-}
-
-void DeviceLocalAccountPolicyService::PolicyBrokerWrapper::Disconnect() {
- if (broker)
- broker->Disconnect();
-}
-
-void DeviceLocalAccountPolicyService::PolicyBrokerWrapper::DeleteBroker() {
- if (!broker)
- return;
- broker->core()->store()->RemoveObserver(parent);
- delete broker;
- broker = NULL;
-}
-
DeviceLocalAccountPolicyService::DeviceLocalAccountPolicyService(
chromeos::SessionManagerClient* session_manager_client,
chromeos::DeviceSettingsService* device_settings_service,
chromeos::CrosSettings* cros_settings,
- scoped_refptr<base::SequencedTaskRunner> background_task_runner)
+ scoped_refptr<base::SequencedTaskRunner> store_background_task_runner,
+ scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner)
: session_manager_client_(session_manager_client),
device_settings_service_(device_settings_service),
cros_settings_(cros_settings),
device_management_service_(NULL),
- background_task_runner_(background_task_runner),
- cros_settings_callback_factory_(this) {
- local_accounts_subscription_ = cros_settings_->AddSettingsObserver(
- chromeos::kAccountsPrefDeviceLocalAccounts,
- base::Bind(&DeviceLocalAccountPolicyService::
- UpdateAccountListIfNonePending,
- base::Unretained(this)));
+ waiting_for_cros_settings_(false),
+ orphan_cache_deletion_state_(NOT_STARTED),
+ store_background_task_runner_(store_background_task_runner),
+ extension_cache_task_runner_(extension_cache_task_runner),
+ local_accounts_subscription_(cros_settings_->AddSettingsObserver(
+ chromeos::kAccountsPrefDeviceLocalAccounts,
+ base::Bind(&DeviceLocalAccountPolicyService::
+ UpdateAccountListIfNonePending,
+ base::Unretained(this)))),
+ weak_factory_(this) {
UpdateAccountList();
}
@@ -173,7 +202,8 @@ void DeviceLocalAccountPolicyService::Connect(
// Connect the brokers.
for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
it != policy_brokers_.end(); ++it) {
- it->second.ConnectIfPossible();
+ it->second->ConnectIfPossible(device_settings_service_,
+ device_management_service_);
}
}
@@ -184,7 +214,7 @@ void DeviceLocalAccountPolicyService::Disconnect() {
// Disconnect the brokers.
for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
it != policy_brokers_.end(); ++it) {
- it->second.Disconnect();
+ it->second->Disconnect();
}
}
@@ -195,7 +225,7 @@ DeviceLocalAccountPolicyBroker*
if (entry == policy_brokers_.end())
return NULL;
- return entry->second.GetBroker();
+ return entry->second;
}
bool DeviceLocalAccountPolicyService::IsPolicyAvailableForUser(
@@ -229,52 +259,214 @@ void DeviceLocalAccountPolicyService::OnStoreError(CloudPolicyStore* store) {
FOR_EACH_OBSERVER(Observer, observers_, OnPolicyUpdated(broker->user_id()));
}
+bool DeviceLocalAccountPolicyService::IsExtensionCacheDirectoryBusy(
+ const std::string& account_id) {
+ return busy_extension_cache_directories_.find(account_id) !=
+ busy_extension_cache_directories_.end();
+}
+
+void DeviceLocalAccountPolicyService::StartExtensionCachesIfPossible() {
+ for (PolicyBrokerMap::iterator it = policy_brokers_.begin();
+ it != policy_brokers_.end(); ++it) {
+ if (!it->second->extension_loader()->IsCacheRunning() &&
+ !IsExtensionCacheDirectoryBusy(it->second->account_id())) {
+ it->second->extension_loader()->StartCache(extension_cache_task_runner_);
+ }
+ }
+}
+
+bool DeviceLocalAccountPolicyService::StartExtensionCacheForAccountIfPresent(
+ const std::string& account_id) {
+ for (PolicyBrokerMap::iterator it = policy_brokers_.begin();
+ it != policy_brokers_.end(); ++it) {
+ if (it->second->account_id() == account_id) {
+ DCHECK(!it->second->extension_loader()->IsCacheRunning());
+ it->second->extension_loader()->StartCache(extension_cache_task_runner_);
+ return true;
+ }
+ }
+ return false;
+}
+
+void DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted() {
+ DCHECK_EQ(IN_PROGRESS, orphan_cache_deletion_state_);
+
+ orphan_cache_deletion_state_ = DONE;
+ StartExtensionCachesIfPossible();
+}
+
+void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown(
+ const std::string& account_id) {
+ DCHECK_NE(NOT_STARTED, orphan_cache_deletion_state_);
+ DCHECK(IsExtensionCacheDirectoryBusy(account_id));
+
+ // The account with |account_id| was deleted and the broker for it has shut
+ // down completely.
+
+ if (StartExtensionCacheForAccountIfPresent(account_id)) {
+ // If another account with the same ID was created in the meantime, its
+ // extension cache is started, reusing the cache directory. The directory no
+ // longer needs to be marked as busy in this case.
+ busy_extension_cache_directories_.erase(account_id);
+ return;
+ }
+
+ // If no account with |account_id| exists anymore, the cache directory should
+ // be removed. The directory must stay marked as busy while the removal is in
+ // progress.
+ extension_cache_task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&DeleteObsoleteExtensionCache, account_id),
+ base::Bind(&DeviceLocalAccountPolicyService::
+ OnObsoleteExtensionCacheDeleted,
+ weak_factory_.GetWeakPtr(),
+ account_id));
+}
+
+void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheDeleted(
+ const std::string& account_id) {
+ DCHECK_EQ(DONE, orphan_cache_deletion_state_);
+ DCHECK(IsExtensionCacheDirectoryBusy(account_id));
+
+ // The cache directory for |account_id| has been deleted. The directory no
+ // longer needs to be marked as busy.
+ busy_extension_cache_directories_.erase(account_id);
+
+ // If another account with the same ID was created in the meantime, start its
+ // extension cache, creating a new cache directory.
+ StartExtensionCacheForAccountIfPresent(account_id);
+}
+
void DeviceLocalAccountPolicyService::UpdateAccountListIfNonePending() {
// Avoid unnecessary calls to UpdateAccountList(): If an earlier call is still
// pending (because the |cros_settings_| are not trusted yet), the updated
// account list will be processed by that call when it eventually runs.
- if (!cros_settings_callback_factory_.HasWeakPtrs())
+ if (!waiting_for_cros_settings_)
UpdateAccountList();
}
void DeviceLocalAccountPolicyService::UpdateAccountList() {
- if (chromeos::CrosSettingsProvider::TRUSTED !=
- cros_settings_->PrepareTrustedValues(
- base::Bind(&DeviceLocalAccountPolicyService::UpdateAccountList,
- cros_settings_callback_factory_.GetWeakPtr()))) {
- return;
+ chromeos::CrosSettingsProvider::TrustedStatus status =
+ cros_settings_->PrepareTrustedValues(
+ base::Bind(&DeviceLocalAccountPolicyService::UpdateAccountList,
+ weak_factory_.GetWeakPtr()));
+ switch (status) {
+ case chromeos::CrosSettingsProvider::TRUSTED:
+ waiting_for_cros_settings_ = false;
+ break;
+ case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
+ waiting_for_cros_settings_ = true;
+ return;
+ case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
+ waiting_for_cros_settings_ = false;
+ return;
}
// Update |policy_brokers_|, keeping existing entries.
PolicyBrokerMap old_policy_brokers;
policy_brokers_.swap(old_policy_brokers);
+ std::set<std::string> subdirectories_to_keep;
const std::vector<DeviceLocalAccount> device_local_accounts =
GetDeviceLocalAccounts(cros_settings_);
for (std::vector<DeviceLocalAccount>::const_iterator it =
device_local_accounts.begin();
it != device_local_accounts.end(); ++it) {
- PolicyBrokerWrapper& wrapper = policy_brokers_[it->user_id];
- wrapper.user_id = it->user_id;
- wrapper.account_id = it->account_id;
- wrapper.parent = this;
-
- // Reuse the existing broker if present.
- PolicyBrokerWrapper& existing_wrapper = old_policy_brokers[it->user_id];
- wrapper.broker = existing_wrapper.broker;
- existing_wrapper.broker = NULL;
+ PolicyBrokerMap::iterator broker_it = old_policy_brokers.find(it->user_id);
+
+ scoped_ptr<DeviceLocalAccountPolicyBroker> broker;
+ bool broker_initialized = false;
+ if (broker_it != old_policy_brokers.end()) {
+ // Reuse the existing broker if present.
+ broker.reset(broker_it->second);
+ old_policy_brokers.erase(broker_it);
+ broker_initialized = true;
+ } else {
+ scoped_ptr<DeviceLocalAccountPolicyStore> store(
+ new DeviceLocalAccountPolicyStore(it->account_id,
+ session_manager_client_,
+ device_settings_service_,
+ store_background_task_runner_));
+ store->AddObserver(this);
+ broker.reset(new DeviceLocalAccountPolicyBroker(
+ *it,
+ store.Pass(),
+ base::MessageLoopProxy::current()));
+ }
// Fire up the cloud connection for fetching policy for the account from
// the cloud if this is an enterprise-managed device.
- wrapper.ConnectIfPossible();
+ broker->ConnectIfPossible(device_settings_service_,
+ device_management_service_);
+
+ policy_brokers_[it->user_id] = broker.release();
+ if (!broker_initialized) {
+ // The broker must be initialized after it has been added to
+ // |policy_brokers_|.
+ policy_brokers_[it->user_id]->Initialize();
+ }
+
+ if (orphan_cache_deletion_state_ == NOT_STARTED) {
+ subdirectories_to_keep.insert(
+ GetCacheSubdirectoryForAccountID(it->account_id));
+ }
+ }
+
+ std::set<std::string> obsolete_account_ids;
+ for (PolicyBrokerMap::const_iterator it = old_policy_brokers.begin();
+ it != old_policy_brokers.end(); ++it) {
+ obsolete_account_ids.insert(it->second->account_id());
+ }
+
+ if (orphan_cache_deletion_state_ == NOT_STARTED) {
+ DCHECK(old_policy_brokers.empty());
+ DCHECK(busy_extension_cache_directories_.empty());
+
+ // If this method is running for the first time, no extension caches have
+ // been started yet. Take this opportunity to do a clean-up by removing
+ // orphaned cache directories not found in |subdirectories_to_keep| from the
+ // cache directory.
+ orphan_cache_deletion_state_ = IN_PROGRESS;
+ extension_cache_task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&DeleteOrphanedExtensionCaches, subdirectories_to_keep),
+ base::Bind(&DeviceLocalAccountPolicyService::
+ OnOrphanedExtensionCachesDeleted,
+ weak_factory_.GetWeakPtr()));
+
+ // Start the extension caches for all brokers. These belong to accounts in
+ // |account_ids| and are not affected by the clean-up.
+ StartExtensionCachesIfPossible();
+ } else {
+ // If this method has run before, obsolete brokers may exist. Shut down
+ // their extension caches and delete the brokers.
+ DeleteBrokers(&old_policy_brokers);
+
+ if (orphan_cache_deletion_state_ == DONE) {
+ // If the initial clean-up of orphaned cache directories has been
+ // complete, start any extension caches that are not running yet but can
+ // be started now because their cache directories are not busy.
+ StartExtensionCachesIfPossible();
+ }
}
- DeleteBrokers(&old_policy_brokers);
FOR_EACH_OBSERVER(Observer, observers_, OnDeviceLocalAccountsChanged());
}
void DeviceLocalAccountPolicyService::DeleteBrokers(PolicyBrokerMap* map) {
- for (PolicyBrokerMap::iterator it = map->begin(); it != map->end(); ++it)
- it->second.DeleteBroker();
+ for (PolicyBrokerMap::iterator it = map->begin(); it != map->end(); ++it) {
+ it->second->core()->store()->RemoveObserver(this);
+ scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader>
+ extension_loader = it->second->extension_loader();
+ if (extension_loader->IsCacheRunning()) {
+ DCHECK(!IsExtensionCacheDirectoryBusy(it->second->account_id()));
+ busy_extension_cache_directories_.insert(it->second->account_id());
+ extension_loader->StopCache(base::Bind(
+ &DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown,
+ weak_factory_.GetWeakPtr(),
+ it->second->account_id()));
+ }
+ delete it->second;
+ }
map->clear();
}
@@ -283,8 +475,8 @@ DeviceLocalAccountPolicyBroker*
CloudPolicyStore* store) {
for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
it != policy_brokers_.end(); ++it) {
- if (it->second.broker && it->second.broker->core()->store() == store)
- return it->second.broker;
+ if (it->second->core()->store() == store)
+ return it->second;
}
return NULL;
}
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service.h b/chrome/browser/chromeos/policy/device_local_account_policy_service.h
index 73b367e..268f0a7 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_service.h
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_service.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_LOCAL_ACCOUNT_POLICY_SERVICE_H_
#include <map>
+#include <set>
#include <string>
#include "base/basictypes.h"
@@ -14,6 +15,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/policy/cloud/cloud_policy_core.h"
#include "chrome/browser/policy/cloud/cloud_policy_store.h"
@@ -23,14 +25,13 @@ class SequencedTaskRunner;
}
namespace chromeos {
-class CrosSettings;
class DeviceSettingsService;
class SessionManagerClient;
}
namespace policy {
-class CloudPolicyClient;
+struct DeviceLocalAccount;
class DeviceLocalAccountPolicyStore;
class DeviceManagementService;
@@ -39,19 +40,31 @@ class DeviceManagementService;
class DeviceLocalAccountPolicyBroker {
public:
// |task_runner| is the runner for policy refresh tasks.
- explicit DeviceLocalAccountPolicyBroker(
- const std::string& user_id,
+ DeviceLocalAccountPolicyBroker(
+ const DeviceLocalAccount& account,
scoped_ptr<DeviceLocalAccountPolicyStore> store,
const scoped_refptr<base::SequencedTaskRunner>& task_runner);
~DeviceLocalAccountPolicyBroker();
+ // Initialize the broker, loading its |store_|.
+ void Initialize();
+
+ // For the difference between |account_id| and |user_id|, see the
+ // documentation of DeviceLocalAccount.
+ const std::string& account_id() const { return account_id_; }
const std::string& user_id() const { return user_id_; }
+ scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader>
+ extension_loader() const { return extension_loader_; }
+
CloudPolicyCore* core() { return &core_; }
const CloudPolicyCore* core() const { return &core_; }
- // Establish a cloud connection for the service.
- void Connect(scoped_ptr<CloudPolicyClient> client);
+ // Fire up the cloud connection for fetching policy for the account from the
+ // cloud if this is an enterprise-managed device.
+ void ConnectIfPossible(
+ chromeos::DeviceSettingsService* device_settings_service,
+ DeviceManagementService* device_management_service);
// Destroy the cloud connection, stopping policy refreshes.
void Disconnect();
@@ -64,8 +77,11 @@ class DeviceLocalAccountPolicyBroker {
std::string GetDisplayName() const;
private:
+ const std::string account_id_;
const std::string user_id_;
- scoped_ptr<DeviceLocalAccountPolicyStore> store_;
+ const scoped_ptr<DeviceLocalAccountPolicyStore> store_;
+ scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader>
+ extension_loader_;
CloudPolicyCore core_;
DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyBroker);
@@ -93,7 +109,8 @@ class DeviceLocalAccountPolicyService : public CloudPolicyStore::Observer {
chromeos::SessionManagerClient* session_manager_client,
chromeos::DeviceSettingsService* device_settings_service,
chromeos::CrosSettings* cros_settings,
- scoped_refptr<base::SequencedTaskRunner> background_task_runner);
+ scoped_refptr<base::SequencedTaskRunner> store_background_task_runner,
+ scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner);
virtual ~DeviceLocalAccountPolicyService();
// Initializes the cloud policy service connection.
@@ -118,31 +135,32 @@ class DeviceLocalAccountPolicyService : public CloudPolicyStore::Observer {
virtual void OnStoreError(CloudPolicyStore* store) OVERRIDE;
private:
- struct PolicyBrokerWrapper {
- PolicyBrokerWrapper();
- ~PolicyBrokerWrapper();
+ typedef std::map<std::string, DeviceLocalAccountPolicyBroker*>
+ PolicyBrokerMap;
- // Return the |broker|, creating it first if necessary.
- DeviceLocalAccountPolicyBroker* GetBroker();
+ // Returns |true| if the directory in which force-installed extensions are
+ // cached for |account_id| is busy, either because a broker that was using
+ // this directory has not shut down completely yet or because the directory is
+ // being deleted.
+ bool IsExtensionCacheDirectoryBusy(const std::string& account_id);
- // Fire up the cloud connection for fetching policy for the account from the
- // cloud if this is an enterprise-managed device.
- void ConnectIfPossible();
+ // Starts any extension caches that are not running yet but can be started now
+ // because their cache directories are no longer busy.
+ void StartExtensionCachesIfPossible();
- // Destroy the cloud connection.
- void Disconnect();
+ // Checks whether a broker exists for |account_id|. If so, starts the broker's
+ // extension cache and returns |true|. Otherwise, returns |false|.
+ bool StartExtensionCacheForAccountIfPresent(const std::string& account_id);
- // Delete the broker.
- void DeleteBroker();
+ // Called back when any extension caches belonging to device-local accounts
+ // that no longer exist have been removed at start-up.
+ void OnOrphanedExtensionCachesDeleted();
- std::string user_id;
- std::string account_id;
- DeviceLocalAccountPolicyService* parent;
- DeviceLocalAccountPolicyBroker* broker;
- scoped_refptr<base::SequencedTaskRunner> background_task_runner;
- };
+ // Called back when the extension cache for |account_id| has been shut down.
+ void OnObsoleteExtensionCacheShutdown(const std::string& account_id);
- typedef std::map<std::string, PolicyBrokerWrapper> PolicyBrokerMap;
+ // Called back when the extension cache for |account_id| has been removed.
+ void OnObsoleteExtensionCacheDeleted(const std::string& account_id);
// Re-queries the list of defined device-local accounts from device settings
// and updates |policy_brokers_| to match that list.
@@ -166,16 +184,33 @@ class DeviceLocalAccountPolicyService : public CloudPolicyStore::Observer {
// The device-local account policy brokers, keyed by user ID.
PolicyBrokerMap policy_brokers_;
+ // Whether a call to UpdateAccountList() is pending because |cros_settings_|
+ // are not trusted yet.
+ bool waiting_for_cros_settings_;
+
+ // Orphaned extension caches are removed at startup. This tracks the status of
+ // that process.
+ enum OrphanCacheDeletionState {
+ NOT_STARTED,
+ IN_PROGRESS,
+ DONE,
+ };
+ OrphanCacheDeletionState orphan_cache_deletion_state_;
+
+ // Account IDs whose extension cache directories are busy, either because a
+ // broker for the account has not shut down completely yet or because the
+ // directory is being deleted.
+ std::set<std::string> busy_extension_cache_directories_;
+
+ const scoped_refptr<base::SequencedTaskRunner> store_background_task_runner_;
+ const scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner_;
+
ObserverList<Observer, true> observers_;
- scoped_ptr<chromeos::CrosSettings::ObserverSubscription>
+ const scoped_ptr<chromeos::CrosSettings::ObserverSubscription>
local_accounts_subscription_;
- scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-
- // Weak pointer factory for cros_settings_->PrepareTrustedValues() callbacks.
- base::WeakPtrFactory<DeviceLocalAccountPolicyService>
- cros_settings_callback_factory_;
+ base::WeakPtrFactory<DeviceLocalAccountPolicyService> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyService);
};
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc b/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc
index 8191a7a..0e640ba 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc
@@ -7,6 +7,17 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_path_override.h"
+#include "base/test/test_simple_task_runner.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/chromeos/policy/device_local_account_policy_provider.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
@@ -20,8 +31,11 @@
#include "chrome/browser/policy/external_data_fetcher.h"
#include "chrome/browser/policy/mock_configuration_policy_provider.h"
#include "chrome/browser/policy/proto/chromeos/chrome_device_policy.pb.h"
+#include "chrome/common/chrome_paths.h"
+#include "chromeos/chromeos_paths.h"
#include "chromeos/dbus/power_policy_controller.h"
#include "policy/policy_constants.h"
+#include "policy/proto/cloud_policy.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::AnyNumber;
@@ -34,6 +48,19 @@ namespace em = enterprise_management;
namespace policy {
+namespace {
+
+const char kAccount1[] = "account1@localhost";
+const char kAccount2[] = "account2@localhost";
+const char kAccount3[] = "account3@localhost";
+
+const char kExtensionID[] = "kbmnembihfiondgfjekmnmcbddelicoi";
+const char kExtensionVersion[] = "1.0.0.0";
+const char kExtensionCRXPath[] = "extensions/hosted_app.crx";
+const char kUpdateURL[] = "https://clients2.google.com/service/update2/crx";
+
+} // namespace
+
class MockDeviceLocalAccountPolicyServiceObserver
: public DeviceLocalAccountPolicyService::Observer {
public:
@@ -41,274 +68,335 @@ class MockDeviceLocalAccountPolicyServiceObserver
MOCK_METHOD0(OnDeviceLocalAccountsChanged, void(void));
};
-class DeviceLocalAccountPolicyServiceTest
+class DeviceLocalAccountPolicyServiceTestBase
: public chromeos::DeviceSettingsTestBase {
public:
- DeviceLocalAccountPolicyServiceTest()
- : public_session_user_id_(GenerateDeviceLocalAccountUserId(
- PolicyBuilder::kFakeUsername,
- DeviceLocalAccount::TYPE_PUBLIC_SESSION)),
- cros_settings_(&device_settings_service_),
- service_(&device_settings_test_helper_,
- &device_settings_service_,
- &cros_settings_,
- loop_.message_loop_proxy()) {}
-
- virtual void SetUp() OVERRIDE {
- DeviceSettingsTestBase::SetUp();
-
- // Values implicitly enforced for public accounts.
- expected_policy_map_.Set(key::kLidCloseAction,
- POLICY_LEVEL_MANDATORY,
- POLICY_SCOPE_USER,
- base::Value::CreateIntegerValue(
- chromeos::PowerPolicyController::
- ACTION_STOP_SESSION),
- NULL);
- expected_policy_map_.Set(key::kShelfAutoHideBehavior,
- POLICY_LEVEL_MANDATORY,
- POLICY_SCOPE_USER,
- Value::CreateStringValue("Never"),
- NULL);
- expected_policy_map_.Set(key::kShowLogoutButtonInTray,
- POLICY_LEVEL_MANDATORY,
- POLICY_SCOPE_USER,
- Value::CreateBooleanValue(true),
- NULL);
- expected_policy_map_.Set(key::kFullscreenAllowed,
- POLICY_LEVEL_MANDATORY,
- POLICY_SCOPE_USER,
- Value::CreateBooleanValue(false),
- NULL);
-
- // Explicitly set value.
- expected_policy_map_.Set(key::kDisableSpdy,
- POLICY_LEVEL_MANDATORY,
- POLICY_SCOPE_USER,
- Value::CreateBooleanValue(true),
- NULL);
-
- device_local_account_policy_.payload().mutable_disablespdy()->set_value(
- true);
- device_local_account_policy_.policy_data().set_policy_type(
- dm_protocol::kChromePublicAccountPolicyType);
- device_local_account_policy_.policy_data().set_settings_entity_id(
- PolicyBuilder::kFakeUsername);
- device_local_account_policy_.Build();
-
- em::DeviceLocalAccountInfoProto* account =
- device_policy_.payload().mutable_device_local_accounts()->add_account();
- account->set_account_id(PolicyBuilder::kFakeUsername);
- account->set_type(
- em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION);
- device_policy_.Build();
-
- service_.AddObserver(&service_observer_);
- }
-
- virtual void TearDown() OVERRIDE {
- service_.RemoveObserver(&service_observer_);
-
- DeviceSettingsTestBase::TearDown();
- }
-
- void InstallDevicePolicy() {
- EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged());
- device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob());
- ReloadDeviceSettings();
- Mock::VerifyAndClearExpectations(&service_observer_);
- }
+ DeviceLocalAccountPolicyServiceTestBase();
- MOCK_METHOD1(OnRefreshDone, void(bool));
+ virtual void SetUp() OVERRIDE;
+
+ void CreatePolicyService();
+
+ void InstallDeviceLocalAccountPolicy(const std::string& account_id);
+ void AddDeviceLocalAccountToPolicy(const std::string& account_id);
+ virtual void InstallDevicePolicy();
- const std::string public_session_user_id_;
+ const std::string account_1_user_id_;
+ const std::string account_2_user_id_;
PolicyMap expected_policy_map_;
UserPolicyBuilder device_local_account_policy_;
chromeos::CrosSettings cros_settings_;
- MockDeviceLocalAccountPolicyServiceObserver service_observer_;
+ scoped_refptr<base::TestSimpleTaskRunner> extension_cache_task_runner_;
MockDeviceManagementService mock_device_management_service_;
- DeviceLocalAccountPolicyService service_;
+ scoped_ptr<DeviceLocalAccountPolicyService> service_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyServiceTestBase);
+};
+
+class DeviceLocalAccountPolicyServiceTest
+ : public DeviceLocalAccountPolicyServiceTestBase {
+ public:
+ MOCK_METHOD1(OnRefreshDone, void(bool));
+
+ protected:
+ DeviceLocalAccountPolicyServiceTest();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ void InstallDevicePolicy() OVERRIDE;
+
+ MockDeviceLocalAccountPolicyServiceObserver service_observer_;
private:
DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyServiceTest);
};
+DeviceLocalAccountPolicyServiceTestBase::
+ DeviceLocalAccountPolicyServiceTestBase()
+ : account_1_user_id_(GenerateDeviceLocalAccountUserId(
+ kAccount1,
+ DeviceLocalAccount::TYPE_PUBLIC_SESSION)),
+ account_2_user_id_(GenerateDeviceLocalAccountUserId(
+ kAccount2,
+ DeviceLocalAccount::TYPE_PUBLIC_SESSION)),
+ cros_settings_(&device_settings_service_),
+ extension_cache_task_runner_(new base::TestSimpleTaskRunner) {
+}
+
+void DeviceLocalAccountPolicyServiceTestBase::SetUp() {
+ chromeos::DeviceSettingsTestBase::SetUp();
+
+ // Values implicitly enforced for public accounts.
+ expected_policy_map_.Set(key::kLidCloseAction,
+ POLICY_LEVEL_MANDATORY,
+ POLICY_SCOPE_USER,
+ base::Value::CreateIntegerValue(
+ chromeos::PowerPolicyController::
+ ACTION_STOP_SESSION),
+ NULL);
+ expected_policy_map_.Set(key::kShelfAutoHideBehavior,
+ POLICY_LEVEL_MANDATORY,
+ POLICY_SCOPE_USER,
+ Value::CreateStringValue("Never"),
+ NULL);
+ expected_policy_map_.Set(key::kShowLogoutButtonInTray,
+ POLICY_LEVEL_MANDATORY,
+ POLICY_SCOPE_USER,
+ Value::CreateBooleanValue(true),
+ NULL);
+ expected_policy_map_.Set(key::kFullscreenAllowed,
+ POLICY_LEVEL_MANDATORY,
+ POLICY_SCOPE_USER,
+ Value::CreateBooleanValue(false),
+ NULL);
+
+ // Explicitly set value.
+ expected_policy_map_.Set(key::kDisableSpdy,
+ POLICY_LEVEL_MANDATORY,
+ POLICY_SCOPE_USER,
+ Value::CreateBooleanValue(true),
+ NULL);
+
+ device_local_account_policy_.payload().mutable_disablespdy()->set_value(
+ true);
+ device_local_account_policy_.policy_data().set_policy_type(
+ dm_protocol::kChromePublicAccountPolicyType);
+}
+
+void DeviceLocalAccountPolicyServiceTestBase::CreatePolicyService() {
+ service_.reset(new DeviceLocalAccountPolicyService(
+ &device_settings_test_helper_,
+ &device_settings_service_,
+ &cros_settings_,
+ loop_.message_loop_proxy(),
+ extension_cache_task_runner_));
+}
+
+void DeviceLocalAccountPolicyServiceTestBase::
+ InstallDeviceLocalAccountPolicy(const std::string& account_id) {
+ device_local_account_policy_.policy_data().set_settings_entity_id(account_id);
+ device_local_account_policy_.policy_data().set_username(account_id);
+ device_local_account_policy_.Build();
+ device_settings_test_helper_.set_device_local_account_policy_blob(
+ account_id, device_local_account_policy_.GetBlob());
+}
+
+void DeviceLocalAccountPolicyServiceTestBase::AddDeviceLocalAccountToPolicy(
+ const std::string& account_id) {
+ em::DeviceLocalAccountInfoProto* account =
+ device_policy_.payload().mutable_device_local_accounts()->add_account();
+ account->set_account_id(account_id);
+ account->set_type(
+ em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION);
+}
+
+void DeviceLocalAccountPolicyServiceTestBase::InstallDevicePolicy() {
+ device_policy_.Build();
+ device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob());
+ ReloadDeviceSettings();
+}
+
+DeviceLocalAccountPolicyServiceTest::DeviceLocalAccountPolicyServiceTest() {
+ CreatePolicyService();
+}
+
+void DeviceLocalAccountPolicyServiceTest::SetUp() {
+ DeviceLocalAccountPolicyServiceTestBase::SetUp();
+ service_->AddObserver(&service_observer_);
+}
+
+void DeviceLocalAccountPolicyServiceTest::TearDown() {
+ service_->RemoveObserver(&service_observer_);
+ DeviceLocalAccountPolicyServiceTestBase::TearDown();
+}
+
+void DeviceLocalAccountPolicyServiceTest::InstallDevicePolicy() {
+ EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged());
+ DeviceLocalAccountPolicyServiceTestBase::InstallDevicePolicy();
+ Mock::VerifyAndClearExpectations(&service_observer_);
+}
+
TEST_F(DeviceLocalAccountPolicyServiceTest, NoAccounts) {
- EXPECT_FALSE(service_.GetBrokerForUser(public_session_user_id_));
+ EXPECT_FALSE(service_->GetBrokerForUser(account_1_user_id_));
}
TEST_F(DeviceLocalAccountPolicyServiceTest, GetBroker) {
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
- EXPECT_EQ(public_session_user_id_, broker->user_id());
+ EXPECT_EQ(account_1_user_id_, broker->user_id());
ASSERT_TRUE(broker->core()->store());
EXPECT_EQ(CloudPolicyStore::STATUS_OK, broker->core()->store()->status());
EXPECT_FALSE(broker->core()->client());
- EXPECT_TRUE(broker->core()->store()->policy_map().empty());
+ EXPECT_FALSE(broker->core()->store()->policy_map().empty());
}
TEST_F(DeviceLocalAccountPolicyServiceTest, LoadNoPolicy) {
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
- EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_));
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
- FlushDeviceSettings();
- Mock::VerifyAndClearExpectations(&service_observer_);
-
+ EXPECT_EQ(account_1_user_id_, broker->user_id());
ASSERT_TRUE(broker->core()->store());
EXPECT_EQ(CloudPolicyStore::STATUS_LOAD_ERROR,
broker->core()->store()->status());
EXPECT_TRUE(broker->core()->store()->policy_map().empty());
- EXPECT_FALSE(service_.IsPolicyAvailableForUser(public_session_user_id_));
+ EXPECT_FALSE(service_->IsPolicyAvailableForUser(account_1_user_id_));
}
TEST_F(DeviceLocalAccountPolicyServiceTest, LoadValidationFailure) {
device_local_account_policy_.policy_data().set_policy_type(
dm_protocol::kChromeUserPolicyType);
- device_local_account_policy_.Build();
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
- EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_));
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
- FlushDeviceSettings();
- Mock::VerifyAndClearExpectations(&service_observer_);
-
+ EXPECT_EQ(account_1_user_id_, broker->user_id());
ASSERT_TRUE(broker->core()->store());
EXPECT_EQ(CloudPolicyStore::STATUS_VALIDATION_ERROR,
broker->core()->store()->status());
EXPECT_TRUE(broker->core()->store()->policy_map().empty());
- EXPECT_FALSE(service_.IsPolicyAvailableForUser(public_session_user_id_));
+ EXPECT_FALSE(service_->IsPolicyAvailableForUser(account_1_user_id_));
}
TEST_F(DeviceLocalAccountPolicyServiceTest, LoadPolicy) {
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
- EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_));
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
- FlushDeviceSettings();
- Mock::VerifyAndClearExpectations(&service_observer_);
-
+ EXPECT_EQ(account_1_user_id_, broker->user_id());
ASSERT_TRUE(broker->core()->store());
- EXPECT_EQ(CloudPolicyStore::STATUS_OK,
- broker->core()->store()->status());
+ EXPECT_EQ(CloudPolicyStore::STATUS_OK, broker->core()->store()->status());
ASSERT_TRUE(broker->core()->store()->policy());
EXPECT_EQ(device_local_account_policy_.policy_data().SerializeAsString(),
broker->core()->store()->policy()->SerializeAsString());
EXPECT_TRUE(expected_policy_map_.Equals(
broker->core()->store()->policy_map()));
- EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_));
+ EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_));
}
TEST_F(DeviceLocalAccountPolicyServiceTest, StoreValidationFailure) {
- device_local_account_policy_.policy_data().set_policy_type(
- dm_protocol::kChromeUserPolicyType);
- device_local_account_policy_.Build();
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
+ Mock::VerifyAndClearExpectations(&service_observer_);
- EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_));
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
+ EXPECT_EQ(account_1_user_id_, broker->user_id());
ASSERT_TRUE(broker->core()->store());
+
+ device_local_account_policy_.policy_data().set_policy_type(
+ dm_protocol::kChromeUserPolicyType);
+ device_local_account_policy_.Build();
broker->core()->store()->Store(device_local_account_policy_.policy());
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
FlushDeviceSettings();
- Mock::VerifyAndClearExpectations(&service_observer_);
- ASSERT_TRUE(broker->core()->store());
EXPECT_EQ(CloudPolicyStore::STATUS_VALIDATION_ERROR,
broker->core()->store()->status());
EXPECT_EQ(CloudPolicyValidatorBase::VALIDATION_WRONG_POLICY_TYPE,
broker->core()->store()->validation_status());
- EXPECT_FALSE(service_.IsPolicyAvailableForUser(public_session_user_id_));
+ EXPECT_FALSE(service_->IsPolicyAvailableForUser(account_1_user_id_));
}
TEST_F(DeviceLocalAccountPolicyServiceTest, StorePolicy) {
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
+ Mock::VerifyAndClearExpectations(&service_observer_);
- EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_));
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
+ EXPECT_EQ(account_1_user_id_, broker->user_id());
ASSERT_TRUE(broker->core()->store());
+
+ device_local_account_policy_.policy_data().set_settings_entity_id(kAccount1);
+ device_local_account_policy_.policy_data().set_username(kAccount1);
+ device_local_account_policy_.Build();
broker->core()->store()->Store(device_local_account_policy_.policy());
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
FlushDeviceSettings();
- Mock::VerifyAndClearExpectations(&service_observer_);
- EXPECT_EQ(device_local_account_policy_.GetBlob(),
- device_settings_test_helper_.device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername));
- EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_));
+ EXPECT_EQ(CloudPolicyStore::STATUS_OK, broker->core()->store()->status());
+ ASSERT_TRUE(broker->core()->store()->policy());
+ EXPECT_EQ(device_local_account_policy_.policy_data().SerializeAsString(),
+ broker->core()->store()->policy()->SerializeAsString());
+ EXPECT_TRUE(expected_policy_map_.Equals(
+ broker->core()->store()->policy_map()));
+ EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_));
}
TEST_F(DeviceLocalAccountPolicyServiceTest, DevicePolicyChange) {
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
- EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged());
device_policy_.payload().mutable_device_local_accounts()->clear_account();
- device_policy_.Build();
- device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob());
- device_settings_service_.PropertyChangeComplete(true);
- FlushDeviceSettings();
- EXPECT_FALSE(service_.GetBrokerForUser(public_session_user_id_));
- Mock::VerifyAndClearExpectations(&service_observer_);
+ InstallDevicePolicy();
+
+ EXPECT_FALSE(service_->GetBrokerForUser(account_1_user_id_));
}
TEST_F(DeviceLocalAccountPolicyServiceTest, DuplicateAccounts) {
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
- DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
- ASSERT_TRUE(broker);
+ Mock::VerifyAndClearExpectations(&service_observer_);
// Add a second entry with a duplicate account name to device policy.
- em::DeviceLocalAccountInfoProto* account =
- device_policy_.payload().mutable_device_local_accounts()->add_account();
- account->set_account_id(PolicyBuilder::kFakeUsername);
- account->set_type(
- em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION);
- device_policy_.Build();
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
- device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob());
-
- EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged());
- EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_));
- device_settings_service_.PropertyChangeComplete(true);
- FlushDeviceSettings();
- Mock::VerifyAndClearExpectations(&service_observer_);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
// Make sure the broker is accessible and policy got loaded.
- broker = service_.GetBrokerForUser(public_session_user_id_);
+ DeviceLocalAccountPolicyBroker* broker =
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
- EXPECT_EQ(public_session_user_id_, broker->user_id());
- EXPECT_TRUE(broker->core()->store()->policy());
+ EXPECT_EQ(account_1_user_id_, broker->user_id());
+ ASSERT_TRUE(broker->core()->store());
+ EXPECT_EQ(CloudPolicyStore::STATUS_OK, broker->core()->store()->status());
+ ASSERT_TRUE(broker->core()->store()->policy());
+ EXPECT_EQ(device_local_account_policy_.policy_data().SerializeAsString(),
+ broker->core()->store()->policy()->SerializeAsString());
+ EXPECT_TRUE(expected_policy_map_.Equals(
+ broker->core()->store()->policy_map()));
+ EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_));
}
TEST_F(DeviceLocalAccountPolicyServiceTest, FetchPolicy) {
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
- service_.Connect(&mock_device_management_service_);
+ service_->Connect(&mock_device_management_service_);
EXPECT_TRUE(broker->core()->client());
em::DeviceManagementRequest request;
@@ -326,7 +414,7 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, FetchPolicy) {
device_policy_.policy_data().device_id(),
_))
.WillOnce(SaveArg<6>(&request));
- EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_));
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
broker->core()->client()->FetchPolicy();
FlushDeviceSettings();
Mock::VerifyAndClearExpectations(&service_observer_);
@@ -336,7 +424,7 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, FetchPolicy) {
EXPECT_EQ(dm_protocol::kChromePublicAccountPolicyType,
request.policy_request().request(0).policy_type());
EXPECT_FALSE(request.policy_request().request(0).has_machine_id());
- EXPECT_EQ(PolicyBuilder::kFakeUsername,
+ EXPECT_EQ(kAccount1,
request.policy_request().request(0).settings_entity_id());
ASSERT_TRUE(broker->core()->store());
@@ -347,26 +435,27 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, FetchPolicy) {
broker->core()->store()->policy()->SerializeAsString());
EXPECT_TRUE(expected_policy_map_.Equals(
broker->core()->store()->policy_map()));
- EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_));
+ EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_));
- EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_))
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_))
.Times(0);
- service_.Disconnect();
+ service_->Disconnect();
EXPECT_FALSE(broker->core()->client());
Mock::VerifyAndClearExpectations(&service_observer_);
- EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_));
+ EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_));
}
TEST_F(DeviceLocalAccountPolicyServiceTest, RefreshPolicy) {
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
InstallDevicePolicy();
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
- service_.Connect(&mock_device_management_service_);
+ service_->Connect(&mock_device_management_service_);
ASSERT_TRUE(broker->core()->service());
em::DeviceManagementResponse response;
@@ -376,7 +465,7 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, RefreshPolicy) {
.WillOnce(mock_device_management_service_.SucceedJob(response));
EXPECT_CALL(mock_device_management_service_, StartJob(_, _, _, _, _, _, _));
EXPECT_CALL(*this, OnRefreshDone(true)).Times(1);
- EXPECT_CALL(service_observer_, OnPolicyUpdated(public_session_user_id_));
+ EXPECT_CALL(service_observer_, OnPolicyUpdated(account_1_user_id_));
broker->core()->service()->RefreshPolicy(
base::Bind(&DeviceLocalAccountPolicyServiceTest::OnRefreshDone,
base::Unretained(this)));
@@ -390,90 +479,368 @@ TEST_F(DeviceLocalAccountPolicyServiceTest, RefreshPolicy) {
broker->core()->store()->status());
EXPECT_TRUE(expected_policy_map_.Equals(
broker->core()->store()->policy_map()));
- EXPECT_TRUE(service_.IsPolicyAvailableForUser(public_session_user_id_));
+ EXPECT_TRUE(service_->IsPolicyAvailableForUser(account_1_user_id_));
+}
+
+class DeviceLocalAccountPolicyExtensionCacheTest
+ : public DeviceLocalAccountPolicyServiceTestBase {
+ protected:
+ DeviceLocalAccountPolicyExtensionCacheTest();
+
+ virtual void SetUp() OVERRIDE;
+
+ base::FilePath GetCacheDirectoryForAccountID(const std::string& account_id);
+
+ base::ScopedTempDir cache_root_dir_;
+ scoped_ptr<base::ScopedPathOverride> cache_root_dir_override_;
+
+ base::FilePath cache_dir_1_;
+ base::FilePath cache_dir_2_;
+ base::FilePath cache_dir_3_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyExtensionCacheTest);
+};
+
+DeviceLocalAccountPolicyExtensionCacheTest::
+ DeviceLocalAccountPolicyExtensionCacheTest() {
+}
+
+void DeviceLocalAccountPolicyExtensionCacheTest::SetUp() {
+ DeviceLocalAccountPolicyServiceTestBase::SetUp();
+ ASSERT_TRUE(cache_root_dir_.CreateUniqueTempDir());
+ cache_root_dir_override_.reset(new base::ScopedPathOverride(
+ chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE,
+ cache_root_dir_.path()));
+
+ cache_dir_1_ = GetCacheDirectoryForAccountID(kAccount1);
+ cache_dir_2_ = GetCacheDirectoryForAccountID(kAccount2);
+ cache_dir_3_ = GetCacheDirectoryForAccountID(kAccount3);
+
+ em::StringList* forcelist = device_local_account_policy_.payload()
+ .mutable_extensioninstallforcelist()->mutable_value();
+ forcelist->add_entries(base::StringPrintf("%s;%s", kExtensionID, kUpdateURL));
+}
+
+base::FilePath DeviceLocalAccountPolicyExtensionCacheTest::
+ GetCacheDirectoryForAccountID(const std::string& account_id) {
+ return cache_root_dir_.path().Append(base::HexEncode(account_id.c_str(),
+ account_id.size()));
+}
+
+// Verifies that during startup, orphaned cache directories are deleted,
+// cache directories belonging to an existing account are preserved and missing
+// cache directories are created. Also verifies that when startup is complete,
+// the caches for all existing accounts are running.
+TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, Startup) {
+ base::FilePath test_data_dir;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
+ const base::FilePath source_crx_file =
+ test_data_dir.Append(kExtensionCRXPath);
+ const std::string target_crx_file_name =
+ base::StringPrintf("%s-%s.crx", kExtensionID, kExtensionVersion);
+
+ // Create and pre-populate a cache directory for account 1.
+ EXPECT_TRUE(file_util::CreateDirectory(cache_dir_1_));
+ EXPECT_TRUE(CopyFile(source_crx_file,
+ cache_dir_1_.Append(target_crx_file_name)));
+
+ // Create and pre-populate a cache directory for account 3.
+ EXPECT_TRUE(file_util::CreateDirectory(cache_dir_3_));
+ EXPECT_TRUE(CopyFile(source_crx_file,
+ cache_dir_3_.Append(target_crx_file_name)));
+
+ // Add accounts 1 and 2 to device policy.
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ InstallDeviceLocalAccountPolicy(kAccount2);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount2);
+ InstallDevicePolicy();
+
+ // Create the DeviceLocalAccountPolicyService, allowing it to finish the
+ // deletion of orphaned cache directories.
+ CreatePolicyService();
+ FlushDeviceSettings();
+ extension_cache_task_runner_->RunUntilIdle();
+
+ // Verify that the cache directory for account 1 and its contents still exist.
+ EXPECT_TRUE(base::DirectoryExists(cache_dir_1_));
+ EXPECT_TRUE(ContentsEqual(source_crx_file,
+ cache_dir_1_.Append(target_crx_file_name)));
+
+ // Verify that a cache directory for account 2 was created.
+ EXPECT_TRUE(base::DirectoryExists(cache_dir_2_));
+
+ // Verify that the cache directory for account 3 was deleted.
+ EXPECT_FALSE(base::DirectoryExists(cache_dir_3_));
+
+ // Verify that the cache for account 1 has been started.
+ DeviceLocalAccountPolicyBroker* broker =
+ service_->GetBrokerForUser(account_1_user_id_);
+ ASSERT_TRUE(broker);
+ EXPECT_TRUE(broker->extension_loader()->IsCacheRunning());
+
+ // Verify that the cache for account 2 has been started.
+ broker = service_->GetBrokerForUser(account_2_user_id_);
+ ASSERT_TRUE(broker);
+ EXPECT_TRUE(broker->extension_loader()->IsCacheRunning());
+}
+
+// Verifies that while the deletion of orphaned cache directories is in
+// progress, the caches for accounts which existed before the deletion started
+// are running but caches for newly added accounts are not started.
+TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, RaceAgainstOrphanDeletion) {
+ // Add account 1 to device policy.
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
+
+ // Create the DeviceLocalAccountPolicyService, triggering the deletion of
+ // orphaned cache directories.
+ CreatePolicyService();
+ FlushDeviceSettings();
+
+ // Verify that the cache for account 1 has been started as it is unaffected by
+ // the orphan deletion.
+ DeviceLocalAccountPolicyBroker* broker =
+ service_->GetBrokerForUser(account_1_user_id_);
+ ASSERT_TRUE(broker);
+ EXPECT_TRUE(broker->extension_loader()->IsCacheRunning());
+
+ // Add account 2 to device policy.
+ InstallDeviceLocalAccountPolicy(kAccount2);
+ AddDeviceLocalAccountToPolicy(kAccount2);
+ InstallDevicePolicy();
+
+ // Verify that the cache for account 2 has not been started yet as the orphan
+ // deletion is still in progress.
+ broker = service_->GetBrokerForUser(account_2_user_id_);
+ ASSERT_TRUE(broker);
+ EXPECT_FALSE(broker->extension_loader()->IsCacheRunning());
+
+ // Allow the orphan deletion to finish.
+ extension_cache_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that the cache for account 2 has been started.
+ EXPECT_TRUE(broker->extension_loader()->IsCacheRunning());
+}
+
+// Verifies that while the shutdown of a cache is in progress, no new cache is
+// started if an account with the same ID is re-added.
+TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, RaceAgainstCacheShutdown) {
+ // Add account 1 to device policy.
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
+
+ // Create the DeviceLocalAccountPolicyService, allowing it to finish the
+ // deletion of orphaned cache directories.
+ CreatePolicyService();
+ FlushDeviceSettings();
+ extension_cache_task_runner_->RunUntilIdle();
+
+ // Remove account 1 from device policy, triggering a shutdown of its cache.
+ device_policy_.payload().mutable_device_local_accounts()->clear_account();
+ InstallDevicePolicy();
+
+ // Re-add account 1 to device policy.
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
+
+ // Verify that the cache for account 1 has not been started yet as the
+ // shutdown of a previous cache for this account ID is still in progress.
+ DeviceLocalAccountPolicyBroker* broker =
+ service_->GetBrokerForUser(account_1_user_id_);
+ ASSERT_TRUE(broker);
+ EXPECT_FALSE(broker->extension_loader()->IsCacheRunning());
+
+ // Allow the cache shutdown to finish.
+ extension_cache_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that the cache directory for account 1 still exists.
+ EXPECT_TRUE(base::DirectoryExists(cache_dir_1_));
+
+ // Verify that the cache for account 1 has been started, reusing the existing
+ // cache directory.
+ EXPECT_TRUE(broker->extension_loader()->IsCacheRunning());
+}
+
+// Verifies that while the deletion of an obsolete cache directory is in
+// progress, no new cache is started if an account with the same ID is re-added.
+TEST_F(DeviceLocalAccountPolicyExtensionCacheTest,
+ RaceAgainstObsoleteDeletion) {
+ // Add account 1 to device policy.
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
+
+ // Create the DeviceLocalAccountPolicyService, allowing it to finish the
+ // deletion of orphaned cache directories.
+ CreatePolicyService();
+ FlushDeviceSettings();
+ extension_cache_task_runner_->RunUntilIdle();
+
+ // Remove account 1 from device policy, allowing the shutdown of its cache to
+ // finish and the deletion of its now obsolete cache directory to begin.
+ device_policy_.payload().mutable_device_local_accounts()->clear_account();
+ InstallDevicePolicy();
+ extension_cache_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+
+ // Re-add account 1 to device policy.
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
+
+ // Verify that the cache for account 1 has not been started yet as the
+ // deletion of the cache directory for this account ID is still in progress.
+ DeviceLocalAccountPolicyBroker* broker =
+ service_->GetBrokerForUser(account_1_user_id_);
+ ASSERT_TRUE(broker);
+ EXPECT_FALSE(broker->extension_loader()->IsCacheRunning());
+
+ // Allow the deletion to finish.
+ extension_cache_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that the cache directory for account 1 was deleted.
+ EXPECT_FALSE(base::DirectoryExists(cache_dir_1_));
+
+ // Verify that the cache for account 1 has been started.
+ EXPECT_TRUE(broker->extension_loader()->IsCacheRunning());
+}
+
+// Verifies that when an account is added and no deletion of cache directories
+// affecting this account is in progress, its cache is started immediately.
+TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, AddAccount) {
+ // Create the DeviceLocalAccountPolicyService, allowing it to finish the
+ // deletion of orphaned cache directories.
+ InstallDevicePolicy();
+ CreatePolicyService();
+ FlushDeviceSettings();
+ extension_cache_task_runner_->RunUntilIdle();
+
+ // Add account 1 to device policy.
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
+
+ // Verify that the cache for account 1 has been started.
+ DeviceLocalAccountPolicyBroker* broker =
+ service_->GetBrokerForUser(account_1_user_id_);
+ ASSERT_TRUE(broker);
+ EXPECT_TRUE(broker->extension_loader()->IsCacheRunning());
+}
+
+// Verifies that when an account is removed, its cache directory is deleted.
+TEST_F(DeviceLocalAccountPolicyExtensionCacheTest, RemoveAccount) {
+ // Add account 1 to device policy.
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
+
+ // Create the DeviceLocalAccountPolicyService, allowing it to finish the
+ // deletion of orphaned cache directories.
+ CreatePolicyService();
+ FlushDeviceSettings();
+ extension_cache_task_runner_->RunUntilIdle();
+
+ // Verify that a cache directory has been created for account 1.
+ EXPECT_TRUE(base::DirectoryExists(cache_dir_1_));
+
+ // Remove account 1 from device policy, allowing the deletion of its now
+ // obsolete cache directory to finish.
+ device_policy_.payload().mutable_device_local_accounts()->clear_account();
+ InstallDevicePolicy();
+ extension_cache_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ extension_cache_task_runner_->RunUntilIdle();
+
+ // Verify that the cache directory for account 1 was deleted.
+ EXPECT_FALSE(base::DirectoryExists(cache_dir_1_));
}
class DeviceLocalAccountPolicyProviderTest
- : public DeviceLocalAccountPolicyServiceTest {
+ : public DeviceLocalAccountPolicyServiceTestBase {
protected:
- DeviceLocalAccountPolicyProviderTest()
- : provider_(
- GenerateDeviceLocalAccountUserId(
- PolicyBuilder::kFakeUsername,
- DeviceLocalAccount::TYPE_PUBLIC_SESSION),
- &service_) {}
-
- virtual void SetUp() OVERRIDE {
- DeviceLocalAccountPolicyServiceTest::SetUp();
- provider_.Init();
- provider_.AddObserver(&provider_observer_);
-
- EXPECT_CALL(service_observer_, OnPolicyUpdated(_)).Times(AnyNumber());
- EXPECT_CALL(service_observer_, OnDeviceLocalAccountsChanged())
- .Times(AnyNumber());
- }
-
- virtual void TearDown() OVERRIDE {
- provider_.RemoveObserver(&provider_observer_);
- provider_.Shutdown();
- DeviceLocalAccountPolicyServiceTest::TearDown();
- }
-
- DeviceLocalAccountPolicyProvider provider_;
+ DeviceLocalAccountPolicyProviderTest();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ scoped_ptr<DeviceLocalAccountPolicyProvider> provider_;
MockConfigurationPolicyObserver provider_observer_;
private:
DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyProviderTest);
};
+DeviceLocalAccountPolicyProviderTest::DeviceLocalAccountPolicyProviderTest() {
+ CreatePolicyService();
+ provider_.reset(new DeviceLocalAccountPolicyProvider(
+ GenerateDeviceLocalAccountUserId(kAccount1,
+ DeviceLocalAccount::TYPE_PUBLIC_SESSION),
+ service_.get()));
+}
+
+void DeviceLocalAccountPolicyProviderTest::SetUp() {
+ DeviceLocalAccountPolicyServiceTestBase::SetUp();
+ provider_->Init();
+ provider_->AddObserver(&provider_observer_);
+}
+
+void DeviceLocalAccountPolicyProviderTest::TearDown() {
+ provider_->RemoveObserver(&provider_observer_);
+ provider_->Shutdown();
+ DeviceLocalAccountPolicyServiceTestBase::TearDown();
+}
+
TEST_F(DeviceLocalAccountPolicyProviderTest, Initialization) {
- EXPECT_FALSE(provider_.IsInitializationComplete(POLICY_DOMAIN_CHROME));
+ EXPECT_FALSE(provider_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
// Policy change should complete initialization.
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1));
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
- device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob());
- ReloadDeviceSettings();
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
+ .Times(AtLeast(1));
+ InstallDevicePolicy();
Mock::VerifyAndClearExpectations(&provider_observer_);
- EXPECT_TRUE(provider_.IsInitializationComplete(POLICY_DOMAIN_CHROME));
+ EXPECT_TRUE(provider_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
// The account disappearing should *not* flip the initialization flag back.
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_))
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
.Times(AnyNumber());
device_policy_.payload().mutable_device_local_accounts()->clear_account();
- device_policy_.Build();
- device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob());
- ReloadDeviceSettings();
+ InstallDevicePolicy();
Mock::VerifyAndClearExpectations(&provider_observer_);
- EXPECT_TRUE(provider_.IsInitializationComplete(POLICY_DOMAIN_CHROME));
+ EXPECT_TRUE(provider_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
}
TEST_F(DeviceLocalAccountPolicyProviderTest, Policy) {
// Policy should load successfully.
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1));
- device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob());
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
- ReloadDeviceSettings();
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
+ .Times(AtLeast(1));
+ InstallDeviceLocalAccountPolicy(kAccount1);
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
Mock::VerifyAndClearExpectations(&provider_observer_);
PolicyBundle expected_policy_bundle;
expected_policy_bundle.Get(PolicyNamespace(
POLICY_DOMAIN_CHROME, std::string())).CopyFrom(expected_policy_map_);
- EXPECT_TRUE(expected_policy_bundle.Equals(provider_.policies()));
+ EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies()));
// Policy change should be reported.
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1));
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
+ .Times(AtLeast(1));
device_local_account_policy_.payload().mutable_disablespdy()->set_value(
false);
- device_local_account_policy_.Build();
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
+ InstallDeviceLocalAccountPolicy(kAccount1);
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
broker->core()->store()->Load();
FlushDeviceSettings();
@@ -486,57 +853,55 @@ TEST_F(DeviceLocalAccountPolicyProviderTest, Policy) {
POLICY_SCOPE_USER,
Value::CreateBooleanValue(false),
NULL);
- EXPECT_TRUE(expected_policy_bundle.Equals(provider_.policies()));
+ EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies()));
// Any values set for the |ShelfAutoHideBehavior|, |ShowLogoutButtonInTray|
// and |ExtensionAllowedTypes| policies should be overridden.
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1));
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
+ .Times(AtLeast(1));
device_local_account_policy_.payload().mutable_shelfautohidebehavior()->
set_value("Always");
device_local_account_policy_.payload().mutable_showlogoutbuttonintray()->
set_value(false);
- device_local_account_policy_.Build();
- device_settings_test_helper_.set_device_local_account_policy_blob(
- PolicyBuilder::kFakeUsername, device_local_account_policy_.GetBlob());
+ InstallDeviceLocalAccountPolicy(kAccount1);
broker->core()->store()->Load();
FlushDeviceSettings();
Mock::VerifyAndClearExpectations(&provider_observer_);
- EXPECT_TRUE(expected_policy_bundle.Equals(provider_.policies()));
+ EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies()));
// Account disappears, policy should stay in effect.
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_))
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
.Times(AnyNumber());
device_policy_.payload().mutable_device_local_accounts()->clear_account();
- device_policy_.Build();
- device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob());
- ReloadDeviceSettings();
+ InstallDevicePolicy();
Mock::VerifyAndClearExpectations(&provider_observer_);
- EXPECT_TRUE(expected_policy_bundle.Equals(provider_.policies()));
+ EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies()));
}
TEST_F(DeviceLocalAccountPolicyProviderTest, RefreshPolicies) {
// If there's no device policy, the refresh completes immediately.
- EXPECT_FALSE(service_.GetBrokerForUser(public_session_user_id_));
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1));
- provider_.RefreshPolicies();
+ EXPECT_FALSE(service_->GetBrokerForUser(account_1_user_id_));
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
+ .Times(AtLeast(1));
+ provider_->RefreshPolicies();
Mock::VerifyAndClearExpectations(&provider_observer_);
// Make device settings appear.
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_))
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
.Times(AnyNumber());
- device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob());
- ReloadDeviceSettings();
- Mock::VerifyAndClearExpectations(&provider_observer_);
- EXPECT_TRUE(service_.GetBrokerForUser(public_session_user_id_));
+ AddDeviceLocalAccountToPolicy(kAccount1);
+ InstallDevicePolicy();
+ EXPECT_TRUE(service_->GetBrokerForUser(account_1_user_id_));
// If there's no cloud connection, refreshes are still immediate.
DeviceLocalAccountPolicyBroker* broker =
- service_.GetBrokerForUser(public_session_user_id_);
+ service_->GetBrokerForUser(account_1_user_id_);
ASSERT_TRUE(broker);
EXPECT_FALSE(broker->core()->client());
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1));
- provider_.RefreshPolicies();
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
+ .Times(AtLeast(1));
+ provider_->RefreshPolicies();
Mock::VerifyAndClearExpectations(&provider_observer_);
// Bring up the cloud connection. The refresh scheduler may fire refreshes at
@@ -546,7 +911,7 @@ TEST_F(DeviceLocalAccountPolicyProviderTest, RefreshPolicies) {
mock_device_management_service_.FailJob(DM_STATUS_REQUEST_FAILED));
EXPECT_CALL(mock_device_management_service_, StartJob(_, _, _, _, _, _, _))
.Times(AnyNumber());
- service_.Connect(&mock_device_management_service_);
+ service_->Connect(&mock_device_management_service_);
FlushDeviceSettings();
Mock::VerifyAndClearExpectations(&mock_device_management_service_);
@@ -556,16 +921,18 @@ TEST_F(DeviceLocalAccountPolicyProviderTest, RefreshPolicies) {
EXPECT_CALL(mock_device_management_service_, CreateJob(_))
.WillOnce(mock_device_management_service_.CreateAsyncJob(&request_job));
EXPECT_CALL(mock_device_management_service_, StartJob(_, _, _, _, _, _, _));
- provider_.RefreshPolicies();
+ provider_->RefreshPolicies();
ReloadDeviceSettings();
Mock::VerifyAndClearExpectations(&provider_observer_);
Mock::VerifyAndClearExpectations(&mock_device_management_service_);
- EXPECT_TRUE(provider_.IsInitializationComplete(POLICY_DOMAIN_CHROME));
+ EXPECT_TRUE(provider_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
// When the response comes in, it should propagate and fire the notification.
- EXPECT_CALL(provider_observer_, OnUpdatePolicy(&provider_)).Times(AtLeast(1));
+ EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
+ .Times(AtLeast(1));
ASSERT_TRUE(request_job);
em::DeviceManagementResponse response;
+ device_local_account_policy_.Build();
response.mutable_policy_response()->add_response()->CopyFrom(
device_local_account_policy_.policy());
request_job->SendResponse(DM_STATUS_SUCCESS, response);
diff --git a/chrome/browser/chromeos/settings/device_settings_test_helper.cc b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
index 04a77ea..2050364 100644
--- a/chrome/browser/chromeos/settings/device_settings_test_helper.cc
+++ b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
@@ -63,6 +63,7 @@ void DeviceSettingsTestHelper::FlushRetrieve() {
for (device_local_account_state = device_local_account_policy_.begin();
device_local_account_state != device_local_account_policy_.end();
++device_local_account_state) {
+ std::vector<RetrievePolicyCallback> callbacks;
callbacks.swap(device_local_account_state->second.retrieve_callbacks_);
for (std::vector<RetrievePolicyCallback>::iterator cb(callbacks.begin());
cb != callbacks.end(); ++cb) {