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_shared_settings_service.h"
7 #include "base/json/json_reader.h"
8 #include "base/json/json_writer.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/prefs/scoped_user_pref_update.h"
11 #include "base/values.h"
12 #include "chrome/common/pref_names.h"
13 #include "components/pref_registry/pref_registry_syncable.h"
14 #include "sync/api/sync_change.h"
15 #include "sync/api/sync_data.h"
16 #include "sync/api/sync_error.h"
17 #include "sync/api/sync_error_factory.h"
18 #include "sync/api/sync_merge_result.h"
19 #include "sync/protocol/sync.pb.h"
21 using base::DictionaryValue
;
23 using syncer::ModelType
;
24 using syncer::SUPERVISED_USER_SHARED_SETTINGS
;
25 using syncer::SyncChange
;
26 using syncer::SyncChangeList
;
27 using syncer::SyncChangeProcessor
;
28 using syncer::SyncData
;
29 using syncer::SyncDataList
;
30 using syncer::SyncError
;
31 using syncer::SyncErrorFactory
;
32 using syncer::SyncMergeResult
;
36 const char kAcknowledged
[] = "acknowledged";
37 const char kValue
[] = "value";
39 DictionaryValue
* FindOrCreateDictionary(DictionaryValue
* parent
,
40 const std::string
& key
) {
41 DictionaryValue
* dict
= NULL
;
42 if (!parent
->GetDictionaryWithoutPathExpansion(key
, &dict
)) {
43 dict
= new DictionaryValue
;
44 parent
->SetWithoutPathExpansion(key
, dict
);
49 class ScopedSupervisedUserSharedSettingsUpdate
{
51 ScopedSupervisedUserSharedSettingsUpdate(PrefService
* prefs
,
52 const std::string
& su_id
)
53 : update_(prefs
, prefs::kSupervisedUserSharedSettings
), su_id_(su_id
) {
54 DCHECK(!su_id
.empty());
56 // A supervised user can only modify their own settings.
57 std::string id
= prefs
->GetString(prefs::kSupervisedUserId
);
58 DCHECK(id
.empty() || id
== su_id
);
61 DictionaryValue
* Get() {
62 return FindOrCreateDictionary(update_
.Get(), su_id_
);
66 DictionaryPrefUpdate update_
;
70 SyncData
CreateSyncDataForValue(
71 const std::string
& su_id
,
72 const std::string
& key
,
73 const Value
& dict_value
) {
74 const DictionaryValue
* dict
= NULL
;
75 if (!dict_value
.GetAsDictionary(&dict
))
78 const Value
* value
= NULL
;
79 if (!dict
->Get(kValue
, &value
))
82 bool acknowledged
= false;
83 dict
->GetBoolean(kAcknowledged
, &acknowledged
);
85 return SupervisedUserSharedSettingsService::CreateSyncDataForSetting(
86 su_id
, key
, *value
, acknowledged
);
92 SupervisedUserSharedSettingsService::SupervisedUserSharedSettingsService(
96 SupervisedUserSharedSettingsService::~SupervisedUserSharedSettingsService() {}
98 void SupervisedUserSharedSettingsService::SetValueInternal(
99 const std::string
& su_id
,
100 const std::string
& key
,
103 ScopedSupervisedUserSharedSettingsUpdate
update(prefs_
, su_id
);
104 DictionaryValue
* update_dict
= update
.Get();
106 DictionaryValue
* dict
= NULL
;
107 bool has_key
= update_dict
->GetDictionaryWithoutPathExpansion(key
, &dict
);
109 dict
= new DictionaryValue
;
110 update_dict
->SetWithoutPathExpansion(key
, dict
);
112 dict
->SetWithoutPathExpansion(kValue
, value
.DeepCopy());
113 dict
->SetBooleanWithoutPathExpansion(kAcknowledged
, acknowledged
);
115 if (!sync_processor_
)
118 SyncData data
= CreateSyncDataForSetting(su_id
, key
, value
, acknowledged
);
119 SyncChange::SyncChangeType change_type
=
120 has_key
? SyncChange::ACTION_UPDATE
: SyncChange::ACTION_ADD
;
121 SyncChangeList changes
;
122 changes
.push_back(SyncChange(FROM_HERE
, change_type
, data
));
123 SyncError error
= sync_processor_
->ProcessSyncChanges(FROM_HERE
, changes
);
124 DCHECK(!error
.IsSet()) << error
.ToString();
127 const Value
* SupervisedUserSharedSettingsService::GetValue(
128 const std::string
& su_id
,
129 const std::string
& key
) {
130 const DictionaryValue
* data
=
131 prefs_
->GetDictionary(prefs::kSupervisedUserSharedSettings
);
132 const DictionaryValue
* dict
= NULL
;
133 if (!data
->GetDictionaryWithoutPathExpansion(su_id
, &dict
))
136 const DictionaryValue
* settings
= NULL
;
137 if (!dict
->GetDictionaryWithoutPathExpansion(key
, &settings
))
140 const Value
* value
= NULL
;
141 if (!settings
->GetWithoutPathExpansion(kValue
, &value
))
147 void SupervisedUserSharedSettingsService::SetValue(
148 const std::string
& su_id
,
149 const std::string
& key
,
150 const Value
& value
) {
151 SetValueInternal(su_id
, key
, value
, true);
155 SupervisedUserSharedSettingsService::ChangeCallbackList::Subscription
>
156 SupervisedUserSharedSettingsService::Subscribe(
157 const SupervisedUserSharedSettingsService::ChangeCallback
& cb
) {
158 return callbacks_
.Add(cb
);
162 void SupervisedUserSharedSettingsService::RegisterProfilePrefs(
163 user_prefs::PrefRegistrySyncable
* registry
) {
164 registry
->RegisterDictionaryPref(
165 prefs::kSupervisedUserSharedSettings
,
166 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
170 SyncData
SupervisedUserSharedSettingsService::CreateSyncDataForSetting(
171 const std::string
& su_id
,
172 const std::string
& key
,
175 std::string json_value
;
176 base::JSONWriter::Write(&value
, &json_value
);
177 ::sync_pb::EntitySpecifics specifics
;
178 specifics
.mutable_managed_user_shared_setting()->set_mu_id(su_id
);
179 specifics
.mutable_managed_user_shared_setting()->set_key(key
);
180 specifics
.mutable_managed_user_shared_setting()->set_value(json_value
);
181 specifics
.mutable_managed_user_shared_setting()->set_acknowledged(
183 std::string title
= su_id
+ ":" + key
;
184 return SyncData::CreateLocalData(title
, title
, specifics
);
187 void SupervisedUserSharedSettingsService::Shutdown() {}
189 syncer::SyncMergeResult
190 SupervisedUserSharedSettingsService::MergeDataAndStartSyncing(
191 syncer::ModelType type
,
192 const syncer::SyncDataList
& initial_sync_data
,
193 scoped_ptr
<syncer::SyncChangeProcessor
> sync_processor
,
194 scoped_ptr
<syncer::SyncErrorFactory
> error_handler
) {
195 DCHECK_EQ(SUPERVISED_USER_SHARED_SETTINGS
, type
);
196 sync_processor_
= sync_processor
.Pass();
197 error_handler_
= error_handler
.Pass();
199 // We keep a map from MU ID to the set of keys that we have seen in the
200 // initial sync data.
201 std::map
<std::string
, std::set
<std::string
> > seen_keys
;
203 // Iterate over all initial sync data, and update it locally. This means that
204 // the value from the server always wins over a local value.
205 for (SyncDataList::const_iterator it
= initial_sync_data
.begin();
206 it
!= initial_sync_data
.end();
208 DCHECK_EQ(SUPERVISED_USER_SHARED_SETTINGS
, it
->GetDataType());
209 const ::sync_pb::ManagedUserSharedSettingSpecifics
&
210 supervised_user_shared_setting
=
211 it
->GetSpecifics().managed_user_shared_setting();
212 scoped_ptr
<Value
> value(
213 base::JSONReader::Read(supervised_user_shared_setting
.value()));
214 const std::string
& su_id
= supervised_user_shared_setting
.mu_id();
215 ScopedSupervisedUserSharedSettingsUpdate
update(prefs_
, su_id
);
216 const std::string
& key
= supervised_user_shared_setting
.key();
217 DictionaryValue
* dict
= FindOrCreateDictionary(update
.Get(), key
);
218 dict
->SetWithoutPathExpansion(kValue
, value
.release());
220 // Every setting we get from the server should have the acknowledged flag
222 DCHECK(supervised_user_shared_setting
.acknowledged());
223 dict
->SetBooleanWithoutPathExpansion(
224 kAcknowledged
, supervised_user_shared_setting
.acknowledged());
225 callbacks_
.Notify(su_id
, key
);
227 seen_keys
[su_id
].insert(key
);
230 // Iterate over all settings that we have locally, which includes settings
231 // that were just synced down. We filter those out using |seen_keys|.
232 SyncChangeList change_list
;
233 const DictionaryValue
* all_settings
=
234 prefs_
->GetDictionary(prefs::kSupervisedUserSharedSettings
);
235 for (DictionaryValue::Iterator
it(*all_settings
); !it
.IsAtEnd();
237 const DictionaryValue
* dict
= NULL
;
238 bool success
= it
.value().GetAsDictionary(&dict
);
241 const std::set
<std::string
>& seen
= seen_keys
[it
.key()];
242 for (DictionaryValue::Iterator
jt(*dict
); !jt
.IsAtEnd(); jt
.Advance()) {
243 // We only need to upload settings that we haven't seen in the initial
244 // sync data (which means they were added locally).
245 if (seen
.count(jt
.key()) > 0)
248 SyncData data
= CreateSyncDataForValue(it
.key(), jt
.key(), jt
.value());
249 DCHECK(data
.IsValid());
250 change_list
.push_back(
251 SyncChange(FROM_HERE
, SyncChange::ACTION_ADD
, data
));
255 SyncMergeResult
result(SUPERVISED_USER_SHARED_SETTINGS
);
256 // Process all the accumulated changes.
257 if (change_list
.size() > 0) {
259 sync_processor_
->ProcessSyncChanges(FROM_HERE
, change_list
));
262 // TODO(bauerb): Statistics?
266 void SupervisedUserSharedSettingsService::StopSyncing(syncer::ModelType type
) {
267 DCHECK_EQ(SUPERVISED_USER_SHARED_SETTINGS
, type
);
268 sync_processor_
.reset();
269 error_handler_
.reset();
272 syncer::SyncDataList
SupervisedUserSharedSettingsService::GetAllSyncData(
273 syncer::ModelType type
) const {
274 DCHECK_EQ(SUPERVISED_USER_SHARED_SETTINGS
, type
);
276 const DictionaryValue
* all_settings
=
277 prefs_
->GetDictionary(prefs::kSupervisedUserSharedSettings
);
278 for (DictionaryValue::Iterator
it(*all_settings
); !it
.IsAtEnd();
280 const DictionaryValue
* dict
= NULL
;
281 bool success
= it
.value().GetAsDictionary(&dict
);
283 for (DictionaryValue::Iterator
jt(*dict
); !jt
.IsAtEnd(); jt
.Advance()) {
284 data
.push_back(CreateSyncDataForValue(it
.key(), jt
.key(), jt
.value()));
290 syncer::SyncError
SupervisedUserSharedSettingsService::ProcessSyncChanges(
291 const tracked_objects::Location
& from_here
,
292 const syncer::SyncChangeList
& change_list
) {
293 for (SyncChangeList::const_iterator it
= change_list
.begin();
294 it
!= change_list
.end();
296 SyncData data
= it
->sync_data();
297 DCHECK_EQ(SUPERVISED_USER_SHARED_SETTINGS
, data
.GetDataType());
298 const ::sync_pb::ManagedUserSharedSettingSpecifics
&
299 supervised_user_shared_setting
=
300 data
.GetSpecifics().managed_user_shared_setting();
301 const std::string
& key
= supervised_user_shared_setting
.key();
302 const std::string
& su_id
= supervised_user_shared_setting
.mu_id();
303 ScopedSupervisedUserSharedSettingsUpdate
update(prefs_
, su_id
);
304 DictionaryValue
* update_dict
= update
.Get();
305 DictionaryValue
* dict
= NULL
;
306 bool has_key
= update_dict
->GetDictionaryWithoutPathExpansion(key
, &dict
);
307 switch (it
->change_type()) {
308 case SyncChange::ACTION_ADD
:
309 case SyncChange::ACTION_UPDATE
: {
310 // Every setting we get from the server should have the acknowledged
312 DCHECK(supervised_user_shared_setting
.acknowledged());
315 // If the supervised user already exists, it should be an update
317 DCHECK_EQ(SyncChange::ACTION_UPDATE
, it
->change_type());
319 // Otherwise, it should be an add action.
320 DCHECK_EQ(SyncChange::ACTION_ADD
, it
->change_type());
321 dict
= new DictionaryValue
;
322 update_dict
->SetWithoutPathExpansion(key
, dict
);
324 scoped_ptr
<Value
> value(
325 base::JSONReader::Read(supervised_user_shared_setting
.value()));
326 dict
->SetWithoutPathExpansion(kValue
, value
.release());
327 dict
->SetBooleanWithoutPathExpansion(
328 kAcknowledged
, supervised_user_shared_setting
.acknowledged());
331 case SyncChange::ACTION_DELETE
: {
333 update_dict
->RemoveWithoutPathExpansion(key
, NULL
);
335 NOTREACHED() << "Trying to delete nonexistent key " << key
;
338 case SyncChange::ACTION_INVALID
: {
343 callbacks_
.Notify(su_id
, key
);