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