Index: chrome/browser/managed_mode/managed_user_shared_settings_service.cc |
diff --git a/chrome/browser/managed_mode/managed_user_shared_settings_service.cc b/chrome/browser/managed_mode/managed_user_shared_settings_service.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e751a59cc246938df2c7d0a508b3e9016f2d2850 |
--- /dev/null |
+++ b/chrome/browser/managed_mode/managed_user_shared_settings_service.cc |
@@ -0,0 +1,346 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/managed_mode/managed_user_shared_settings_service.h" |
+ |
+#include "base/json/json_reader.h" |
+#include "base/json/json_writer.h" |
+#include "base/prefs/pref_service.h" |
+#include "base/prefs/scoped_user_pref_update.h" |
+#include "base/values.h" |
+#include "chrome/common/pref_names.h" |
+#include "components/user_prefs/pref_registry_syncable.h" |
+#include "sync/api/sync_change.h" |
+#include "sync/api/sync_data.h" |
+#include "sync/api/sync_error.h" |
+#include "sync/api/sync_error_factory.h" |
+#include "sync/api/sync_merge_result.h" |
+#include "sync/protocol/sync.pb.h" |
+ |
+using base::DictionaryValue; |
+using base::Value; |
+using syncer::MANAGED_USER_SHARED_SETTINGS; |
+using syncer::ModelType; |
+using syncer::SyncChange; |
+using syncer::SyncChangeList; |
+using syncer::SyncChangeProcessor; |
+using syncer::SyncData; |
+using syncer::SyncDataList; |
+using syncer::SyncError; |
+using syncer::SyncErrorFactory; |
+using syncer::SyncMergeResult; |
+ |
+namespace { |
+ |
+const char kAcknowledged[] = "acknowledged"; |
+const char kValue[] = "value"; |
+ |
+DictionaryValue* FindOrCreateDictionary(DictionaryValue* parent, |
+ const std::string& key) { |
+ DictionaryValue* dict = NULL; |
+ if (!parent->GetDictionaryWithoutPathExpansion(key, &dict)) { |
+ dict = new DictionaryValue; |
+ parent->SetWithoutPathExpansion(key, dict); |
+ } |
+ return dict; |
+} |
+ |
+class ScopedManagedUserSharedSettingsUpdate { |
+ public: |
+ ScopedManagedUserSharedSettingsUpdate(PrefService* prefs, |
+ const std::string& mu_id) |
+ : update_(prefs, prefs::kManagedUserSharedSettings), mu_id_(mu_id) { |
+ DCHECK(!mu_id.empty()); |
+ |
+ // A supervised user can only modify their own settings. |
+ std::string id = prefs->GetString(prefs::kManagedUserId); |
+ DCHECK(id.empty() || id == mu_id); |
+ } |
+ |
+ DictionaryValue* Get() { |
+ return FindOrCreateDictionary(update_.Get(), mu_id_); |
+ } |
+ |
+ private: |
+ DictionaryPrefUpdate update_; |
+ std::string mu_id_; |
+}; |
+ |
+SyncData CreateSyncDataForValue( |
+ const std::string& mu_id, |
+ const std::string& key, |
+ const Value& dict_value) { |
+ const DictionaryValue* dict = NULL; |
+ if (!dict_value.GetAsDictionary(&dict)) |
+ return SyncData(); |
+ |
+ const Value* value = NULL; |
+ if (!dict->Get(kValue, &value)) |
+ return SyncData(); |
+ |
+ bool acknowledged = false; |
+ dict->GetBoolean(kAcknowledged, &acknowledged); |
+ |
+ return ManagedUserSharedSettingsService::CreateSyncDataForSetting( |
+ mu_id, key, *value, acknowledged); |
+} |
+ |
+} // namespace |
+ |
+ |
+ManagedUserSharedSettingsService::ManagedUserSharedSettingsService( |
+ PrefService* prefs) |
+ : prefs_(prefs) {} |
+ |
+ManagedUserSharedSettingsService::~ManagedUserSharedSettingsService() {} |
+ |
+void ManagedUserSharedSettingsService::SetValueInternal( |
+ const std::string& mu_id, |
+ const std::string& key, |
+ const Value& value, |
+ bool acknowledged) { |
+ ScopedManagedUserSharedSettingsUpdate update(prefs_, mu_id); |
+ DictionaryValue* update_dict = update.Get(); |
+ |
+ DictionaryValue* dict = NULL; |
+ bool has_key = update_dict->GetDictionaryWithoutPathExpansion(key, &dict); |
+ if (!has_key) { |
+ dict = new DictionaryValue; |
+ update_dict->SetWithoutPathExpansion(key, dict); |
+ } |
+ dict->SetWithoutPathExpansion(kValue, value.DeepCopy()); |
+ dict->SetBooleanWithoutPathExpansion(kAcknowledged, acknowledged); |
+ |
+ if (!sync_processor_) |
+ return; |
+ |
+ SyncData data = CreateSyncDataForSetting(mu_id, key, value, acknowledged); |
+ SyncChange::SyncChangeType change_type = |
+ has_key ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD; |
+ SyncChangeList changes; |
+ changes.push_back(SyncChange(FROM_HERE, change_type, data)); |
+ SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
+ DCHECK(!error.IsSet()) << error.ToString(); |
+} |
+ |
+const Value* ManagedUserSharedSettingsService::GetValue( |
+ const std::string& mu_id, |
+ const std::string& key) { |
+ const DictionaryValue* data = |
+ prefs_->GetDictionary(prefs::kManagedUserSharedSettings); |
+ const DictionaryValue* dict = NULL; |
+ if (!data->GetDictionaryWithoutPathExpansion(mu_id, &dict)) |
+ return NULL; |
+ |
+ const DictionaryValue* settings = NULL; |
+ if (!dict->GetDictionaryWithoutPathExpansion(key, &settings)) |
+ return NULL; |
+ |
+ const Value* value = NULL; |
+ if (!settings->GetWithoutPathExpansion(kValue, &value)) |
+ return NULL; |
+ |
+ return value; |
+} |
+ |
+void ManagedUserSharedSettingsService::SetValue( |
+ const std::string& mu_id, |
+ const std::string& key, |
+ const Value& value) { |
+ SetValueInternal(mu_id, key, value, true); |
+} |
+ |
+scoped_ptr<ManagedUserSharedSettingsService::ChangeCallbackList::Subscription> |
+ManagedUserSharedSettingsService::Subscribe( |
+ const ManagedUserSharedSettingsService::ChangeCallback& cb) { |
+ return callbacks_.Add(cb); |
+} |
+ |
+// static |
+void ManagedUserSharedSettingsService::RegisterProfilePrefs( |
+ user_prefs::PrefRegistrySyncable* registry) { |
+ registry->RegisterDictionaryPref( |
+ prefs::kManagedUserSharedSettings, |
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
+} |
+ |
+// static |
+SyncData ManagedUserSharedSettingsService::CreateSyncDataForSetting( |
+ const std::string& mu_id, |
+ const std::string& key, |
+ const Value& value, |
+ bool acknowledged) { |
+ std::string json_value; |
+ base::JSONWriter::Write(&value, &json_value); |
+ ::sync_pb::EntitySpecifics specifics; |
+ specifics.mutable_managed_user_shared_setting()->set_mu_id(mu_id); |
+ specifics.mutable_managed_user_shared_setting()->set_key(key); |
+ specifics.mutable_managed_user_shared_setting()->set_value(json_value); |
+ specifics.mutable_managed_user_shared_setting()->set_acknowledged( |
+ acknowledged); |
+ std::string title = mu_id + ":" + key; |
+ return SyncData::CreateLocalData(title, title, specifics); |
+} |
+ |
+void ManagedUserSharedSettingsService::Shutdown() {} |
+ |
+syncer::SyncMergeResult |
+ManagedUserSharedSettingsService::MergeDataAndStartSyncing( |
+ syncer::ModelType type, |
+ const syncer::SyncDataList& initial_sync_data, |
+ scoped_ptr<syncer::SyncChangeProcessor> sync_processor, |
+ scoped_ptr<syncer::SyncErrorFactory> error_handler) { |
+ DCHECK_EQ(MANAGED_USER_SHARED_SETTINGS, type); |
+ sync_processor_ = sync_processor.Pass(); |
+ error_handler_ = error_handler.Pass(); |
+ |
+ // We keep a map from MU ID to the set of keys that we have seen in the |
+ // initial sync data. |
+ std::map<std::string, std::set<std::string> > seen_keys; |
+ |
+ // Iterate over all initial sync data, and update it locally. This means that |
+ // the value from the server always wins over a local value. |
+ for (SyncDataList::const_iterator it = initial_sync_data.begin(); |
+ it != initial_sync_data.end(); |
+ ++it) { |
+ DCHECK_EQ(MANAGED_USER_SHARED_SETTINGS, it->GetDataType()); |
+ const ::sync_pb::ManagedUserSharedSettingSpecifics& |
+ managed_user_shared_setting = |
+ it->GetSpecifics().managed_user_shared_setting(); |
+ scoped_ptr<Value> value( |
+ base::JSONReader::Read(managed_user_shared_setting.value())); |
+ const std::string& mu_id = managed_user_shared_setting.mu_id(); |
+ ScopedManagedUserSharedSettingsUpdate update(prefs_, mu_id); |
+ const std::string& key = managed_user_shared_setting.key(); |
+ DictionaryValue* dict = FindOrCreateDictionary(update.Get(), key); |
+ dict->SetWithoutPathExpansion(kValue, value.release()); |
+ |
+ // Every setting we get from the server should have the acknowledged flag |
+ // set. |
+ DCHECK(managed_user_shared_setting.acknowledged()); |
+ dict->SetBooleanWithoutPathExpansion( |
+ kAcknowledged, managed_user_shared_setting.acknowledged()); |
+ callbacks_.Notify(mu_id, key); |
+ |
+ seen_keys[mu_id].insert(key); |
+ } |
+ |
+ // Iterate over all settings that we have locally, which includes settings |
+ // that were just synced down. We filter those out using |seen_keys|. |
+ SyncChangeList change_list; |
+ const DictionaryValue* all_settings = |
+ prefs_->GetDictionary(prefs::kManagedUserSharedSettings); |
+ for (DictionaryValue::Iterator it(*all_settings); !it.IsAtEnd(); |
+ it.Advance()) { |
+ const DictionaryValue* dict = NULL; |
+ bool success = it.value().GetAsDictionary(&dict); |
+ DCHECK(success); |
+ |
+ const std::set<std::string>& seen = seen_keys[it.key()]; |
+ for (DictionaryValue::Iterator jt(*dict); !jt.IsAtEnd(); jt.Advance()) { |
+ // We only need to upload settings that we haven't seen in the initial |
+ // sync data (which means they were added locally). |
+ if (seen.count(jt.key()) > 0) |
+ continue; |
+ |
+ SyncData data = CreateSyncDataForValue(it.key(), jt.key(), jt.value()); |
+ DCHECK(data.IsValid()); |
+ change_list.push_back( |
+ SyncChange(FROM_HERE, SyncChange::ACTION_ADD, data)); |
+ } |
+ } |
+ |
+ SyncMergeResult result(MANAGED_USER_SHARED_SETTINGS); |
+ // Process all the accumulated changes. |
+ if (change_list.size() > 0) { |
+ result.set_error( |
+ sync_processor_->ProcessSyncChanges(FROM_HERE, change_list)); |
+ } |
+ |
+ // TODO(bauerb): Statistics? |
+ return result; |
+} |
+ |
+void ManagedUserSharedSettingsService::StopSyncing(syncer::ModelType type) { |
+ DCHECK_EQ(MANAGED_USER_SHARED_SETTINGS, type); |
+ sync_processor_.reset(); |
+ error_handler_.reset(); |
+} |
+ |
+syncer::SyncDataList ManagedUserSharedSettingsService::GetAllSyncData( |
+ syncer::ModelType type) const { |
+ DCHECK_EQ(MANAGED_USER_SHARED_SETTINGS, type); |
+ SyncDataList data; |
+ const DictionaryValue* all_settings = |
+ prefs_->GetDictionary(prefs::kManagedUserSharedSettings); |
+ for (DictionaryValue::Iterator it(*all_settings); !it.IsAtEnd(); |
+ it.Advance()) { |
+ const DictionaryValue* dict = NULL; |
+ bool success = it.value().GetAsDictionary(&dict); |
+ DCHECK(success); |
+ for (DictionaryValue::Iterator jt(*dict); !jt.IsAtEnd(); jt.Advance()) { |
+ data.push_back(CreateSyncDataForValue(it.key(), jt.key(), jt.value())); |
+ } |
+ } |
+ return data; |
+} |
+ |
+syncer::SyncError ManagedUserSharedSettingsService::ProcessSyncChanges( |
+ const tracked_objects::Location& from_here, |
+ const syncer::SyncChangeList& change_list) { |
+ for (SyncChangeList::const_iterator it = change_list.begin(); |
+ it != change_list.end(); |
+ ++it) { |
+ SyncData data = it->sync_data(); |
+ DCHECK_EQ(MANAGED_USER_SHARED_SETTINGS, data.GetDataType()); |
+ const ::sync_pb::ManagedUserSharedSettingSpecifics& |
+ managed_user_shared_setting = |
+ data.GetSpecifics().managed_user_shared_setting(); |
+ const std::string& key = managed_user_shared_setting.key(); |
+ const std::string& mu_id = managed_user_shared_setting.mu_id(); |
+ ScopedManagedUserSharedSettingsUpdate update(prefs_, mu_id); |
+ DictionaryValue* update_dict = update.Get(); |
+ DictionaryValue* dict = NULL; |
+ bool has_key = update_dict->GetDictionaryWithoutPathExpansion(key, &dict); |
+ switch (it->change_type()) { |
+ case SyncChange::ACTION_ADD: |
+ case SyncChange::ACTION_UPDATE: { |
+ // Every setting we get from the server should have the acknowledged |
+ // flag set. |
+ DCHECK(managed_user_shared_setting.acknowledged()); |
+ |
+ if (has_key) { |
+ // If the managed user already exists, it should be an update action. |
+ DCHECK_EQ(SyncChange::ACTION_UPDATE, it->change_type()); |
+ } else { |
+ // Otherwise, it should be an add action. |
+ DCHECK_EQ(SyncChange::ACTION_ADD, it->change_type()); |
+ dict = new DictionaryValue; |
+ update_dict->SetWithoutPathExpansion(key, dict); |
+ } |
+ scoped_ptr<Value> value( |
+ base::JSONReader::Read(managed_user_shared_setting.value())); |
+ dict->SetWithoutPathExpansion(kValue, value.release()); |
+ dict->SetBooleanWithoutPathExpansion( |
+ kAcknowledged, managed_user_shared_setting.acknowledged()); |
+ break; |
+ } |
+ case SyncChange::ACTION_DELETE: { |
+ if (has_key) |
+ update_dict->RemoveWithoutPathExpansion(key, NULL); |
+ else |
+ NOTREACHED() << "Trying to delete nonexistent key " << key; |
+ break; |
+ } |
+ case SyncChange::ACTION_INVALID: { |
+ NOTREACHED(); |
+ break; |
+ } |
+ } |
+ callbacks_.Notify(mu_id, key); |
+ } |
+ |
+ SyncError error; |
+ return error; |
+} |