1// Copyright (c) 2012 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 "base/compiler_specific.h"
6#include "base/files/file_util.h"
7#include "base/files/scoped_temp_dir.h"
8#include "base/json/json_string_value_serializer.h"
9#include "base/memory/ref_counted.h"
10#include "base/message_loop/message_loop.h"
11#include "base/message_loop/message_loop_proxy.h"
12#include "base/sequenced_task_runner.h"
13#include "base/strings/string_number_conversions.h"
14#include "base/values.h"
15#include "components/policy/core/common/async_policy_provider.h"
16#include "components/policy/core/common/config_dir_policy_loader.h"
17#include "components/policy/core/common/configuration_policy_provider_test.h"
18#include "components/policy/core/common/policy_bundle.h"
19#include "components/policy/core/common/policy_map.h"
20
21namespace policy {
22
23namespace {
24
25// Subdirectory of the config dir that contains mandatory policies.
26const base::FilePath::CharType kMandatoryPath[] = FILE_PATH_LITERAL("managed");
27
28class TestHarness : public PolicyProviderTestHarness {
29 public:
30  TestHarness();
31  virtual ~TestHarness();
32
33  virtual void SetUp() OVERRIDE;
34
35  virtual ConfigurationPolicyProvider* CreateProvider(
36      SchemaRegistry* registry,
37      scoped_refptr<base::SequencedTaskRunner> task_runner) OVERRIDE;
38
39  virtual void InstallEmptyPolicy() OVERRIDE;
40  virtual void InstallStringPolicy(const std::string& policy_name,
41                                   const std::string& policy_value) OVERRIDE;
42  virtual void InstallIntegerPolicy(const std::string& policy_name,
43                                    int policy_value) OVERRIDE;
44  virtual void InstallBooleanPolicy(const std::string& policy_name,
45                                    bool policy_value) OVERRIDE;
46  virtual void InstallStringListPolicy(
47      const std::string& policy_name,
48      const base::ListValue* policy_value) OVERRIDE;
49  virtual void InstallDictionaryPolicy(
50      const std::string& policy_name,
51      const base::DictionaryValue* policy_value) OVERRIDE;
52  virtual void Install3rdPartyPolicy(
53      const base::DictionaryValue* policies) OVERRIDE;
54
55  const base::FilePath& test_dir() { return test_dir_.path(); }
56
57  // JSON-encode a dictionary and write it to a file.
58  void WriteConfigFile(const base::DictionaryValue& dict,
59                       const std::string& file_name);
60
61  // Returns a unique name for a policy file. Each subsequent call returns a new
62  // name that comes lexicographically after the previous one.
63  std::string NextConfigFileName();
64
65  static PolicyProviderTestHarness* Create();
66
67 private:
68  base::ScopedTempDir test_dir_;
69  int next_policy_file_index_;
70
71  DISALLOW_COPY_AND_ASSIGN(TestHarness);
72};
73
74TestHarness::TestHarness()
75    : PolicyProviderTestHarness(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE),
76      next_policy_file_index_(100) {}
77
78TestHarness::~TestHarness() {}
79
80void TestHarness::SetUp() {
81  ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
82}
83
84ConfigurationPolicyProvider* TestHarness::CreateProvider(
85    SchemaRegistry* registry,
86    scoped_refptr<base::SequencedTaskRunner> task_runner) {
87  scoped_ptr<AsyncPolicyLoader> loader(new ConfigDirPolicyLoader(
88      task_runner, test_dir(), POLICY_SCOPE_MACHINE));
89  return new AsyncPolicyProvider(registry, loader.Pass());
90}
91
92void TestHarness::InstallEmptyPolicy() {
93  base::DictionaryValue dict;
94  WriteConfigFile(dict, NextConfigFileName());
95}
96
97void TestHarness::InstallStringPolicy(const std::string& policy_name,
98                                      const std::string& policy_value) {
99  base::DictionaryValue dict;
100  dict.SetString(policy_name, policy_value);
101  WriteConfigFile(dict, NextConfigFileName());
102}
103
104void TestHarness::InstallIntegerPolicy(const std::string& policy_name,
105                                       int policy_value) {
106  base::DictionaryValue dict;
107  dict.SetInteger(policy_name, policy_value);
108  WriteConfigFile(dict, NextConfigFileName());
109}
110
111void TestHarness::InstallBooleanPolicy(const std::string& policy_name,
112                                       bool policy_value) {
113  base::DictionaryValue dict;
114  dict.SetBoolean(policy_name, policy_value);
115  WriteConfigFile(dict, NextConfigFileName());
116}
117
118void TestHarness::InstallStringListPolicy(const std::string& policy_name,
119                                          const base::ListValue* policy_value) {
120  base::DictionaryValue dict;
121  dict.Set(policy_name, policy_value->DeepCopy());
122  WriteConfigFile(dict, NextConfigFileName());
123}
124
125void TestHarness::InstallDictionaryPolicy(
126    const std::string& policy_name,
127    const base::DictionaryValue* policy_value) {
128  base::DictionaryValue dict;
129  dict.Set(policy_name, policy_value->DeepCopy());
130  WriteConfigFile(dict, NextConfigFileName());
131}
132
133void TestHarness::Install3rdPartyPolicy(const base::DictionaryValue* policies) {
134  base::DictionaryValue dict;
135  dict.Set("3rdparty", policies->DeepCopy());
136  WriteConfigFile(dict, NextConfigFileName());
137}
138
139void TestHarness::WriteConfigFile(const base::DictionaryValue& dict,
140                                  const std::string& file_name) {
141  std::string data;
142  JSONStringValueSerializer serializer(&data);
143  serializer.Serialize(dict);
144  const base::FilePath mandatory_dir(test_dir().Append(kMandatoryPath));
145  ASSERT_TRUE(base::CreateDirectory(mandatory_dir));
146  const base::FilePath file_path(mandatory_dir.AppendASCII(file_name));
147  ASSERT_EQ((int) data.size(),
148            base::WriteFile(file_path, data.c_str(), data.size()));
149}
150
151std::string TestHarness::NextConfigFileName() {
152  EXPECT_LE(next_policy_file_index_, 999);
153  return std::string("policy") + base::IntToString(next_policy_file_index_++);
154}
155
156// static
157PolicyProviderTestHarness* TestHarness::Create() {
158  return new TestHarness();
159}
160
161}  // namespace
162
163// Instantiate abstract test case for basic policy reading tests.
164INSTANTIATE_TEST_CASE_P(
165    ConfigDirPolicyLoaderTest,
166    ConfigurationPolicyProviderTest,
167    testing::Values(TestHarness::Create));
168
169// Instantiate abstract test case for 3rd party policy reading tests.
170INSTANTIATE_TEST_CASE_P(
171    ConfigDir3rdPartyPolicyLoaderTest,
172    Configuration3rdPartyPolicyProviderTest,
173    testing::Values(TestHarness::Create));
174
175// Some tests that exercise special functionality in ConfigDirPolicyLoader.
176class ConfigDirPolicyLoaderTest : public PolicyTestBase {
177 protected:
178  virtual void SetUp() OVERRIDE {
179    PolicyTestBase::SetUp();
180    harness_.SetUp();
181  }
182
183  TestHarness harness_;
184};
185
186// The preferences dictionary is expected to be empty when there are no files to
187// load.
188TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsEmpty) {
189  ConfigDirPolicyLoader loader(
190      loop_.message_loop_proxy(), harness_.test_dir(), POLICY_SCOPE_MACHINE);
191  scoped_ptr<PolicyBundle> bundle(loader.Load());
192  ASSERT_TRUE(bundle.get());
193  const PolicyBundle kEmptyBundle;
194  EXPECT_TRUE(bundle->Equals(kEmptyBundle));
195}
196
197// Reading from a non-existent directory should result in an empty preferences
198// dictionary.
199TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsNonExistentDirectory) {
200  base::FilePath non_existent_dir(
201      harness_.test_dir().Append(FILE_PATH_LITERAL("not_there")));
202  ConfigDirPolicyLoader loader(
203      loop_.message_loop_proxy(), non_existent_dir, POLICY_SCOPE_MACHINE);
204  scoped_ptr<PolicyBundle> bundle(loader.Load());
205  ASSERT_TRUE(bundle.get());
206  const PolicyBundle kEmptyBundle;
207  EXPECT_TRUE(bundle->Equals(kEmptyBundle));
208}
209
210// Test merging values from different files.
211TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsMergePrefs) {
212  // Write a bunch of data files in order to increase the chance to detect the
213  // provider not respecting lexicographic ordering when reading them. Since the
214  // filesystem may return files in arbitrary order, there is no way to be sure,
215  // but this is better than nothing.
216  base::DictionaryValue test_dict_bar;
217  test_dict_bar.SetString("HomepageLocation", "http://bar.com");
218  for (unsigned int i = 1; i <= 4; ++i)
219    harness_.WriteConfigFile(test_dict_bar, base::IntToString(i));
220  base::DictionaryValue test_dict_foo;
221  test_dict_foo.SetString("HomepageLocation", "http://foo.com");
222  harness_.WriteConfigFile(test_dict_foo, "9");
223  for (unsigned int i = 5; i <= 8; ++i)
224    harness_.WriteConfigFile(test_dict_bar, base::IntToString(i));
225
226  ConfigDirPolicyLoader loader(
227      loop_.message_loop_proxy(), harness_.test_dir(), POLICY_SCOPE_USER);
228  scoped_ptr<PolicyBundle> bundle(loader.Load());
229  ASSERT_TRUE(bundle.get());
230  PolicyBundle expected_bundle;
231  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
232      .LoadFrom(&test_dict_foo, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
233  EXPECT_TRUE(bundle->Equals(expected_bundle));
234}
235
236}  // namespace policy
237