1 // Copyright 2013 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/managed_mode/managed_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/managed_mode/managed_mode_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::MANAGED_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 kManagedUserInternalItemPrefix
[] = "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
, kManagedUserInternalItemPrefix
, false);
52 ManagedUserSettingsService::ManagedUserSettingsService()
53 : active_(false), local_settings_(new base::DictionaryValue
) {}
55 ManagedUserSettingsService::~ManagedUserSettingsService() {}
57 void ManagedUserSettingsService::Init(
58 base::FilePath profile_path
,
59 base::SequencedTaskRunner
* sequenced_task_runner
,
60 bool load_synchronously
) {
62 profile_path
.Append(chrome::kManagedUserSettingsFilename
);
63 PersistentPrefStore
* store
= new JsonPrefStore(
64 path
, sequenced_task_runner
, scoped_ptr
<PrefFilter
>());
66 if (load_synchronously
)
69 store_
->ReadPrefsAsync(NULL
);
72 void ManagedUserSettingsService::Init(
73 scoped_refptr
<PersistentPrefStore
> store
) {
76 store_
->AddObserver(this);
79 void ManagedUserSettingsService::Subscribe(const SettingsCallback
& callback
) {
81 scoped_ptr
<base::DictionaryValue
> settings
= GetSettings();
82 callback
.Run(settings
.get());
85 subscribers_
.push_back(callback
);
88 void ManagedUserSettingsService::Activate() {
93 bool ManagedUserSettingsService::IsReady() {
94 return store_
->IsInitializationComplete();
97 void ManagedUserSettingsService::Clear() {
98 store_
->RemoveValue(kAtomicSettings
);
99 store_
->RemoveValue(kSplitSettings
);
103 std::string
ManagedUserSettingsService::MakeSplitSettingKey(
104 const std::string
& prefix
,
105 const std::string
& key
) {
106 return prefix
+ kSplitSettingKeySeparator
+ key
;
109 void ManagedUserSettingsService::UploadItem(const std::string
& key
,
110 scoped_ptr
<base::Value
> value
) {
111 DCHECK(!SettingShouldApplyToPrefs(key
));
113 std::string key_suffix
= key
;
114 base::DictionaryValue
* dict
= NULL
;
115 if (sync_processor_
) {
116 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
117 dict
= GetDictionaryAndSplitKey(&key_suffix
);
118 DCHECK(GetQueuedItems()->empty());
119 SyncChangeList change_list
;
120 SyncData data
= CreateSyncDataForSetting(key
, *value
);
121 SyncChange::SyncChangeType change_type
=
122 dict
->HasKey(key_suffix
) ? SyncChange::ACTION_UPDATE
123 : SyncChange::ACTION_ADD
;
124 change_list
.push_back(SyncChange(FROM_HERE
, change_type
, data
));
126 sync_processor_
->ProcessSyncChanges(FROM_HERE
, change_list
);
127 DCHECK(!error
.IsSet()) << error
.ToString();
129 // Queue the item up to be uploaded when we start syncing
130 // (in MergeDataAndStartSyncing()).
131 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
132 dict
= GetQueuedItems();
134 dict
->SetWithoutPathExpansion(key_suffix
, value
.release());
137 void ManagedUserSettingsService::SetLocalSettingForTesting(
138 const std::string
& key
,
139 scoped_ptr
<base::Value
> value
) {
141 local_settings_
->SetWithoutPathExpansion(key
, value
.release());
143 local_settings_
->RemoveWithoutPathExpansion(key
, NULL
);
149 SyncData
ManagedUserSettingsService::CreateSyncDataForSetting(
150 const std::string
& name
,
151 const base::Value
& value
) {
152 std::string json_value
;
153 base::JSONWriter::Write(&value
, &json_value
);
154 ::sync_pb::EntitySpecifics specifics
;
155 specifics
.mutable_managed_user_setting()->set_name(name
);
156 specifics
.mutable_managed_user_setting()->set_value(json_value
);
157 return SyncData::CreateLocalData(name
, name
, specifics
);
160 void ManagedUserSettingsService::Shutdown() {
161 store_
->RemoveObserver(this);
164 SyncMergeResult
ManagedUserSettingsService::MergeDataAndStartSyncing(
166 const SyncDataList
& initial_sync_data
,
167 scoped_ptr
<SyncChangeProcessor
> sync_processor
,
168 scoped_ptr
<SyncErrorFactory
> error_handler
) {
169 DCHECK_EQ(MANAGED_USER_SETTINGS
, type
);
170 sync_processor_
= sync_processor
.Pass();
171 error_handler_
= error_handler
.Pass();
173 // Clear all atomic and split settings, then recreate them from Sync data.
175 for (SyncDataList::const_iterator it
= initial_sync_data
.begin();
176 it
!= initial_sync_data
.end(); ++it
) {
177 DCHECK_EQ(MANAGED_USER_SETTINGS
, it
->GetDataType());
178 const ::sync_pb::ManagedUserSettingSpecifics
& managed_user_setting
=
179 it
->GetSpecifics().managed_user_setting();
180 scoped_ptr
<base::Value
> value(
181 JSONReader::Read(managed_user_setting
.value()));
182 std::string name_suffix
= managed_user_setting
.name();
183 base::DictionaryValue
* dict
= GetDictionaryAndSplitKey(&name_suffix
);
184 dict
->SetWithoutPathExpansion(name_suffix
, value
.release());
186 store_
->ReportValueChanged(kAtomicSettings
);
187 store_
->ReportValueChanged(kSplitSettings
);
190 // Upload all the queued up items (either with an ADD or an UPDATE action,
191 // depending on whether they already exist) and move them to split settings.
192 SyncChangeList change_list
;
193 base::DictionaryValue
* queued_items
= GetQueuedItems();
194 for (base::DictionaryValue::Iterator
it(*queued_items
); !it
.IsAtEnd();
196 std::string key_suffix
= it
.key();
197 base::DictionaryValue
* dict
= GetDictionaryAndSplitKey(&key_suffix
);
198 SyncData data
= CreateSyncDataForSetting(it
.key(), it
.value());
199 SyncChange::SyncChangeType change_type
=
200 dict
->HasKey(key_suffix
) ? SyncChange::ACTION_UPDATE
201 : SyncChange::ACTION_ADD
;
202 change_list
.push_back(SyncChange(FROM_HERE
, change_type
, data
));
203 dict
->SetWithoutPathExpansion(key_suffix
, it
.value().DeepCopy());
205 queued_items
->Clear();
207 SyncMergeResult
result(MANAGED_USER_SETTINGS
);
208 // Process all the accumulated changes from the queued items.
209 if (change_list
.size() > 0) {
210 store_
->ReportValueChanged(kQueuedItems
);
212 sync_processor_
->ProcessSyncChanges(FROM_HERE
, change_list
));
215 // TODO(bauerb): Statistics?
219 void ManagedUserSettingsService::StopSyncing(ModelType type
) {
220 DCHECK_EQ(syncer::MANAGED_USER_SETTINGS
, type
);
221 sync_processor_
.reset();
222 error_handler_
.reset();
225 SyncDataList
ManagedUserSettingsService::GetAllSyncData(
226 ModelType type
) const {
227 DCHECK_EQ(syncer::MANAGED_USER_SETTINGS
, type
);
229 for (base::DictionaryValue::Iterator
it(*GetAtomicSettings()); !it
.IsAtEnd();
231 data
.push_back(CreateSyncDataForSetting(it
.key(), it
.value()));
233 for (base::DictionaryValue::Iterator
it(*GetSplitSettings()); !it
.IsAtEnd();
235 const base::DictionaryValue
* dict
= NULL
;
236 it
.value().GetAsDictionary(&dict
);
237 for (base::DictionaryValue::Iterator
jt(*dict
);
238 !jt
.IsAtEnd(); jt
.Advance()) {
239 data
.push_back(CreateSyncDataForSetting(
240 MakeSplitSettingKey(it
.key(), jt
.key()), jt
.value()));
243 DCHECK_EQ(0u, GetQueuedItems()->size());
247 SyncError
ManagedUserSettingsService::ProcessSyncChanges(
248 const tracked_objects::Location
& from_here
,
249 const SyncChangeList
& change_list
) {
250 for (SyncChangeList::const_iterator it
= change_list
.begin();
251 it
!= change_list
.end(); ++it
) {
252 SyncData data
= it
->sync_data();
253 DCHECK_EQ(MANAGED_USER_SETTINGS
, data
.GetDataType());
254 const ::sync_pb::ManagedUserSettingSpecifics
& managed_user_setting
=
255 data
.GetSpecifics().managed_user_setting();
256 std::string key
= managed_user_setting
.name();
257 base::DictionaryValue
* dict
= GetDictionaryAndSplitKey(&key
);
258 switch (it
->change_type()) {
259 case SyncChange::ACTION_ADD
:
260 case SyncChange::ACTION_UPDATE
: {
261 scoped_ptr
<base::Value
> value(
262 JSONReader::Read(managed_user_setting
.value()));
263 if (dict
->HasKey(key
)) {
264 DLOG_IF(WARNING
, it
->change_type() == SyncChange::ACTION_ADD
)
265 << "Value for key " << key
<< " already exists";
267 DLOG_IF(WARNING
, it
->change_type() == SyncChange::ACTION_UPDATE
)
268 << "Value for key " << key
<< " doesn't exist yet";
270 dict
->SetWithoutPathExpansion(key
, value
.release());
273 case SyncChange::ACTION_DELETE
: {
274 DLOG_IF(WARNING
, !dict
->HasKey(key
)) << "Trying to delete nonexistent "
276 dict
->RemoveWithoutPathExpansion(key
, NULL
);
279 case SyncChange::ACTION_INVALID
: {
285 store_
->ReportValueChanged(kAtomicSettings
);
286 store_
->ReportValueChanged(kSplitSettings
);
293 void ManagedUserSettingsService::OnPrefValueChanged(const std::string
& key
) {}
295 void ManagedUserSettingsService::OnInitializationCompleted(bool success
) {
301 base::DictionaryValue
* ManagedUserSettingsService::GetOrCreateDictionary(
302 const std::string
& key
) const {
303 base::Value
* value
= NULL
;
304 base::DictionaryValue
* dict
= NULL
;
305 if (store_
->GetMutableValue(key
, &value
)) {
306 bool success
= value
->GetAsDictionary(&dict
);
309 dict
= new base::DictionaryValue
;
310 store_
->SetValue(key
, dict
);
316 base::DictionaryValue
* ManagedUserSettingsService::GetAtomicSettings() const {
317 return GetOrCreateDictionary(kAtomicSettings
);
320 base::DictionaryValue
* ManagedUserSettingsService::GetSplitSettings() const {
321 return GetOrCreateDictionary(kSplitSettings
);
324 base::DictionaryValue
* ManagedUserSettingsService::GetQueuedItems() const {
325 return GetOrCreateDictionary(kQueuedItems
);
328 base::DictionaryValue
* ManagedUserSettingsService::GetDictionaryAndSplitKey(
329 std::string
* key
) const {
330 size_t pos
= key
->find_first_of(kSplitSettingKeySeparator
);
331 if (pos
== std::string::npos
)
332 return GetAtomicSettings();
334 base::DictionaryValue
* split_settings
= GetSplitSettings();
335 std::string prefix
= key
->substr(0, pos
);
336 base::DictionaryValue
* dict
= NULL
;
337 if (!split_settings
->GetDictionary(prefix
, &dict
)) {
338 dict
= new base::DictionaryValue
;
339 DCHECK(!split_settings
->HasKey(prefix
));
340 split_settings
->Set(prefix
, dict
);
342 key
->erase(0, pos
+ 1);
346 scoped_ptr
<base::DictionaryValue
> ManagedUserSettingsService::GetSettings() {
349 return scoped_ptr
<base::DictionaryValue
>();
351 scoped_ptr
<base::DictionaryValue
> settings(local_settings_
->DeepCopy());
353 base::DictionaryValue
* atomic_settings
= GetAtomicSettings();
354 for (base::DictionaryValue::Iterator
it(*atomic_settings
); !it
.IsAtEnd();
356 if (!SettingShouldApplyToPrefs(it
.key()))
359 settings
->Set(it
.key(), it
.value().DeepCopy());
362 base::DictionaryValue
* split_settings
= GetSplitSettings();
363 for (base::DictionaryValue::Iterator
it(*split_settings
); !it
.IsAtEnd();
365 if (!SettingShouldApplyToPrefs(it
.key()))
368 settings
->Set(it
.key(), it
.value().DeepCopy());
371 return settings
.Pass();
374 void ManagedUserSettingsService::InformSubscribers() {
378 scoped_ptr
<base::DictionaryValue
> settings
= GetSettings();
379 for (std::vector
<SettingsCallback
>::iterator it
= subscribers_
.begin();
380 it
!= subscribers_
.end(); ++it
) {
381 it
->Run(settings
.get());