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