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_settings_service.h"
6
7 #include <stddef.h>
8
9 #include <set>
10 #include <utility>
11
12 #include "base/callback.h"
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/metrics/user_metrics.h"
17 #include "base/strings/string_util.h"
18 #include "base/values.h"
19 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
20 #include "chrome/common/chrome_constants.h"
21 #include "components/prefs/json_pref_store.h"
22 #include "components/prefs/pref_filter.h"
23 #include "components/sync/model/sync_change.h"
24 #include "components/sync/model/sync_change_processor.h"
25 #include "components/sync/model/sync_error_factory.h"
26 #include "components/sync/protocol/sync.pb.h"
27 #include "content/public/browser/browser_thread.h"
28
29 using base::DictionaryValue;
30 using base::JSONReader;
31 using base::UserMetricsAction;
32 using base::Value;
33 using content::BrowserThread;
34 using syncer::ModelError;
35 using syncer::ModelType;
36 using syncer::SUPERVISED_USER_SETTINGS;
37 using syncer::SyncChange;
38 using syncer::SyncChangeList;
39 using syncer::SyncChangeProcessor;
40 using syncer::SyncData;
41 using syncer::SyncDataList;
42 using syncer::SyncErrorFactory;
43
44 const char kAtomicSettings[] = "atomic_settings";
45 const char kSupervisedUserInternalItemPrefix[] = "X-";
46 const char kQueuedItems[] = "queued_items";
47 const char kSplitSettingKeySeparator = ':';
48 const char kSplitSettings[] = "split_settings";
49
50 namespace {
51
SettingShouldApplyToPrefs(const std::string & name)52 bool SettingShouldApplyToPrefs(const std::string& name) {
53 return !base::StartsWith(name, kSupervisedUserInternalItemPrefix,
54 base::CompareCase::INSENSITIVE_ASCII);
55 }
56
57 } // namespace
58
SupervisedUserSettingsService()59 SupervisedUserSettingsService::SupervisedUserSettingsService()
60 : active_(false),
61 initialization_failed_(false),
62 local_settings_(new base::DictionaryValue) {}
63
~SupervisedUserSettingsService()64 SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
65
Init(base::FilePath profile_path,base::SequencedTaskRunner * sequenced_task_runner,bool load_synchronously)66 void SupervisedUserSettingsService::Init(
67 base::FilePath profile_path,
68 base::SequencedTaskRunner* sequenced_task_runner,
69 bool load_synchronously) {
70 base::FilePath path =
71 profile_path.Append(chrome::kSupervisedUserSettingsFilename);
72 PersistentPrefStore* store = new JsonPrefStore(
73 path, std::unique_ptr<PrefFilter>(), sequenced_task_runner);
74 Init(store);
75 if (load_synchronously) {
76 store_->ReadPrefs();
77 DCHECK(IsReady());
78 } else {
79 store_->ReadPrefsAsync(nullptr);
80 }
81 }
82
Init(scoped_refptr<PersistentPrefStore> store)83 void SupervisedUserSettingsService::Init(
84 scoped_refptr<PersistentPrefStore> store) {
85 DCHECK(!store_.get());
86 store_ = store;
87 store_->AddObserver(this);
88 }
89
90 std::unique_ptr<
91 SupervisedUserSettingsService::SettingsCallbackList::Subscription>
SubscribeForSettingsChange(const SettingsCallback & callback)92 SupervisedUserSettingsService::SubscribeForSettingsChange(
93 const SettingsCallback& callback) {
94 if (IsReady()) {
95 std::unique_ptr<base::DictionaryValue> settings = GetSettings();
96 callback.Run(settings.get());
97 }
98
99 return settings_callback_list_.Add(callback);
100 }
101
102 std::unique_ptr<
103 SupervisedUserSettingsService::ShutdownCallbackList::Subscription>
SubscribeForShutdown(const ShutdownCallback & callback)104 SupervisedUserSettingsService::SubscribeForShutdown(
105 const ShutdownCallback& callback) {
106 return shutdown_callback_list_.Add(callback);
107 }
108
SetActive(bool active)109 void SupervisedUserSettingsService::SetActive(bool active) {
110 active_ = active;
111 InformSubscribers();
112 }
113
IsReady() const114 bool SupervisedUserSettingsService::IsReady() const {
115 // Initialization cannot be complete but have failed at the same time.
116 DCHECK(!(store_->IsInitializationComplete() && initialization_failed_));
117 return initialization_failed_ || store_->IsInitializationComplete();
118 }
119
Clear()120 void SupervisedUserSettingsService::Clear() {
121 store_->RemoveValue(kAtomicSettings,
122 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
123 store_->RemoveValue(kSplitSettings,
124 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
125 }
126
127 // static
MakeSplitSettingKey(const std::string & prefix,const std::string & key)128 std::string SupervisedUserSettingsService::MakeSplitSettingKey(
129 const std::string& prefix,
130 const std::string& key) {
131 return prefix + kSplitSettingKeySeparator + key;
132 }
133
UploadItem(const std::string & key,std::unique_ptr<base::Value> value)134 void SupervisedUserSettingsService::UploadItem(
135 const std::string& key,
136 std::unique_ptr<base::Value> value) {
137 DCHECK(!SettingShouldApplyToPrefs(key));
138 PushItemToSync(key, std::move(value));
139 }
140
PushItemToSync(const std::string & key,std::unique_ptr<base::Value> value)141 void SupervisedUserSettingsService::PushItemToSync(
142 const std::string& key,
143 std::unique_ptr<base::Value> value) {
144 std::string key_suffix = key;
145 base::DictionaryValue* dict = nullptr;
146 if (sync_processor_) {
147 base::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
148 dict = GetDictionaryAndSplitKey(&key_suffix);
149 DCHECK(GetQueuedItems()->empty());
150 SyncChangeList change_list;
151 SyncData data = CreateSyncDataForSetting(key, *value);
152 SyncChange::SyncChangeType change_type =
153 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
154 : SyncChange::ACTION_ADD;
155 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
156 base::Optional<ModelError> error =
157 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
158 DCHECK(!error.has_value()) << error.value().ToString();
159 } else {
160 // Queue the item up to be uploaded when we start syncing
161 // (in MergeDataAndStartSyncing()).
162 base::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
163 dict = GetQueuedItems();
164 }
165 dict->SetWithoutPathExpansion(key_suffix, std::move(value));
166 }
167
SetLocalSetting(const std::string & key,std::unique_ptr<base::Value> value)168 void SupervisedUserSettingsService::SetLocalSetting(
169 const std::string& key,
170 std::unique_ptr<base::Value> value) {
171 if (value)
172 local_settings_->SetWithoutPathExpansion(key, std::move(value));
173 else
174 local_settings_->RemoveKey(key);
175
176 InformSubscribers();
177 }
178
179 // static
CreateSyncDataForSetting(const std::string & name,const base::Value & value)180 SyncData SupervisedUserSettingsService::CreateSyncDataForSetting(
181 const std::string& name,
182 const base::Value& value) {
183 std::string json_value;
184 base::JSONWriter::Write(value, &json_value);
185 ::sync_pb::EntitySpecifics specifics;
186 specifics.mutable_managed_user_setting()->set_name(name);
187 specifics.mutable_managed_user_setting()->set_value(json_value);
188 return SyncData::CreateLocalData(name, name, specifics);
189 }
190
Shutdown()191 void SupervisedUserSettingsService::Shutdown() {
192 store_->RemoveObserver(this);
193 shutdown_callback_list_.Notify();
194 }
195
WaitUntilReadyToSync(base::OnceClosure done)196 void SupervisedUserSettingsService::WaitUntilReadyToSync(
197 base::OnceClosure done) {
198 DCHECK(!wait_until_ready_to_sync_cb_);
199 if (IsReady()) {
200 std::move(done).Run();
201 } else {
202 // Wait until OnInitializationCompleted().
203 wait_until_ready_to_sync_cb_ = std::move(done);
204 }
205 }
206
207 base::Optional<syncer::ModelError>
MergeDataAndStartSyncing(ModelType type,const SyncDataList & initial_sync_data,std::unique_ptr<SyncChangeProcessor> sync_processor,std::unique_ptr<SyncErrorFactory> error_handler)208 SupervisedUserSettingsService::MergeDataAndStartSyncing(
209 ModelType type,
210 const SyncDataList& initial_sync_data,
211 std::unique_ptr<SyncChangeProcessor> sync_processor,
212 std::unique_ptr<SyncErrorFactory> error_handler) {
213 DCHECK_EQ(SUPERVISED_USER_SETTINGS, type);
214 sync_processor_ = std::move(sync_processor);
215 error_handler_ = std::move(error_handler);
216
217 std::set<std::string> seen_keys;
218 for (base::DictionaryValue::Iterator it(*GetAtomicSettings()); !it.IsAtEnd();
219 it.Advance()) {
220 seen_keys.insert(it.key());
221 }
222 // Getting number of split setting items.
223 for (base::DictionaryValue::Iterator it(*GetSplitSettings()); !it.IsAtEnd();
224 it.Advance()) {
225 const base::DictionaryValue* dict = nullptr;
226 it.value().GetAsDictionary(&dict);
227 for (base::DictionaryValue::Iterator jt(*dict); !jt.IsAtEnd();
228 jt.Advance()) {
229 seen_keys.insert(MakeSplitSettingKey(it.key(), jt.key()));
230 }
231 }
232
233 // Getting number of queued items.
234 base::DictionaryValue* queued_items = GetQueuedItems();
235
236 // Clear all atomic and split settings, then recreate them from Sync data.
237 Clear();
238 std::set<std::string> added_sync_keys;
239 for (const SyncData& sync_data : initial_sync_data) {
240 DCHECK_EQ(SUPERVISED_USER_SETTINGS, sync_data.GetDataType());
241 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
242 sync_data.GetSpecifics().managed_user_setting();
243 std::unique_ptr<base::Value> value =
244 JSONReader::ReadDeprecated(supervised_user_setting.value());
245 // Wrongly formatted input will cause null values.
246 // SetWithoutPathExpansion below requires non-null values.
247 if (!value) {
248 DLOG(ERROR) << "Invalid managed user setting value: "
249 << supervised_user_setting.value()
250 << ". Values must be JSON values.";
251 continue;
252 }
253 std::string name_suffix = supervised_user_setting.name();
254 std::string name_key = name_suffix;
255 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&name_suffix);
256 dict->SetWithoutPathExpansion(name_suffix, std::move(value));
257 if (seen_keys.find(name_key) == seen_keys.end()) {
258 added_sync_keys.insert(name_key);
259 }
260 }
261
262 store_->ReportValueChanged(kAtomicSettings,
263 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
264 store_->ReportValueChanged(kSplitSettings,
265 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
266 InformSubscribers();
267
268 // Upload all the queued up items (either with an ADD or an UPDATE action,
269 // depending on whether they already exist) and move them to split settings.
270 SyncChangeList change_list;
271 for (base::DictionaryValue::Iterator it(*queued_items); !it.IsAtEnd();
272 it.Advance()) {
273 std::string key_suffix = it.key();
274 std::string name_key = key_suffix;
275 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key_suffix);
276 SyncData data = CreateSyncDataForSetting(it.key(), it.value());
277 SyncChange::SyncChangeType change_type =
278 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
279 : SyncChange::ACTION_ADD;
280 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
281 dict->SetKey(key_suffix, it.value().Clone());
282 }
283 queued_items->Clear();
284
285 // Process all the accumulated changes from the queued items.
286 if (!change_list.empty()) {
287 store_->ReportValueChanged(kQueuedItems,
288 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
289 return sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
290 }
291
292 return base::nullopt;
293 }
294
StopSyncing(ModelType type)295 void SupervisedUserSettingsService::StopSyncing(ModelType type) {
296 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
297 sync_processor_.reset();
298 error_handler_.reset();
299 }
300
GetAllSyncDataForTesting(ModelType type) const301 SyncDataList SupervisedUserSettingsService::GetAllSyncDataForTesting(
302 ModelType type) const {
303 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
304 SyncDataList data;
305 for (base::DictionaryValue::Iterator it(*GetAtomicSettings()); !it.IsAtEnd();
306 it.Advance()) {
307 data.push_back(CreateSyncDataForSetting(it.key(), it.value()));
308 }
309 for (base::DictionaryValue::Iterator it(*GetSplitSettings()); !it.IsAtEnd();
310 it.Advance()) {
311 const base::DictionaryValue* dict = nullptr;
312 it.value().GetAsDictionary(&dict);
313 for (base::DictionaryValue::Iterator jt(*dict);
314 !jt.IsAtEnd(); jt.Advance()) {
315 data.push_back(CreateSyncDataForSetting(
316 MakeSplitSettingKey(it.key(), jt.key()), jt.value()));
317 }
318 }
319 DCHECK_EQ(0u, GetQueuedItems()->size());
320 return data;
321 }
322
323 base::Optional<syncer::ModelError>
ProcessSyncChanges(const base::Location & from_here,const SyncChangeList & change_list)324 SupervisedUserSettingsService::ProcessSyncChanges(
325 const base::Location& from_here,
326 const SyncChangeList& change_list) {
327 for (const SyncChange& sync_change : change_list) {
328 SyncData data = sync_change.sync_data();
329 DCHECK_EQ(SUPERVISED_USER_SETTINGS, data.GetDataType());
330 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
331 data.GetSpecifics().managed_user_setting();
332 std::string key = supervised_user_setting.name();
333 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key);
334 SyncChange::SyncChangeType change_type = sync_change.change_type();
335 switch (change_type) {
336 case SyncChange::ACTION_ADD:
337 case SyncChange::ACTION_UPDATE: {
338 std::unique_ptr<base::Value> value =
339 JSONReader::ReadDeprecated(supervised_user_setting.value());
340 if (dict->HasKey(key)) {
341 DLOG_IF(WARNING, change_type == SyncChange::ACTION_ADD)
342 << "Value for key " << key << " already exists";
343 } else {
344 DLOG_IF(WARNING, change_type == SyncChange::ACTION_UPDATE)
345 << "Value for key " << key << " doesn't exist yet";
346 }
347 dict->SetWithoutPathExpansion(key, std::move(value));
348 break;
349 }
350 case SyncChange::ACTION_DELETE: {
351 DLOG_IF(WARNING, !dict->HasKey(key)) << "Trying to delete nonexistent "
352 << "key " << key;
353 dict->RemoveKey(key);
354 break;
355 }
356 case SyncChange::ACTION_INVALID: {
357 NOTREACHED();
358 break;
359 }
360 }
361 }
362 store_->ReportValueChanged(kAtomicSettings,
363 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
364 store_->ReportValueChanged(kSplitSettings,
365 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
366 InformSubscribers();
367
368 return base::nullopt;
369 }
370
OnPrefValueChanged(const std::string & key)371 void SupervisedUserSettingsService::OnPrefValueChanged(const std::string& key) {
372 }
373
OnInitializationCompleted(bool success)374 void SupervisedUserSettingsService::OnInitializationCompleted(bool success) {
375 if (!success) {
376 // If this happens, it means the profile directory was not found. There is
377 // not much we can do, but the whole profile will probably be useless
378 // anyway. Just mark initialization as failed and continue otherwise,
379 // because subscribers might still expect to be called back.
380 initialization_failed_ = true;
381 }
382
383 DCHECK(IsReady());
384
385 if (wait_until_ready_to_sync_cb_)
386 std::move(wait_until_ready_to_sync_cb_).Run();
387
388 InformSubscribers();
389 }
390
391 const base::DictionaryValue*
LocalSettingsForTest() const392 SupervisedUserSettingsService::LocalSettingsForTest() const {
393 return local_settings_.get();
394 }
395
GetDictionaryAndSplitKey(std::string * key) const396 base::DictionaryValue* SupervisedUserSettingsService::GetDictionaryAndSplitKey(
397 std::string* key) const {
398 size_t pos = key->find_first_of(kSplitSettingKeySeparator);
399 if (pos == std::string::npos)
400 return GetAtomicSettings();
401
402 base::DictionaryValue* split_settings = GetSplitSettings();
403 std::string prefix = key->substr(0, pos);
404 base::DictionaryValue* dict = nullptr;
405 if (!split_settings->GetDictionary(prefix, &dict)) {
406 DCHECK(!split_settings->HasKey(prefix));
407 dict = split_settings->SetDictionary(
408 prefix, std::make_unique<base::DictionaryValue>());
409 }
410 key->erase(0, pos + 1);
411 return dict;
412 }
413
GetOrCreateDictionary(const std::string & key) const414 base::DictionaryValue* SupervisedUserSettingsService::GetOrCreateDictionary(
415 const std::string& key) const {
416 base::Value* value = nullptr;
417 if (!store_->GetMutableValue(key, &value)) {
418 store_->SetValue(
419 key, std::make_unique<base::Value>(base::Value::Type::DICTIONARY),
420 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
421 store_->GetMutableValue(key, &value);
422 }
423 base::DictionaryValue* dict = nullptr;
424 bool success = value->GetAsDictionary(&dict);
425 DCHECK(success);
426 return dict;
427 }
428
429 base::DictionaryValue*
GetAtomicSettings() const430 SupervisedUserSettingsService::GetAtomicSettings() const {
431 return GetOrCreateDictionary(kAtomicSettings);
432 }
433
GetSplitSettings() const434 base::DictionaryValue* SupervisedUserSettingsService::GetSplitSettings() const {
435 return GetOrCreateDictionary(kSplitSettings);
436 }
437
GetQueuedItems() const438 base::DictionaryValue* SupervisedUserSettingsService::GetQueuedItems() const {
439 return GetOrCreateDictionary(kQueuedItems);
440 }
441
442 std::unique_ptr<base::DictionaryValue>
GetSettings()443 SupervisedUserSettingsService::GetSettings() {
444 DCHECK(IsReady());
445 if (!active_ || initialization_failed_)
446 return std::unique_ptr<base::DictionaryValue>();
447
448 std::unique_ptr<base::DictionaryValue> settings(local_settings_->DeepCopy());
449
450 base::DictionaryValue* atomic_settings = GetAtomicSettings();
451 for (base::DictionaryValue::Iterator it(*atomic_settings); !it.IsAtEnd();
452 it.Advance()) {
453 if (!SettingShouldApplyToPrefs(it.key()))
454 continue;
455
456 settings->Set(it.key(), std::make_unique<base::Value>(it.value().Clone()));
457 }
458
459 base::DictionaryValue* split_settings = GetSplitSettings();
460 for (base::DictionaryValue::Iterator it(*split_settings); !it.IsAtEnd();
461 it.Advance()) {
462 if (!SettingShouldApplyToPrefs(it.key()))
463 continue;
464
465 settings->Set(it.key(), std::make_unique<base::Value>(it.value().Clone()));
466 }
467
468 return settings;
469 }
470
InformSubscribers()471 void SupervisedUserSettingsService::InformSubscribers() {
472 if (!IsReady())
473 return;
474
475 std::unique_ptr<base::DictionaryValue> settings = GetSettings();
476 settings_callback_list_.Notify(settings.get());
477 }
478