1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "components/policy/core/common/cloud/component_cloud_policy_store.h" 6 7#include <map> 8#include <string> 9 10#include "base/basictypes.h" 11#include "base/bind.h" 12#include "base/callback.h" 13#include "base/files/scoped_temp_dir.h" 14#include "base/memory/ref_counted.h" 15#include "base/test/test_simple_task_runner.h" 16#include "components/policy/core/common/cloud/cloud_policy_constants.h" 17#include "components/policy/core/common/cloud/policy_builder.h" 18#include "components/policy/core/common/cloud/resource_cache.h" 19#include "components/policy/core/common/external_data_fetcher.h" 20#include "crypto/sha2.h" 21#include "policy/proto/chrome_extension_policy.pb.h" 22#include "policy/proto/device_management_backend.pb.h" 23#include "testing/gmock/include/gmock/gmock.h" 24#include "testing/gtest/include/gtest/gtest.h" 25 26namespace em = enterprise_management; 27 28using testing::Mock; 29 30namespace policy { 31 32namespace { 33 34const char kTestExtension[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 35const char kTestDownload[] = "http://example.com/getpolicy?id=123"; 36const char kTestPolicy[] = 37 "{" 38 " \"Name\": {" 39 " \"Value\": \"disabled\"" 40 " }," 41 " \"Second\": {" 42 " \"Value\": \"maybe\"," 43 " \"Level\": \"Recommended\"" 44 " }" 45 "}"; 46 47std::string TestPolicyHash() { 48 return crypto::SHA256HashString(kTestPolicy); 49} 50 51bool NotEqual(const std::string& expected, const std::string& key) { 52 return key != expected; 53} 54 55bool True(const std::string& ignored) { 56 return true; 57} 58 59class MockComponentCloudPolicyStoreDelegate 60 : public ComponentCloudPolicyStore::Delegate { 61 public: 62 virtual ~MockComponentCloudPolicyStoreDelegate() {} 63 64 MOCK_METHOD0(OnComponentCloudPolicyStoreUpdated, void()); 65}; 66 67} // namespace 68 69class ComponentCloudPolicyStoreTest : public testing::Test { 70 protected: 71 virtual void SetUp() OVERRIDE { 72 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 73 cache_.reset(new ResourceCache( 74 temp_dir_.path(), 75 make_scoped_refptr(new base::TestSimpleTaskRunner))); 76 store_.reset(new ComponentCloudPolicyStore(&store_delegate_, cache_.get())); 77 store_->SetCredentials(ComponentPolicyBuilder::kFakeUsername, 78 ComponentPolicyBuilder::kFakeToken); 79 80 builder_.policy_data().set_policy_type( 81 dm_protocol::kChromeExtensionPolicyType); 82 builder_.policy_data().set_settings_entity_id(kTestExtension); 83 builder_.payload().set_download_url(kTestDownload); 84 builder_.payload().set_secure_hash(TestPolicyHash()); 85 86 PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, kTestExtension); 87 PolicyMap& policy = expected_bundle_.Get(ns); 88 policy.Set("Name", 89 POLICY_LEVEL_MANDATORY, 90 POLICY_SCOPE_USER, 91 new base::StringValue("disabled"), 92 NULL); 93 policy.Set("Second", 94 POLICY_LEVEL_RECOMMENDED, 95 POLICY_SCOPE_USER, 96 new base::StringValue("maybe"), 97 NULL); 98 } 99 100 // Returns true if the policy exposed by the |store_| is empty. 101 bool IsEmpty() { 102 return store_->policy().begin() == store_->policy().end(); 103 } 104 105 scoped_ptr<em::PolicyFetchResponse> CreateResponse() { 106 builder_.Build(); 107 return make_scoped_ptr(new em::PolicyFetchResponse(builder_.policy())); 108 } 109 110 std::string CreateSerializedResponse() { 111 builder_.Build(); 112 return builder_.GetBlob(); 113 } 114 115 base::ScopedTempDir temp_dir_; 116 scoped_ptr<ResourceCache> cache_; 117 scoped_ptr<ComponentCloudPolicyStore> store_; 118 MockComponentCloudPolicyStoreDelegate store_delegate_; 119 ComponentPolicyBuilder builder_; 120 PolicyBundle expected_bundle_; 121}; 122 123TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicy) { 124 em::ExternalPolicyData payload; 125 PolicyNamespace ns; 126 EXPECT_TRUE(store_->ValidatePolicy(CreateResponse(), &ns, &payload)); 127 EXPECT_EQ(POLICY_DOMAIN_EXTENSIONS, ns.domain); 128 EXPECT_EQ(kTestExtension, ns.component_id); 129 EXPECT_EQ(kTestDownload, payload.download_url()); 130 EXPECT_EQ(TestPolicyHash(), payload.secure_hash()); 131} 132 133TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyWrongUsername) { 134 builder_.policy_data().set_username("anotheruser@example.com"); 135 em::ExternalPolicyData payload; 136 PolicyNamespace ns; 137 EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload)); 138} 139 140TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyWrongDMToken) { 141 builder_.policy_data().set_request_token("notmytoken"); 142 em::ExternalPolicyData payload; 143 PolicyNamespace ns; 144 EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload)); 145} 146 147TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyBadType) { 148 builder_.policy_data().set_policy_type(dm_protocol::kChromeUserPolicyType); 149 em::ExternalPolicyData payload; 150 PolicyNamespace ns; 151 EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload)); 152} 153 154TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyBadDownloadUrl) { 155 builder_.payload().set_download_url("invalidurl"); 156 em::ExternalPolicyData payload; 157 PolicyNamespace ns; 158 EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload)); 159} 160 161TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyEmptyDownloadUrl) { 162 builder_.payload().clear_download_url(); 163 builder_.payload().clear_secure_hash(); 164 em::ExternalPolicyData payload; 165 PolicyNamespace ns; 166 // This is valid; it's how "no policy" is signalled to the client. 167 EXPECT_TRUE(store_->ValidatePolicy(CreateResponse(), &ns, &payload)); 168} 169 170TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyBadPayload) { 171 builder_.clear_payload(); 172 builder_.policy_data().set_policy_value("broken"); 173 em::ExternalPolicyData payload; 174 PolicyNamespace ns; 175 EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload)); 176} 177 178TEST_F(ComponentCloudPolicyStoreTest, ValidateNoCredentials) { 179 store_.reset(new ComponentCloudPolicyStore(&store_delegate_, cache_.get())); 180 em::ExternalPolicyData payload; 181 PolicyNamespace ns; 182 EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload)); 183} 184 185TEST_F(ComponentCloudPolicyStoreTest, ValidateWrongCredentials) { 186 em::ExternalPolicyData payload; 187 PolicyNamespace ns; 188 // Verify that the default response validates with the right credentials. 189 EXPECT_TRUE(store_->ValidatePolicy(CreateResponse(), &ns, &payload)); 190 // Now store that response. 191 EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated()); 192 EXPECT_TRUE(store_->Store( 193 ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy)); 194 Mock::VerifyAndClearExpectations(&store_delegate_); 195 EXPECT_TRUE(store_->policy().Equals(expected_bundle_)); 196 // And verify that the response data in the cache. 197 std::map<std::string, std::string> contents; 198 cache_->LoadAllSubkeys("extension-policy", &contents); 199 EXPECT_FALSE(contents.empty()); 200 201 // Try loading the cached response data with wrong credentials. 202 ComponentCloudPolicyStore another_store(&store_delegate_, cache_.get()); 203 another_store.SetCredentials("wrongdude@example.com", "wrongtoken"); 204 another_store.Load(); 205 const PolicyBundle empty_bundle; 206 EXPECT_TRUE(another_store.policy().Equals(empty_bundle)); 207 208 // The failure to read wiped the cache. 209 cache_->LoadAllSubkeys("extension-policy", &contents); 210 EXPECT_TRUE(contents.empty()); 211} 212 213TEST_F(ComponentCloudPolicyStoreTest, StoreAndLoad) { 214 // Initially empty. 215 EXPECT_TRUE(IsEmpty()); 216 store_->Load(); 217 EXPECT_TRUE(IsEmpty()); 218 219 // Store policy for an unsupported domain. 220 PolicyNamespace ns(POLICY_DOMAIN_CHROME, kTestExtension); 221 builder_.policy_data().set_policy_type(dm_protocol::kChromeUserPolicyType); 222 EXPECT_FALSE(store_->Store( 223 ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy)); 224 225 // Store policy with the wrong hash. 226 builder_.policy_data().set_policy_type( 227 dm_protocol::kChromeExtensionPolicyType); 228 ns.domain = POLICY_DOMAIN_EXTENSIONS; 229 builder_.payload().set_secure_hash("badash"); 230 EXPECT_FALSE(store_->Store( 231 ns, CreateSerializedResponse(), "badash", kTestPolicy)); 232 233 // Store policy without a hash. 234 builder_.payload().clear_secure_hash(); 235 EXPECT_FALSE(store_->Store( 236 ns, CreateSerializedResponse(), std::string(), kTestPolicy)); 237 238 // Store policy with invalid JSON data. 239 static const char kInvalidData[] = "{ not json }"; 240 const std::string invalid_data_hash = crypto::SHA256HashString(kInvalidData); 241 builder_.payload().set_secure_hash(invalid_data_hash); 242 EXPECT_FALSE(store_->Store( 243 ns, CreateSerializedResponse(), invalid_data_hash, kInvalidData)); 244 245 // All of those failed. 246 EXPECT_TRUE(IsEmpty()); 247 EXPECT_EQ(std::string(), store_->GetCachedHash(ns)); 248 249 // Now store a valid policy. 250 builder_.payload().set_secure_hash(TestPolicyHash()); 251 EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated()); 252 EXPECT_TRUE(store_->Store( 253 ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy)); 254 Mock::VerifyAndClearExpectations(&store_delegate_); 255 EXPECT_FALSE(IsEmpty()); 256 EXPECT_TRUE(store_->policy().Equals(expected_bundle_)); 257 EXPECT_EQ(TestPolicyHash(), store_->GetCachedHash(ns)); 258 259 // Loading from the cache validates the policy data again. 260 ComponentCloudPolicyStore another_store(&store_delegate_, cache_.get()); 261 another_store.SetCredentials(ComponentPolicyBuilder::kFakeUsername, 262 ComponentPolicyBuilder::kFakeToken); 263 another_store.Load(); 264 EXPECT_TRUE(another_store.policy().Equals(expected_bundle_)); 265 EXPECT_EQ(TestPolicyHash(), another_store.GetCachedHash(ns)); 266} 267 268TEST_F(ComponentCloudPolicyStoreTest, Updates) { 269 // Store some policies. 270 PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, kTestExtension); 271 EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated()); 272 EXPECT_TRUE(store_->Store( 273 ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy)); 274 Mock::VerifyAndClearExpectations(&store_delegate_); 275 EXPECT_FALSE(IsEmpty()); 276 EXPECT_TRUE(store_->policy().Equals(expected_bundle_)); 277 278 // Deleting a non-existant namespace doesn't trigger updates. 279 PolicyNamespace ns_fake(POLICY_DOMAIN_EXTENSIONS, "nosuchid"); 280 store_->Delete(ns_fake); 281 Mock::VerifyAndClearExpectations(&store_delegate_); 282 283 // Deleting a namespace that has policies triggers an update. 284 EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated()); 285 store_->Delete(ns); 286 Mock::VerifyAndClearExpectations(&store_delegate_); 287} 288 289TEST_F(ComponentCloudPolicyStoreTest, Purge) { 290 // Store a valid policy. 291 EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated()); 292 PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, kTestExtension); 293 EXPECT_TRUE(store_->Store( 294 ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy)); 295 Mock::VerifyAndClearExpectations(&store_delegate_); 296 EXPECT_FALSE(IsEmpty()); 297 EXPECT_TRUE(store_->policy().Equals(expected_bundle_)); 298 299 // Purge other components. 300 store_->Purge(POLICY_DOMAIN_EXTENSIONS, 301 base::Bind(&NotEqual, kTestExtension)); 302 303 // The policy for |ns| is still served. 304 EXPECT_TRUE(store_->policy().Equals(expected_bundle_)); 305 306 // Loading the store again will still see |ns|. 307 ComponentCloudPolicyStore another_store(&store_delegate_, cache_.get()); 308 const PolicyBundle empty_bundle; 309 EXPECT_TRUE(another_store.policy().Equals(empty_bundle)); 310 another_store.SetCredentials(ComponentPolicyBuilder::kFakeUsername, 311 ComponentPolicyBuilder::kFakeToken); 312 another_store.Load(); 313 EXPECT_TRUE(another_store.policy().Equals(expected_bundle_)); 314 315 // Now purge everything. 316 EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated()); 317 store_->Purge(POLICY_DOMAIN_EXTENSIONS, base::Bind(&True)); 318 Mock::VerifyAndClearExpectations(&store_delegate_); 319 320 // No policies are served anymore. 321 EXPECT_TRUE(store_->policy().Equals(empty_bundle)); 322 323 // And they aren't loaded anymore either. 324 ComponentCloudPolicyStore yet_another_store(&store_delegate_, cache_.get()); 325 yet_another_store.SetCredentials(ComponentPolicyBuilder::kFakeUsername, 326 ComponentPolicyBuilder::kFakeToken); 327 yet_another_store.Load(); 328 EXPECT_TRUE(yet_another_store.policy().Equals(empty_bundle)); 329} 330 331} // namespace policy 332