1// Copyright 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/configuration_policy_provider_test.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "base/values.h"
11#include "components/policy/core/common/configuration_policy_provider.h"
12#include "components/policy/core/common/external_data_fetcher.h"
13#include "components/policy/core/common/mock_configuration_policy_provider.h"
14#include "components/policy/core/common/policy_bundle.h"
15#include "components/policy/core/common/policy_map.h"
16#include "components/policy/core/common/policy_namespace.h"
17#include "testing/gmock/include/gmock/gmock.h"
18
19using ::testing::Mock;
20using ::testing::_;
21
22namespace policy {
23
24const char kTestChromeSchema[] =
25    "{"
26    "  \"type\": \"object\","
27    "  \"properties\": {"
28    "    \"StringPolicy\": { \"type\": \"string\" },"
29    "    \"BooleanPolicy\": { \"type\": \"boolean\" },"
30    "    \"IntegerPolicy\": { \"type\": \"integer\" },"
31    "    \"StringListPolicy\": {"
32    "      \"type\": \"array\","
33    "      \"items\": { \"type\": \"string\" }"
34    "    },"
35    "    \"DictionaryPolicy\": {"
36    "      \"type\": \"object\","
37    "      \"properties\": {"
38    "        \"bool\": { \"type\": \"boolean\" },"
39    "        \"double\": { \"type\": \"number\" },"
40    "        \"int\": { \"type\": \"integer\" },"
41    "        \"string\": { \"type\": \"string\" },"
42    "        \"array\": {"
43    "          \"type\": \"array\","
44    "          \"items\": { \"type\": \"string\" }"
45    "        },"
46    "        \"dictionary\": {"
47    "          \"type\": \"object\","
48    "          \"properties\": {"
49    "            \"sub\": { \"type\": \"string\" },"
50    "            \"sublist\": {"
51    "              \"type\": \"array\","
52    "              \"items\": {"
53    "                \"type\": \"object\","
54    "                \"properties\": {"
55    "                  \"aaa\": { \"type\": \"integer\" },"
56    "                  \"bbb\": { \"type\": \"integer\" },"
57    "                  \"ccc\": { \"type\": \"string\" },"
58    "                  \"ddd\": { \"type\": \"string\" }"
59    "                }"
60    "              }"
61    "            }"
62    "          }"
63    "        },"
64    "        \"list\": {"
65    "          \"type\": \"array\","
66    "          \"items\": {"
67    "            \"type\": \"object\","
68    "            \"properties\": {"
69    "              \"subdictindex\": { \"type\": \"integer\" },"
70    "              \"subdict\": {"
71    "                \"type\": \"object\","
72    "                \"properties\": {"
73    "                  \"bool\": { \"type\": \"boolean\" },"
74    "                  \"double\": { \"type\": \"number\" },"
75    "                  \"int\": { \"type\": \"integer\" },"
76    "                  \"string\": { \"type\": \"string\" }"
77    "                }"
78    "              }"
79    "            }"
80    "          }"
81    "        },"
82    "        \"dict\": {"
83    "          \"type\": \"object\","
84    "          \"properties\": {"
85    "            \"bool\": { \"type\": \"boolean\" },"
86    "            \"double\": { \"type\": \"number\" },"
87    "            \"int\": { \"type\": \"integer\" },"
88    "            \"string\": { \"type\": \"string\" },"
89    "            \"list\": {"
90    "              \"type\": \"array\","
91    "              \"items\": {"
92    "                \"type\": \"object\","
93    "                \"properties\": {"
94    "                  \"subdictindex\": { \"type\": \"integer\" },"
95    "                  \"subdict\": {"
96    "                    \"type\": \"object\","
97    "                    \"properties\": {"
98    "                      \"bool\": { \"type\": \"boolean\" },"
99    "                      \"double\": { \"type\": \"number\" },"
100    "                      \"int\": { \"type\": \"integer\" },"
101    "                      \"string\": { \"type\": \"string\" }"
102    "                    }"
103    "                  }"
104    "                }"
105    "              }"
106    "            }"
107    "          }"
108    "        }"
109    "      }"
110    "    }"
111    "  }"
112    "}";
113
114namespace test_keys {
115
116const char kKeyString[] = "StringPolicy";
117const char kKeyBoolean[] = "BooleanPolicy";
118const char kKeyInteger[] = "IntegerPolicy";
119const char kKeyStringList[] = "StringListPolicy";
120const char kKeyDictionary[] = "DictionaryPolicy";
121
122}  // namespace test_keys
123
124PolicyTestBase::PolicyTestBase() {}
125
126PolicyTestBase::~PolicyTestBase() {}
127
128void PolicyTestBase::SetUp() {
129  const PolicyNamespace ns(POLICY_DOMAIN_CHROME, "");
130  ASSERT_TRUE(RegisterSchema(ns, kTestChromeSchema));
131}
132
133void PolicyTestBase::TearDown() {
134  loop_.RunUntilIdle();
135}
136
137bool PolicyTestBase::RegisterSchema(const PolicyNamespace& ns,
138                                    const std::string& schema_string) {
139  std::string error;
140  Schema schema = Schema::Parse(schema_string, &error);
141  if (schema.valid()) {
142    schema_registry_.RegisterComponent(ns, schema);
143    return true;
144  }
145  ADD_FAILURE() << error;
146  return false;
147}
148
149PolicyProviderTestHarness::PolicyProviderTestHarness(PolicyLevel level,
150                                                     PolicyScope scope)
151    : level_(level), scope_(scope) {}
152
153PolicyProviderTestHarness::~PolicyProviderTestHarness() {}
154
155PolicyLevel PolicyProviderTestHarness::policy_level() const {
156  return level_;
157}
158
159PolicyScope PolicyProviderTestHarness::policy_scope() const {
160  return scope_;
161}
162
163void PolicyProviderTestHarness::Install3rdPartyPolicy(
164    const base::DictionaryValue* policies) {
165  FAIL();
166}
167
168ConfigurationPolicyProviderTest::ConfigurationPolicyProviderTest() {}
169
170ConfigurationPolicyProviderTest::~ConfigurationPolicyProviderTest() {}
171
172void ConfigurationPolicyProviderTest::SetUp() {
173  PolicyTestBase::SetUp();
174
175  test_harness_.reset((*GetParam())());
176  test_harness_->SetUp();
177
178  const PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
179  Schema chrome_schema = *schema_registry_.schema_map()->GetSchema(chrome_ns);
180  Schema extension_schema =
181      chrome_schema.GetKnownProperty(test_keys::kKeyDictionary);
182  ASSERT_TRUE(extension_schema.valid());
183  schema_registry_.RegisterComponent(
184      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
185                      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
186      extension_schema);
187  schema_registry_.RegisterComponent(
188      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
189                      "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
190      extension_schema);
191  schema_registry_.RegisterComponent(
192      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
193                      "cccccccccccccccccccccccccccccccc"),
194      extension_schema);
195
196  provider_.reset(test_harness_->CreateProvider(&schema_registry_,
197                                                loop_.message_loop_proxy()));
198  provider_->Init(&schema_registry_);
199  // Some providers do a reload on init. Make sure any notifications generated
200  // are fired now.
201  loop_.RunUntilIdle();
202
203  const PolicyBundle kEmptyBundle;
204  EXPECT_TRUE(provider_->policies().Equals(kEmptyBundle));
205}
206
207void ConfigurationPolicyProviderTest::TearDown() {
208  // Give providers the chance to clean up after themselves on the file thread.
209  provider_->Shutdown();
210  provider_.reset();
211
212  PolicyTestBase::TearDown();
213}
214
215void ConfigurationPolicyProviderTest::CheckValue(
216    const char* policy_name,
217    const base::Value& expected_value,
218    base::Closure install_value) {
219  // Install the value, reload policy and check the provider for the value.
220  install_value.Run();
221  provider_->RefreshPolicies();
222  loop_.RunUntilIdle();
223  PolicyBundle expected_bundle;
224  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
225      .Set(policy_name,
226           test_harness_->policy_level(),
227           test_harness_->policy_scope(),
228           expected_value.DeepCopy(),
229           NULL);
230  EXPECT_TRUE(provider_->policies().Equals(expected_bundle));
231  // TODO(joaodasilva): set the policy in the POLICY_DOMAIN_EXTENSIONS too,
232  // and extend the |expected_bundle|, once all providers are ready.
233}
234
235TEST_P(ConfigurationPolicyProviderTest, Empty) {
236  provider_->RefreshPolicies();
237  loop_.RunUntilIdle();
238  const PolicyBundle kEmptyBundle;
239  EXPECT_TRUE(provider_->policies().Equals(kEmptyBundle));
240}
241
242TEST_P(ConfigurationPolicyProviderTest, StringValue) {
243  const char kTestString[] = "string_value";
244  base::StringValue expected_value(kTestString);
245  CheckValue(test_keys::kKeyString,
246             expected_value,
247             base::Bind(&PolicyProviderTestHarness::InstallStringPolicy,
248                        base::Unretained(test_harness_.get()),
249                        test_keys::kKeyString,
250                        kTestString));
251}
252
253TEST_P(ConfigurationPolicyProviderTest, BooleanValue) {
254  base::FundamentalValue expected_value(true);
255  CheckValue(test_keys::kKeyBoolean,
256             expected_value,
257             base::Bind(&PolicyProviderTestHarness::InstallBooleanPolicy,
258                        base::Unretained(test_harness_.get()),
259                        test_keys::kKeyBoolean,
260                        true));
261}
262
263TEST_P(ConfigurationPolicyProviderTest, IntegerValue) {
264  base::FundamentalValue expected_value(42);
265  CheckValue(test_keys::kKeyInteger,
266             expected_value,
267             base::Bind(&PolicyProviderTestHarness::InstallIntegerPolicy,
268                        base::Unretained(test_harness_.get()),
269                        test_keys::kKeyInteger,
270                        42));
271}
272
273TEST_P(ConfigurationPolicyProviderTest, StringListValue) {
274  base::ListValue expected_value;
275  expected_value.Set(0U, new base::StringValue("first"));
276  expected_value.Set(1U, new base::StringValue("second"));
277  CheckValue(test_keys::kKeyStringList,
278             expected_value,
279             base::Bind(&PolicyProviderTestHarness::InstallStringListPolicy,
280                        base::Unretained(test_harness_.get()),
281                        test_keys::kKeyStringList,
282                        &expected_value));
283}
284
285TEST_P(ConfigurationPolicyProviderTest, DictionaryValue) {
286  base::DictionaryValue expected_value;
287  expected_value.SetBoolean("bool", true);
288  expected_value.SetDouble("double", 123.456);
289  expected_value.SetInteger("int", 123);
290  expected_value.SetString("string", "omg");
291
292  base::ListValue* list = new base::ListValue();
293  list->Set(0U, new base::StringValue("first"));
294  list->Set(1U, new base::StringValue("second"));
295  expected_value.Set("array", list);
296
297  base::DictionaryValue* dict = new base::DictionaryValue();
298  dict->SetString("sub", "value");
299  list = new base::ListValue();
300  base::DictionaryValue* sub = new base::DictionaryValue();
301  sub->SetInteger("aaa", 111);
302  sub->SetInteger("bbb", 222);
303  list->Append(sub);
304  sub = new base::DictionaryValue();
305  sub->SetString("ccc", "333");
306  sub->SetString("ddd", "444");
307  list->Append(sub);
308  dict->Set("sublist", list);
309  expected_value.Set("dictionary", dict);
310
311  CheckValue(test_keys::kKeyDictionary,
312             expected_value,
313             base::Bind(&PolicyProviderTestHarness::InstallDictionaryPolicy,
314                        base::Unretained(test_harness_.get()),
315                        test_keys::kKeyDictionary,
316                        &expected_value));
317}
318
319TEST_P(ConfigurationPolicyProviderTest, RefreshPolicies) {
320  PolicyBundle bundle;
321  EXPECT_TRUE(provider_->policies().Equals(bundle));
322
323  // OnUpdatePolicy is called even when there are no changes.
324  MockConfigurationPolicyObserver observer;
325  provider_->AddObserver(&observer);
326  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(1);
327  provider_->RefreshPolicies();
328  loop_.RunUntilIdle();
329  Mock::VerifyAndClearExpectations(&observer);
330
331  EXPECT_TRUE(provider_->policies().Equals(bundle));
332
333  // OnUpdatePolicy is called when there are changes.
334  test_harness_->InstallStringPolicy(test_keys::kKeyString, "value");
335  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(1);
336  provider_->RefreshPolicies();
337  loop_.RunUntilIdle();
338  Mock::VerifyAndClearExpectations(&observer);
339
340  bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
341      .Set(test_keys::kKeyString,
342           test_harness_->policy_level(),
343           test_harness_->policy_scope(),
344           new base::StringValue("value"),
345           NULL);
346  EXPECT_TRUE(provider_->policies().Equals(bundle));
347  provider_->RemoveObserver(&observer);
348}
349
350Configuration3rdPartyPolicyProviderTest::
351    Configuration3rdPartyPolicyProviderTest() {}
352
353Configuration3rdPartyPolicyProviderTest::
354    ~Configuration3rdPartyPolicyProviderTest() {}
355
356TEST_P(Configuration3rdPartyPolicyProviderTest, Load3rdParty) {
357  base::DictionaryValue policy_dict;
358  policy_dict.SetBoolean("bool", true);
359  policy_dict.SetDouble("double", 123.456);
360  policy_dict.SetInteger("int", 789);
361  policy_dict.SetString("string", "string value");
362
363  base::ListValue* list = new base::ListValue();
364  for (int i = 0; i < 2; ++i) {
365    base::DictionaryValue* dict = new base::DictionaryValue();
366    dict->SetInteger("subdictindex", i);
367    dict->Set("subdict", policy_dict.DeepCopy());
368    list->Append(dict);
369  }
370  policy_dict.Set("list", list);
371  policy_dict.Set("dict", policy_dict.DeepCopy());
372
373  // Install these policies as a Chrome policy.
374  test_harness_->InstallDictionaryPolicy(test_keys::kKeyDictionary,
375                                         &policy_dict);
376  // Install them as 3rd party policies too.
377  base::DictionaryValue policy_3rdparty;
378  policy_3rdparty.Set("extensions.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
379                      policy_dict.DeepCopy());
380  policy_3rdparty.Set("extensions.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
381                      policy_dict.DeepCopy());
382  // Install invalid 3rd party policies that shouldn't be loaded. These also
383  // help detecting memory leaks in the code paths that detect invalid input.
384  policy_3rdparty.Set("invalid-domain.component", policy_dict.DeepCopy());
385  policy_3rdparty.Set("extensions.cccccccccccccccccccccccccccccccc",
386                      new base::StringValue("invalid-value"));
387  test_harness_->Install3rdPartyPolicy(&policy_3rdparty);
388
389  provider_->RefreshPolicies();
390  loop_.RunUntilIdle();
391
392  PolicyMap expected_policy;
393  expected_policy.Set(test_keys::kKeyDictionary,
394                      test_harness_->policy_level(),
395                      test_harness_->policy_scope(),
396                      policy_dict.DeepCopy(),
397                      NULL);
398  PolicyBundle expected_bundle;
399  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
400      .CopyFrom(expected_policy);
401  expected_policy.Clear();
402  expected_policy.LoadFrom(&policy_dict,
403                           test_harness_->policy_level(),
404                           test_harness_->policy_scope());
405  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
406                                      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
407      .CopyFrom(expected_policy);
408  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
409                                      "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"))
410      .CopyFrom(expected_policy);
411  EXPECT_TRUE(provider_->policies().Equals(expected_bundle));
412}
413
414}  // namespace policy
415