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"
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
{
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
);
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
))
77 // Ignore folders that correspond to registered whitelists.
78 if (registered_whitelists
.count(crx_id
) > 0)
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
{
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
{}
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
;
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 {
132 bool SupervisedUserWhitelistComponentInstallerTraits::OnCustomInstall(
133 const base::DictionaryValue
& manifest
,
134 const base::FilePath
& install_dir
) {
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
));
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 {
161 class SupervisedUserWhitelistInstallerImpl
162 : public SupervisedUserWhitelistInstaller
,
163 public ProfileInfoCacheObserver
{
165 SupervisedUserWhitelistInstallerImpl(ComponentUpdateService
* cus
,
166 ProfileInfoCache
* profile_info_cache
,
167 PrefService
* local_state
);
168 ~SupervisedUserWhitelistInstallerImpl() override
{}
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
)
209 local_state_(local_state
),
211 weak_ptr_factory_(this) {
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(
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;
245 pref_dict
->GetDictionaryWithoutPathExpansion(crx_id
, &whitelist_dict
);
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())
255 pref_dict
->RemoveWithoutPathExpansion(crx_id
, nullptr);
256 const ComponentUpdateService::Status status
=
257 cus_
->UnregisterComponent(crx_id
);
258 DCHECK_EQ(ComponentUpdateService::kOk
, status
);
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();
276 const base::DictionaryValue
* dict
= nullptr;
277 it
.value().GetAsDictionary(&dict
);
279 bool result
= dict
->GetString(kName
, &name
);
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
) {
297 size_t separator
= whitelist
.find(':');
298 if (separator
!= std::string::npos
) {
299 id
= whitelist
.substr(0, separator
);
300 name
= whitelist
.substr(separator
+ 1);
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
);
335 base::ListValue
* clients
= nullptr;
336 if (!whitelist_dict
->GetList(kClients
, &clients
)) {
338 clients
= new base::ListValue
;
339 whitelist_dict
->Set(kClients
, clients
);
341 bool success
= clients
->AppendIfNotPresent(new base::StringValue(client_id
));
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
);
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
);
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
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();
376 UnregisterWhitelistInternal(pref_dict
, client_id
, it
.key());
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
));
392 void SupervisedUserWhitelistInstaller::RegisterPrefs(
393 PrefRegistrySimple
* registry
) {
394 registry
->RegisterDictionaryPref(prefs::kRegisteredSupervisedUserWhitelists
);
398 std::string
SupervisedUserWhitelistInstaller::ClientIdForProfilePath(
399 const base::FilePath
& profile_path
) {
400 // See ProfileInfoCache::CacheKeyFromProfilePath().
401 return profile_path
.BaseName().MaybeAsASCII();
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
;
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';
419 hash
.push_back(16 * byte
+ val
);
427 void SupervisedUserWhitelistInstaller::TriggerComponentUpdate(
428 OnDemandUpdater
* updater
,
429 const std::string
& crx_id
) {
430 updater
->OnDemandUpdate(crx_id
);
433 } // namespace component_updater