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/supervised_user/supervised_user_site_list.h"
7 #include "base/files/file_util.h"
8 #include "base/json/json_file_value_serializer.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/task_runner_util.h"
12 #include "base/values.h"
13 #include "content/public/browser/browser_thread.h"
16 const int kSitelistFormatVersion
= 1;
18 const char kHostnameHashesKey
[] = "hostname_hashes";
19 const char kNameKey
[] = "name";
20 const char kSitesKey
[] = "sites";
21 const char kSitelistFormatVersionKey
[] = "version";
22 const char kUrlKey
[] = "url";
23 const char kWhitelistKey
[] = "whitelist";
27 scoped_ptr
<base::Value
> ReadFileOnBlockingThread(const base::FilePath
& path
) {
28 SCOPED_UMA_HISTOGRAM_TIMER("ManagedUsers.Whitelist.ReadDuration");
29 JSONFileValueDeserializer
deserializer(path
);
31 std::string error_msg
;
32 scoped_ptr
<base::Value
> value(
33 deserializer
.Deserialize(&error_code
, &error_msg
));
35 LOG(ERROR
) << "Couldn't load site list " << path
.value() << ": "
41 // Takes a DictionaryValue entry from the JSON file and fills the whitelist
42 // (via URL patterns or hostname hashes) and the URL in the corresponding Site
44 void AddWhitelistEntries(const base::DictionaryValue
* site_dict
,
45 SupervisedUserSiteList::Site
* site
) {
48 const base::ListValue
* whitelist
= nullptr;
49 if (site_dict
->GetList(kWhitelistKey
, &whitelist
)) {
51 for (const base::Value
* entry
: *whitelist
) {
53 if (!entry
->GetAsString(&pattern
)) {
54 LOG(ERROR
) << "Invalid whitelist entry";
58 site
->patterns
.push_back(pattern
);
62 const base::ListValue
* hash_list
= nullptr;
63 if (site_dict
->GetList(kHostnameHashesKey
, &hash_list
)) {
65 for (const base::Value
* entry
: *hash_list
) {
67 if (!entry
->GetAsString(&hash
)) {
68 LOG(ERROR
) << "Invalid hostname_hashes entry";
71 // TODO(treib): Check that |hash| has exactly 40 (2*base::kSHA1Length)
72 // characters from [0-9a-fA-F]. Or just store the raw bytes (from
73 // base::HexStringToBytes).
75 site
->hostname_hashes
.push_back(hash
);
82 // Fall back to using a whitelist based on the URL.
84 if (!site_dict
->GetString(kUrlKey
, &url_str
)) {
85 LOG(ERROR
) << "Whitelist is invalid";
90 if (!url
.is_valid()) {
91 LOG(ERROR
) << "URL " << url_str
<< " is invalid";
95 site
->patterns
.push_back(url
.host());
100 SupervisedUserSiteList::Site::Site(const base::string16
& name
) : name(name
) {
103 SupervisedUserSiteList::Site::~Site() {
106 void SupervisedUserSiteList::Load(const base::FilePath
& path
,
107 const LoadedCallback
& callback
) {
108 base::PostTaskAndReplyWithResult(
109 content::BrowserThread::GetBlockingPool()
110 ->GetTaskRunnerWithShutdownBehavior(
111 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
)
113 FROM_HERE
, base::Bind(&ReadFileOnBlockingThread
, path
),
114 base::Bind(&SupervisedUserSiteList::OnJsonLoaded
, path
,
115 base::TimeTicks::Now(), callback
));
118 SupervisedUserSiteList::SupervisedUserSiteList(const base::ListValue
& sites
) {
119 for (const base::Value
* site
: sites
) {
120 const base::DictionaryValue
* entry
= nullptr;
121 if (!site
->GetAsDictionary(&entry
)) {
122 LOG(ERROR
) << "Entry is invalid";
127 entry
->GetString(kNameKey
, &name
);
128 sites_
.push_back(Site(name
));
129 AddWhitelistEntries(entry
, &sites_
.back());
133 SupervisedUserSiteList::~SupervisedUserSiteList() {
137 void SupervisedUserSiteList::OnJsonLoaded(
138 const base::FilePath
& path
,
139 base::TimeTicks start_time
,
140 const SupervisedUserSiteList::LoadedCallback
& callback
,
141 scoped_ptr
<base::Value
> value
) {
145 if (!start_time
.is_null()) {
146 UMA_HISTOGRAM_TIMES("ManagedUsers.Whitelist.JsonParseDuration",
147 base::TimeTicks::Now() - start_time
);
150 base::DictionaryValue
* dict
= nullptr;
151 if (!value
->GetAsDictionary(&dict
)) {
152 LOG(ERROR
) << "Site list " << path
.value() << " is invalid";
157 if (!dict
->GetInteger(kSitelistFormatVersionKey
, &version
)) {
158 LOG(ERROR
) << "Site list " << path
.value() << " has invalid version";
162 if (version
> kSitelistFormatVersion
) {
163 LOG(ERROR
) << "Site list " << path
.value() << " has a too new format";
167 base::ListValue
* sites
= nullptr;
168 if (!dict
->GetList(kSitesKey
, &sites
)) {
169 LOG(ERROR
) << "Site list " << path
.value() << " does not contain any sites";
173 callback
.Run(make_scoped_refptr(new SupervisedUserSiteList(*sites
)));