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_settings_service.h"
7 #include "base/callback.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/prefs/json_pref_store.h"
11 #include "base/prefs/pref_filter.h"
12 #include "base/strings/string_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
15 #include "chrome/common/chrome_constants.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/user_metrics.h"
18 #include "sync/api/sync_change.h"
19 #include "sync/api/sync_error_factory.h"
20 #include "sync/protocol/sync.pb.h"
22 using base::DictionaryValue
;
23 using base::JSONReader
;
24 using base::UserMetricsAction
;
26 using content::BrowserThread
;
27 using syncer::SUPERVISED_USER_SETTINGS
;
28 using syncer::ModelType
;
29 using syncer::SyncChange
;
30 using syncer::SyncChangeList
;
31 using syncer::SyncChangeProcessor
;
32 using syncer::SyncData
;
33 using syncer::SyncDataList
;
34 using syncer::SyncError
;
35 using syncer::SyncErrorFactory
;
36 using syncer::SyncMergeResult
;
38 const char kAtomicSettings
[] = "atomic_settings";
39 const char kSupervisedUserInternalItemPrefix
[] = "X-";
40 const char kQueuedItems
[] = "queued_items";
41 const char kSplitSettingKeySeparator
= ':';
42 const char kSplitSettings
[] = "split_settings";
46 bool SettingShouldApplyToPrefs(const std::string
& name
) {
47 return !StartsWithASCII(name
, kSupervisedUserInternalItemPrefix
, false);
52 SupervisedUserSettingsService::SupervisedUserSettingsService()
54 initialization_failed_(false),
55 local_settings_(new base::DictionaryValue
) {
58 SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
60 void SupervisedUserSettingsService::Init(
61 base::FilePath profile_path
,
62 base::SequencedTaskRunner
* sequenced_task_runner
,
63 bool load_synchronously
) {
65 profile_path
.Append(chrome::kSupervisedUserSettingsFilename
);
66 PersistentPrefStore
* store
= new JsonPrefStore(
67 path
, sequenced_task_runner
, scoped_ptr
<PrefFilter
>());
69 if (load_synchronously
) {
71 // TODO(bauerb): Temporary CHECK while investigating
72 // https://crbug.com/425785. Remove (or change to DCHECK) once the bug
74 CHECK(store_
->IsInitializationComplete());
76 store_
->ReadPrefsAsync(NULL
);
80 void SupervisedUserSettingsService::Init(
81 scoped_refptr
<PersistentPrefStore
> store
) {
82 DCHECK(!store_
.get());
84 store_
->AddObserver(this);
87 void SupervisedUserSettingsService::Subscribe(
88 const SettingsCallback
& callback
) {
90 scoped_ptr
<base::DictionaryValue
> settings
= GetSettings();
91 callback
.Run(settings
.get());
94 subscribers_
.push_back(callback
);
97 void SupervisedUserSettingsService::SetActive(bool active
) {
102 bool SupervisedUserSettingsService::IsReady() {
103 // Initialization cannot be complete but have failed at the same time.
104 DCHECK(!(store_
->IsInitializationComplete() && initialization_failed_
));
105 return initialization_failed_
|| store_
->IsInitializationComplete();
108 void SupervisedUserSettingsService::Clear() {
109 store_
->RemoveValue(kAtomicSettings
,
110 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
111 store_
->RemoveValue(kSplitSettings
,
112 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
116 std::string
SupervisedUserSettingsService::MakeSplitSettingKey(
117 const std::string
& prefix
,
118 const std::string
& key
) {
119 return prefix
+ kSplitSettingKeySeparator
+ key
;
122 void SupervisedUserSettingsService::UploadItem(const std::string
& key
,
123 scoped_ptr
<base::Value
> value
) {
124 DCHECK(!SettingShouldApplyToPrefs(key
));
126 std::string key_suffix
= key
;
127 base::DictionaryValue
* dict
= NULL
;
128 if (sync_processor_
) {
129 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
130 dict
= GetDictionaryAndSplitKey(&key_suffix
);
131 DCHECK(GetQueuedItems()->empty());
132 SyncChangeList change_list
;
133 SyncData data
= CreateSyncDataForSetting(key
, *value
);
134 SyncChange::SyncChangeType change_type
=
135 dict
->HasKey(key_suffix
) ? SyncChange::ACTION_UPDATE
136 : SyncChange::ACTION_ADD
;
137 change_list
.push_back(SyncChange(FROM_HERE
, change_type
, data
));
139 sync_processor_
->ProcessSyncChanges(FROM_HERE
, change_list
);
140 DCHECK(!error
.IsSet()) << error
.ToString();
142 // Queue the item up to be uploaded when we start syncing
143 // (in MergeDataAndStartSyncing()).
144 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
145 dict
= GetQueuedItems();
147 dict
->SetWithoutPathExpansion(key_suffix
, value
.release());
150 void SupervisedUserSettingsService::SetLocalSetting(
151 const std::string
& key
,
152 scoped_ptr
<base::Value
> value
) {
154 local_settings_
->SetWithoutPathExpansion(key
, value
.release());
156 local_settings_
->RemoveWithoutPathExpansion(key
, NULL
);
162 SyncData
SupervisedUserSettingsService::CreateSyncDataForSetting(
163 const std::string
& name
,
164 const base::Value
& value
) {
165 std::string json_value
;
166 base::JSONWriter::Write(value
, &json_value
);
167 ::sync_pb::EntitySpecifics specifics
;
168 specifics
.mutable_managed_user_setting()->set_name(name
);
169 specifics
.mutable_managed_user_setting()->set_value(json_value
);
170 return SyncData::CreateLocalData(name
, name
, specifics
);
173 void SupervisedUserSettingsService::Shutdown() {
174 store_
->RemoveObserver(this);
177 SyncMergeResult
SupervisedUserSettingsService::MergeDataAndStartSyncing(
179 const SyncDataList
& initial_sync_data
,
180 scoped_ptr
<SyncChangeProcessor
> sync_processor
,
181 scoped_ptr
<SyncErrorFactory
> error_handler
) {
182 DCHECK_EQ(SUPERVISED_USER_SETTINGS
, type
);
183 sync_processor_
= sync_processor
.Pass();
184 error_handler_
= error_handler
.Pass();
186 // Clear all atomic and split settings, then recreate them from Sync data.
188 for (const SyncData
& sync_data
: initial_sync_data
) {
189 DCHECK_EQ(SUPERVISED_USER_SETTINGS
, sync_data
.GetDataType());
190 const ::sync_pb::ManagedUserSettingSpecifics
& supervised_user_setting
=
191 sync_data
.GetSpecifics().managed_user_setting();
192 scoped_ptr
<base::Value
> value(
193 JSONReader::DeprecatedRead(supervised_user_setting
.value()));
194 std::string name_suffix
= supervised_user_setting
.name();
195 base::DictionaryValue
* dict
= GetDictionaryAndSplitKey(&name_suffix
);
196 dict
->SetWithoutPathExpansion(name_suffix
, value
.release());
198 store_
->ReportValueChanged(kAtomicSettings
,
199 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
200 store_
->ReportValueChanged(kSplitSettings
,
201 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
204 // Upload all the queued up items (either with an ADD or an UPDATE action,
205 // depending on whether they already exist) and move them to split settings.
206 SyncChangeList change_list
;
207 base::DictionaryValue
* queued_items
= GetQueuedItems();
208 for (base::DictionaryValue::Iterator
it(*queued_items
); !it
.IsAtEnd();
210 std::string key_suffix
= it
.key();
211 base::DictionaryValue
* dict
= GetDictionaryAndSplitKey(&key_suffix
);
212 SyncData data
= CreateSyncDataForSetting(it
.key(), it
.value());
213 SyncChange::SyncChangeType change_type
=
214 dict
->HasKey(key_suffix
) ? SyncChange::ACTION_UPDATE
215 : SyncChange::ACTION_ADD
;
216 change_list
.push_back(SyncChange(FROM_HERE
, change_type
, data
));
217 dict
->SetWithoutPathExpansion(key_suffix
, it
.value().DeepCopy());
219 queued_items
->Clear();
221 SyncMergeResult
result(SUPERVISED_USER_SETTINGS
);
222 // Process all the accumulated changes from the queued items.
223 if (change_list
.size() > 0) {
224 store_
->ReportValueChanged(kQueuedItems
,
225 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
227 sync_processor_
->ProcessSyncChanges(FROM_HERE
, change_list
));
230 // TODO(bauerb): Statistics?
234 void SupervisedUserSettingsService::StopSyncing(ModelType type
) {
235 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS
, type
);
236 sync_processor_
.reset();
237 error_handler_
.reset();
240 SyncDataList
SupervisedUserSettingsService::GetAllSyncData(
241 ModelType type
) const {
242 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS
, type
);
244 for (base::DictionaryValue::Iterator
it(*GetAtomicSettings()); !it
.IsAtEnd();
246 data
.push_back(CreateSyncDataForSetting(it
.key(), it
.value()));
248 for (base::DictionaryValue::Iterator
it(*GetSplitSettings()); !it
.IsAtEnd();
250 const base::DictionaryValue
* dict
= NULL
;
251 it
.value().GetAsDictionary(&dict
);
252 for (base::DictionaryValue::Iterator
jt(*dict
);
253 !jt
.IsAtEnd(); jt
.Advance()) {
254 data
.push_back(CreateSyncDataForSetting(
255 MakeSplitSettingKey(it
.key(), jt
.key()), jt
.value()));
258 DCHECK_EQ(0u, GetQueuedItems()->size());
262 SyncError
SupervisedUserSettingsService::ProcessSyncChanges(
263 const tracked_objects::Location
& from_here
,
264 const SyncChangeList
& change_list
) {
265 for (const SyncChange
& sync_change
: change_list
) {
266 SyncData data
= sync_change
.sync_data();
267 DCHECK_EQ(SUPERVISED_USER_SETTINGS
, data
.GetDataType());
268 const ::sync_pb::ManagedUserSettingSpecifics
& supervised_user_setting
=
269 data
.GetSpecifics().managed_user_setting();
270 std::string key
= supervised_user_setting
.name();
271 base::DictionaryValue
* dict
= GetDictionaryAndSplitKey(&key
);
272 SyncChange::SyncChangeType change_type
= sync_change
.change_type();
273 switch (change_type
) {
274 case SyncChange::ACTION_ADD
:
275 case SyncChange::ACTION_UPDATE
: {
276 scoped_ptr
<base::Value
> value(
277 JSONReader::DeprecatedRead(supervised_user_setting
.value()));
278 if (dict
->HasKey(key
)) {
279 DLOG_IF(WARNING
, change_type
== SyncChange::ACTION_ADD
)
280 << "Value for key " << key
<< " already exists";
282 DLOG_IF(WARNING
, change_type
== SyncChange::ACTION_UPDATE
)
283 << "Value for key " << key
<< " doesn't exist yet";
285 dict
->SetWithoutPathExpansion(key
, value
.release());
288 case SyncChange::ACTION_DELETE
: {
289 DLOG_IF(WARNING
, !dict
->HasKey(key
)) << "Trying to delete nonexistent "
291 dict
->RemoveWithoutPathExpansion(key
, NULL
);
294 case SyncChange::ACTION_INVALID
: {
300 store_
->ReportValueChanged(kAtomicSettings
,
301 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
302 store_
->ReportValueChanged(kSplitSettings
,
303 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
310 void SupervisedUserSettingsService::OnPrefValueChanged(const std::string
& key
) {
313 void SupervisedUserSettingsService::OnInitializationCompleted(bool success
) {
315 // If this happens, it means the profile directory was not found. There is
316 // not much we can do, but the whole profile will probably be useless
317 // anyway. Just mark initialization as failed and continue otherwise,
318 // because subscribers might still expect to be called back.
319 initialization_failed_
= true;
322 // TODO(bauerb): Temporary CHECK while investigating https://crbug.com/425785.
323 // Remove (or change back to DCHECK) once the bug is fixed.
328 base::DictionaryValue
* SupervisedUserSettingsService::GetOrCreateDictionary(
329 const std::string
& key
) const {
330 base::Value
* value
= NULL
;
331 base::DictionaryValue
* dict
= NULL
;
332 if (store_
->GetMutableValue(key
, &value
)) {
333 bool success
= value
->GetAsDictionary(&dict
);
336 dict
= new base::DictionaryValue
;
337 store_
->SetValue(key
, dict
, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
343 base::DictionaryValue
*
344 SupervisedUserSettingsService::GetAtomicSettings() const {
345 return GetOrCreateDictionary(kAtomicSettings
);
348 base::DictionaryValue
* SupervisedUserSettingsService::GetSplitSettings() const {
349 return GetOrCreateDictionary(kSplitSettings
);
352 base::DictionaryValue
* SupervisedUserSettingsService::GetQueuedItems() const {
353 return GetOrCreateDictionary(kQueuedItems
);
356 base::DictionaryValue
* SupervisedUserSettingsService::GetDictionaryAndSplitKey(
357 std::string
* key
) const {
358 size_t pos
= key
->find_first_of(kSplitSettingKeySeparator
);
359 if (pos
== std::string::npos
)
360 return GetAtomicSettings();
362 base::DictionaryValue
* split_settings
= GetSplitSettings();
363 std::string prefix
= key
->substr(0, pos
);
364 base::DictionaryValue
* dict
= NULL
;
365 if (!split_settings
->GetDictionary(prefix
, &dict
)) {
366 dict
= new base::DictionaryValue
;
367 DCHECK(!split_settings
->HasKey(prefix
));
368 split_settings
->Set(prefix
, dict
);
370 key
->erase(0, pos
+ 1);
374 scoped_ptr
<base::DictionaryValue
> SupervisedUserSettingsService::GetSettings() {
376 if (!active_
|| initialization_failed_
)
377 return scoped_ptr
<base::DictionaryValue
>();
379 scoped_ptr
<base::DictionaryValue
> settings(local_settings_
->DeepCopy());
381 base::DictionaryValue
* atomic_settings
= GetAtomicSettings();
382 for (base::DictionaryValue::Iterator
it(*atomic_settings
); !it
.IsAtEnd();
384 if (!SettingShouldApplyToPrefs(it
.key()))
387 settings
->Set(it
.key(), it
.value().DeepCopy());
390 base::DictionaryValue
* split_settings
= GetSplitSettings();
391 for (base::DictionaryValue::Iterator
it(*split_settings
); !it
.IsAtEnd();
393 if (!SettingShouldApplyToPrefs(it
.key()))
396 settings
->Set(it
.key(), it
.value().DeepCopy());
399 return settings
.Pass();
402 void SupervisedUserSettingsService::InformSubscribers() {
406 scoped_ptr
<base::DictionaryValue
> settings
= GetSettings();
407 for (const auto& callback
: subscribers_
)
408 callback
.Run(settings
.get());