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 !base::StartsWith(name
, kSupervisedUserInternalItemPrefix
,
48 base::CompareCase::INSENSITIVE_ASCII
);
53 SupervisedUserSettingsService::SupervisedUserSettingsService(Profile
* profile
)
56 initialization_failed_(false),
57 local_settings_(new base::DictionaryValue
) {
60 SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
62 void SupervisedUserSettingsService::Init(
63 base::FilePath profile_path
,
64 base::SequencedTaskRunner
* sequenced_task_runner
,
65 bool load_synchronously
) {
67 profile_path
.Append(chrome::kSupervisedUserSettingsFilename
);
68 PersistentPrefStore
* store
= new JsonPrefStore(
69 path
, sequenced_task_runner
, scoped_ptr
<PrefFilter
>());
71 if (load_synchronously
) {
73 // TODO(bauerb): Temporary CHECK while investigating
74 // https://crbug.com/425785. Remove (or change to DCHECK) once the bug
76 CHECK(store_
->IsInitializationComplete());
78 store_
->ReadPrefsAsync(NULL
);
82 void SupervisedUserSettingsService::Init(
83 scoped_refptr
<PersistentPrefStore
> store
) {
84 DCHECK(!store_
.get());
86 store_
->AddObserver(this);
89 scoped_ptr
<SupervisedUserSettingsService::SettingsCallbackList::Subscription
>
90 SupervisedUserSettingsService::Subscribe(
91 const SettingsCallback
& callback
) {
93 scoped_ptr
<base::DictionaryValue
> settings
= GetSettings();
94 callback
.Run(settings
.get());
97 return callback_list_
.Add(callback
);
100 Profile
* SupervisedUserSettingsService::GetProfile(){
104 void SupervisedUserSettingsService::SetActive(bool active
) {
109 bool SupervisedUserSettingsService::IsReady() {
110 // Initialization cannot be complete but have failed at the same time.
111 DCHECK(!(store_
->IsInitializationComplete() && initialization_failed_
));
112 return initialization_failed_
|| store_
->IsInitializationComplete();
115 void SupervisedUserSettingsService::Clear() {
116 store_
->RemoveValue(kAtomicSettings
,
117 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
118 store_
->RemoveValue(kSplitSettings
,
119 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
123 std::string
SupervisedUserSettingsService::MakeSplitSettingKey(
124 const std::string
& prefix
,
125 const std::string
& key
) {
126 return prefix
+ kSplitSettingKeySeparator
+ key
;
129 void SupervisedUserSettingsService::UploadItem(const std::string
& key
,
130 scoped_ptr
<base::Value
> value
) {
131 DCHECK(!SettingShouldApplyToPrefs(key
));
133 std::string key_suffix
= key
;
134 base::DictionaryValue
* dict
= NULL
;
135 if (sync_processor_
) {
136 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
137 dict
= GetDictionaryAndSplitKey(&key_suffix
);
138 DCHECK(GetQueuedItems()->empty());
139 SyncChangeList change_list
;
140 SyncData data
= CreateSyncDataForSetting(key
, *value
);
141 SyncChange::SyncChangeType change_type
=
142 dict
->HasKey(key_suffix
) ? SyncChange::ACTION_UPDATE
143 : SyncChange::ACTION_ADD
;
144 change_list
.push_back(SyncChange(FROM_HERE
, change_type
, data
));
146 sync_processor_
->ProcessSyncChanges(FROM_HERE
, change_list
);
147 DCHECK(!error
.IsSet()) << error
.ToString();
149 // Queue the item up to be uploaded when we start syncing
150 // (in MergeDataAndStartSyncing()).
151 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
152 dict
= GetQueuedItems();
154 dict
->SetWithoutPathExpansion(key_suffix
, value
.release());
157 void SupervisedUserSettingsService::SetLocalSetting(
158 const std::string
& key
,
159 scoped_ptr
<base::Value
> value
) {
161 local_settings_
->SetWithoutPathExpansion(key
, value
.release());
163 local_settings_
->RemoveWithoutPathExpansion(key
, NULL
);
169 SyncData
SupervisedUserSettingsService::CreateSyncDataForSetting(
170 const std::string
& name
,
171 const base::Value
& value
) {
172 std::string json_value
;
173 base::JSONWriter::Write(value
, &json_value
);
174 ::sync_pb::EntitySpecifics specifics
;
175 specifics
.mutable_managed_user_setting()->set_name(name
);
176 specifics
.mutable_managed_user_setting()->set_value(json_value
);
177 return SyncData::CreateLocalData(name
, name
, specifics
);
180 void SupervisedUserSettingsService::Shutdown() {
181 store_
->RemoveObserver(this);
184 SyncMergeResult
SupervisedUserSettingsService::MergeDataAndStartSyncing(
186 const SyncDataList
& initial_sync_data
,
187 scoped_ptr
<SyncChangeProcessor
> sync_processor
,
188 scoped_ptr
<SyncErrorFactory
> error_handler
) {
189 DCHECK_EQ(SUPERVISED_USER_SETTINGS
, type
);
190 sync_processor_
= sync_processor
.Pass();
191 error_handler_
= error_handler
.Pass();
193 // Clear all atomic and split settings, then recreate them from Sync data.
195 for (const SyncData
& sync_data
: initial_sync_data
) {
196 DCHECK_EQ(SUPERVISED_USER_SETTINGS
, sync_data
.GetDataType());
197 const ::sync_pb::ManagedUserSettingSpecifics
& supervised_user_setting
=
198 sync_data
.GetSpecifics().managed_user_setting();
199 scoped_ptr
<base::Value
> value
=
200 JSONReader::Read(supervised_user_setting
.value());
201 std::string name_suffix
= supervised_user_setting
.name();
202 base::DictionaryValue
* dict
= GetDictionaryAndSplitKey(&name_suffix
);
203 dict
->SetWithoutPathExpansion(name_suffix
, value
.release());
205 store_
->ReportValueChanged(kAtomicSettings
,
206 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
207 store_
->ReportValueChanged(kSplitSettings
,
208 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
211 // Upload all the queued up items (either with an ADD or an UPDATE action,
212 // depending on whether they already exist) and move them to split settings.
213 SyncChangeList change_list
;
214 base::DictionaryValue
* queued_items
= GetQueuedItems();
215 for (base::DictionaryValue::Iterator
it(*queued_items
); !it
.IsAtEnd();
217 std::string key_suffix
= it
.key();
218 base::DictionaryValue
* dict
= GetDictionaryAndSplitKey(&key_suffix
);
219 SyncData data
= CreateSyncDataForSetting(it
.key(), it
.value());
220 SyncChange::SyncChangeType change_type
=
221 dict
->HasKey(key_suffix
) ? SyncChange::ACTION_UPDATE
222 : SyncChange::ACTION_ADD
;
223 change_list
.push_back(SyncChange(FROM_HERE
, change_type
, data
));
224 dict
->SetWithoutPathExpansion(key_suffix
, it
.value().DeepCopy());
226 queued_items
->Clear();
228 SyncMergeResult
result(SUPERVISED_USER_SETTINGS
);
229 // Process all the accumulated changes from the queued items.
230 if (change_list
.size() > 0) {
231 store_
->ReportValueChanged(kQueuedItems
,
232 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
234 sync_processor_
->ProcessSyncChanges(FROM_HERE
, change_list
));
237 // TODO(bauerb): Statistics?
241 void SupervisedUserSettingsService::StopSyncing(ModelType type
) {
242 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS
, type
);
243 sync_processor_
.reset();
244 error_handler_
.reset();
247 SyncDataList
SupervisedUserSettingsService::GetAllSyncData(
248 ModelType type
) const {
249 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS
, type
);
251 for (base::DictionaryValue::Iterator
it(*GetAtomicSettings()); !it
.IsAtEnd();
253 data
.push_back(CreateSyncDataForSetting(it
.key(), it
.value()));
255 for (base::DictionaryValue::Iterator
it(*GetSplitSettings()); !it
.IsAtEnd();
257 const base::DictionaryValue
* dict
= NULL
;
258 it
.value().GetAsDictionary(&dict
);
259 for (base::DictionaryValue::Iterator
jt(*dict
);
260 !jt
.IsAtEnd(); jt
.Advance()) {
261 data
.push_back(CreateSyncDataForSetting(
262 MakeSplitSettingKey(it
.key(), jt
.key()), jt
.value()));
265 DCHECK_EQ(0u, GetQueuedItems()->size());
269 SyncError
SupervisedUserSettingsService::ProcessSyncChanges(
270 const tracked_objects::Location
& from_here
,
271 const SyncChangeList
& change_list
) {
272 for (const SyncChange
& sync_change
: change_list
) {
273 SyncData data
= sync_change
.sync_data();
274 DCHECK_EQ(SUPERVISED_USER_SETTINGS
, data
.GetDataType());
275 const ::sync_pb::ManagedUserSettingSpecifics
& supervised_user_setting
=
276 data
.GetSpecifics().managed_user_setting();
277 std::string key
= supervised_user_setting
.name();
278 base::DictionaryValue
* dict
= GetDictionaryAndSplitKey(&key
);
279 SyncChange::SyncChangeType change_type
= sync_change
.change_type();
280 switch (change_type
) {
281 case SyncChange::ACTION_ADD
:
282 case SyncChange::ACTION_UPDATE
: {
283 scoped_ptr
<base::Value
> value
=
284 JSONReader::Read(supervised_user_setting
.value());
285 if (dict
->HasKey(key
)) {
286 DLOG_IF(WARNING
, change_type
== SyncChange::ACTION_ADD
)
287 << "Value for key " << key
<< " already exists";
289 DLOG_IF(WARNING
, change_type
== SyncChange::ACTION_UPDATE
)
290 << "Value for key " << key
<< " doesn't exist yet";
292 dict
->SetWithoutPathExpansion(key
, value
.release());
295 case SyncChange::ACTION_DELETE
: {
296 DLOG_IF(WARNING
, !dict
->HasKey(key
)) << "Trying to delete nonexistent "
298 dict
->RemoveWithoutPathExpansion(key
, NULL
);
301 case SyncChange::ACTION_INVALID
: {
307 store_
->ReportValueChanged(kAtomicSettings
,
308 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
309 store_
->ReportValueChanged(kSplitSettings
,
310 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
317 void SupervisedUserSettingsService::OnPrefValueChanged(const std::string
& key
) {
320 void SupervisedUserSettingsService::OnInitializationCompleted(bool success
) {
322 // If this happens, it means the profile directory was not found. There is
323 // not much we can do, but the whole profile will probably be useless
324 // anyway. Just mark initialization as failed and continue otherwise,
325 // because subscribers might still expect to be called back.
326 initialization_failed_
= true;
329 // TODO(bauerb): Temporary CHECK while investigating https://crbug.com/425785.
330 // Remove (or change back to DCHECK) once the bug is fixed.
335 base::DictionaryValue
* SupervisedUserSettingsService::GetOrCreateDictionary(
336 const std::string
& key
) const {
337 base::Value
* value
= NULL
;
338 base::DictionaryValue
* dict
= NULL
;
339 if (store_
->GetMutableValue(key
, &value
)) {
340 bool success
= value
->GetAsDictionary(&dict
);
343 dict
= new base::DictionaryValue
;
344 store_
->SetValue(key
, make_scoped_ptr(dict
),
345 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
351 base::DictionaryValue
*
352 SupervisedUserSettingsService::GetAtomicSettings() const {
353 return GetOrCreateDictionary(kAtomicSettings
);
356 base::DictionaryValue
* SupervisedUserSettingsService::GetSplitSettings() const {
357 return GetOrCreateDictionary(kSplitSettings
);
360 base::DictionaryValue
* SupervisedUserSettingsService::GetQueuedItems() const {
361 return GetOrCreateDictionary(kQueuedItems
);
364 base::DictionaryValue
* SupervisedUserSettingsService::GetDictionaryAndSplitKey(
365 std::string
* key
) const {
366 size_t pos
= key
->find_first_of(kSplitSettingKeySeparator
);
367 if (pos
== std::string::npos
)
368 return GetAtomicSettings();
370 base::DictionaryValue
* split_settings
= GetSplitSettings();
371 std::string prefix
= key
->substr(0, pos
);
372 base::DictionaryValue
* dict
= NULL
;
373 if (!split_settings
->GetDictionary(prefix
, &dict
)) {
374 dict
= new base::DictionaryValue
;
375 DCHECK(!split_settings
->HasKey(prefix
));
376 split_settings
->Set(prefix
, dict
);
378 key
->erase(0, pos
+ 1);
382 scoped_ptr
<base::DictionaryValue
> SupervisedUserSettingsService::GetSettings() {
384 if (!active_
|| initialization_failed_
)
385 return scoped_ptr
<base::DictionaryValue
>();
387 scoped_ptr
<base::DictionaryValue
> settings(local_settings_
->DeepCopy());
389 base::DictionaryValue
* atomic_settings
= GetAtomicSettings();
390 for (base::DictionaryValue::Iterator
it(*atomic_settings
); !it
.IsAtEnd();
392 if (!SettingShouldApplyToPrefs(it
.key()))
395 settings
->Set(it
.key(), it
.value().DeepCopy());
398 base::DictionaryValue
* split_settings
= GetSplitSettings();
399 for (base::DictionaryValue::Iterator
it(*split_settings
); !it
.IsAtEnd();
401 if (!SettingShouldApplyToPrefs(it
.key()))
404 settings
->Set(it
.key(), it
.value().DeepCopy());
407 return settings
.Pass();
410 void SupervisedUserSettingsService::InformSubscribers() {
414 scoped_ptr
<base::DictionaryValue
> settings
= GetSettings();
415 callback_list_
.Notify(settings
.get());