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.
4 
5 #include "chrome/browser/supervised_user/supervised_user_allowlist_service.h"
6 
7 #include <stddef.h>
8 
9 #include <string>
10 #include <utility>
11 
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/metrics/user_metrics.h"
17 #include "base/metrics/user_metrics_action.h"
18 #include "base/strings/string_split.h"
19 #include "base/values.h"
20 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
21 #include "chrome/browser/supervised_user/supervised_user_site_list.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/pref_names.h"
24 #include "components/pref_registry/pref_registry_syncable.h"
25 #include "components/prefs/pref_service.h"
26 #include "components/prefs/scoped_user_pref_update.h"
27 #include "components/sync/model/sync_change.h"
28 #include "components/sync/model/sync_change_processor.h"
29 #include "components/sync/model/sync_data.h"
30 #include "components/sync/model/sync_error.h"
31 #include "components/sync/model/sync_error_factory.h"
32 #include "components/sync/protocol/sync.pb.h"
33 
34 const char kName[] = "name";
35 
SupervisedUserAllowlistService(PrefService * prefs,component_updater::SupervisedUserWhitelistInstaller * installer,const std::string & client_id)36 SupervisedUserAllowlistService::SupervisedUserAllowlistService(
37     PrefService* prefs,
38     component_updater::SupervisedUserWhitelistInstaller* installer,
39     const std::string& client_id)
40     : prefs_(prefs), installer_(installer), client_id_(client_id) {
41   DCHECK(prefs);
42 }
43 
~SupervisedUserAllowlistService()44 SupervisedUserAllowlistService::~SupervisedUserAllowlistService() {}
45 
46 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)47 void SupervisedUserAllowlistService::RegisterProfilePrefs(
48     user_prefs::PrefRegistrySyncable* registry) {
49   registry->RegisterDictionaryPref(prefs::kSupervisedUserAllowlists);
50 }
51 
Init()52 void SupervisedUserAllowlistService::Init() {
53   const base::DictionaryValue* allowlists =
54       prefs_->GetDictionary(prefs::kSupervisedUserAllowlists);
55   for (base::DictionaryValue::Iterator it(*allowlists); !it.IsAtEnd();
56        it.Advance()) {
57     registered_allowlists_.insert(it.key());
58   }
59   UMA_HISTOGRAM_COUNTS_100("ManagedUsers.Whitelist.Count", allowlists->size());
60 
61   // The installer can be null in some unit tests.
62   if (!installer_)
63     return;
64 
65   installer_->Subscribe(
66       base::BindRepeating(&SupervisedUserAllowlistService::OnAllowlistReady,
67                           weak_ptr_factory_.GetWeakPtr()));
68 
69   // Register allowlists specified on the command line.
70   for (const auto& allowlist : GetAllowlistsFromCommandLine())
71     RegisterAllowlist(allowlist.first, allowlist.second, FROM_COMMAND_LINE);
72 }
73 
AddSiteListsChangedCallback(const SiteListsChangedCallback & callback)74 void SupervisedUserAllowlistService::AddSiteListsChangedCallback(
75     const SiteListsChangedCallback& callback) {
76   site_lists_changed_callbacks_.push_back(callback);
77 
78   std::vector<scoped_refptr<SupervisedUserSiteList>> allowlists;
79   GetLoadedAllowlists(&allowlists);
80   callback.Run(allowlists);
81 }
82 
83 // static
84 std::map<std::string, std::string>
GetAllowlistsFromCommandLine()85 SupervisedUserAllowlistService::GetAllowlistsFromCommandLine() {
86   std::map<std::string, std::string> allowlists;
87   const base::CommandLine* command_line =
88       base::CommandLine::ForCurrentProcess();
89   std::string command_line_allowlists = command_line->GetSwitchValueASCII(
90       switches::kInstallSupervisedUserAllowlists);
91   std::vector<base::StringPiece> string_pieces =
92       base::SplitStringPiece(command_line_allowlists, ",",
93                              base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
94   for (const base::StringPiece& allowlist : string_pieces) {
95     std::string id;
96     std::string name;
97     size_t separator = allowlist.find(':');
98     if (separator != base::StringPiece::npos) {
99       id = std::string(allowlist.substr(0, separator));
100       name = std::string(allowlist.substr(separator + 1));
101     } else {
102       id = std::string(allowlist);
103     }
104 
105     const bool result = allowlists.insert(std::make_pair(id, name)).second;
106     DCHECK(result);
107   }
108 
109   return allowlists;
110 }
111 
LoadAllowlistForTesting(const std::string & id,const base::string16 & title,const base::FilePath & path)112 void SupervisedUserAllowlistService::LoadAllowlistForTesting(
113     const std::string& id,
114     const base::string16& title,
115     const base::FilePath& path) {
116   bool result = registered_allowlists_.insert(id).second;
117   DCHECK(result);
118   OnAllowlistReady(id, title, base::FilePath(), path);
119 }
120 
UnloadAllowlist(const std::string & id)121 void SupervisedUserAllowlistService::UnloadAllowlist(const std::string& id) {
122   bool result = registered_allowlists_.erase(id) > 0u;
123   DCHECK(result);
124   loaded_allowlists_.erase(id);
125   NotifyAllowlistsChanged();
126 }
127 
128 // static
CreateAllowlistSyncData(const std::string & id,const std::string & name)129 syncer::SyncData SupervisedUserAllowlistService::CreateAllowlistSyncData(
130     const std::string& id,
131     const std::string& name) {
132   sync_pb::EntitySpecifics specifics;
133   sync_pb::ManagedUserWhitelistSpecifics* allowlist =
134       specifics.mutable_managed_user_whitelist();
135   allowlist->set_id(id);
136   allowlist->set_name(name);
137 
138   return syncer::SyncData::CreateLocalData(id, name, specifics);
139 }
140 
WaitUntilReadyToSync(base::OnceClosure done)141 void SupervisedUserAllowlistService::WaitUntilReadyToSync(
142     base::OnceClosure done) {
143   // This service handles sync events at any time.
144   std::move(done).Run();
145 }
146 
147 base::Optional<syncer::ModelError>
MergeDataAndStartSyncing(syncer::ModelType type,const syncer::SyncDataList & initial_sync_data,std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,std::unique_ptr<syncer::SyncErrorFactory> error_handler)148 SupervisedUserAllowlistService::MergeDataAndStartSyncing(
149     syncer::ModelType type,
150     const syncer::SyncDataList& initial_sync_data,
151     std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
152     std::unique_ptr<syncer::SyncErrorFactory> error_handler) {
153   DCHECK_EQ(syncer::SUPERVISED_USER_ALLOWLISTS, type);
154 
155   syncer::SyncChangeList change_list;
156 
157   DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUserAllowlists);
158   base::DictionaryValue* pref_dict = update.Get();
159   std::set<std::string> seen_ids;
160 
161   for (const syncer::SyncData& sync_data : initial_sync_data) {
162     DCHECK_EQ(syncer::SUPERVISED_USER_ALLOWLISTS, sync_data.GetDataType());
163     const sync_pb::ManagedUserWhitelistSpecifics& allowlist =
164         sync_data.GetSpecifics().managed_user_whitelist();
165     std::string id = allowlist.id();
166     std::string name = allowlist.name();
167     seen_ids.insert(id);
168     base::DictionaryValue* dict = nullptr;
169     if (pref_dict->GetDictionary(id, &dict)) {
170       std::string old_name;
171       bool result = dict->GetString(kName, &old_name);
172       DCHECK(result);
173       if (name != old_name) {
174         SetAllowlistProperties(dict, allowlist);
175       }
176     } else {
177       AddNewAllowlist(pref_dict, allowlist);
178     }
179   }
180 
181   std::set<std::string> ids_to_remove;
182   for (base::DictionaryValue::Iterator it(*pref_dict); !it.IsAtEnd();
183        it.Advance()) {
184     if (seen_ids.find(it.key()) == seen_ids.end())
185       ids_to_remove.insert(it.key());
186   }
187 
188   for (const std::string& id : ids_to_remove)
189     RemoveAllowlist(pref_dict, id);
190 
191   // Notify if allowlists have been uninstalled. We will notify about newly
192   // added allowlists later, when they are actually available
193   // (in OnAllowlistLoaded).
194   if (!ids_to_remove.empty())
195     NotifyAllowlistsChanged();
196 
197   // The function does not generate any errors, so it can always return
198   // base::nullopt.
199   return base::nullopt;
200 }
201 
StopSyncing(syncer::ModelType type)202 void SupervisedUserAllowlistService::StopSyncing(syncer::ModelType type) {
203   DCHECK_EQ(syncer::SUPERVISED_USER_ALLOWLISTS, type);
204 }
205 
GetAllSyncDataForTesting(syncer::ModelType type) const206 syncer::SyncDataList SupervisedUserAllowlistService::GetAllSyncDataForTesting(
207     syncer::ModelType type) const {
208   syncer::SyncDataList sync_data;
209   const base::DictionaryValue* allowlists =
210       prefs_->GetDictionary(prefs::kSupervisedUserAllowlists);
211   for (base::DictionaryValue::Iterator it(*allowlists); !it.IsAtEnd();
212        it.Advance()) {
213     const std::string& id = it.key();
214     const base::DictionaryValue* dict = nullptr;
215     it.value().GetAsDictionary(&dict);
216     std::string name;
217     bool result = dict->GetString(kName, &name);
218     DCHECK(result);
219     sync_pb::EntitySpecifics specifics;
220     sync_pb::ManagedUserWhitelistSpecifics* allowlist =
221         specifics.mutable_managed_user_whitelist();
222     allowlist->set_id(id);
223     allowlist->set_name(name);
224     sync_data.push_back(syncer::SyncData::CreateLocalData(id, name, specifics));
225   }
226   return sync_data;
227 }
228 
229 base::Optional<syncer::ModelError>
ProcessSyncChanges(const base::Location & from_here,const syncer::SyncChangeList & change_list)230 SupervisedUserAllowlistService::ProcessSyncChanges(
231     const base::Location& from_here,
232     const syncer::SyncChangeList& change_list) {
233   bool allowlists_removed = false;
234   DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUserAllowlists);
235   base::DictionaryValue* pref_dict = update.Get();
236   for (const syncer::SyncChange& sync_change : change_list) {
237     syncer::SyncData data = sync_change.sync_data();
238     DCHECK_EQ(syncer::SUPERVISED_USER_ALLOWLISTS, data.GetDataType());
239     const sync_pb::ManagedUserWhitelistSpecifics& allowlist =
240         data.GetSpecifics().managed_user_whitelist();
241     std::string id = allowlist.id();
242     switch (sync_change.change_type()) {
243       case syncer::SyncChange::ACTION_ADD: {
244         DCHECK(!pref_dict->HasKey(id)) << id;
245         AddNewAllowlist(pref_dict, allowlist);
246         break;
247       }
248       case syncer::SyncChange::ACTION_UPDATE: {
249         base::DictionaryValue* dict = nullptr;
250         pref_dict->GetDictionaryWithoutPathExpansion(id, &dict);
251         SetAllowlistProperties(dict, allowlist);
252         break;
253       }
254       case syncer::SyncChange::ACTION_DELETE: {
255         DCHECK(pref_dict->HasKey(id)) << id;
256         RemoveAllowlist(pref_dict, id);
257         allowlists_removed = true;
258         break;
259       }
260       case syncer::SyncChange::ACTION_INVALID: {
261         NOTREACHED();
262         break;
263       }
264     }
265   }
266 
267   if (allowlists_removed)
268     NotifyAllowlistsChanged();
269 
270   return base::nullopt;
271 }
272 
AddNewAllowlist(base::DictionaryValue * pref_dict,const sync_pb::ManagedUserWhitelistSpecifics & allowlist)273 void SupervisedUserAllowlistService::AddNewAllowlist(
274     base::DictionaryValue* pref_dict,
275     const sync_pb::ManagedUserWhitelistSpecifics& allowlist) {
276   base::RecordAction(base::UserMetricsAction("ManagedUsers_Whitelist_Added"));
277 
278   RegisterAllowlist(allowlist.id(), allowlist.name(), FROM_SYNC);
279   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
280   SetAllowlistProperties(dict.get(), allowlist);
281   pref_dict->SetWithoutPathExpansion(allowlist.id(), std::move(dict));
282 }
283 
SetAllowlistProperties(base::DictionaryValue * dict,const sync_pb::ManagedUserWhitelistSpecifics & allowlist)284 void SupervisedUserAllowlistService::SetAllowlistProperties(
285     base::DictionaryValue* dict,
286     const sync_pb::ManagedUserWhitelistSpecifics& allowlist) {
287   dict->SetString(kName, allowlist.name());
288 }
289 
RemoveAllowlist(base::DictionaryValue * pref_dict,const std::string & id)290 void SupervisedUserAllowlistService::RemoveAllowlist(
291     base::DictionaryValue* pref_dict,
292     const std::string& id) {
293   base::RecordAction(base::UserMetricsAction("ManagedUsers_Whitelist_Removed"));
294 
295   pref_dict->RemoveKey(id);
296   installer_->UnregisterWhitelist(client_id_, id);
297   UnloadAllowlist(id);
298 }
299 
RegisterAllowlist(const std::string & id,const std::string & name,AllowlistSource source)300 void SupervisedUserAllowlistService::RegisterAllowlist(const std::string& id,
301                                                        const std::string& name,
302                                                        AllowlistSource source) {
303   bool result = registered_allowlists_.insert(id).second;
304   DCHECK(result);
305 
306   // Using an empty client ID for allowlists installed from the command line
307   // causes the installer to not persist the installation, so the allowlist will
308   // be removed the next time the browser is started without the command line
309   // flag.
310   installer_->RegisterWhitelist(
311       source == FROM_COMMAND_LINE ? std::string() : client_id_, id, name);
312 }
313 
GetLoadedAllowlists(std::vector<scoped_refptr<SupervisedUserSiteList>> * allowlists)314 void SupervisedUserAllowlistService::GetLoadedAllowlists(
315     std::vector<scoped_refptr<SupervisedUserSiteList>>* allowlists) {
316   for (const auto& allowlist : loaded_allowlists_)
317     allowlists->push_back(allowlist.second);
318 }
319 
NotifyAllowlistsChanged()320 void SupervisedUserAllowlistService::NotifyAllowlistsChanged() {
321   std::vector<scoped_refptr<SupervisedUserSiteList>> allowlists;
322   GetLoadedAllowlists(&allowlists);
323 
324   for (const auto& callback : site_lists_changed_callbacks_)
325     callback.Run(allowlists);
326 }
327 
OnAllowlistReady(const std::string & id,const base::string16 & title,const base::FilePath & large_icon_path,const base::FilePath & allowlist_path)328 void SupervisedUserAllowlistService::OnAllowlistReady(
329     const std::string& id,
330     const base::string16& title,
331     const base::FilePath& large_icon_path,
332     const base::FilePath& allowlist_path) {
333   // If we did not register the allowlist or it has been unregistered in the
334   // mean time, ignore it.
335   if (registered_allowlists_.count(id) == 0u)
336     return;
337 
338   SupervisedUserSiteList::Load(
339       id, title, large_icon_path, allowlist_path,
340       base::Bind(&SupervisedUserAllowlistService::OnAllowlistLoaded,
341                  weak_ptr_factory_.GetWeakPtr(), id));
342 }
343 
OnAllowlistLoaded(const std::string & id,const scoped_refptr<SupervisedUserSiteList> & allowlist)344 void SupervisedUserAllowlistService::OnAllowlistLoaded(
345     const std::string& id,
346     const scoped_refptr<SupervisedUserSiteList>& allowlist) {
347   if (!allowlist) {
348     LOG(WARNING) << "Couldn't load allowlist " << id;
349     return;
350   }
351   // If the allowlist has been unregistered in the mean time, ignore it.
352   if (registered_allowlists_.count(id) == 0u)
353     return;
354 
355   loaded_allowlists_[id] = allowlist;
356   NotifyAllowlistsChanged();
357 }
358