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_settings_service.h"
6
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"
21
22using base::DictionaryValue;
23using base::JSONReader;
24using base::UserMetricsAction;
25using base::Value;
26using content::BrowserThread;
27using syncer::SUPERVISED_USER_SETTINGS;
28using syncer::ModelType;
29using syncer::SyncChange;
30using syncer::SyncChangeList;
31using syncer::SyncChangeProcessor;
32using syncer::SyncData;
33using syncer::SyncDataList;
34using syncer::SyncError;
35using syncer::SyncErrorFactory;
36using syncer::SyncMergeResult;
37
38const char kAtomicSettings[] = "atomic_settings";
39const char kSupervisedUserInternalItemPrefix[] = "X-";
40const char kQueuedItems[] = "queued_items";
41const char kSplitSettingKeySeparator = ':';
42const char kSplitSettings[] = "split_settings";
43
44namespace {
45
46bool SettingShouldApplyToPrefs(const std::string& name) {
47  return !StartsWithASCII(name, kSupervisedUserInternalItemPrefix, false);
48}
49
50}  // namespace
51
52SupervisedUserSettingsService::SupervisedUserSettingsService()
53    : active_(false), local_settings_(new base::DictionaryValue) {}
54
55SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
56
57void SupervisedUserSettingsService::Init(
58    base::FilePath profile_path,
59    base::SequencedTaskRunner* sequenced_task_runner,
60    bool load_synchronously) {
61  base::FilePath path =
62      profile_path.Append(chrome::kSupervisedUserSettingsFilename);
63  PersistentPrefStore* store = new JsonPrefStore(
64      path, sequenced_task_runner, scoped_ptr<PrefFilter>());
65  Init(store);
66  if (load_synchronously)
67    store_->ReadPrefs();
68  else
69    store_->ReadPrefsAsync(NULL);
70}
71
72void SupervisedUserSettingsService::Init(
73    scoped_refptr<PersistentPrefStore> store) {
74  DCHECK(!store_.get());
75  store_ = store;
76  store_->AddObserver(this);
77}
78
79void SupervisedUserSettingsService::Subscribe(
80    const SettingsCallback& callback) {
81  if (IsReady()) {
82    scoped_ptr<base::DictionaryValue> settings = GetSettings();
83    callback.Run(settings.get());
84  }
85
86  subscribers_.push_back(callback);
87}
88
89void SupervisedUserSettingsService::SetActive(bool active) {
90  active_ = active;
91  InformSubscribers();
92}
93
94bool SupervisedUserSettingsService::IsReady() {
95  return store_->IsInitializationComplete();
96}
97
98void SupervisedUserSettingsService::Clear() {
99  store_->RemoveValue(kAtomicSettings);
100  store_->RemoveValue(kSplitSettings);
101}
102
103// static
104std::string SupervisedUserSettingsService::MakeSplitSettingKey(
105    const std::string& prefix,
106    const std::string& key) {
107  return prefix + kSplitSettingKeySeparator + key;
108}
109
110void SupervisedUserSettingsService::UploadItem(const std::string& key,
111                                               scoped_ptr<base::Value> value) {
112  DCHECK(!SettingShouldApplyToPrefs(key));
113
114  std::string key_suffix = key;
115  base::DictionaryValue* dict = NULL;
116  if (sync_processor_) {
117    content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
118    dict = GetDictionaryAndSplitKey(&key_suffix);
119    DCHECK(GetQueuedItems()->empty());
120    SyncChangeList change_list;
121    SyncData data = CreateSyncDataForSetting(key, *value);
122    SyncChange::SyncChangeType change_type =
123        dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
124                                 : SyncChange::ACTION_ADD;
125    change_list.push_back(SyncChange(FROM_HERE, change_type, data));
126    SyncError error =
127        sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
128    DCHECK(!error.IsSet()) << error.ToString();
129  } else {
130    // Queue the item up to be uploaded when we start syncing
131    // (in MergeDataAndStartSyncing()).
132    content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
133    dict = GetQueuedItems();
134  }
135  dict->SetWithoutPathExpansion(key_suffix, value.release());
136}
137
138void SupervisedUserSettingsService::SetLocalSettingForTesting(
139    const std::string& key,
140    scoped_ptr<base::Value> value) {
141  if (value)
142    local_settings_->SetWithoutPathExpansion(key, value.release());
143  else
144    local_settings_->RemoveWithoutPathExpansion(key, NULL);
145
146  InformSubscribers();
147}
148
149// static
150SyncData SupervisedUserSettingsService::CreateSyncDataForSetting(
151    const std::string& name,
152    const base::Value& value) {
153  std::string json_value;
154  base::JSONWriter::Write(&value, &json_value);
155  ::sync_pb::EntitySpecifics specifics;
156  specifics.mutable_managed_user_setting()->set_name(name);
157  specifics.mutable_managed_user_setting()->set_value(json_value);
158  return SyncData::CreateLocalData(name, name, specifics);
159}
160
161void SupervisedUserSettingsService::Shutdown() {
162  store_->RemoveObserver(this);
163}
164
165SyncMergeResult SupervisedUserSettingsService::MergeDataAndStartSyncing(
166    ModelType type,
167    const SyncDataList& initial_sync_data,
168    scoped_ptr<SyncChangeProcessor> sync_processor,
169    scoped_ptr<SyncErrorFactory> error_handler) {
170  DCHECK_EQ(SUPERVISED_USER_SETTINGS, type);
171  sync_processor_ = sync_processor.Pass();
172  error_handler_ = error_handler.Pass();
173
174  // Clear all atomic and split settings, then recreate them from Sync data.
175  Clear();
176  for (SyncDataList::const_iterator it = initial_sync_data.begin();
177       it != initial_sync_data.end(); ++it) {
178    DCHECK_EQ(SUPERVISED_USER_SETTINGS, it->GetDataType());
179    const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
180        it->GetSpecifics().managed_user_setting();
181    scoped_ptr<base::Value> value(
182        JSONReader::Read(supervised_user_setting.value()));
183    std::string name_suffix = supervised_user_setting.name();
184    base::DictionaryValue* dict = GetDictionaryAndSplitKey(&name_suffix);
185    dict->SetWithoutPathExpansion(name_suffix, value.release());
186  }
187  store_->ReportValueChanged(kAtomicSettings);
188  store_->ReportValueChanged(kSplitSettings);
189  InformSubscribers();
190
191  // Upload all the queued up items (either with an ADD or an UPDATE action,
192  // depending on whether they already exist) and move them to split settings.
193  SyncChangeList change_list;
194  base::DictionaryValue* queued_items = GetQueuedItems();
195  for (base::DictionaryValue::Iterator it(*queued_items); !it.IsAtEnd();
196       it.Advance()) {
197    std::string key_suffix = it.key();
198    base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key_suffix);
199    SyncData data = CreateSyncDataForSetting(it.key(), it.value());
200    SyncChange::SyncChangeType change_type =
201        dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
202                                 : SyncChange::ACTION_ADD;
203    change_list.push_back(SyncChange(FROM_HERE, change_type, data));
204    dict->SetWithoutPathExpansion(key_suffix, it.value().DeepCopy());
205  }
206  queued_items->Clear();
207
208  SyncMergeResult result(SUPERVISED_USER_SETTINGS);
209  // Process all the accumulated changes from the queued items.
210  if (change_list.size() > 0) {
211    store_->ReportValueChanged(kQueuedItems);
212    result.set_error(
213        sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
214  }
215
216  // TODO(bauerb): Statistics?
217  return result;
218}
219
220void SupervisedUserSettingsService::StopSyncing(ModelType type) {
221  DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
222  sync_processor_.reset();
223  error_handler_.reset();
224}
225
226SyncDataList SupervisedUserSettingsService::GetAllSyncData(
227    ModelType type) const {
228  DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
229  SyncDataList data;
230  for (base::DictionaryValue::Iterator it(*GetAtomicSettings()); !it.IsAtEnd();
231       it.Advance()) {
232    data.push_back(CreateSyncDataForSetting(it.key(), it.value()));
233  }
234  for (base::DictionaryValue::Iterator it(*GetSplitSettings()); !it.IsAtEnd();
235       it.Advance()) {
236    const base::DictionaryValue* dict = NULL;
237    it.value().GetAsDictionary(&dict);
238    for (base::DictionaryValue::Iterator jt(*dict);
239         !jt.IsAtEnd(); jt.Advance()) {
240      data.push_back(CreateSyncDataForSetting(
241          MakeSplitSettingKey(it.key(), jt.key()), jt.value()));
242    }
243  }
244  DCHECK_EQ(0u, GetQueuedItems()->size());
245  return data;
246}
247
248SyncError SupervisedUserSettingsService::ProcessSyncChanges(
249    const tracked_objects::Location& from_here,
250    const SyncChangeList& change_list) {
251  for (SyncChangeList::const_iterator it = change_list.begin();
252       it != change_list.end(); ++it) {
253    SyncData data = it->sync_data();
254    DCHECK_EQ(SUPERVISED_USER_SETTINGS, data.GetDataType());
255    const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
256        data.GetSpecifics().managed_user_setting();
257    std::string key = supervised_user_setting.name();
258    base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key);
259    switch (it->change_type()) {
260      case SyncChange::ACTION_ADD:
261      case SyncChange::ACTION_UPDATE: {
262        scoped_ptr<base::Value> value(
263            JSONReader::Read(supervised_user_setting.value()));
264        if (dict->HasKey(key)) {
265          DLOG_IF(WARNING, it->change_type() == SyncChange::ACTION_ADD)
266              << "Value for key " << key << " already exists";
267        } else {
268          DLOG_IF(WARNING, it->change_type() == SyncChange::ACTION_UPDATE)
269              << "Value for key " << key << " doesn't exist yet";
270        }
271        dict->SetWithoutPathExpansion(key, value.release());
272        break;
273      }
274      case SyncChange::ACTION_DELETE: {
275        DLOG_IF(WARNING, !dict->HasKey(key)) << "Trying to delete nonexistent "
276                                             << "key " << key;
277        dict->RemoveWithoutPathExpansion(key, NULL);
278        break;
279      }
280      case SyncChange::ACTION_INVALID: {
281        NOTREACHED();
282        break;
283      }
284    }
285  }
286  store_->ReportValueChanged(kAtomicSettings);
287  store_->ReportValueChanged(kSplitSettings);
288  InformSubscribers();
289
290  SyncError error;
291  return error;
292}
293
294void SupervisedUserSettingsService::OnPrefValueChanged(const std::string& key) {
295}
296
297void SupervisedUserSettingsService::OnInitializationCompleted(bool success) {
298  DCHECK(success);
299  DCHECK(IsReady());
300  InformSubscribers();
301}
302
303base::DictionaryValue* SupervisedUserSettingsService::GetOrCreateDictionary(
304    const std::string& key) const {
305  base::Value* value = NULL;
306  base::DictionaryValue* dict = NULL;
307  if (store_->GetMutableValue(key, &value)) {
308    bool success = value->GetAsDictionary(&dict);
309    DCHECK(success);
310  } else {
311    dict = new base::DictionaryValue;
312    store_->SetValue(key, dict);
313  }
314
315  return dict;
316}
317
318base::DictionaryValue*
319SupervisedUserSettingsService::GetAtomicSettings() const {
320  return GetOrCreateDictionary(kAtomicSettings);
321}
322
323base::DictionaryValue* SupervisedUserSettingsService::GetSplitSettings() const {
324  return GetOrCreateDictionary(kSplitSettings);
325}
326
327base::DictionaryValue* SupervisedUserSettingsService::GetQueuedItems() const {
328  return GetOrCreateDictionary(kQueuedItems);
329}
330
331base::DictionaryValue* SupervisedUserSettingsService::GetDictionaryAndSplitKey(
332    std::string* key) const {
333  size_t pos = key->find_first_of(kSplitSettingKeySeparator);
334  if (pos == std::string::npos)
335    return GetAtomicSettings();
336
337  base::DictionaryValue* split_settings = GetSplitSettings();
338  std::string prefix = key->substr(0, pos);
339  base::DictionaryValue* dict = NULL;
340  if (!split_settings->GetDictionary(prefix, &dict)) {
341    dict = new base::DictionaryValue;
342    DCHECK(!split_settings->HasKey(prefix));
343    split_settings->Set(prefix, dict);
344  }
345  key->erase(0, pos + 1);
346  return dict;
347}
348
349scoped_ptr<base::DictionaryValue> SupervisedUserSettingsService::GetSettings() {
350  DCHECK(IsReady());
351  if (!active_)
352    return scoped_ptr<base::DictionaryValue>();
353
354  scoped_ptr<base::DictionaryValue> settings(local_settings_->DeepCopy());
355
356  base::DictionaryValue* atomic_settings = GetAtomicSettings();
357  for (base::DictionaryValue::Iterator it(*atomic_settings); !it.IsAtEnd();
358       it.Advance()) {
359    if (!SettingShouldApplyToPrefs(it.key()))
360      continue;
361
362    settings->Set(it.key(), it.value().DeepCopy());
363  }
364
365  base::DictionaryValue* split_settings = GetSplitSettings();
366  for (base::DictionaryValue::Iterator it(*split_settings); !it.IsAtEnd();
367       it.Advance()) {
368    if (!SettingShouldApplyToPrefs(it.key()))
369      continue;
370
371    settings->Set(it.key(), it.value().DeepCopy());
372  }
373
374  return settings.Pass();
375}
376
377void SupervisedUserSettingsService::InformSubscribers() {
378  if (!IsReady())
379    return;
380
381  scoped_ptr<base::DictionaryValue> settings = GetSettings();
382  for (std::vector<SettingsCallback>::iterator it = subscribers_.begin();
383       it != subscribers_.end(); ++it) {
384    it->Run(settings.get());
385  }
386}
387