Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / chrome / browser / supervised_user / supervised_user_settings_service.cc
blobdc47d60812b47957b423375c0e037c09551315d3
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;
25 using base::Value;
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";
44 namespace {
46 bool SettingShouldApplyToPrefs(const std::string& name) {
47 return !StartsWithASCII(name, kSupervisedUserInternalItemPrefix, false);
50 } // namespace
52 SupervisedUserSettingsService::SupervisedUserSettingsService()
53 : active_(false),
54 initialization_failed_(false),
55 local_settings_(new base::DictionaryValue) {
58 SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
60 void SupervisedUserSettingsService::Init(
61 base::FilePath profile_path,
62 base::SequencedTaskRunner* sequenced_task_runner,
63 bool load_synchronously) {
64 base::FilePath path =
65 profile_path.Append(chrome::kSupervisedUserSettingsFilename);
66 PersistentPrefStore* store = new JsonPrefStore(
67 path, sequenced_task_runner, scoped_ptr<PrefFilter>());
68 Init(store);
69 if (load_synchronously) {
70 store_->ReadPrefs();
71 // TODO(bauerb): Temporary CHECK while investigating
72 // https://crbug.com/425785. Remove (or change to DCHECK) once the bug
73 // is fixed.
74 CHECK(store_->IsInitializationComplete());
75 } else {
76 store_->ReadPrefsAsync(NULL);
80 void SupervisedUserSettingsService::Init(
81 scoped_refptr<PersistentPrefStore> store) {
82 DCHECK(!store_.get());
83 store_ = store;
84 store_->AddObserver(this);
87 void SupervisedUserSettingsService::Subscribe(
88 const SettingsCallback& callback) {
89 if (IsReady()) {
90 scoped_ptr<base::DictionaryValue> settings = GetSettings();
91 callback.Run(settings.get());
94 subscribers_.push_back(callback);
97 void SupervisedUserSettingsService::SetActive(bool active) {
98 active_ = active;
99 InformSubscribers();
102 bool SupervisedUserSettingsService::IsReady() {
103 // Initialization cannot be complete but have failed at the same time.
104 DCHECK(!(store_->IsInitializationComplete() && initialization_failed_));
105 return initialization_failed_ || store_->IsInitializationComplete();
108 void SupervisedUserSettingsService::Clear() {
109 store_->RemoveValue(kAtomicSettings,
110 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
111 store_->RemoveValue(kSplitSettings,
112 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
115 // static
116 std::string SupervisedUserSettingsService::MakeSplitSettingKey(
117 const std::string& prefix,
118 const std::string& key) {
119 return prefix + kSplitSettingKeySeparator + key;
122 void SupervisedUserSettingsService::UploadItem(const std::string& key,
123 scoped_ptr<base::Value> value) {
124 DCHECK(!SettingShouldApplyToPrefs(key));
126 std::string key_suffix = key;
127 base::DictionaryValue* dict = NULL;
128 if (sync_processor_) {
129 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
130 dict = GetDictionaryAndSplitKey(&key_suffix);
131 DCHECK(GetQueuedItems()->empty());
132 SyncChangeList change_list;
133 SyncData data = CreateSyncDataForSetting(key, *value);
134 SyncChange::SyncChangeType change_type =
135 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
136 : SyncChange::ACTION_ADD;
137 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
138 SyncError error =
139 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
140 DCHECK(!error.IsSet()) << error.ToString();
141 } else {
142 // Queue the item up to be uploaded when we start syncing
143 // (in MergeDataAndStartSyncing()).
144 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
145 dict = GetQueuedItems();
147 dict->SetWithoutPathExpansion(key_suffix, value.release());
150 void SupervisedUserSettingsService::SetLocalSetting(
151 const std::string& key,
152 scoped_ptr<base::Value> value) {
153 if (value)
154 local_settings_->SetWithoutPathExpansion(key, value.release());
155 else
156 local_settings_->RemoveWithoutPathExpansion(key, NULL);
158 InformSubscribers();
161 // static
162 SyncData SupervisedUserSettingsService::CreateSyncDataForSetting(
163 const std::string& name,
164 const base::Value& value) {
165 std::string json_value;
166 base::JSONWriter::Write(value, &json_value);
167 ::sync_pb::EntitySpecifics specifics;
168 specifics.mutable_managed_user_setting()->set_name(name);
169 specifics.mutable_managed_user_setting()->set_value(json_value);
170 return SyncData::CreateLocalData(name, name, specifics);
173 void SupervisedUserSettingsService::Shutdown() {
174 store_->RemoveObserver(this);
177 SyncMergeResult SupervisedUserSettingsService::MergeDataAndStartSyncing(
178 ModelType type,
179 const SyncDataList& initial_sync_data,
180 scoped_ptr<SyncChangeProcessor> sync_processor,
181 scoped_ptr<SyncErrorFactory> error_handler) {
182 DCHECK_EQ(SUPERVISED_USER_SETTINGS, type);
183 sync_processor_ = sync_processor.Pass();
184 error_handler_ = error_handler.Pass();
186 // Clear all atomic and split settings, then recreate them from Sync data.
187 Clear();
188 for (const SyncData& sync_data : initial_sync_data) {
189 DCHECK_EQ(SUPERVISED_USER_SETTINGS, sync_data.GetDataType());
190 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
191 sync_data.GetSpecifics().managed_user_setting();
192 scoped_ptr<base::Value> value(
193 JSONReader::DeprecatedRead(supervised_user_setting.value()));
194 std::string name_suffix = supervised_user_setting.name();
195 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&name_suffix);
196 dict->SetWithoutPathExpansion(name_suffix, value.release());
198 store_->ReportValueChanged(kAtomicSettings,
199 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
200 store_->ReportValueChanged(kSplitSettings,
201 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
202 InformSubscribers();
204 // Upload all the queued up items (either with an ADD or an UPDATE action,
205 // depending on whether they already exist) and move them to split settings.
206 SyncChangeList change_list;
207 base::DictionaryValue* queued_items = GetQueuedItems();
208 for (base::DictionaryValue::Iterator it(*queued_items); !it.IsAtEnd();
209 it.Advance()) {
210 std::string key_suffix = it.key();
211 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key_suffix);
212 SyncData data = CreateSyncDataForSetting(it.key(), it.value());
213 SyncChange::SyncChangeType change_type =
214 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
215 : SyncChange::ACTION_ADD;
216 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
217 dict->SetWithoutPathExpansion(key_suffix, it.value().DeepCopy());
219 queued_items->Clear();
221 SyncMergeResult result(SUPERVISED_USER_SETTINGS);
222 // Process all the accumulated changes from the queued items.
223 if (change_list.size() > 0) {
224 store_->ReportValueChanged(kQueuedItems,
225 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
226 result.set_error(
227 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
230 // TODO(bauerb): Statistics?
231 return result;
234 void SupervisedUserSettingsService::StopSyncing(ModelType type) {
235 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
236 sync_processor_.reset();
237 error_handler_.reset();
240 SyncDataList SupervisedUserSettingsService::GetAllSyncData(
241 ModelType type) const {
242 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
243 SyncDataList data;
244 for (base::DictionaryValue::Iterator it(*GetAtomicSettings()); !it.IsAtEnd();
245 it.Advance()) {
246 data.push_back(CreateSyncDataForSetting(it.key(), it.value()));
248 for (base::DictionaryValue::Iterator it(*GetSplitSettings()); !it.IsAtEnd();
249 it.Advance()) {
250 const base::DictionaryValue* dict = NULL;
251 it.value().GetAsDictionary(&dict);
252 for (base::DictionaryValue::Iterator jt(*dict);
253 !jt.IsAtEnd(); jt.Advance()) {
254 data.push_back(CreateSyncDataForSetting(
255 MakeSplitSettingKey(it.key(), jt.key()), jt.value()));
258 DCHECK_EQ(0u, GetQueuedItems()->size());
259 return data;
262 SyncError SupervisedUserSettingsService::ProcessSyncChanges(
263 const tracked_objects::Location& from_here,
264 const SyncChangeList& change_list) {
265 for (const SyncChange& sync_change : change_list) {
266 SyncData data = sync_change.sync_data();
267 DCHECK_EQ(SUPERVISED_USER_SETTINGS, data.GetDataType());
268 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
269 data.GetSpecifics().managed_user_setting();
270 std::string key = supervised_user_setting.name();
271 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key);
272 SyncChange::SyncChangeType change_type = sync_change.change_type();
273 switch (change_type) {
274 case SyncChange::ACTION_ADD:
275 case SyncChange::ACTION_UPDATE: {
276 scoped_ptr<base::Value> value(
277 JSONReader::DeprecatedRead(supervised_user_setting.value()));
278 if (dict->HasKey(key)) {
279 DLOG_IF(WARNING, change_type == SyncChange::ACTION_ADD)
280 << "Value for key " << key << " already exists";
281 } else {
282 DLOG_IF(WARNING, change_type == SyncChange::ACTION_UPDATE)
283 << "Value for key " << key << " doesn't exist yet";
285 dict->SetWithoutPathExpansion(key, value.release());
286 break;
288 case SyncChange::ACTION_DELETE: {
289 DLOG_IF(WARNING, !dict->HasKey(key)) << "Trying to delete nonexistent "
290 << "key " << key;
291 dict->RemoveWithoutPathExpansion(key, NULL);
292 break;
294 case SyncChange::ACTION_INVALID: {
295 NOTREACHED();
296 break;
300 store_->ReportValueChanged(kAtomicSettings,
301 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
302 store_->ReportValueChanged(kSplitSettings,
303 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
304 InformSubscribers();
306 SyncError error;
307 return error;
310 void SupervisedUserSettingsService::OnPrefValueChanged(const std::string& key) {
313 void SupervisedUserSettingsService::OnInitializationCompleted(bool success) {
314 if (!success) {
315 // If this happens, it means the profile directory was not found. There is
316 // not much we can do, but the whole profile will probably be useless
317 // anyway. Just mark initialization as failed and continue otherwise,
318 // because subscribers might still expect to be called back.
319 initialization_failed_ = true;
322 // TODO(bauerb): Temporary CHECK while investigating https://crbug.com/425785.
323 // Remove (or change back to DCHECK) once the bug is fixed.
324 CHECK(IsReady());
325 InformSubscribers();
328 base::DictionaryValue* SupervisedUserSettingsService::GetOrCreateDictionary(
329 const std::string& key) const {
330 base::Value* value = NULL;
331 base::DictionaryValue* dict = NULL;
332 if (store_->GetMutableValue(key, &value)) {
333 bool success = value->GetAsDictionary(&dict);
334 DCHECK(success);
335 } else {
336 dict = new base::DictionaryValue;
337 store_->SetValue(key, dict, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
340 return dict;
343 base::DictionaryValue*
344 SupervisedUserSettingsService::GetAtomicSettings() const {
345 return GetOrCreateDictionary(kAtomicSettings);
348 base::DictionaryValue* SupervisedUserSettingsService::GetSplitSettings() const {
349 return GetOrCreateDictionary(kSplitSettings);
352 base::DictionaryValue* SupervisedUserSettingsService::GetQueuedItems() const {
353 return GetOrCreateDictionary(kQueuedItems);
356 base::DictionaryValue* SupervisedUserSettingsService::GetDictionaryAndSplitKey(
357 std::string* key) const {
358 size_t pos = key->find_first_of(kSplitSettingKeySeparator);
359 if (pos == std::string::npos)
360 return GetAtomicSettings();
362 base::DictionaryValue* split_settings = GetSplitSettings();
363 std::string prefix = key->substr(0, pos);
364 base::DictionaryValue* dict = NULL;
365 if (!split_settings->GetDictionary(prefix, &dict)) {
366 dict = new base::DictionaryValue;
367 DCHECK(!split_settings->HasKey(prefix));
368 split_settings->Set(prefix, dict);
370 key->erase(0, pos + 1);
371 return dict;
374 scoped_ptr<base::DictionaryValue> SupervisedUserSettingsService::GetSettings() {
375 DCHECK(IsReady());
376 if (!active_ || initialization_failed_)
377 return scoped_ptr<base::DictionaryValue>();
379 scoped_ptr<base::DictionaryValue> settings(local_settings_->DeepCopy());
381 base::DictionaryValue* atomic_settings = GetAtomicSettings();
382 for (base::DictionaryValue::Iterator it(*atomic_settings); !it.IsAtEnd();
383 it.Advance()) {
384 if (!SettingShouldApplyToPrefs(it.key()))
385 continue;
387 settings->Set(it.key(), it.value().DeepCopy());
390 base::DictionaryValue* split_settings = GetSplitSettings();
391 for (base::DictionaryValue::Iterator it(*split_settings); !it.IsAtEnd();
392 it.Advance()) {
393 if (!SettingShouldApplyToPrefs(it.key()))
394 continue;
396 settings->Set(it.key(), it.value().DeepCopy());
399 return settings.Pass();
402 void SupervisedUserSettingsService::InformSubscribers() {
403 if (!IsReady())
404 return;
406 scoped_ptr<base::DictionaryValue> settings = GetSettings();
407 for (const auto& callback : subscribers_)
408 callback.Run(settings.get());