summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorjoaodasilva@chromium.org <joaodasilva@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-12 17:48:01 +0000
committerjoaodasilva@chromium.org <joaodasilva@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-12 17:48:01 +0000
commitd769050f460f7926381fb37444d588f24349d946 (patch)
tree354223abe17f3b8d706c0641ab6d05f141543a75 /components
parent8a9bc03543c148ef78649ce2cc0ca54b65347df5 (diff)
downloadchromium_src-d769050f460f7926381fb37444d588f24349d946.zip
chromium_src-d769050f460f7926381fb37444d588f24349d946.tar.gz
chromium_src-d769050f460f7926381fb37444d588f24349d946.tar.bz2
Move UserCloudPolicyManager and UserCloudPolicyStore into the policy component.
This will enable using those classes on the iOS build. BUG=271392 TBR=jochen@chromium.org Review URL: https://codereview.chromium.org/105573005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@240359 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r--components/components_tests.gyp11
-rw-r--r--components/policy.gypi20
-rw-r--r--components/policy/core/common/cloud/DEPS7
-rw-r--r--components/policy/core/common/cloud/mock_user_cloud_policy_store.cc15
-rw-r--r--components/policy/core/common/cloud/mock_user_cloud_policy_store.h38
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_manager.cc101
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_manager.h97
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_manager_unittest.cc96
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_store.cc254
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_store.h93
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_store_base.cc51
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_store_base.h57
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc367
13 files changed, 1207 insertions, 0 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 49e32a3..2c1eea3 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -189,6 +189,9 @@
}],
['configuration_policy==1', {
'dependencies': [
+ # TODO(joaodasilva): remove this dependency. This is needed to
+ # get the include path for policy_constants.h.
+ '../chrome/app/policy/cloud_policy_codegen.gyp:policy_test_support',
'components.gyp:policy_component_test_support',
],
'sources': [
@@ -208,6 +211,8 @@
'policy/core/common/cloud/external_policy_data_updater_unittest.cc',
'policy/core/common/cloud/rate_limiter_unittest.cc',
'policy/core/common/cloud/resource_cache_unittest.cc',
+ 'policy/core/common/cloud/user_cloud_policy_manager_unittest.cc',
+ 'policy/core/common/cloud/user_cloud_policy_store_unittest.cc',
'policy/core/common/cloud/user_info_fetcher_unittest.cc',
'policy/core/common/config_dir_policy_loader_unittest.cc',
'policy/core/common/forwarding_policy_provider_unittest.cc',
@@ -236,6 +241,12 @@
'policy/core/common/config_dir_policy_loader_unittest.cc',
],
}],
+ ['chromeos==1', {
+ 'sources!': [
+ 'policy/core/common/cloud/user_cloud_policy_manager_unittest.cc',
+ 'policy/core/common/cloud/user_cloud_policy_store_unittest.cc',
+ ],
+ }],
],
}],
],
diff --git a/components/policy.gypi b/components/policy.gypi
index af9db89..a69a1f1 100644
--- a/components/policy.gypi
+++ b/components/policy.gypi
@@ -83,6 +83,12 @@
'policy/core/common/cloud/resource_cache.h',
'policy/core/common/cloud/system_policy_request_context.cc',
'policy/core/common/cloud/system_policy_request_context.h',
+ 'policy/core/common/cloud/user_cloud_policy_manager.cc',
+ 'policy/core/common/cloud/user_cloud_policy_manager.h',
+ 'policy/core/common/cloud/user_cloud_policy_store.cc',
+ 'policy/core/common/cloud/user_cloud_policy_store.h',
+ 'policy/core/common/cloud/user_cloud_policy_store_base.cc',
+ 'policy/core/common/cloud/user_cloud_policy_store_base.h',
'policy/core/common/cloud/user_info_fetcher.cc',
'policy/core/common/cloud/user_info_fetcher.h',
'policy/core/common/cloud/user_policy_request_context.cc',
@@ -170,6 +176,10 @@
'sources!': [
'policy/core/common/cloud/cloud_policy_client_registration_helper.cc',
'policy/core/common/cloud/cloud_policy_client_registration_helper.h',
+ 'policy/core/common/cloud/user_cloud_policy_manager.cc',
+ 'policy/core/common/cloud/user_cloud_policy_manager.h',
+ 'policy/core/common/cloud/user_cloud_policy_store.cc',
+ 'policy/core/common/cloud/user_cloud_policy_store.h',
],
}],
],
@@ -251,6 +261,8 @@
'policy/core/common/cloud/mock_cloud_policy_store.h',
'policy/core/common/cloud/mock_device_management_service.cc',
'policy/core/common/cloud/mock_device_management_service.h',
+ 'policy/core/common/cloud/mock_user_cloud_policy_store.cc',
+ 'policy/core/common/cloud/mock_user_cloud_policy_store.h',
'policy/core/common/cloud/policy_builder.cc',
'policy/core/common/cloud/policy_builder.h',
'policy/core/common/configuration_policy_provider_test.cc',
@@ -264,6 +276,14 @@
'policy/core/common/preferences_mock_mac.cc',
'policy/core/common/preferences_mock_mac.h',
],
+ 'conditions': [
+ ['chromeos==1', {
+ 'sources!': [
+ 'policy/core/common/cloud/mock_user_cloud_policy_store.cc',
+ 'policy/core/common/cloud/mock_user_cloud_policy_store.h',
+ ],
+ }],
+ ],
},
],
}],
diff --git a/components/policy/core/common/cloud/DEPS b/components/policy/core/common/cloud/DEPS
new file mode 100644
index 0000000..458b8b5
--- /dev/null
+++ b/components/policy/core/common/cloud/DEPS
@@ -0,0 +1,7 @@
+# TODO(joaodasilva): remove this DEPS exception after moving chrome/app/policy
+# into the component.
+specific_include_rules = {
+ 'user_cloud_policy_store_unittest\.cc': [
+ "+policy/policy_constants.h",
+ ],
+}
diff --git a/components/policy/core/common/cloud/mock_user_cloud_policy_store.cc b/components/policy/core/common/cloud/mock_user_cloud_policy_store.cc
new file mode 100644
index 0000000..74166ca
--- /dev/null
+++ b/components/policy/core/common/cloud/mock_user_cloud_policy_store.cc
@@ -0,0 +1,15 @@
+// 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 "components/policy/core/common/cloud/mock_user_cloud_policy_store.h"
+
+namespace policy {
+
+MockUserCloudPolicyStore::MockUserCloudPolicyStore()
+ : UserCloudPolicyStore(base::FilePath(),
+ scoped_refptr<base::SequencedTaskRunner>()) {}
+
+MockUserCloudPolicyStore::~MockUserCloudPolicyStore() {}
+
+} // namespace policy
diff --git a/components/policy/core/common/cloud/mock_user_cloud_policy_store.h b/components/policy/core/common/cloud/mock_user_cloud_policy_store.h
new file mode 100644
index 0000000..8fae220
--- /dev/null
+++ b/components/policy/core/common/cloud/mock_user_cloud_policy_store.h
@@ -0,0 +1,38 @@
+// 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 COMPONENTS_POLICY_CORE_COMMON_CLOUD_MOCK_USER_CLOUD_POLICY_STORE_H_
+#define COMPONENTS_POLICY_CORE_COMMON_CLOUD_MOCK_USER_CLOUD_POLICY_STORE_H_
+
+#include "components/policy/core/common/cloud/user_cloud_policy_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace policy {
+
+class MockUserCloudPolicyStore : public UserCloudPolicyStore {
+ public:
+ MockUserCloudPolicyStore();
+ virtual ~MockUserCloudPolicyStore();
+
+ MOCK_METHOD1(Store, void(const enterprise_management::PolicyFetchResponse&));
+ MOCK_METHOD0(Load, void(void));
+ MOCK_METHOD0(LoadImmediately, void(void));
+ MOCK_METHOD0(Clear, void(void));
+
+ // Publish the protected members.
+ using CloudPolicyStore::NotifyStoreLoaded;
+ using CloudPolicyStore::NotifyStoreError;
+
+ using CloudPolicyStore::policy_map_;
+ using CloudPolicyStore::policy_;
+ using CloudPolicyStore::status_;
+ using UserCloudPolicyStore::signin_username_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockUserCloudPolicyStore);
+};
+
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_CLOUD_MOCK_USER_CLOUD_POLICY_STORE_H_
diff --git a/components/policy/core/common/cloud/user_cloud_policy_manager.cc b/components/policy/core/common/cloud/user_cloud_policy_manager.cc
new file mode 100644
index 0000000..6e47339
--- /dev/null
+++ b/components/policy/core/common/cloud/user_cloud_policy_manager.cc
@@ -0,0 +1,101 @@
+// 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 "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/sequenced_task_runner.h"
+#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/core/common/cloud/cloud_policy_service.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_store.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/policy/core/common/policy_types.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+UserCloudPolicyManager::UserCloudPolicyManager(
+ scoped_ptr<UserCloudPolicyStore> store,
+ const base::FilePath& component_policy_cache_path,
+ scoped_ptr<CloudExternalDataManager> external_data_manager,
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+ const scoped_refptr<base::SequencedTaskRunner>& file_task_runner,
+ const scoped_refptr<base::SequencedTaskRunner>& io_task_runner)
+ : CloudPolicyManager(
+ PolicyNamespaceKey(GetChromeUserPolicyType(), std::string()),
+ store.get(),
+ task_runner,
+ file_task_runner,
+ io_task_runner),
+ store_(store.Pass()),
+ component_policy_cache_path_(component_policy_cache_path),
+ external_data_manager_(external_data_manager.Pass()) {}
+
+UserCloudPolicyManager::~UserCloudPolicyManager() {}
+
+void UserCloudPolicyManager::Shutdown() {
+ if (external_data_manager_)
+ external_data_manager_->Disconnect();
+ CloudPolicyManager::Shutdown();
+}
+
+void UserCloudPolicyManager::SetSigninUsername(const std::string& username) {
+ store_->SetSigninUsername(username);
+}
+
+void UserCloudPolicyManager::Connect(
+ PrefService* local_state,
+ scoped_refptr<net::URLRequestContextGetter> request_context,
+ scoped_ptr<CloudPolicyClient> client) {
+ core()->Connect(client.Pass());
+ core()->StartRefreshScheduler();
+ core()->TrackRefreshDelayPref(local_state,
+ policy_prefs::kUserPolicyRefreshRate);
+ if (external_data_manager_)
+ external_data_manager_->Connect(request_context);
+
+ CreateComponentCloudPolicyService(component_policy_cache_path_,
+ request_context);
+}
+
+// static
+scoped_ptr<CloudPolicyClient>
+UserCloudPolicyManager::CreateCloudPolicyClient(
+ DeviceManagementService* device_management_service,
+ scoped_refptr<net::URLRequestContextGetter> request_context) {
+ return make_scoped_ptr(
+ new CloudPolicyClient(
+ std::string(),
+ std::string(),
+ USER_AFFILIATION_NONE,
+ NULL,
+ device_management_service,
+ request_context)).Pass();
+}
+
+void UserCloudPolicyManager::DisconnectAndRemovePolicy() {
+ if (external_data_manager_)
+ external_data_manager_->Disconnect();
+ core()->Disconnect();
+
+ // store_->Clear() will publish the updated, empty policy. The component
+ // policy service must be cleared before OnStoreLoaded() is issued, so that
+ // component policies are also empty at CheckAndPublishPolicy().
+ ClearAndDestroyComponentCloudPolicyService();
+
+ // When the |store_| is cleared, it informs the |external_data_manager_| that
+ // all external data references have been removed, causing the
+ // |external_data_manager_| to clear its cache as well.
+ store_->Clear();
+}
+
+bool UserCloudPolicyManager::IsClientRegistered() const {
+ return client() && client()->is_registered();
+}
+
+} // namespace policy
diff --git a/components/policy/core/common/cloud/user_cloud_policy_manager.h b/components/policy/core/common/cloud/user_cloud_policy_manager.h
new file mode 100644
index 0000000..5c4068e
--- /dev/null
+++ b/components/policy/core/common/cloud/user_cloud_policy_manager.h
@@ -0,0 +1,97 @@
+// 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 COMPONENTS_POLICY_CORE_COMMON_CLOUD_USER_CLOUD_POLICY_MANAGER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_CLOUD_USER_CLOUD_POLICY_MANAGER_H_
+
+#include <string>
+
+#include "base/basictypes.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 "components/policy/core/common/cloud/cloud_policy_manager.h"
+#include "components/policy/policy_export.h"
+
+class PrefService;
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace policy {
+
+class CloudExternalDataManager;
+class DeviceManagementService;
+class UserCloudPolicyStore;
+
+// UserCloudPolicyManager handles initialization of user policy.
+class POLICY_EXPORT UserCloudPolicyManager : public CloudPolicyManager {
+ public:
+ // |task_runner| is the runner for policy refresh tasks.
+ // |file_task_runner| is used for file operations. Currently this must be
+ // the FILE BrowserThread.
+ // |io_task_runner| is used for network IO. Currently this must be the IO
+ // BrowserThread.
+ UserCloudPolicyManager(
+ scoped_ptr<UserCloudPolicyStore> store,
+ const base::FilePath& component_policy_cache_path,
+ scoped_ptr<CloudExternalDataManager> external_data_manager,
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+ const scoped_refptr<base::SequencedTaskRunner>& file_task_runner,
+ const scoped_refptr<base::SequencedTaskRunner>& io_task_runner);
+ virtual ~UserCloudPolicyManager();
+
+ // ConfigurationPolicyProvider overrides:
+ virtual void Shutdown() OVERRIDE;
+
+ void SetSigninUsername(const std::string& username);
+
+ // Initializes the cloud connection. |local_state| must stay valid until this
+ // object is deleted or DisconnectAndRemovePolicy() gets called. Virtual for
+ // mocking.
+ virtual void Connect(
+ PrefService* local_state,
+ scoped_refptr<net::URLRequestContextGetter> request_context,
+ scoped_ptr<CloudPolicyClient> client);
+
+ // Shuts down the UserCloudPolicyManager (removes and stops refreshing the
+ // cached cloud policy). This is typically called when a profile is being
+ // disassociated from a given user (e.g. during signout). No policy will be
+ // provided by this object until the next time Initialize() is invoked.
+ void DisconnectAndRemovePolicy();
+
+ // Returns true if the underlying CloudPolicyClient is already registered.
+ // Virtual for mocking.
+ virtual bool IsClientRegistered() const;
+
+ // Creates a CloudPolicyClient for this client. Used in situations where
+ // callers want to create a DMToken without actually initializing the
+ // profile's policy infrastructure.
+ static scoped_ptr<CloudPolicyClient> CreateCloudPolicyClient(
+ DeviceManagementService* device_management_service,
+ scoped_refptr<net::URLRequestContextGetter> request_context);
+
+ private:
+ // Typed pointer to the store owned by UserCloudPolicyManager. Note that
+ // CloudPolicyManager only keeps a plain CloudPolicyStore pointer.
+ scoped_ptr<UserCloudPolicyStore> store_;
+
+ // Path where policy for components will be cached.
+ base::FilePath component_policy_cache_path_;
+
+ // Manages external data referenced by policies.
+ scoped_ptr<CloudExternalDataManager> external_data_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyManager);
+};
+
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_CLOUD_USER_CLOUD_POLICY_MANAGER_H_
diff --git a/components/policy/core/common/cloud/user_cloud_policy_manager_unittest.cc b/components/policy/core/common/cloud/user_cloud_policy_manager_unittest.cc
new file mode 100644
index 0000000..0602b28
--- /dev/null
+++ b/components/policy/core/common/cloud/user_cloud_policy_manager_unittest.cc
@@ -0,0 +1,96 @@
+// 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 "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/sequenced_task_runner.h"
+#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
+#include "components/policy/core/common/cloud/mock_user_cloud_policy_store.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::Mock;
+using testing::_;
+
+namespace policy {
+namespace {
+
+class UserCloudPolicyManagerTest : public testing::Test {
+ protected:
+ UserCloudPolicyManagerTest() : store_(NULL) {}
+
+ virtual void SetUp() OVERRIDE {
+ // Set up a policy map for testing.
+ policy_map_.Set("key", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+ base::Value::CreateStringValue("value"), NULL);
+ expected_bundle_.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+ .CopyFrom(policy_map_);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (manager_) {
+ manager_->RemoveObserver(&observer_);
+ manager_->Shutdown();
+ }
+ }
+
+ void CreateManager() {
+ store_ = new MockUserCloudPolicyStore();
+ EXPECT_CALL(*store_, Load());
+ manager_.reset(new UserCloudPolicyManager(
+ scoped_ptr<UserCloudPolicyStore>(store_),
+ base::FilePath(),
+ scoped_ptr<CloudExternalDataManager>(),
+ loop_.message_loop_proxy(),
+ loop_.message_loop_proxy(),
+ loop_.message_loop_proxy()));
+ manager_->Init(&schema_registry_);
+ manager_->AddObserver(&observer_);
+ Mock::VerifyAndClearExpectations(store_);
+ }
+
+ // Required by the refresh scheduler that's created by the manager.
+ base::MessageLoop loop_;
+
+ // Convenience policy objects.
+ PolicyMap policy_map_;
+ PolicyBundle expected_bundle_;
+
+ // Policy infrastructure.
+ SchemaRegistry schema_registry_;
+ MockConfigurationPolicyObserver observer_;
+ MockUserCloudPolicyStore* store_; // Not owned.
+ scoped_ptr<UserCloudPolicyManager> manager_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyManagerTest);
+};
+
+TEST_F(UserCloudPolicyManagerTest, DisconnectAndRemovePolicy) {
+ // Load policy, make sure it goes away when DisconnectAndRemovePolicy() is
+ // called.
+ CreateManager();
+ store_->policy_map_.CopyFrom(policy_map_);
+ EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get()));
+ store_->NotifyStoreLoaded();
+ EXPECT_TRUE(expected_bundle_.Equals(manager_->policies()));
+ EXPECT_TRUE(manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+ EXPECT_CALL(*store_, Clear());
+ manager_->DisconnectAndRemovePolicy();
+ EXPECT_FALSE(manager_->core()->service());
+}
+
+} // namespace
+} // namespace policy
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store.cc b/components/policy/core/common/cloud/user_cloud_policy_store.cc
new file mode 100644
index 0000000..c800328
--- /dev/null
+++ b/components/policy/core/common/cloud/user_cloud_policy_store.cc
@@ -0,0 +1,254 @@
+// 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 "components/policy/core/common/cloud/user_cloud_policy_store.h"
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/location.h"
+#include "base/task_runner_util.h"
+#include "policy/proto/cloud_policy.pb.h"
+#include "policy/proto/device_management_backend.pb.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+enum PolicyLoadStatus {
+ // Policy blob was successfully loaded and parsed.
+ LOAD_RESULT_SUCCESS,
+
+ // No previously stored policy was found.
+ LOAD_RESULT_NO_POLICY_FILE,
+
+ // Could not load the previously stored policy due to either a parse or
+ // file read error.
+ LOAD_RESULT_LOAD_ERROR,
+};
+
+// Struct containing the result of a policy load - if |status| ==
+// LOAD_RESULT_SUCCESS, |policy| is initialized from the policy file on disk.
+struct PolicyLoadResult {
+ PolicyLoadStatus status;
+ em::PolicyFetchResponse policy;
+};
+
+namespace {
+
+// Subdirectory in the user's profile for storing user policies.
+const base::FilePath::CharType kPolicyDir[] = FILE_PATH_LITERAL("Policy");
+// File in the above directory for storing user policy data.
+const base::FilePath::CharType kPolicyCacheFile[] =
+ FILE_PATH_LITERAL("User Policy");
+
+// Loads policy from the backing file. Returns a PolicyLoadResult with the
+// results of the fetch.
+policy::PolicyLoadResult LoadPolicyFromDisk(const base::FilePath& path) {
+ policy::PolicyLoadResult result;
+ // If the backing file does not exist, just return.
+ if (!base::PathExists(path)) {
+ result.status = policy::LOAD_RESULT_NO_POLICY_FILE;
+ return result;
+ }
+ std::string data;
+ if (!base::ReadFileToString(path, &data) ||
+ !result.policy.ParseFromArray(data.c_str(), data.size())) {
+ LOG(WARNING) << "Failed to read or parse policy data from " << path.value();
+ result.status = policy::LOAD_RESULT_LOAD_ERROR;
+ return result;
+ }
+
+ result.status = policy::LOAD_RESULT_SUCCESS;
+ return result;
+}
+
+// Stores policy to the backing file (must be called via a task on
+// the background thread).
+void StorePolicyToDiskOnBackgroundThread(
+ const base::FilePath& path,
+ const em::PolicyFetchResponse& policy) {
+ DVLOG(1) << "Storing policy to " << path.value();
+ std::string data;
+ if (!policy.SerializeToString(&data)) {
+ DLOG(WARNING) << "Failed to serialize policy data";
+ return;
+ }
+
+ if (!base::CreateDirectory(path.DirName())) {
+ DLOG(WARNING) << "Failed to create directory " << path.DirName().value();
+ return;
+ }
+
+ int size = data.size();
+ if (file_util::WriteFile(path, data.c_str(), size) != size) {
+ DLOG(WARNING) << "Failed to write " << path.value();
+ }
+}
+
+} // namespace
+
+UserCloudPolicyStore::UserCloudPolicyStore(
+ const base::FilePath& path,
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner)
+ : UserCloudPolicyStoreBase(background_task_runner),
+ weak_factory_(this),
+ backing_file_path_(path) {}
+
+UserCloudPolicyStore::~UserCloudPolicyStore() {}
+
+// static
+scoped_ptr<UserCloudPolicyStore> UserCloudPolicyStore::Create(
+ const base::FilePath& profile_path,
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner) {
+ base::FilePath path =
+ profile_path.Append(kPolicyDir).Append(kPolicyCacheFile);
+ return make_scoped_ptr(
+ new UserCloudPolicyStore(path, background_task_runner));
+}
+
+void UserCloudPolicyStore::SetSigninUsername(const std::string& username) {
+ signin_username_ = username;
+}
+
+void UserCloudPolicyStore::LoadImmediately() {
+ DVLOG(1) << "Initiating immediate policy load from disk";
+ // Cancel any pending Load/Store/Validate operations.
+ weak_factory_.InvalidateWeakPtrs();
+ // Load the policy from disk...
+ PolicyLoadResult result = LoadPolicyFromDisk(backing_file_path_);
+ // ...and install it, reporting success/failure to any observers.
+ PolicyLoaded(false, result);
+}
+
+void UserCloudPolicyStore::Clear() {
+ background_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ base::IgnoreResult(&base::DeleteFile), backing_file_path_, false));
+ policy_.reset();
+ policy_map_.Clear();
+ NotifyStoreLoaded();
+}
+
+void UserCloudPolicyStore::Load() {
+ DVLOG(1) << "Initiating policy load from disk";
+ // Cancel any pending Load/Store/Validate operations.
+ weak_factory_.InvalidateWeakPtrs();
+
+ // Start a new Load operation and have us get called back when it is
+ // complete.
+ base::PostTaskAndReplyWithResult(
+ background_task_runner(),
+ FROM_HERE,
+ base::Bind(&LoadPolicyFromDisk, backing_file_path_),
+ base::Bind(&UserCloudPolicyStore::PolicyLoaded,
+ weak_factory_.GetWeakPtr(), true));
+}
+
+void UserCloudPolicyStore::PolicyLoaded(bool validate_in_background,
+ PolicyLoadResult result) {
+ switch (result.status) {
+ case LOAD_RESULT_LOAD_ERROR:
+ status_ = STATUS_LOAD_ERROR;
+ NotifyStoreError();
+ break;
+
+ case LOAD_RESULT_NO_POLICY_FILE:
+ DVLOG(1) << "No policy found on disk";
+ NotifyStoreLoaded();
+ break;
+
+ case LOAD_RESULT_SUCCESS: {
+ // Found policy on disk - need to validate it before it can be used.
+ scoped_ptr<em::PolicyFetchResponse> cloud_policy(
+ new em::PolicyFetchResponse(result.policy));
+ Validate(cloud_policy.Pass(),
+ validate_in_background,
+ base::Bind(
+ &UserCloudPolicyStore::InstallLoadedPolicyAfterValidation,
+ weak_factory_.GetWeakPtr()));
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+}
+
+void UserCloudPolicyStore::InstallLoadedPolicyAfterValidation(
+ UserCloudPolicyValidator* validator) {
+ validation_status_ = validator->status();
+ if (!validator->success()) {
+ DVLOG(1) << "Validation failed: status=" << validation_status_;
+ status_ = STATUS_VALIDATION_ERROR;
+ NotifyStoreError();
+ return;
+ }
+
+ DVLOG(1) << "Validation succeeded - installing policy with dm_token: " <<
+ validator->policy_data()->request_token();
+ DVLOG(1) << "Device ID: " << validator->policy_data()->device_id();
+
+ InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
+ status_ = STATUS_OK;
+ NotifyStoreLoaded();
+}
+
+void UserCloudPolicyStore::Store(const em::PolicyFetchResponse& policy) {
+ // Stop any pending requests to store policy, then validate the new policy
+ // before storing it.
+ weak_factory_.InvalidateWeakPtrs();
+ scoped_ptr<em::PolicyFetchResponse> policy_copy(
+ new em::PolicyFetchResponse(policy));
+ Validate(policy_copy.Pass(),
+ true,
+ base::Bind(&UserCloudPolicyStore::StorePolicyAfterValidation,
+ weak_factory_.GetWeakPtr()));
+}
+
+void UserCloudPolicyStore::Validate(
+ scoped_ptr<em::PolicyFetchResponse> policy,
+ bool validate_in_background,
+ const UserCloudPolicyValidator::CompletionCallback& callback) {
+ // Configure the validator.
+ scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(
+ policy.Pass(),
+ CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE);
+
+ // Validate the username if the user is signed in.
+ if (!signin_username_.empty())
+ validator->ValidateUsername(signin_username_);
+
+ if (validate_in_background) {
+ // Start validation in the background. The Validator will free itself once
+ // validation is complete.
+ validator.release()->StartValidation(callback);
+ } else {
+ // Run validation immediately and invoke the callback with the results.
+ validator->RunValidation();
+ callback.Run(validator.get());
+ }
+}
+
+void UserCloudPolicyStore::StorePolicyAfterValidation(
+ UserCloudPolicyValidator* validator) {
+ validation_status_ = validator->status();
+ DVLOG(1) << "Policy validation complete: status = " << validation_status_;
+ if (!validator->success()) {
+ status_ = STATUS_VALIDATION_ERROR;
+ NotifyStoreError();
+ return;
+ }
+
+ // Persist the validated policy (just fire a task - don't bother getting a
+ // reply because we can't do anything if it fails).
+ background_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&StorePolicyToDiskOnBackgroundThread,
+ backing_file_path_, *validator->policy()));
+ InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
+ status_ = STATUS_OK;
+ NotifyStoreLoaded();
+}
+
+} // namespace policy
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store.h b/components/policy/core/common/cloud/user_cloud_policy_store.h
new file mode 100644
index 0000000..4dc7e24
--- /dev/null
+++ b/components/policy/core/common/cloud/user_cloud_policy_store.h
@@ -0,0 +1,93 @@
+// 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 COMPONENTS_POLICY_CORE_COMMON_CLOUD_USER_CLOUD_POLICY_STORE_H_
+#define COMPONENTS_POLICY_CORE_COMMON_CLOUD_USER_CLOUD_POLICY_STORE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_store_base.h"
+#include "components/policy/policy_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace policy {
+
+// Implements a cloud policy store that is stored in a simple file in the user's
+// profile directory. This is used on (non-chromeos) platforms that do not have
+// a secure storage implementation.
+class POLICY_EXPORT UserCloudPolicyStore : public UserCloudPolicyStoreBase {
+ public:
+ // Creates a policy store associated with a signed-in (or in the progress of
+ // it) user.
+ UserCloudPolicyStore(
+ const base::FilePath& policy_file,
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner);
+ virtual ~UserCloudPolicyStore();
+
+ // Factory method for creating a UserCloudPolicyStore for a profile with path
+ // |profile_path|.
+ static scoped_ptr<UserCloudPolicyStore> Create(
+ const base::FilePath& profile_path,
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner);
+
+ // Sets the username from signin for validation of the policy.
+ void SetSigninUsername(const std::string& username);
+
+ // Loads policy immediately on the current thread. Virtual for mocks.
+ virtual void LoadImmediately();
+
+ // Deletes any existing policy blob and notifies observers via OnStoreLoaded()
+ // that the blob has changed. Virtual for mocks.
+ virtual void Clear();
+
+ // CloudPolicyStore implementation.
+ virtual void Load() OVERRIDE;
+ virtual void Store(
+ const enterprise_management::PolicyFetchResponse& policy) OVERRIDE;
+
+ protected:
+ std::string signin_username_;
+
+ private:
+ // Callback invoked when a new policy has been loaded from disk. If
+ // |validate_in_background| is true, then policy is validated via a background
+ // thread.
+ void PolicyLoaded(bool validate_in_background,
+ struct PolicyLoadResult policy_load_result);
+
+ // Starts policy blob validation. |callback| is invoked once validation is
+ // complete. If |validate_in_background| is true, then the validation work
+ // occurs on a background thread (results are sent back to the calling
+ // thread).
+ void Validate(
+ scoped_ptr<enterprise_management::PolicyFetchResponse> policy,
+ bool validate_in_background,
+ const UserCloudPolicyValidator::CompletionCallback& callback);
+
+ // Callback invoked to install a just-loaded policy after validation has
+ // finished.
+ void InstallLoadedPolicyAfterValidation(UserCloudPolicyValidator* validator);
+
+ // Callback invoked to store the policy after validation has finished.
+ void StorePolicyAfterValidation(UserCloudPolicyValidator* validator);
+
+ // WeakPtrFactory used to create callbacks for validating and storing policy.
+ base::WeakPtrFactory<UserCloudPolicyStore> weak_factory_;
+
+ // Path to file where we store persisted policy.
+ base::FilePath backing_file_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStore);
+};
+
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_CLOUD_USER_CLOUD_POLICY_STORE_H_
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store_base.cc b/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
new file mode 100644
index 0000000..de5adee
--- /dev/null
+++ b/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
@@ -0,0 +1,51 @@
+// 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 "components/policy/core/common/cloud/user_cloud_policy_store_base.h"
+
+#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/core/common/policy_map.h"
+#include "policy/proto/cloud_policy.pb.h"
+
+namespace policy {
+
+// Decodes a CloudPolicySettings object into a policy map. The implementation is
+// generated code in policy/cloud_policy_generated.cc.
+void DecodePolicy(const enterprise_management::CloudPolicySettings& policy,
+ base::WeakPtr<CloudExternalDataManager> external_data_manager,
+ PolicyMap* policies);
+
+UserCloudPolicyStoreBase::UserCloudPolicyStoreBase(
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner)
+ : background_task_runner_(background_task_runner) {}
+
+UserCloudPolicyStoreBase::~UserCloudPolicyStoreBase() {
+}
+
+scoped_ptr<UserCloudPolicyValidator> UserCloudPolicyStoreBase::CreateValidator(
+ scoped_ptr<enterprise_management::PolicyFetchResponse> policy,
+ CloudPolicyValidatorBase::ValidateTimestampOption timestamp_option) {
+ // Configure the validator.
+ UserCloudPolicyValidator* validator =
+ UserCloudPolicyValidator::Create(policy.Pass(), background_task_runner_);
+ validator->ValidatePolicyType(GetChromeUserPolicyType());
+ validator->ValidateAgainstCurrentPolicy(
+ policy_.get(),
+ timestamp_option,
+ CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
+ validator->ValidatePayload();
+ return scoped_ptr<UserCloudPolicyValidator>(validator);
+}
+
+void UserCloudPolicyStoreBase::InstallPolicy(
+ scoped_ptr<enterprise_management::PolicyData> policy_data,
+ scoped_ptr<enterprise_management::CloudPolicySettings> payload) {
+ // Decode the payload.
+ policy_map_.Clear();
+ DecodePolicy(*payload, external_data_manager(), &policy_map_);
+ policy_ = policy_data.Pass();
+}
+
+} // namespace policy
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store_base.h b/components/policy/core/common/cloud/user_cloud_policy_store_base.h
new file mode 100644
index 0000000..1dd9743
--- /dev/null
+++ b/components/policy/core/common/cloud/user_cloud_policy_store_base.h
@@ -0,0 +1,57 @@
+// 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 COMPONENTS_POLICY_CORE_COMMON_CLOUD_USER_CLOUD_POLICY_STORE_BASE_H_
+#define COMPONENTS_POLICY_CORE_COMMON_CLOUD_USER_CLOUD_POLICY_STORE_BASE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+#include "components/policy/core/common/cloud/cloud_policy_validator.h"
+#include "components/policy/policy_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace policy {
+
+// Base class that implements common cross-platform UserCloudPolicyStore
+// functionality.
+class POLICY_EXPORT UserCloudPolicyStoreBase : public CloudPolicyStore {
+ public:
+ explicit UserCloudPolicyStoreBase(
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner);
+ virtual ~UserCloudPolicyStoreBase();
+
+ protected:
+ // Creates a validator configured to validate a user policy. The caller owns
+ // the resulting object until StartValidation() is invoked.
+ scoped_ptr<UserCloudPolicyValidator> CreateValidator(
+ scoped_ptr<enterprise_management::PolicyFetchResponse> policy,
+ CloudPolicyValidatorBase::ValidateTimestampOption option);
+
+ // Sets |policy_data| and |payload| as the active policy.
+ void InstallPolicy(
+ scoped_ptr<enterprise_management::PolicyData> policy_data,
+ scoped_ptr<enterprise_management::CloudPolicySettings> payload);
+
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner() const {
+ return background_task_runner_;
+ }
+
+ private:
+ // Task runner for background file operations.
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStoreBase);
+};
+
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_CLOUD_USER_CLOUD_POLICY_STORE_BASE_H_
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc b/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
new file mode 100644
index 0000000..0220251
--- /dev/null
+++ b/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
@@ -0,0 +1,367 @@
+// 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 "components/policy/core/common/cloud/user_cloud_policy_store.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/run_loop.h"
+#include "components/policy/core/common/cloud/mock_cloud_external_data_manager.h"
+#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
+#include "components/policy/core/common/cloud/policy_builder.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "policy/policy_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::AllOf;
+using testing::Eq;
+using testing::Mock;
+using testing::Property;
+using testing::Sequence;
+
+namespace policy {
+
+namespace {
+
+void RunUntilIdle() {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+}
+
+class UserCloudPolicyStoreTest : public testing::Test {
+ public:
+ UserCloudPolicyStoreTest() : loop_(base::MessageLoop::TYPE_UI) {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
+ store_.reset(
+ new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ external_data_manager_.reset(new MockCloudExternalDataManager);
+ external_data_manager_->SetPolicyStore(store_.get());
+ store_->SetSigninUsername(PolicyBuilder::kFakeUsername);
+ store_->AddObserver(&observer_);
+
+ policy_.payload().mutable_passwordmanagerenabled()->set_value(true);
+ policy_.payload().mutable_urlblacklist()->mutable_value()->add_entries(
+ "chromium.org");
+
+ policy_.Build();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ store_->RemoveObserver(&observer_);
+ external_data_manager_.reset();
+ store_.reset();
+ RunUntilIdle();
+ }
+
+ base::FilePath policy_file() {
+ return tmp_dir_.path().AppendASCII("policy");
+ }
+
+ // Verifies that store_->policy_map() has the appropriate entries.
+ void VerifyPolicyMap(CloudPolicyStore* store) {
+ EXPECT_EQ(2U, store->policy_map().size());
+ const PolicyMap::Entry* entry =
+ store->policy_map().Get(key::kPasswordManagerEnabled);
+ ASSERT_TRUE(entry);
+ EXPECT_TRUE(base::FundamentalValue(true).Equals(entry->value));
+ ASSERT_TRUE(store->policy_map().Get(key::kURLBlacklist));
+ }
+
+ // Install an expectation on |observer_| for an error code.
+ void ExpectError(CloudPolicyStore* store, CloudPolicyStore::Status error) {
+ EXPECT_CALL(observer_,
+ OnStoreError(AllOf(Eq(store),
+ Property(&CloudPolicyStore::status,
+ Eq(error)))));
+ }
+
+ UserPolicyBuilder policy_;
+ MockCloudPolicyStoreObserver observer_;
+ scoped_ptr<UserCloudPolicyStore> store_;
+ scoped_ptr<MockCloudExternalDataManager> external_data_manager_;
+
+ // CloudPolicyValidator() requires a FILE thread so declare one here. Both
+ // |ui_thread_| and |file_thread_| share the same MessageLoop |loop_| so
+ // callers can use RunLoop to manage both virtual threads.
+ base::MessageLoop loop_;
+
+ base::ScopedTempDir tmp_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStoreTest);
+};
+
+TEST_F(UserCloudPolicyStoreTest, LoadWithNoFile) {
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+
+ Sequence s;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
+ store_->Load();
+ RunUntilIdle();
+
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+}
+
+TEST_F(UserCloudPolicyStoreTest, LoadWithInvalidFile) {
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+
+ // Create a bogus file.
+ ASSERT_TRUE(base::CreateDirectory(policy_file().DirName()));
+ std::string bogus_data = "bogus_data";
+ int size = bogus_data.size();
+ ASSERT_EQ(size, file_util::WriteFile(policy_file(),
+ bogus_data.c_str(),
+ bogus_data.size()));
+
+ ExpectError(store_.get(), CloudPolicyStore::STATUS_LOAD_ERROR);
+ store_->Load();
+ RunUntilIdle();
+
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+}
+
+TEST_F(UserCloudPolicyStoreTest, LoadImmediatelyWithNoFile) {
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+
+ Sequence s;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
+ store_->LoadImmediately(); // Should load without running the message loop.
+
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+}
+
+TEST_F(UserCloudPolicyStoreTest, LoadImmediatelyWithInvalidFile) {
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+
+ // Create a bogus file.
+ ASSERT_TRUE(base::CreateDirectory(policy_file().DirName()));
+ std::string bogus_data = "bogus_data";
+ int size = bogus_data.size();
+ ASSERT_EQ(size, file_util::WriteFile(policy_file(),
+ bogus_data.c_str(),
+ bogus_data.size()));
+
+ ExpectError(store_.get(), CloudPolicyStore::STATUS_LOAD_ERROR);
+ store_->LoadImmediately(); // Should load without running the message loop.
+
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+}
+
+TEST_F(UserCloudPolicyStoreTest, Store) {
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+
+ // Store a simple policy and make sure it ends up as the currently active
+ // policy.
+ Sequence s;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
+ store_->Store(policy_.policy());
+ RunUntilIdle();
+
+ // Policy should be decoded and stored.
+ ASSERT_TRUE(store_->policy());
+ EXPECT_EQ(policy_.policy_data().SerializeAsString(),
+ store_->policy()->SerializeAsString());
+ VerifyPolicyMap(store_.get());
+ EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
+}
+
+TEST_F(UserCloudPolicyStoreTest, StoreThenClear) {
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+
+ // Store a simple policy and make sure the file exists.
+ // policy.
+ Sequence s1;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s1);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s1);
+ store_->Store(policy_.policy());
+ RunUntilIdle();
+
+ EXPECT_TRUE(store_->policy());
+ EXPECT_FALSE(store_->policy_map().empty());
+
+ Mock::VerifyAndClearExpectations(external_data_manager_.get());
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ // Policy file should exist.
+ ASSERT_TRUE(base::PathExists(policy_file()));
+
+ Sequence s2;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s2);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s2);
+ store_->Clear();
+ RunUntilIdle();
+
+ // Policy file should not exist.
+ ASSERT_TRUE(!base::PathExists(policy_file()));
+
+ // Policy should be gone.
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+ EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
+}
+
+TEST_F(UserCloudPolicyStoreTest, StoreTwoTimes) {
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+
+ // Store a simple policy then store a second policy before the first one
+ // finishes validating, and make sure the second policy ends up as the active
+ // policy.
+ Sequence s1;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s1);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s1);
+
+ UserPolicyBuilder first_policy;
+ first_policy.payload().mutable_passwordmanagerenabled()->set_value(false);
+ first_policy.Build();
+ store_->Store(first_policy.policy());
+ RunUntilIdle();
+
+ Mock::VerifyAndClearExpectations(external_data_manager_.get());
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ Sequence s2;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s2);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s2);
+
+ store_->Store(policy_.policy());
+ RunUntilIdle();
+
+ // Policy should be decoded and stored.
+ ASSERT_TRUE(store_->policy());
+ EXPECT_EQ(policy_.policy_data().SerializeAsString(),
+ store_->policy()->SerializeAsString());
+ VerifyPolicyMap(store_.get());
+ EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
+}
+
+TEST_F(UserCloudPolicyStoreTest, StoreThenLoad) {
+ // Store a simple policy and make sure it can be read back in.
+ // policy.
+ Sequence s;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
+ store_->Store(policy_.policy());
+ RunUntilIdle();
+
+ // Now, make sure the policy can be read back in from a second store.
+ scoped_ptr<UserCloudPolicyStore> store2(
+ new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ store2->SetSigninUsername(PolicyBuilder::kFakeUsername);
+ store2->AddObserver(&observer_);
+ EXPECT_CALL(observer_, OnStoreLoaded(store2.get()));
+ store2->Load();
+ RunUntilIdle();
+
+ ASSERT_TRUE(store2->policy());
+ EXPECT_EQ(policy_.policy_data().SerializeAsString(),
+ store2->policy()->SerializeAsString());
+ VerifyPolicyMap(store2.get());
+ EXPECT_EQ(CloudPolicyStore::STATUS_OK, store2->status());
+ store2->RemoveObserver(&observer_);
+}
+
+TEST_F(UserCloudPolicyStoreTest, StoreThenLoadImmediately) {
+ // Store a simple policy and make sure it can be read back in.
+ // policy.
+ Sequence s;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
+ store_->Store(policy_.policy());
+ RunUntilIdle();
+
+ // Now, make sure the policy can be read back in from a second store.
+ scoped_ptr<UserCloudPolicyStore> store2(
+ new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ store2->SetSigninUsername(PolicyBuilder::kFakeUsername);
+ store2->AddObserver(&observer_);
+ EXPECT_CALL(observer_, OnStoreLoaded(store2.get()));
+ store2->LoadImmediately(); // Should load without running the message loop.
+
+ ASSERT_TRUE(store2->policy());
+ EXPECT_EQ(policy_.policy_data().SerializeAsString(),
+ store2->policy()->SerializeAsString());
+ VerifyPolicyMap(store2.get());
+ EXPECT_EQ(CloudPolicyStore::STATUS_OK, store2->status());
+ store2->RemoveObserver(&observer_);
+}
+
+TEST_F(UserCloudPolicyStoreTest, StoreValidationError) {
+ // Create an invalid policy (no policy type).
+ policy_.policy_data().clear_policy_type();
+ policy_.Build();
+
+ // Store policy.
+ ExpectError(store_.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
+ store_->Store(policy_.policy());
+ RunUntilIdle();
+ ASSERT_FALSE(store_->policy());
+}
+
+TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
+ // Force a validation error by changing the username after policy is stored.
+ Sequence s;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
+ store_->Store(policy_.policy());
+ RunUntilIdle();
+
+ // Sign out, and sign back in as a different user, and try to load the profile
+ // data (should fail due to mismatched username).
+ scoped_ptr<UserCloudPolicyStore> store2(
+ new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ store2->SetSigninUsername("foobar@foobar.com");
+ store2->AddObserver(&observer_);
+ ExpectError(store2.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
+ store2->Load();
+ RunUntilIdle();
+
+ ASSERT_FALSE(store2->policy());
+ store2->RemoveObserver(&observer_);
+
+ // Sign out - we should be able to load the policy (don't check usernames
+ // when signed out).
+ scoped_ptr<UserCloudPolicyStore> store3(
+ new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ store3->AddObserver(&observer_);
+ EXPECT_CALL(observer_, OnStoreLoaded(store3.get()));
+ store3->Load();
+ RunUntilIdle();
+
+ ASSERT_TRUE(store3->policy());
+ store3->RemoveObserver(&observer_);
+
+ // Now start a signin as a different user - this should fail validation.
+ scoped_ptr<UserCloudPolicyStore> store4(
+ new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ store4->SetSigninUsername("foobar@foobar.com");
+ store4->AddObserver(&observer_);
+ ExpectError(store4.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
+ store4->Load();
+ RunUntilIdle();
+
+ ASSERT_FALSE(store4->policy());
+ store4->RemoveObserver(&observer_);
+}
+
+} // namespace
+
+} // namespace policy