Add more checks to investigate SupervisedUserPrefStore crash at startup.
[chromium-blink-merge.git] / chrome / browser / component_updater / supervised_user_whitelist_installer.cc
blob2c3cac66b07f381e2874739a0b390a36316ddeb3
1 // Copyright 2014 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.
5 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/command_line.h"
10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/location.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/metrics/user_metrics.h"
16 #include "base/metrics/user_metrics_action.h"
17 #include "base/path_service.h"
18 #include "base/prefs/pref_registry_simple.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/prefs/scoped_user_pref_update.h"
21 #include "base/scoped_observer.h"
22 #include "base/sequenced_task_runner.h"
23 #include "base/strings/string_split.h"
24 #include "base/strings/string_util.h"
25 #include "chrome/browser/profiles/profile_info_cache.h"
26 #include "chrome/browser/profiles/profile_info_cache_observer.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/component_updater/component_updater_paths.h"
30 #include "components/component_updater/component_updater_service.h"
31 #include "components/component_updater/default_component_installer.h"
32 #include "components/crx_file/id_util.h"
34 namespace component_updater {
36 namespace {
38 // See the corresponding entries in extensions::manifest_keys.
39 const char kContentPack[] = "content_pack";
40 const char kContentPackSites[] = "sites";
42 const char kClients[] = "clients";
43 const char kName[] = "name";
45 base::FilePath GetWhitelistPath(const base::DictionaryValue& manifest,
46 const base::FilePath& install_dir) {
47 const base::DictionaryValue* content_pack_dict = nullptr;
48 if (!manifest.GetDictionary(kContentPack, &content_pack_dict))
49 return base::FilePath();
51 base::FilePath::StringType whitelist_file;
52 if (!content_pack_dict->GetString(kContentPackSites, &whitelist_file))
53 return base::FilePath();
55 return install_dir.Append(whitelist_file);
58 void RemoveUnregisteredWhitelistsOnTaskRunner(
59 const std::set<std::string>& registered_whitelists) {
60 base::FilePath base_dir;
61 PathService::Get(DIR_SUPERVISED_USER_WHITELISTS, &base_dir);
62 if (base_dir.empty())
63 return;
65 std::vector<base::FilePath> paths;
66 base::FileEnumerator file_enumerator(base_dir, false,
67 base::FileEnumerator::DIRECTORIES);
68 for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
69 path = file_enumerator.Next()) {
70 const std::string crx_id = path.BaseName().MaybeAsASCII();
72 // Ignore folders that don't have valid CRX ID names. These folders are not
73 // managed by the component installer, so do not try to remove them.
74 if (!crx_file::id_util::IdIsValid(crx_id))
75 continue;
77 // Ignore folders that correspond to registered whitelists.
78 if (registered_whitelists.count(crx_id) > 0)
79 continue;
81 base::RecordAction(
82 base::UserMetricsAction("ManagedUsers_Whitelist_UncleanUninstall"));
84 if (!base::DeleteFile(path, true))
85 DLOG(ERROR) << "Couldn't delete " << path.value();
89 class SupervisedUserWhitelistComponentInstallerTraits
90 : public ComponentInstallerTraits {
91 public:
92 SupervisedUserWhitelistComponentInstallerTraits(
93 const std::string& crx_id,
94 const std::string& name,
95 const base::Callback<void(const base::FilePath&)>& callback)
96 : crx_id_(crx_id), name_(name), callback_(callback) {}
97 ~SupervisedUserWhitelistComponentInstallerTraits() override {}
99 private:
100 // ComponentInstallerTraits overrides:
101 bool VerifyInstallation(const base::DictionaryValue& manifest,
102 const base::FilePath& install_dir) const override;
103 bool CanAutoUpdate() const override;
104 bool OnCustomInstall(const base::DictionaryValue& manifest,
105 const base::FilePath& install_dir) override;
106 void ComponentReady(const base::Version& version,
107 const base::FilePath& install_dir,
108 scoped_ptr<base::DictionaryValue> manifest) override;
109 base::FilePath GetBaseDirectory() const override;
110 void GetHash(std::vector<uint8_t>* hash) const override;
111 std::string GetName() const override;
113 std::string crx_id_;
114 std::string name_;
115 base::Callback<void(const base::FilePath&)> callback_;
117 DISALLOW_COPY_AND_ASSIGN(SupervisedUserWhitelistComponentInstallerTraits);
120 bool SupervisedUserWhitelistComponentInstallerTraits::VerifyInstallation(
121 const base::DictionaryValue& manifest,
122 const base::FilePath& install_dir) const {
123 // Check whether the whitelist exists at the path specified by the manifest.
124 // This does not check whether the whitelist is wellformed.
125 return base::PathExists(GetWhitelistPath(manifest, install_dir));
128 bool SupervisedUserWhitelistComponentInstallerTraits::CanAutoUpdate() const {
129 return true;
132 bool SupervisedUserWhitelistComponentInstallerTraits::OnCustomInstall(
133 const base::DictionaryValue& manifest,
134 const base::FilePath& install_dir) {
135 return true;
138 void SupervisedUserWhitelistComponentInstallerTraits::ComponentReady(
139 const base::Version& version,
140 const base::FilePath& install_dir,
141 scoped_ptr<base::DictionaryValue> manifest) {
142 callback_.Run(GetWhitelistPath(*manifest, install_dir));
145 base::FilePath
146 SupervisedUserWhitelistComponentInstallerTraits::GetBaseDirectory() const {
147 base::FilePath whitelist_directory;
148 PathService::Get(DIR_SUPERVISED_USER_WHITELISTS, &whitelist_directory);
149 return whitelist_directory.AppendASCII(crx_id_);
152 void SupervisedUserWhitelistComponentInstallerTraits::GetHash(
153 std::vector<uint8_t>* hash) const {
154 *hash = SupervisedUserWhitelistInstaller::GetHashFromCrxId(crx_id_);
157 std::string SupervisedUserWhitelistComponentInstallerTraits::GetName() const {
158 return name_;
161 class SupervisedUserWhitelistInstallerImpl
162 : public SupervisedUserWhitelistInstaller,
163 public ProfileInfoCacheObserver {
164 public:
165 SupervisedUserWhitelistInstallerImpl(ComponentUpdateService* cus,
166 ProfileInfoCache* profile_info_cache,
167 PrefService* local_state);
168 ~SupervisedUserWhitelistInstallerImpl() override {}
170 private:
171 void RegisterComponent(const std::string& crx_id, const std::string& name);
172 void RegisterNewComponent(const std::string& crx_id, const std::string& name);
173 bool UnregisterWhitelistInternal(base::DictionaryValue* pref_dict,
174 const std::string& client_id,
175 const std::string& crx_id);
177 void OnWhitelistReady(const std::string& crx_id,
178 const base::FilePath& whitelist_path);
180 // SupervisedUserWhitelistInstaller overrides:
181 void RegisterComponents() override;
182 void Subscribe(const WhitelistReadyCallback& callback) override;
183 void RegisterWhitelist(const std::string& client_id,
184 const std::string& crx_id,
185 const std::string& name) override;
186 void UnregisterWhitelist(const std::string& client_id,
187 const std::string& crx_id) override;
189 // ProfileInfoCacheObserver overrides:
190 void OnProfileWillBeRemoved(const base::FilePath& profile_path) override;
192 ComponentUpdateService* cus_;
193 PrefService* local_state_;
195 std::vector<WhitelistReadyCallback> callbacks_;
197 ScopedObserver<ProfileInfoCache, ProfileInfoCacheObserver> observer_;
199 base::WeakPtrFactory<SupervisedUserWhitelistInstallerImpl> weak_ptr_factory_;
201 DISALLOW_COPY_AND_ASSIGN(SupervisedUserWhitelistInstallerImpl);
204 SupervisedUserWhitelistInstallerImpl::SupervisedUserWhitelistInstallerImpl(
205 ComponentUpdateService* cus,
206 ProfileInfoCache* profile_info_cache,
207 PrefService* local_state)
208 : cus_(cus),
209 local_state_(local_state),
210 observer_(this),
211 weak_ptr_factory_(this) {
212 DCHECK(cus);
213 DCHECK(local_state);
214 // In unit tests, the profile info cache can be null.
215 if (profile_info_cache)
216 observer_.Add(profile_info_cache);
219 void SupervisedUserWhitelistInstallerImpl::RegisterComponent(
220 const std::string& crx_id,
221 const std::string& name) {
222 scoped_ptr<ComponentInstallerTraits> traits(
223 new SupervisedUserWhitelistComponentInstallerTraits(
224 crx_id, name,
225 base::Bind(&SupervisedUserWhitelistInstallerImpl::OnWhitelistReady,
226 weak_ptr_factory_.GetWeakPtr(), crx_id)));
227 scoped_refptr<DefaultComponentInstaller> installer(
228 new DefaultComponentInstaller(traits.Pass()));
229 installer->Register(cus_);
232 void SupervisedUserWhitelistInstallerImpl::RegisterNewComponent(
233 const std::string& crx_id,
234 const std::string& name) {
235 RegisterComponent(crx_id, name);
236 TriggerComponentUpdate(&cus_->GetOnDemandUpdater(), crx_id);
239 bool SupervisedUserWhitelistInstallerImpl::UnregisterWhitelistInternal(
240 base::DictionaryValue* pref_dict,
241 const std::string& client_id,
242 const std::string& crx_id) {
243 base::DictionaryValue* whitelist_dict = nullptr;
244 bool success =
245 pref_dict->GetDictionaryWithoutPathExpansion(crx_id, &whitelist_dict);
246 DCHECK(success);
247 base::ListValue* clients = nullptr;
248 success = whitelist_dict->GetList(kClients, &clients);
250 bool removed = clients->Remove(base::StringValue(client_id), nullptr);
252 if (!clients->empty())
253 return removed;
255 pref_dict->RemoveWithoutPathExpansion(crx_id, nullptr);
256 const ComponentUpdateService::Status status =
257 cus_->UnregisterComponent(crx_id);
258 DCHECK_EQ(ComponentUpdateService::kOk, status);
260 return removed;
263 void SupervisedUserWhitelistInstallerImpl::OnWhitelistReady(
264 const std::string& crx_id,
265 const base::FilePath& whitelist_path) {
266 for (const auto& callback : callbacks_)
267 callback.Run(crx_id, whitelist_path);
270 void SupervisedUserWhitelistInstallerImpl::RegisterComponents() {
271 std::set<std::string> registered_whitelists;
272 const base::DictionaryValue* whitelists =
273 local_state_->GetDictionary(prefs::kRegisteredSupervisedUserWhitelists);
274 for (base::DictionaryValue::Iterator it(*whitelists); !it.IsAtEnd();
275 it.Advance()) {
276 const base::DictionaryValue* dict = nullptr;
277 it.value().GetAsDictionary(&dict);
278 std::string name;
279 bool result = dict->GetString(kName, &name);
280 DCHECK(result);
281 const std::string& id = it.key();
282 RegisterComponent(id, name);
284 registered_whitelists.insert(id);
287 // Register whitelists specified on the command line.
288 const base::CommandLine* command_line =
289 base::CommandLine::ForCurrentProcess();
290 std::string command_line_whitelists = command_line->GetSwitchValueASCII(
291 switches::kInstallSupervisedUserWhitelists);
292 std::vector<std::string> split_whitelists;
293 base::SplitString(command_line_whitelists, ',', &split_whitelists);
294 for (const std::string& whitelist : split_whitelists) {
295 std::string id;
296 std::string name;
297 size_t separator = whitelist.find(':');
298 if (separator != std::string::npos) {
299 id = whitelist.substr(0, separator);
300 name = whitelist.substr(separator + 1);
301 } else {
302 id = whitelist;
304 RegisterNewComponent(id, name);
306 registered_whitelists.insert(id);
309 cus_->GetSequencedTaskRunner()->PostTask(
310 FROM_HERE, base::Bind(&RemoveUnregisteredWhitelistsOnTaskRunner,
311 registered_whitelists));
314 void SupervisedUserWhitelistInstallerImpl::Subscribe(
315 const WhitelistReadyCallback& callback) {
316 return callbacks_.push_back(callback);
319 void SupervisedUserWhitelistInstallerImpl::RegisterWhitelist(
320 const std::string& client_id,
321 const std::string& crx_id,
322 const std::string& name) {
323 DictionaryPrefUpdate update(local_state_,
324 prefs::kRegisteredSupervisedUserWhitelists);
325 base::DictionaryValue* pref_dict = update.Get();
326 base::DictionaryValue* whitelist_dict = nullptr;
327 bool newly_added = false;
328 if (!pref_dict->GetDictionaryWithoutPathExpansion(crx_id, &whitelist_dict)) {
329 whitelist_dict = new base::DictionaryValue;
330 whitelist_dict->SetString(kName, name);
331 pref_dict->SetWithoutPathExpansion(crx_id, whitelist_dict);
332 newly_added = true;
335 base::ListValue* clients = nullptr;
336 if (!whitelist_dict->GetList(kClients, &clients)) {
337 DCHECK(newly_added);
338 clients = new base::ListValue;
339 whitelist_dict->Set(kClients, clients);
341 bool success = clients->AppendIfNotPresent(new base::StringValue(client_id));
342 DCHECK(success);
344 if (!newly_added) {
345 // Sanity-check that the stored name is equal to the name passed in.
346 // In release builds this is a no-op.
347 std::string stored_name;
348 DCHECK(whitelist_dict->GetString(kName, &stored_name));
349 DCHECK_EQ(stored_name, name);
350 return;
353 RegisterNewComponent(crx_id, name);
356 void SupervisedUserWhitelistInstallerImpl::UnregisterWhitelist(
357 const std::string& client_id,
358 const std::string& crx_id) {
359 DictionaryPrefUpdate update(local_state_,
360 prefs::kRegisteredSupervisedUserWhitelists);
361 bool removed = UnregisterWhitelistInternal(update.Get(), client_id, crx_id);
362 DCHECK(removed);
365 void SupervisedUserWhitelistInstallerImpl::OnProfileWillBeRemoved(
366 const base::FilePath& profile_path) {
367 std::string client_id = ClientIdForProfilePath(profile_path);
369 // Go through all registered whitelists and possibly unregister them for this
370 // client.
371 DictionaryPrefUpdate update(local_state_,
372 prefs::kRegisteredSupervisedUserWhitelists);
373 base::DictionaryValue* pref_dict = update.Get();
374 for (base::DictionaryValue::Iterator it(*pref_dict); !it.IsAtEnd();
375 it.Advance()) {
376 UnregisterWhitelistInternal(pref_dict, client_id, it.key());
380 } // namespace
382 // static
383 scoped_ptr<SupervisedUserWhitelistInstaller>
384 SupervisedUserWhitelistInstaller::Create(ComponentUpdateService* cus,
385 ProfileInfoCache* profile_info_cache,
386 PrefService* local_state) {
387 return make_scoped_ptr(new SupervisedUserWhitelistInstallerImpl(
388 cus, profile_info_cache, local_state));
391 // static
392 void SupervisedUserWhitelistInstaller::RegisterPrefs(
393 PrefRegistrySimple* registry) {
394 registry->RegisterDictionaryPref(prefs::kRegisteredSupervisedUserWhitelists);
397 // static
398 std::string SupervisedUserWhitelistInstaller::ClientIdForProfilePath(
399 const base::FilePath& profile_path) {
400 // See ProfileInfoCache::CacheKeyFromProfilePath().
401 return profile_path.BaseName().MaybeAsASCII();
404 // static
405 std::vector<uint8_t> SupervisedUserWhitelistInstaller::GetHashFromCrxId(
406 const std::string& crx_id) {
407 DCHECK(crx_file::id_util::IdIsValid(crx_id));
409 std::vector<uint8_t> hash;
410 uint8_t byte = 0;
411 for (size_t i = 0; i < crx_id.size(); ++i) {
412 // Uppercase characters in IDs are technically legal.
413 int val = base::ToLowerASCII(crx_id[i]) - 'a';
414 DCHECK_GE(val, 0);
415 DCHECK_LT(val, 16);
416 if (i % 2 == 0) {
417 byte = val;
418 } else {
419 hash.push_back(16 * byte + val);
420 byte = 0;
423 return hash;
426 // static
427 void SupervisedUserWhitelistInstaller::TriggerComponentUpdate(
428 OnDemandUpdater* updater,
429 const std::string& crx_id) {
430 updater->OnDemandUpdate(crx_id);
433 } // namespace component_updater