root/chrome/browser/managed_mode/managed_user_shared_settings_service.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. FindOrCreateDictionary
  2. mu_id_
  3. Get
  4. CreateSyncDataForValue
  5. SetValueInternal
  6. GetValue
  7. SetValue
  8. Subscribe
  9. RegisterProfilePrefs
  10. CreateSyncDataForSetting
  11. Shutdown
  12. MergeDataAndStartSyncing
  13. StopSyncing
  14. GetAllSyncData
  15. ProcessSyncChanges

// Copyright 2014 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;
}

/* [<][>][^][v][top][bottom][index][help] */