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