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