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_shared_settings_service.h"
6
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"
20
21using base::DictionaryValue;
22using base::Value;
23using syncer::ModelType;
24using syncer::SUPERVISED_USER_SHARED_SETTINGS;
25using syncer::SyncChange;
26using syncer::SyncChangeList;
27using syncer::SyncChangeProcessor;
28using syncer::SyncData;
29using syncer::SyncDataList;
30using syncer::SyncError;
31using syncer::SyncErrorFactory;
32using syncer::SyncMergeResult;
33
34namespace {
35
36const char kAcknowledged[] = "acknowledged";
37const char kValue[] = "value";
38
39DictionaryValue* 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);
45  }
46  return dict;
47}
48
49class ScopedSupervisedUserSharedSettingsUpdate {
50 public:
51  ScopedSupervisedUserSharedSettingsUpdate(PrefService* prefs,
52                                           const std::string& su_id)
53      : update_(prefs, prefs::kSupervisedUserSharedSettings), su_id_(su_id) {
54    DCHECK(!su_id.empty());
55
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);
59  }
60
61  DictionaryValue* Get() {
62    return FindOrCreateDictionary(update_.Get(), su_id_);
63  }
64
65 private:
66  DictionaryPrefUpdate update_;
67  std::string su_id_;
68};
69
70SyncData 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))
76    return SyncData();
77
78  const Value* value = NULL;
79  if (!dict->Get(kValue, &value))
80    return SyncData();
81
82  bool acknowledged = false;
83  dict->GetBoolean(kAcknowledged, &acknowledged);
84
85  return SupervisedUserSharedSettingsService::CreateSyncDataForSetting(
86      su_id, key, *value, acknowledged);
87}
88
89}  // namespace
90
91
92SupervisedUserSharedSettingsService::SupervisedUserSharedSettingsService(
93    PrefService* prefs)
94    : prefs_(prefs) {}
95
96SupervisedUserSharedSettingsService::~SupervisedUserSharedSettingsService() {}
97
98void SupervisedUserSharedSettingsService::SetValueInternal(
99    const std::string& su_id,
100    const std::string& key,
101    const Value& value,
102    bool acknowledged) {
103  ScopedSupervisedUserSharedSettingsUpdate update(prefs_, su_id);
104  DictionaryValue* update_dict = update.Get();
105
106  DictionaryValue* dict = NULL;
107  bool has_key = update_dict->GetDictionaryWithoutPathExpansion(key, &dict);
108  if (!has_key) {
109    dict = new DictionaryValue;
110    update_dict->SetWithoutPathExpansion(key, dict);
111  }
112  dict->SetWithoutPathExpansion(kValue, value.DeepCopy());
113  dict->SetBooleanWithoutPathExpansion(kAcknowledged, acknowledged);
114
115  if (!sync_processor_)
116    return;
117
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();
125}
126
127const 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))
134    return NULL;
135
136  const DictionaryValue* settings = NULL;
137  if (!dict->GetDictionaryWithoutPathExpansion(key, &settings))
138    return NULL;
139
140  const Value* value = NULL;
141  if (!settings->GetWithoutPathExpansion(kValue, &value))
142    return NULL;
143
144  return value;
145}
146
147void SupervisedUserSharedSettingsService::SetValue(
148    const std::string& su_id,
149    const std::string& key,
150    const Value& value) {
151  SetValueInternal(su_id, key, value, true);
152}
153
154scoped_ptr<
155    SupervisedUserSharedSettingsService::ChangeCallbackList::Subscription>
156SupervisedUserSharedSettingsService::Subscribe(
157    const SupervisedUserSharedSettingsService::ChangeCallback& cb) {
158  return callbacks_.Add(cb);
159}
160
161// static
162void SupervisedUserSharedSettingsService::RegisterProfilePrefs(
163    user_prefs::PrefRegistrySyncable* registry) {
164  registry->RegisterDictionaryPref(
165      prefs::kSupervisedUserSharedSettings,
166      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
167}
168
169// static
170SyncData SupervisedUserSharedSettingsService::CreateSyncDataForSetting(
171    const std::string& su_id,
172    const std::string& key,
173    const Value& value,
174    bool acknowledged) {
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(
182      acknowledged);
183  std::string title = su_id + ":" + key;
184  return SyncData::CreateLocalData(title, title, specifics);
185}
186
187void SupervisedUserSharedSettingsService::Shutdown() {}
188
189syncer::SyncMergeResult
190SupervisedUserSharedSettingsService::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();
198
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;
202
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();
207       ++it) {
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());
219
220    // Every setting we get from the server should have the acknowledged flag
221    // set.
222    DCHECK(supervised_user_shared_setting.acknowledged());
223    dict->SetBooleanWithoutPathExpansion(
224        kAcknowledged, supervised_user_shared_setting.acknowledged());
225    callbacks_.Notify(su_id, key);
226
227    seen_keys[su_id].insert(key);
228  }
229
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();
236       it.Advance()) {
237    const DictionaryValue* dict = NULL;
238    bool success = it.value().GetAsDictionary(&dict);
239    DCHECK(success);
240
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)
246        continue;
247
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));
252    }
253  }
254
255  SyncMergeResult result(SUPERVISED_USER_SHARED_SETTINGS);
256  // Process all the accumulated changes.
257  if (change_list.size() > 0) {
258    result.set_error(
259        sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
260  }
261
262  // TODO(bauerb): Statistics?
263  return result;
264}
265
266void SupervisedUserSharedSettingsService::StopSyncing(syncer::ModelType type) {
267  DCHECK_EQ(SUPERVISED_USER_SHARED_SETTINGS, type);
268  sync_processor_.reset();
269  error_handler_.reset();
270}
271
272syncer::SyncDataList SupervisedUserSharedSettingsService::GetAllSyncData(
273    syncer::ModelType type) const {
274  DCHECK_EQ(SUPERVISED_USER_SHARED_SETTINGS, type);
275  SyncDataList data;
276  const DictionaryValue* all_settings =
277      prefs_->GetDictionary(prefs::kSupervisedUserSharedSettings);
278  for (DictionaryValue::Iterator it(*all_settings); !it.IsAtEnd();
279       it.Advance()) {
280    const DictionaryValue* dict = NULL;
281    bool success = it.value().GetAsDictionary(&dict);
282    DCHECK(success);
283    for (DictionaryValue::Iterator jt(*dict); !jt.IsAtEnd(); jt.Advance()) {
284      data.push_back(CreateSyncDataForValue(it.key(), jt.key(), jt.value()));
285    }
286  }
287  return data;
288}
289
290syncer::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();
295       ++it) {
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
311        // flag set.
312        DCHECK(supervised_user_shared_setting.acknowledged());
313
314        if (has_key) {
315          // If the supervised user already exists, it should be an update
316          // action.
317          DCHECK_EQ(SyncChange::ACTION_UPDATE, it->change_type());
318        } else {
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);
323        }
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());
329        break;
330      }
331      case SyncChange::ACTION_DELETE: {
332        if (has_key)
333          update_dict->RemoveWithoutPathExpansion(key, NULL);
334        else
335          NOTREACHED() << "Trying to delete nonexistent key " << key;
336        break;
337      }
338      case SyncChange::ACTION_INVALID: {
339        NOTREACHED();
340        break;
341      }
342    }
343    callbacks_.Notify(su_id, key);
344  }
345
346  SyncError error;
347  return error;
348}
349