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
,
172 const std::string
& name
,
173 const base::Closure
& callback
);
174 void RegisterNewComponent(const std::string
& crx_id
, const std::string
& name
);
175 bool UnregisterWhitelistInternal(base::DictionaryValue
* pref_dict
,
176 const std::string
& client_id
,
177 const std::string
& crx_id
);
179 void OnWhitelistReady(const std::string
& crx_id
,
180 const base::FilePath
& whitelist_path
);
182 // SupervisedUserWhitelistInstaller overrides:
183 void RegisterComponents() override
;
184 void Subscribe(const WhitelistReadyCallback
& callback
) override
;
185 void RegisterWhitelist(const std::string
& client_id
,
186 const std::string
& crx_id
,
187 const std::string
& name
) override
;
188 void UnregisterWhitelist(const std::string
& client_id
,
189 const std::string
& crx_id
) override
;
191 // ProfileInfoCacheObserver overrides:
192 void OnProfileWillBeRemoved(const base::FilePath
& profile_path
) override
;
194 ComponentUpdateService
* cus_
;
195 PrefService
* local_state_
;
197 std::vector
<WhitelistReadyCallback
> callbacks_
;
199 ScopedObserver
<ProfileInfoCache
, ProfileInfoCacheObserver
> observer_
;
201 base::WeakPtrFactory
<SupervisedUserWhitelistInstallerImpl
> weak_ptr_factory_
;
203 DISALLOW_COPY_AND_ASSIGN(SupervisedUserWhitelistInstallerImpl
);
206 SupervisedUserWhitelistInstallerImpl::SupervisedUserWhitelistInstallerImpl(
207 ComponentUpdateService
* cus
,
208 ProfileInfoCache
* profile_info_cache
,
209 PrefService
* local_state
)
211 local_state_(local_state
),
213 weak_ptr_factory_(this) {
216 // In unit tests, the profile info cache can be null.
217 if (profile_info_cache
)
218 observer_
.Add(profile_info_cache
);
221 void SupervisedUserWhitelistInstallerImpl::RegisterComponent(
222 const std::string
& crx_id
,
223 const std::string
& name
,
224 const base::Closure
& callback
) {
225 scoped_ptr
<ComponentInstallerTraits
> traits(
226 new SupervisedUserWhitelistComponentInstallerTraits(
228 base::Bind(&SupervisedUserWhitelistInstallerImpl::OnWhitelistReady
,
229 weak_ptr_factory_
.GetWeakPtr(), crx_id
)));
230 scoped_refptr
<DefaultComponentInstaller
> installer(
231 new DefaultComponentInstaller(traits
.Pass()));
232 installer
->Register(cus_
, callback
);
235 void SupervisedUserWhitelistInstallerImpl::RegisterNewComponent(
236 const std::string
& crx_id
,
237 const std::string
& name
) {
240 base::Bind(&SupervisedUserWhitelistInstallerImpl::TriggerComponentUpdate
,
241 &cus_
->GetOnDemandUpdater(), crx_id
));
244 bool SupervisedUserWhitelistInstallerImpl::UnregisterWhitelistInternal(
245 base::DictionaryValue
* pref_dict
,
246 const std::string
& client_id
,
247 const std::string
& crx_id
) {
248 base::DictionaryValue
* whitelist_dict
= nullptr;
250 pref_dict
->GetDictionaryWithoutPathExpansion(crx_id
, &whitelist_dict
);
252 base::ListValue
* clients
= nullptr;
253 success
= whitelist_dict
->GetList(kClients
, &clients
);
255 bool removed
= clients
->Remove(base::StringValue(client_id
), nullptr);
257 if (!clients
->empty())
260 pref_dict
->RemoveWithoutPathExpansion(crx_id
, nullptr);
261 const ComponentUpdateService::Status status
=
262 cus_
->UnregisterComponent(crx_id
);
263 DCHECK_EQ(ComponentUpdateService::kOk
, status
);
268 void SupervisedUserWhitelistInstallerImpl::OnWhitelistReady(
269 const std::string
& crx_id
,
270 const base::FilePath
& whitelist_path
) {
271 for (const auto& callback
: callbacks_
)
272 callback
.Run(crx_id
, whitelist_path
);
275 void SupervisedUserWhitelistInstallerImpl::RegisterComponents() {
276 std::set
<std::string
> registered_whitelists
;
277 const base::DictionaryValue
* whitelists
=
278 local_state_
->GetDictionary(prefs::kRegisteredSupervisedUserWhitelists
);
279 for (base::DictionaryValue::Iterator
it(*whitelists
); !it
.IsAtEnd();
281 const base::DictionaryValue
* dict
= nullptr;
282 it
.value().GetAsDictionary(&dict
);
284 bool result
= dict
->GetString(kName
, &name
);
286 const std::string
& id
= it
.key();
287 RegisterComponent(id
, name
, base::Closure());
289 registered_whitelists
.insert(id
);
292 // Register whitelists specified on the command line.
293 const base::CommandLine
* command_line
=
294 base::CommandLine::ForCurrentProcess();
295 std::string command_line_whitelists
= command_line
->GetSwitchValueASCII(
296 switches::kInstallSupervisedUserWhitelists
);
297 std::vector
<std::string
> split_whitelists
;
298 base::SplitString(command_line_whitelists
, ',', &split_whitelists
);
299 for (const std::string
& whitelist
: split_whitelists
) {
302 size_t separator
= whitelist
.find(':');
303 if (separator
!= std::string::npos
) {
304 id
= whitelist
.substr(0, separator
);
305 name
= whitelist
.substr(separator
+ 1);
309 RegisterNewComponent(id
, name
);
311 registered_whitelists
.insert(id
);
314 cus_
->GetSequencedTaskRunner()->PostTask(
315 FROM_HERE
, base::Bind(&RemoveUnregisteredWhitelistsOnTaskRunner
,
316 registered_whitelists
));
319 void SupervisedUserWhitelistInstallerImpl::Subscribe(
320 const WhitelistReadyCallback
& callback
) {
321 return callbacks_
.push_back(callback
);
324 void SupervisedUserWhitelistInstallerImpl::RegisterWhitelist(
325 const std::string
& client_id
,
326 const std::string
& crx_id
,
327 const std::string
& name
) {
328 DictionaryPrefUpdate
update(local_state_
,
329 prefs::kRegisteredSupervisedUserWhitelists
);
330 base::DictionaryValue
* pref_dict
= update
.Get();
331 base::DictionaryValue
* whitelist_dict
= nullptr;
332 bool newly_added
= false;
333 if (!pref_dict
->GetDictionaryWithoutPathExpansion(crx_id
, &whitelist_dict
)) {
334 whitelist_dict
= new base::DictionaryValue
;
335 whitelist_dict
->SetString(kName
, name
);
336 pref_dict
->SetWithoutPathExpansion(crx_id
, whitelist_dict
);
340 base::ListValue
* clients
= nullptr;
341 if (!whitelist_dict
->GetList(kClients
, &clients
)) {
343 clients
= new base::ListValue
;
344 whitelist_dict
->Set(kClients
, clients
);
346 bool success
= clients
->AppendIfNotPresent(new base::StringValue(client_id
));
350 // Sanity-check that the stored name is equal to the name passed in.
351 // In release builds this is a no-op.
352 std::string stored_name
;
353 DCHECK(whitelist_dict
->GetString(kName
, &stored_name
));
354 DCHECK_EQ(stored_name
, name
);
358 RegisterNewComponent(crx_id
, name
);
361 void SupervisedUserWhitelistInstallerImpl::UnregisterWhitelist(
362 const std::string
& client_id
,
363 const std::string
& crx_id
) {
364 DictionaryPrefUpdate
update(local_state_
,
365 prefs::kRegisteredSupervisedUserWhitelists
);
366 bool removed
= UnregisterWhitelistInternal(update
.Get(), client_id
, crx_id
);
370 void SupervisedUserWhitelistInstallerImpl::OnProfileWillBeRemoved(
371 const base::FilePath
& profile_path
) {
372 std::string client_id
= ClientIdForProfilePath(profile_path
);
374 // Go through all registered whitelists and possibly unregister them for this
376 DictionaryPrefUpdate
update(local_state_
,
377 prefs::kRegisteredSupervisedUserWhitelists
);
378 base::DictionaryValue
* pref_dict
= update
.Get();
379 for (base::DictionaryValue::Iterator
it(*pref_dict
); !it
.IsAtEnd();
381 UnregisterWhitelistInternal(pref_dict
, client_id
, it
.key());
388 scoped_ptr
<SupervisedUserWhitelistInstaller
>
389 SupervisedUserWhitelistInstaller::Create(ComponentUpdateService
* cus
,
390 ProfileInfoCache
* profile_info_cache
,
391 PrefService
* local_state
) {
392 return make_scoped_ptr(new SupervisedUserWhitelistInstallerImpl(
393 cus
, profile_info_cache
, local_state
));
397 void SupervisedUserWhitelistInstaller::RegisterPrefs(
398 PrefRegistrySimple
* registry
) {
399 registry
->RegisterDictionaryPref(prefs::kRegisteredSupervisedUserWhitelists
);
403 std::string
SupervisedUserWhitelistInstaller::ClientIdForProfilePath(
404 const base::FilePath
& profile_path
) {
405 // See ProfileInfoCache::CacheKeyFromProfilePath().
406 return profile_path
.BaseName().MaybeAsASCII();
410 std::vector
<uint8_t> SupervisedUserWhitelistInstaller::GetHashFromCrxId(
411 const std::string
& crx_id
) {
412 DCHECK(crx_file::id_util::IdIsValid(crx_id
));
414 std::vector
<uint8_t> hash
;
416 for (size_t i
= 0; i
< crx_id
.size(); ++i
) {
417 // Uppercase characters in IDs are technically legal.
418 int val
= base::ToLowerASCII(crx_id
[i
]) - 'a';
424 hash
.push_back(16 * byte
+ val
);
432 void SupervisedUserWhitelistInstaller::TriggerComponentUpdate(
433 OnDemandUpdater
* updater
,
434 const std::string
& crx_id
) {
435 ComponentUpdateService::Status status
= updater
->OnDemandUpdate(crx_id
);
436 DCHECK_EQ(ComponentUpdateService::kOk
, status
);
439 } // namespace component_updater