OLD | NEW |
| (Empty) |
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 "chrome/browser/policy/config_dir_policy_loader.h" | |
6 | |
7 #include <algorithm> | |
8 #include <set> | |
9 #include <string> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/bind_helpers.h" | |
13 #include "base/file_util.h" | |
14 #include "base/files/file_enumerator.h" | |
15 #include "base/json/json_file_value_serializer.h" | |
16 #include "base/json/json_reader.h" | |
17 #include "base/logging.h" | |
18 #include "base/platform_file.h" | |
19 #include "base/stl_util.h" | |
20 #include "chrome/browser/policy/policy_load_status.h" | |
21 #include "components/policy/core/common/policy_bundle.h" | |
22 | |
23 namespace policy { | |
24 | |
25 namespace { | |
26 | |
27 // Subdirectories that contain the mandatory and recommended policies. | |
28 const base::FilePath::CharType kMandatoryConfigDir[] = | |
29 FILE_PATH_LITERAL("managed"); | |
30 const base::FilePath::CharType kRecommendedConfigDir[] = | |
31 FILE_PATH_LITERAL("recommended"); | |
32 | |
33 PolicyLoadStatus JsonErrorToPolicyLoadStatus(int status) { | |
34 switch (status) { | |
35 case JSONFileValueSerializer::JSON_ACCESS_DENIED: | |
36 case JSONFileValueSerializer::JSON_CANNOT_READ_FILE: | |
37 case JSONFileValueSerializer::JSON_FILE_LOCKED: | |
38 return POLICY_LOAD_STATUS_READ_ERROR; | |
39 case JSONFileValueSerializer::JSON_NO_SUCH_FILE: | |
40 return POLICY_LOAD_STATUS_MISSING; | |
41 case base::JSONReader::JSON_INVALID_ESCAPE: | |
42 case base::JSONReader::JSON_SYNTAX_ERROR: | |
43 case base::JSONReader::JSON_UNEXPECTED_TOKEN: | |
44 case base::JSONReader::JSON_TRAILING_COMMA: | |
45 case base::JSONReader::JSON_TOO_MUCH_NESTING: | |
46 case base::JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT: | |
47 case base::JSONReader::JSON_UNSUPPORTED_ENCODING: | |
48 case base::JSONReader::JSON_UNQUOTED_DICTIONARY_KEY: | |
49 return POLICY_LOAD_STATUS_PARSE_ERROR; | |
50 case base::JSONReader::JSON_NO_ERROR: | |
51 NOTREACHED(); | |
52 return POLICY_LOAD_STATUS_STARTED; | |
53 } | |
54 NOTREACHED() << "Invalid status " << status; | |
55 return POLICY_LOAD_STATUS_PARSE_ERROR; | |
56 } | |
57 | |
58 } // namespace | |
59 | |
60 ConfigDirPolicyLoader::ConfigDirPolicyLoader( | |
61 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
62 const base::FilePath& config_dir, | |
63 PolicyScope scope) | |
64 : AsyncPolicyLoader(task_runner), config_dir_(config_dir), scope_(scope) {} | |
65 | |
66 ConfigDirPolicyLoader::~ConfigDirPolicyLoader() {} | |
67 | |
68 void ConfigDirPolicyLoader::InitOnBackgroundThread() { | |
69 base::FilePathWatcher::Callback callback = | |
70 base::Bind(&ConfigDirPolicyLoader::OnFileUpdated, base::Unretained(this)); | |
71 mandatory_watcher_.Watch(config_dir_.Append(kMandatoryConfigDir), false, | |
72 callback); | |
73 recommended_watcher_.Watch(config_dir_.Append(kRecommendedConfigDir), false, | |
74 callback); | |
75 } | |
76 | |
77 scoped_ptr<PolicyBundle> ConfigDirPolicyLoader::Load() { | |
78 scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); | |
79 LoadFromPath(config_dir_.Append(kMandatoryConfigDir), | |
80 POLICY_LEVEL_MANDATORY, | |
81 bundle.get()); | |
82 LoadFromPath(config_dir_.Append(kRecommendedConfigDir), | |
83 POLICY_LEVEL_RECOMMENDED, | |
84 bundle.get()); | |
85 return bundle.Pass(); | |
86 } | |
87 | |
88 base::Time ConfigDirPolicyLoader::LastModificationTime() { | |
89 static const base::FilePath::CharType* kConfigDirSuffixes[] = { | |
90 kMandatoryConfigDir, | |
91 kRecommendedConfigDir, | |
92 }; | |
93 | |
94 base::Time last_modification = base::Time(); | |
95 base::PlatformFileInfo info; | |
96 | |
97 for (size_t i = 0; i < arraysize(kConfigDirSuffixes); ++i) { | |
98 base::FilePath path(config_dir_.Append(kConfigDirSuffixes[i])); | |
99 | |
100 // Skip if the file doesn't exist, or it isn't a directory. | |
101 if (!base::GetFileInfo(path, &info) || !info.is_directory) | |
102 continue; | |
103 | |
104 // Enumerate the files and find the most recent modification timestamp. | |
105 base::FileEnumerator file_enumerator(path, false, | |
106 base::FileEnumerator::FILES); | |
107 for (base::FilePath config_file = file_enumerator.Next(); | |
108 !config_file.empty(); | |
109 config_file = file_enumerator.Next()) { | |
110 if (base::GetFileInfo(config_file, &info) && !info.is_directory) | |
111 last_modification = std::max(last_modification, info.last_modified); | |
112 } | |
113 } | |
114 | |
115 return last_modification; | |
116 } | |
117 | |
118 void ConfigDirPolicyLoader::LoadFromPath(const base::FilePath& path, | |
119 PolicyLevel level, | |
120 PolicyBundle* bundle) { | |
121 // Enumerate the files and sort them lexicographically. | |
122 std::set<base::FilePath> files; | |
123 base::FileEnumerator file_enumerator(path, false, | |
124 base::FileEnumerator::FILES); | |
125 for (base::FilePath config_file_path = file_enumerator.Next(); | |
126 !config_file_path.empty(); config_file_path = file_enumerator.Next()) | |
127 files.insert(config_file_path); | |
128 | |
129 PolicyLoadStatusSample status; | |
130 if (files.empty()) { | |
131 status.Add(POLICY_LOAD_STATUS_NO_POLICY); | |
132 return; | |
133 } | |
134 | |
135 // Start with an empty dictionary and merge the files' contents. | |
136 // The files are processed in reverse order because |MergeFrom| gives priority | |
137 // to existing keys, but the ConfigDirPolicyProvider gives priority to the | |
138 // last file in lexicographic order. | |
139 for (std::set<base::FilePath>::reverse_iterator config_file_iter = | |
140 files.rbegin(); config_file_iter != files.rend(); | |
141 ++config_file_iter) { | |
142 JSONFileValueSerializer deserializer(*config_file_iter); | |
143 deserializer.set_allow_trailing_comma(true); | |
144 int error_code = 0; | |
145 std::string error_msg; | |
146 scoped_ptr<base::Value> value( | |
147 deserializer.Deserialize(&error_code, &error_msg)); | |
148 if (!value.get()) { | |
149 LOG(WARNING) << "Failed to read configuration file " | |
150 << config_file_iter->value() << ": " << error_msg; | |
151 status.Add(JsonErrorToPolicyLoadStatus(error_code)); | |
152 continue; | |
153 } | |
154 base::DictionaryValue* dictionary_value = NULL; | |
155 if (!value->GetAsDictionary(&dictionary_value)) { | |
156 LOG(WARNING) << "Expected JSON dictionary in configuration file " | |
157 << config_file_iter->value(); | |
158 status.Add(POLICY_LOAD_STATUS_PARSE_ERROR); | |
159 continue; | |
160 } | |
161 | |
162 // Detach the "3rdparty" node. | |
163 scoped_ptr<base::Value> third_party; | |
164 if (dictionary_value->Remove("3rdparty", &third_party)) | |
165 Merge3rdPartyPolicy(third_party.get(), level, bundle); | |
166 | |
167 // Add chrome policy. | |
168 PolicyMap policy_map; | |
169 policy_map.LoadFrom(dictionary_value, level, scope_); | |
170 bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())) | |
171 .MergeFrom(policy_map); | |
172 } | |
173 } | |
174 | |
175 void ConfigDirPolicyLoader::Merge3rdPartyPolicy( | |
176 const base::Value* policies, | |
177 PolicyLevel level, | |
178 PolicyBundle* bundle) { | |
179 // The first-level entries in |policies| are PolicyDomains. The second-level | |
180 // entries are component IDs, and the third-level entries are the policies | |
181 // for that domain/component namespace. | |
182 | |
183 const base::DictionaryValue* domains_dictionary; | |
184 if (!policies->GetAsDictionary(&domains_dictionary)) { | |
185 LOG(WARNING) << "3rdparty value is not a dictionary!"; | |
186 return; | |
187 } | |
188 | |
189 // Helper to lookup a domain given its string name. | |
190 std::map<std::string, PolicyDomain> supported_domains; | |
191 supported_domains["extensions"] = POLICY_DOMAIN_EXTENSIONS; | |
192 | |
193 for (base::DictionaryValue::Iterator domains_it(*domains_dictionary); | |
194 !domains_it.IsAtEnd(); domains_it.Advance()) { | |
195 if (!ContainsKey(supported_domains, domains_it.key())) { | |
196 LOG(WARNING) << "Unsupported 3rd party policy domain: " | |
197 << domains_it.key(); | |
198 continue; | |
199 } | |
200 | |
201 const base::DictionaryValue* components_dictionary; | |
202 if (!domains_it.value().GetAsDictionary(&components_dictionary)) { | |
203 LOG(WARNING) << "3rdparty/" << domains_it.key() | |
204 << " value is not a dictionary!"; | |
205 continue; | |
206 } | |
207 | |
208 PolicyDomain domain = supported_domains[domains_it.key()]; | |
209 for (base::DictionaryValue::Iterator components_it(*components_dictionary); | |
210 !components_it.IsAtEnd(); components_it.Advance()) { | |
211 const base::DictionaryValue* policy_dictionary; | |
212 if (!components_it.value().GetAsDictionary(&policy_dictionary)) { | |
213 LOG(WARNING) << "3rdparty/" << domains_it.key() << "/" | |
214 << components_it.key() << " value is not a dictionary!"; | |
215 continue; | |
216 } | |
217 | |
218 PolicyMap policy; | |
219 policy.LoadFrom(policy_dictionary, level, scope_); | |
220 bundle->Get(PolicyNamespace(domain, components_it.key())) | |
221 .MergeFrom(policy); | |
222 } | |
223 } | |
224 } | |
225 | |
226 void ConfigDirPolicyLoader::OnFileUpdated(const base::FilePath& path, | |
227 bool error) { | |
228 if (!error) | |
229 Reload(false); | |
230 } | |
231 | |
232 } // namespace policy | |
OLD | NEW |