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