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 "chrome/browser/safe_json_parser.h"
14 #include "content/public/browser/browser_thread.h"
17 const int kSitelistFormatVersion
= 1;
19 const char kHostnameHashesKey
[] = "hostname_hashes";
20 const char kNameKey
[] = "name";
21 const char kSitesKey
[] = "sites";
22 const char kSitelistFormatVersionKey
[] = "version";
23 const char kUrlKey
[] = "url";
24 const char kWhitelistKey
[] = "whitelist";
28 bool g_load_in_process
= false;
30 std::string
ReadFileOnBlockingThread(const base::FilePath
& path
) {
31 SCOPED_UMA_HISTOGRAM_TIMER("ManagedUsers.Whitelist.ReadDuration");
33 base::ReadFileToString(path
, &contents
);
37 void HandleError(const base::FilePath
& path
, const std::string
& error
) {
38 LOG(ERROR
) << "Couldn't load site list " << path
.value() << ": " << error
;
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
) {
46 std::vector
<std::string
>* patterns
= &site
->patterns
;
49 const base::ListValue
* whitelist
= nullptr;
50 if (site_dict
->GetList(kWhitelistKey
, &whitelist
)) {
52 for (const base::Value
* entry
: *whitelist
) {
54 if (!entry
->GetAsString(&pattern
)) {
55 LOG(ERROR
) << "Invalid whitelist entry";
59 patterns
->push_back(pattern
);
63 std::vector
<std::string
>* hashes
= &site
->hostname_hashes
;
64 const base::ListValue
* hash_list
= nullptr;
65 if (site_dict
->GetList(kHostnameHashesKey
, &hash_list
)) {
67 for (const base::Value
* entry
: *hash_list
) {
69 if (!entry
->GetAsString(&hash
)) {
70 LOG(ERROR
) << "Invalid whitelist entry";
74 hashes
->push_back(hash
);
81 // Fall back to using a whitelist based on the URL.
83 if (!site_dict
->GetString(kUrlKey
, &url_str
)) {
84 LOG(ERROR
) << "Whitelist is invalid";
89 if (!url
.is_valid()) {
90 LOG(ERROR
) << "URL " << url_str
<< " is invalid";
94 patterns
->push_back(url
.host());
99 SupervisedUserSiteList::Site::Site(const base::string16
& name
) : name(name
) {
102 SupervisedUserSiteList::Site::~Site() {
105 void SupervisedUserSiteList::Load(const base::FilePath
& path
,
106 const LoadedCallback
& callback
) {
107 base::PostTaskAndReplyWithResult(
108 content::BrowserThread::GetBlockingPool(),
110 base::Bind(&ReadFileOnBlockingThread
, path
),
111 base::Bind(&SupervisedUserSiteList::ParseJson
, path
, callback
));
115 void SupervisedUserSiteList::SetLoadInProcessForTesting(bool in_process
) {
116 g_load_in_process
= in_process
;
119 SupervisedUserSiteList::SupervisedUserSiteList(const base::ListValue
& sites
) {
120 for (const base::Value
* site
: sites
) {
121 const base::DictionaryValue
* entry
= nullptr;
122 if (!site
->GetAsDictionary(&entry
)) {
123 LOG(ERROR
) << "Entry is invalid";
128 entry
->GetString(kNameKey
, &name
);
129 sites_
.push_back(Site(name
));
130 AddWhitelistEntries(entry
, &sites_
.back());
134 SupervisedUserSiteList::~SupervisedUserSiteList() {
138 void SupervisedUserSiteList::ParseJson(
139 const base::FilePath
& path
,
140 const SupervisedUserSiteList::LoadedCallback
& callback
,
141 const std::string
& json
) {
142 if (g_load_in_process
) {
143 JSONFileValueDeserializer
deserializer(path
);
145 scoped_ptr
<base::Value
> value(deserializer
.Deserialize(nullptr, &error
));
147 HandleError(path
, error
);
151 OnJsonParseSucceeded(path
, base::TimeTicks(), callback
, value
.Pass());
155 // TODO(bauerb): Use batch mode to load multiple whitelists?
156 scoped_refptr
<SafeJsonParser
> parser(new SafeJsonParser(
158 base::Bind(&SupervisedUserSiteList::OnJsonParseSucceeded
, path
,
159 base::TimeTicks::Now(), callback
),
160 base::Bind(&HandleError
, path
)));
165 void SupervisedUserSiteList::OnJsonParseSucceeded(
166 const base::FilePath
& path
,
167 base::TimeTicks start_time
,
168 const SupervisedUserSiteList::LoadedCallback
& callback
,
169 scoped_ptr
<base::Value
> value
) {
170 if (!start_time
.is_null()) {
171 UMA_HISTOGRAM_TIMES("ManagedUsers.Whitelist.JsonParseDuration",
172 base::TimeTicks::Now() - start_time
);
175 base::DictionaryValue
* dict
= nullptr;
176 if (!value
->GetAsDictionary(&dict
)) {
177 LOG(ERROR
) << "Site list " << path
.value() << " is invalid";
182 if (!dict
->GetInteger(kSitelistFormatVersionKey
, &version
)) {
183 LOG(ERROR
) << "Site list " << path
.value() << " has invalid version";
187 if (version
> kSitelistFormatVersion
) {
188 LOG(ERROR
) << "Site list " << path
.value() << " has a too new format";
192 base::ListValue
* sites
= nullptr;
193 if (!dict
->GetList(kSitesKey
, &sites
)) {
194 LOG(ERROR
) << "Site list " << path
.value()
195 << " does not contain any sites";
199 callback
.Run(make_scoped_refptr(new SupervisedUserSiteList(*sites
)));