| Index: components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
|
| 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
|
| index c7378c74806925da7ab9cde9da2e3fa3b82f0aae..0162e0a67eac62447f5a41c84714600b415ace57 100644
|
| --- a/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
|
| +++ b/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
|
| @@ -9,6 +9,7 @@
|
| #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/cloud_policy_constants.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"
|
| @@ -32,6 +33,8 @@ void RunUntilIdle() {
|
| run_loop.RunUntilIdle();
|
| }
|
|
|
| +} // namespace
|
| +
|
| class UserCloudPolicyStoreTest : public testing::Test {
|
| public:
|
| UserCloudPolicyStoreTest() {}
|
| @@ -39,15 +42,21 @@ class UserCloudPolicyStoreTest : public testing::Test {
|
| virtual void SetUp() OVERRIDE {
|
| ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
|
| store_.reset(
|
| - new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
|
| + new UserCloudPolicyStore(policy_file(),
|
| + key_file(),
|
| + GetPolicyVerificationKey(),
|
| + 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");
|
| + // Install an initial public key, so that by default the validation of
|
| + // the stored/loaded policy blob succeeds (it looks like a new key
|
| + // provision).
|
| + policy_.SetDefaultInitialSigningKey();
|
| +
|
| + InitPolicyPayload(&policy_.payload());
|
|
|
| policy_.Build();
|
| }
|
| @@ -59,10 +68,20 @@ class UserCloudPolicyStoreTest : public testing::Test {
|
| RunUntilIdle();
|
| }
|
|
|
| + void InitPolicyPayload(enterprise_management::CloudPolicySettings* payload) {
|
| + payload->mutable_passwordmanagerenabled()->set_value(true);
|
| + payload->mutable_urlblacklist()->mutable_value()->add_entries(
|
| + "chromium.org");
|
| + }
|
| +
|
| base::FilePath policy_file() {
|
| return tmp_dir_.path().AppendASCII("policy");
|
| }
|
|
|
| + base::FilePath key_file() {
|
| + return tmp_dir_.path().AppendASCII("policy_key");
|
| + }
|
| +
|
| // Verifies that store_->policy_map() has the appropriate entries.
|
| void VerifyPolicyMap(CloudPolicyStore* store) {
|
| EXPECT_EQ(2U, store->policy_map().size());
|
| @@ -81,6 +100,18 @@ class UserCloudPolicyStoreTest : public testing::Test {
|
| Eq(error)))));
|
| }
|
|
|
| + void StorePolicyAndEnsureLoaded(
|
| + const enterprise_management::PolicyFetchResponse& policy) {
|
| + Sequence s;
|
| + EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
|
| + EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
|
| + store_->Store(policy);
|
| + RunUntilIdle();
|
| + Mock::VerifyAndClearExpectations(external_data_manager_.get());
|
| + Mock::VerifyAndClearExpectations(&observer_);
|
| + ASSERT_TRUE(store_->policy());
|
| + }
|
| +
|
| UserPolicyBuilder policy_;
|
| MockCloudPolicyStoreObserver observer_;
|
| scoped_ptr<UserCloudPolicyStore> store_;
|
| @@ -162,20 +193,54 @@ TEST_F(UserCloudPolicyStoreTest, LoadImmediatelyWithInvalidFile) {
|
| EXPECT_TRUE(store_->policy_map().empty());
|
| }
|
|
|
| +// Load file from cache with no key data, then migrate to have a key.
|
| +TEST_F(UserCloudPolicyStoreTest, Migration) {
|
| + UserPolicyBuilder unsigned_builder;
|
| + unsigned_builder.UnsetSigningKey();
|
| + InitPolicyPayload(&unsigned_builder.payload());
|
| + unsigned_builder.Build();
|
| + // Policy should be unsigned.
|
| + EXPECT_FALSE(unsigned_builder.policy().has_policy_data_signature());
|
| +
|
| + // Write policy to disk.
|
| + std::string data;
|
| + ASSERT_TRUE(unsigned_builder.policy().SerializeToString(&data));
|
| + ASSERT_TRUE(base::CreateDirectory(policy_file().DirName()));
|
| + int size = data.size();
|
| + ASSERT_EQ(size, file_util::WriteFile(policy_file(), data.c_str(), size));
|
| +
|
| + // Now make sure the data can get loaded.
|
| + 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.
|
| + Mock::VerifyAndClearExpectations(external_data_manager_.get());
|
| + Mock::VerifyAndClearExpectations(&observer_);
|
| +
|
| + ASSERT_TRUE(store_->policy());
|
| + EXPECT_EQ(unsigned_builder.policy_data().SerializeAsString(),
|
| + store_->policy()->SerializeAsString());
|
| + VerifyPolicyMap(store_.get());
|
| + EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
|
| + EXPECT_TRUE(store_->policy_key().empty());
|
| + EXPECT_FALSE(base::PathExists(key_file()));
|
| +
|
| + // Now mimic a new policy coming down - this should result in a new key
|
| + // being installed.
|
| + StorePolicyAndEnsureLoaded(policy_.policy());
|
| + EXPECT_EQ(policy_.policy().new_public_key(), store_->policy_key());
|
| + EXPECT_TRUE(base::PathExists(key_file()));
|
| +}
|
| +
|
| 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();
|
| + StorePolicyAndEnsureLoaded(policy_.policy());
|
|
|
| // Policy should be decoded and stored.
|
| - ASSERT_TRUE(store_->policy());
|
| EXPECT_EQ(policy_.policy_data().SerializeAsString(),
|
| store_->policy()->SerializeAsString());
|
| VerifyPolicyMap(store_.get());
|
| @@ -188,18 +253,9 @@ TEST_F(UserCloudPolicyStoreTest, StoreThenClear) {
|
|
|
| // 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());
|
| + StorePolicyAndEnsureLoaded(policy_.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()));
|
|
|
| @@ -218,6 +274,47 @@ TEST_F(UserCloudPolicyStoreTest, StoreThenClear) {
|
| EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
|
| }
|
|
|
| +TEST_F(UserCloudPolicyStoreTest, StoreRotatedKey) {
|
| + 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.
|
| + StorePolicyAndEnsureLoaded(policy_.policy());
|
| + EXPECT_FALSE(policy_.policy().has_new_public_key_signature());
|
| + std::string original_policy_key = policy_.policy().new_public_key();
|
| + EXPECT_EQ(original_policy_key, store_->policy_key());
|
| +
|
| + // Now do key rotation.
|
| + policy_.SetDefaultSigningKey();
|
| + policy_.SetDefaultNewSigningKey();
|
| + policy_.Build();
|
| + EXPECT_TRUE(policy_.policy().has_new_public_key_signature());
|
| + EXPECT_NE(original_policy_key, policy_.policy().new_public_key());
|
| + StorePolicyAndEnsureLoaded(policy_.policy());
|
| + EXPECT_EQ(policy_.policy().new_public_key(), store_->policy_key());
|
| +}
|
| +
|
| +TEST_F(UserCloudPolicyStoreTest, ProvisionKeyTwice) {
|
| + 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.
|
| + StorePolicyAndEnsureLoaded(policy_.policy());
|
| +
|
| + // Now try sending down policy signed with a different key (i.e. do key
|
| + // rotation with a key not signed with the original signing key).
|
| + policy_.UnsetSigningKey();
|
| + policy_.SetDefaultNewSigningKey();
|
| + policy_.Build();
|
| + EXPECT_FALSE(policy_.policy().has_new_public_key_signature());
|
| +
|
| + ExpectError(store_.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
|
| + store_->Store(policy_.policy());
|
| + RunUntilIdle();
|
| +}
|
| +
|
| TEST_F(UserCloudPolicyStoreTest, StoreTwoTimes) {
|
| EXPECT_FALSE(store_->policy());
|
| EXPECT_TRUE(store_->policy_map().empty());
|
| @@ -225,28 +322,20 @@ TEST_F(UserCloudPolicyStoreTest, StoreTwoTimes) {
|
| // 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.SetDefaultInitialSigningKey();
|
| 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);
|
| + StorePolicyAndEnsureLoaded(first_policy.policy());
|
|
|
| - store_->Store(policy_.policy());
|
| - RunUntilIdle();
|
| + // Rebuild policy with the same signing key as |first_policy| (no rotation).
|
| + policy_.UnsetNewSigningKey();
|
| + policy_.SetDefaultSigningKey();
|
| + policy_.Build();
|
| + ASSERT_FALSE(policy_.policy().has_new_public_key());
|
| + StorePolicyAndEnsureLoaded(policy_.policy());
|
|
|
| // Policy should be decoded and stored.
|
| - ASSERT_TRUE(store_->policy());
|
| EXPECT_EQ(policy_.policy_data().SerializeAsString(),
|
| store_->policy()->SerializeAsString());
|
| VerifyPolicyMap(store_.get());
|
| @@ -256,15 +345,15 @@ TEST_F(UserCloudPolicyStoreTest, StoreTwoTimes) {
|
| 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();
|
| + StorePolicyAndEnsureLoaded(policy_.policy());
|
| + EXPECT_FALSE(store_->policy_key().empty());
|
|
|
| // 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()));
|
| + new UserCloudPolicyStore(policy_file(),
|
| + key_file(),
|
| + GetPolicyVerificationKey(),
|
| + loop_.message_loop_proxy()));
|
| store2->SetSigninUsername(PolicyBuilder::kFakeUsername);
|
| store2->AddObserver(&observer_);
|
| EXPECT_CALL(observer_, OnStoreLoaded(store2.get()));
|
| @@ -277,20 +366,21 @@ TEST_F(UserCloudPolicyStoreTest, StoreThenLoad) {
|
| VerifyPolicyMap(store2.get());
|
| EXPECT_EQ(CloudPolicyStore::STATUS_OK, store2->status());
|
| store2->RemoveObserver(&observer_);
|
| + // Make sure that we properly resurrected the keys.
|
| + EXPECT_EQ(store2->policy_key(), store_->policy_key());
|
| }
|
|
|
| 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();
|
| + StorePolicyAndEnsureLoaded(policy_.policy());
|
|
|
| // 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()));
|
| + new UserCloudPolicyStore(policy_file(),
|
| + key_file(),
|
| + GetPolicyVerificationKey(),
|
| + loop_.message_loop_proxy()));
|
| store2->SetSigninUsername(PolicyBuilder::kFakeUsername);
|
| store2->AddObserver(&observer_);
|
| EXPECT_CALL(observer_, OnStoreLoaded(store2.get()));
|
| @@ -316,18 +406,28 @@ TEST_F(UserCloudPolicyStoreTest, StoreValidationError) {
|
| 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);
|
| +TEST_F(UserCloudPolicyStoreTest, StoreUnsigned) {
|
| + // Create unsigned policy, try to store it, should get a validation error.
|
| + policy_.policy().mutable_policy_data_signature()->clear();
|
| +
|
| + // 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.
|
| + StorePolicyAndEnsureLoaded(policy_.policy());
|
|
|
| // 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()));
|
| + new UserCloudPolicyStore(policy_file(),
|
| + key_file(),
|
| + GetPolicyVerificationKey(),
|
| + loop_.message_loop_proxy()));
|
| store2->SetSigninUsername("foobar@foobar.com");
|
| store2->AddObserver(&observer_);
|
| ExpectError(store2.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
|
| @@ -340,7 +440,10 @@ TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
|
| // 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()));
|
| + new UserCloudPolicyStore(policy_file(),
|
| + key_file(),
|
| + GetPolicyVerificationKey(),
|
| + loop_.message_loop_proxy()));
|
| store3->AddObserver(&observer_);
|
| EXPECT_CALL(observer_, OnStoreLoaded(store3.get()));
|
| store3->Load();
|
| @@ -351,7 +454,10 @@ TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
|
|
|
| // Now start a signin as a different user - this should fail validation.
|
| scoped_ptr<UserCloudPolicyStore> store4(
|
| - new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
|
| + new UserCloudPolicyStore(policy_file(),
|
| + key_file(),
|
| + GetPolicyVerificationKey(),
|
| + loop_.message_loop_proxy()));
|
| store4->SetSigninUsername("foobar@foobar.com");
|
| store4->AddObserver(&observer_);
|
| ExpectError(store4.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
|
| @@ -362,6 +468,8 @@ TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
|
| store4->RemoveObserver(&observer_);
|
| }
|
|
|
| -} // namespace
|
| +// TODO(atwilson): Add a test to detect tampered policy
|
| +// (new_public_key_verification_signature() doesn't match the right key -
|
| +// http://crbug.com/275291).
|
|
|
| } // namespace policy
|
|
|