1 // Copyright 2020 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/browser/policy_pref_mapping_test.h"
6
7 #include <map>
8 #include <memory>
9 #include <set>
10 #include <string>
11 #include <vector>
12
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/json/json_reader.h"
16 #include "base/logging.h"
17 #include "base/macros.h"
18 #include "base/strings/string_util.h"
19 #include "base/threading/thread_restrictions.h"
20 #include "base/values.h"
21 #include "build/branding_buildflags.h"
22 #include "build/build_config.h"
23 #include "components/policy/core/common/mock_configuration_policy_provider.h"
24 #include "components/policy/core/common/schema.h"
25 #include "components/policy/policy_constants.h"
26 #include "components/prefs/pref_service.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "url/gurl.h"
29
30 namespace policy {
31
32 namespace {
33
34 // The name of the template example in policy_test_cases.json that does not need
35 // to be parsed.
36 const char kTemplateSampleTest[] = "-- Template --";
37
GetPolicyName(const std::string & policy_name_decorated)38 std::string GetPolicyName(const std::string& policy_name_decorated) {
39 const size_t offset = policy_name_decorated.find('.');
40 if (offset != std::string::npos)
41 return policy_name_decorated.substr(0, offset);
42 return policy_name_decorated;
43 }
44
45 // Contains the details of a single test case verifying that the controlled
46 // setting indicators for a pref affected by a policy work correctly. This is
47 // part of the data loaded from chrome/test/data/policy/policy_test_cases.json.
48 class PrefIndicatorTest {
49 public:
PrefIndicatorTest(const base::Value & indicator_test)50 explicit PrefIndicatorTest(const base::Value& indicator_test) {
51 base::Optional<bool> readonly = indicator_test.FindBoolKey("readonly");
52 const std::string* value = indicator_test.FindStringKey("value");
53 const std::string* selector = indicator_test.FindStringKey("selector");
54 const std::string* test_url = indicator_test.FindStringKey("test_url");
55 const std::string* pref = indicator_test.FindStringKey("pref");
56 const std::string* test_setup_js =
57 indicator_test.FindStringKey("test_setup_js");
58
59 readonly_ = readonly.value_or(false);
60 value_ = value ? *value : std::string();
61 test_url_ = test_url ? *test_url : std::string();
62 test_setup_js_ = test_setup_js ? *test_setup_js : std::string();
63 selector_ = selector ? *selector : std::string();
64 pref_ = pref ? *pref : std::string();
65 }
66
67 ~PrefIndicatorTest() = default;
68
value() const69 const std::string& value() const { return value_; }
test_url() const70 const std::string& test_url() const { return test_url_; }
test_setup_js() const71 const std::string& test_setup_js() const { return test_setup_js_; }
selector() const72 const std::string& selector() const { return selector_; }
pref() const73 const std::string& pref() const { return pref_; }
74
readonly() const75 bool readonly() const { return readonly_; }
76
77 private:
78 bool readonly_;
79 std::string value_;
80 std::string test_url_;
81 std::string test_setup_js_;
82 std::string selector_;
83 std::string pref_;
84
85 DISALLOW_COPY_AND_ASSIGN(PrefIndicatorTest);
86 };
87
88 // Contains the testing details for a single pref affected by one or multiple
89 // policies. This is part of the data loaded from
90 // chrome/test/data/policy/policy_test_cases.json.
91 class PrefTestCase {
92 public:
PrefTestCase(const std::string & name,const base::Value & settings)93 explicit PrefTestCase(const std::string& name, const base::Value& settings) {
94 const base::Value* value = settings.FindKey("value");
95 const base::Value* indicator_test = settings.FindDictKey("indicator_test");
96 is_local_state_ = settings.FindBoolKey("local_state").value_or(false);
97 check_for_mandatory_ =
98 settings.FindBoolKey("check_for_mandatory").value_or(true);
99 check_for_recommended_ =
100 settings.FindBoolKey("check_for_recommended").value_or(true);
101 expect_default_ = settings.FindBoolKey("expect_default").value_or(false);
102
103 pref_ = name;
104 if (value)
105 value_ = value->CreateDeepCopy();
106 if (indicator_test) {
107 pref_indicator_test_ =
108 std::make_unique<PrefIndicatorTest>(*indicator_test);
109 }
110 }
111
112 ~PrefTestCase() = default;
113 PrefTestCase(const PrefTestCase& other) = delete;
114 PrefTestCase& operator=(const PrefTestCase& other) = delete;
115
pref() const116 const std::string& pref() const { return pref_; }
value() const117 const base::Value* value() const { return value_.get(); }
118
is_local_state() const119 bool is_local_state() const { return is_local_state_; }
120
check_for_mandatory() const121 bool check_for_mandatory() const { return check_for_mandatory_; }
122
check_for_recommended() const123 bool check_for_recommended() const { return check_for_recommended_; }
124
expect_default() const125 bool expect_default() const { return expect_default_; }
126
indicator_test_case() const127 const PrefIndicatorTest* indicator_test_case() const {
128 return pref_indicator_test_.get();
129 }
130
131 private:
132 std::string pref_;
133 std::unique_ptr<base::Value> value_;
134 bool is_local_state_;
135 bool check_for_mandatory_;
136 bool check_for_recommended_;
137 bool expect_default_;
138 std::unique_ptr<PrefIndicatorTest> pref_indicator_test_;
139 };
140
141 // Contains the testing details for a single pref affected by a policy. This is
142 // part of the data loaded from chrome/test/data/policy/policy_test_cases.json.
143 class PolicyPrefMappingTest {
144 public:
PolicyPrefMappingTest(const base::Value & mapping)145 explicit PolicyPrefMappingTest(const base::Value& mapping) {
146 const base::Value* policies = mapping.FindDictKey("policies");
147 const base::Value* prefs = mapping.FindDictKey("prefs");
148 if (policies)
149 policies_ = policies->Clone();
150 if (prefs) {
151 for (const auto& pref_setting : prefs->DictItems())
152 prefs_.push_back(std::make_unique<PrefTestCase>(pref_setting.first,
153 pref_setting.second));
154 }
155 const base::Value* required_preprocessor_macros_value =
156 mapping.FindListKey("required_preprocessor_macros");
157 if (required_preprocessor_macros_value) {
158 for (const auto& macro : required_preprocessor_macros_value->GetList())
159 required_preprocessor_macros_.push_back(macro.GetString());
160 }
161 }
162 ~PolicyPrefMappingTest() = default;
163 PolicyPrefMappingTest(const PolicyPrefMappingTest& other) = delete;
164 PolicyPrefMappingTest& operator=(const PolicyPrefMappingTest& other) = delete;
165
policies() const166 const base::Value& policies() const { return policies_; }
167
prefs() const168 const std::vector<std::unique_ptr<PrefTestCase>>& prefs() const {
169 return prefs_;
170 }
171
required_preprocessor_macros() const172 const std::vector<std::string>& required_preprocessor_macros() const {
173 return required_preprocessor_macros_;
174 }
175
176 private:
177 const std::string pref_;
178 base::Value policies_;
179 std::vector<std::unique_ptr<PrefTestCase>> prefs_;
180 std::vector<std::string> required_preprocessor_macros_;
181 };
182
183 // Populates preprocessor macros as strings that policy pref mapping test cases
184 // can depend on and implements a check if such a test case should run according
185 // to the preprocessor macros.
186 class PreprocessorMacrosChecker {
187 public:
PreprocessorMacrosChecker()188 PreprocessorMacrosChecker() {
189 // If you are adding a macro mapping here, please also add it to the
190 // documentation of 'required_preprocessor_macros' in
191 // policy_test_cases.json.
192 #if defined(USE_CUPS)
193 enabled_preprocessor_macros.insert("USE_CUPS");
194 #endif
195 }
196 ~PreprocessorMacrosChecker() = default;
197 PreprocessorMacrosChecker(const PreprocessorMacrosChecker& other) = delete;
198 PreprocessorMacrosChecker& operator=(const PreprocessorMacrosChecker& other) =
199 delete;
200
201 // Returns true if |test| may be executed based on its reuquired preprocessor
202 // macros.
SupportsTest(const PolicyPrefMappingTest * test) const203 bool SupportsTest(const PolicyPrefMappingTest* test) const {
204 for (const std::string& required_macro :
205 test->required_preprocessor_macros()) {
206 if (enabled_preprocessor_macros.find(required_macro) ==
207 enabled_preprocessor_macros.end()) {
208 return false;
209 }
210 }
211 return true;
212 }
213
214 private:
215 std::set<std::string> enabled_preprocessor_macros;
216 };
217
218 // Contains the testing details for a single policy. This is part of the data
219 // loaded from chrome/test/data/policy/policy_test_cases.json.
220 class PolicyTestCase {
221 public:
PolicyTestCase(const std::string & name,const base::Value & test_case)222 PolicyTestCase(const std::string& name, const base::Value& test_case)
223 : name_(name) {
224 is_official_only_ = test_case.FindBoolKey("official_only").value_or(false);
225 can_be_recommended_ =
226 test_case.FindBoolKey("can_be_recommended").value_or(false);
227
228 const base::Value* os_list = test_case.FindListKey("os");
229 if (os_list) {
230 for (const auto& os : os_list->GetList()) {
231 if (os.is_string())
232 supported_os_.push_back(os.GetString());
233 }
234 }
235
236 const base::Value* test_policy = test_case.FindDictKey("test_policy");
237 if (test_policy)
238 test_policy_ = test_policy->CreateDeepCopy();
239
240 const base::Value* policy_pref_mapping_tests =
241 test_case.FindListKey("policy_pref_mapping_test");
242 if (policy_pref_mapping_tests) {
243 for (const auto& mapping : policy_pref_mapping_tests->GetList()) {
244 if (mapping.is_dict()) {
245 policy_pref_mapping_test_.push_back(
246 std::make_unique<PolicyPrefMappingTest>(mapping));
247 }
248 }
249 }
250 }
251
252 ~PolicyTestCase() = default;
253 PolicyTestCase(const PolicyTestCase& other) = delete;
254 PolicyTestCase& operator=(const PolicyTestCase& other) = delete;
255
name() const256 const std::string& name() const { return name_; }
257
is_official_only() const258 bool is_official_only() const { return is_official_only_; }
259
can_be_recommended() const260 bool can_be_recommended() const { return can_be_recommended_; }
261
IsOsSupported() const262 bool IsOsSupported() const {
263 #if defined(OS_WIN)
264 const std::string os("win");
265 #elif defined(OS_IOS)
266 const std::string os("ios");
267 #elif defined(OS_APPLE)
268 const std::string os("mac");
269 #elif defined(OS_CHROMEOS)
270 const std::string os("chromeos");
271 #elif defined(OS_LINUX)
272 const std::string os("linux");
273 #elif defined(OS_FREEBSD)
274 const std::string os("freebsd");
275 #else
276 #error "Unknown platform"
277 #endif
278 return base::Contains(supported_os_, os);
279 }
AddSupportedOs(const std::string & os)280 void AddSupportedOs(const std::string& os) { supported_os_.push_back(os); }
281
IsSupported() const282 bool IsSupported() const {
283 #if !BUILDFLAG(GOOGLE_CHROME_BRANDING)
284 if (is_official_only())
285 return false;
286 #endif
287 return IsOsSupported();
288 }
289
test_policy() const290 const base::Value* test_policy() const { return test_policy_.get(); }
291
292 const std::vector<std::unique_ptr<PolicyPrefMappingTest>>&
policy_pref_mapping_test() const293 policy_pref_mapping_test() const {
294 return policy_pref_mapping_test_;
295 }
296
297 private:
298 std::string name_;
299 bool is_official_only_;
300 bool can_be_recommended_;
301 std::vector<std::string> supported_os_;
302 std::unique_ptr<base::Value> test_policy_;
303 std::vector<std::unique_ptr<PolicyPrefMappingTest>> policy_pref_mapping_test_;
304 };
305
306 // Parses all policy test cases and makes them available in a map.
307 class PolicyTestCases {
308 public:
309 typedef std::vector<std::unique_ptr<PolicyTestCase>> PolicyTestCaseVector;
310 typedef std::map<std::string, PolicyTestCaseVector> PolicyTestCaseMap;
311 typedef PolicyTestCaseMap::const_iterator iterator;
312
PolicyTestCases(const base::FilePath & test_case_path)313 explicit PolicyTestCases(const base::FilePath& test_case_path) {
314 base::ScopedAllowBlockingForTesting allow_blocking;
315 std::string json;
316 if (!base::ReadFileToString(test_case_path, &json)) {
317 ADD_FAILURE();
318 return;
319 }
320 base::DictionaryValue* dict = nullptr;
321 base::JSONReader::ValueWithError parsed_json =
322 base::JSONReader::ReadAndReturnValueWithError(json);
323 if (!parsed_json.value || !parsed_json.value->GetAsDictionary(&dict)) {
324 ADD_FAILURE() << "Error parsing policy_test_cases.json: "
325 << parsed_json.error_message;
326 return;
327 }
328 for (const auto& it : dict->DictItems()) {
329 const std::string policy_name = GetPolicyName(it.first);
330 if (policy_name == kTemplateSampleTest)
331 continue;
332 auto policy_test_case =
333 std::make_unique<PolicyTestCase>(it.first, it.second);
334 policy_test_cases_[policy_name].push_back(std::move(policy_test_case));
335 }
336 }
337
338 ~PolicyTestCases() = default;
339 PolicyTestCases(const PolicyTestCases& other) = delete;
340 PolicyTestCases& operator=(const PolicyTestCases& other) = delete;
341
Get(const std::string & name) const342 const PolicyTestCaseVector* Get(const std::string& name) const {
343 const iterator it = policy_test_cases_.find(name);
344 return it == end() ? nullptr : &it->second;
345 }
346
map() const347 const PolicyTestCaseMap& map() const { return policy_test_cases_; }
begin() const348 iterator begin() const { return policy_test_cases_.begin(); }
end() const349 iterator end() const { return policy_test_cases_.end(); }
350
351 private:
352 PolicyTestCaseMap policy_test_cases_;
353 };
354
SetProviderPolicy(MockConfigurationPolicyProvider * provider,const base::Value & policies,PolicyLevel level)355 void SetProviderPolicy(MockConfigurationPolicyProvider* provider,
356 const base::Value& policies,
357 PolicyLevel level) {
358 PolicyMap policy_map;
359 #if defined(OS_CHROMEOS)
360 SetEnterpriseUsersDefaults(&policy_map);
361 #endif // defined(OS_CHROMEOS)
362 for (const auto& it : policies.DictItems()) {
363 const PolicyDetails* policy_details = GetChromePolicyDetails(it.first);
364 ASSERT_TRUE(policy_details);
365 policy_map.Set(
366 it.first, level, POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
367 it.second.Clone(),
368 policy_details->max_external_data_size
369 ? std::make_unique<ExternalDataFetcher>(nullptr, it.first)
370 : nullptr);
371 }
372 provider->UpdateChromePolicy(policy_map);
373 }
374
375 } // namespace
376
VerifyAllPoliciesHaveATestCase(const base::FilePath & test_case_path)377 void VerifyAllPoliciesHaveATestCase(const base::FilePath& test_case_path) {
378 // Verifies that all known policies have a test case in the JSON file.
379 // This test fails when a policy is added to
380 // components/policy/resources/policy_templates.json but a test case is not
381 // added to chrome/test/data/policy/policy_test_cases.json.
382 Schema chrome_schema = Schema::Wrap(GetChromeSchemaData());
383 ASSERT_TRUE(chrome_schema.valid());
384
385 PolicyTestCases policy_test_cases(test_case_path);
386 for (Schema::Iterator it = chrome_schema.GetPropertiesIterator();
387 !it.IsAtEnd(); it.Advance()) {
388 auto policy = policy_test_cases.map().find(it.key());
389 if (policy == policy_test_cases.map().end()) {
390 ADD_FAILURE() << "Missing policy test case for: " << it.key();
391 continue;
392 }
393
394 bool has_test_case_for_this_os = false;
395 for (const auto& test_case : policy->second) {
396 has_test_case_for_this_os |= test_case->IsSupported();
397 if (has_test_case_for_this_os)
398 break;
399 }
400
401 // This can only be a warning as many policies are not really testable
402 // this way and only present as a single line in the file.
403 // Although they could at least contain the "os" and "test_policy" fields.
404 // See http://crbug.com/791125.
405 LOG_IF(WARNING, !has_test_case_for_this_os)
406 << "Policy " << policy->first
407 << " is marked as supported on this OS in policy_templates.json but "
408 << "there is no test for this platform in policy_test_cases.json.";
409 }
410 }
411
412 // Verifies that policies make their corresponding preferences become managed,
413 // and that the user can't override that setting.
VerifyPolicyToPrefMappings(const base::FilePath & test_case_path,PrefService * local_state,PrefService * user_prefs,MockConfigurationPolicyProvider * provider,const std::string & skipped_pref_prefix)414 void VerifyPolicyToPrefMappings(const base::FilePath& test_case_path,
415 PrefService* local_state,
416 PrefService* user_prefs,
417 MockConfigurationPolicyProvider* provider,
418 const std::string& skipped_pref_prefix) {
419 Schema chrome_schema = Schema::Wrap(GetChromeSchemaData());
420 ASSERT_TRUE(chrome_schema.valid());
421
422 const PreprocessorMacrosChecker preprocessor_macros_checker;
423 const PolicyTestCases test_cases(test_case_path);
424 for (const auto& policy : test_cases) {
425 for (const auto& test_case : policy.second) {
426 const auto& pref_mappings = test_case->policy_pref_mapping_test();
427 if (!chrome_schema.GetKnownProperty(policy.first).valid()) {
428 // If the policy is supported on this platform according to the test it
429 // should be known otherwise we signal this as a failure.
430 // =====================================================================
431 // !NOTE! If you see this assertion after changing Chrome's VERSION most
432 // probably the mentioned policy was deprecated and deleted. Verify this
433 // in policy_templates.json and remove the corresponding test entry
434 // in policy_test_cases.json. Don't completely delete it from there just
435 // replace it's definition with a single "note" value stating its
436 // deprecation date (see other examples present in the file already).
437 // =====================================================================
438 EXPECT_FALSE(test_case->IsSupported())
439 << "Policy " << policy.first
440 << " is marked as supported on this OS but does not exist in the "
441 << "Chrome policy schema.";
442 continue;
443 }
444
445 if (!test_case->IsSupported() || pref_mappings.empty())
446 continue;
447
448 LOG(INFO) << "Testing policy: " << policy.first;
449
450 for (const auto& pref_mapping : pref_mappings) {
451 if (!preprocessor_macros_checker.SupportsTest(pref_mapping.get())) {
452 LOG(INFO) << " Skipping policy_pref_mapping_test because of "
453 << "preprocessor macros";
454 continue;
455 }
456
457 for (const auto& pref_case : pref_mapping->prefs()) {
458 // Skip Chrome OS preferences that use a different backend and cannot
459 // be retrieved through the prefs mechanism.
460 if (!skipped_pref_prefix.empty() &&
461 base::StartsWith(pref_case->pref(), skipped_pref_prefix,
462 base::CompareCase::SENSITIVE))
463 continue;
464
465 // Skip preferences that should not be checked when the policy is set
466 // to a mandatory value.
467 if (!pref_case->check_for_mandatory())
468 continue;
469
470 PrefService* prefs =
471 pref_case->is_local_state() ? local_state : user_prefs;
472 // The preference must have been registered.
473 const PrefService::Preference* pref =
474 prefs->FindPreference(pref_case->pref().c_str());
475 ASSERT_TRUE(pref)
476 << "Pref " << pref_case->pref().c_str() << " not registered";
477
478 // Verify that setting the policy overrides the pref.
479 provider->UpdateChromePolicy(PolicyMap());
480 prefs->ClearPref(pref_case->pref().c_str());
481 EXPECT_TRUE(pref->IsDefaultValue());
482 EXPECT_TRUE(pref->IsUserModifiable());
483 EXPECT_FALSE(pref->IsUserControlled());
484 EXPECT_FALSE(pref->IsManaged());
485
486 ASSERT_NO_FATAL_FAILURE(SetProviderPolicy(
487 provider, pref_mapping->policies(), POLICY_LEVEL_MANDATORY));
488 if (pref_case->expect_default()) {
489 EXPECT_TRUE(pref->IsDefaultValue());
490 EXPECT_TRUE(pref->IsUserModifiable());
491 EXPECT_FALSE(pref->IsUserControlled());
492 EXPECT_FALSE(pref->IsManaged());
493 } else {
494 EXPECT_FALSE(pref->IsDefaultValue());
495 EXPECT_FALSE(pref->IsUserModifiable());
496 EXPECT_FALSE(pref->IsUserControlled());
497 EXPECT_TRUE(pref->IsManaged());
498 }
499 if (pref_case->value()) {
500 EXPECT_TRUE(pref->GetValue()->Equals(pref_case->value()))
501 << *pref->GetValue() << " != " << *pref_case->value();
502 }
503 }
504 }
505 }
506 }
507 }
508
509 } // namespace policy
510