blob: 3728e854e3c9a9359ad7360139822316753a2a32 [file] [log] [blame]
// 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_sync_service.h"
#include "base/bind.h"
#include "base/values.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/profiles/profile.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 user_prefs::PrefRegistrySyncable;
using syncer::MANAGED_USERS;
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;
using sync_pb::ManagedUserSpecifics;
namespace {
const char kAcknowledged[] = "acknowledged";
const char kName[] = "name";
const char kMasterKey[] = "masterKey";
SyncData CreateLocalSyncData(const std::string& id,
const std::string& name,
bool acknowledged,
const std::string& master_key) {
::sync_pb::EntitySpecifics specifics;
specifics.mutable_managed_user()->set_id(id);
specifics.mutable_managed_user()->set_name(name);
if (!master_key.empty())
specifics.mutable_managed_user()->set_master_key(master_key);
if (acknowledged)
specifics.mutable_managed_user()->set_acknowledged(true);
return SyncData::CreateLocalData(id, name, specifics);
}
} // namespace
ManagedUserSyncService::ManagedUserSyncService(PrefService* prefs)
: prefs_(prefs) {
pref_change_registrar_.Init(prefs_);
pref_change_registrar_.Add(
prefs::kGoogleServicesLastUsername,
base::Bind(&ManagedUserSyncService::OnLastSignedInUsernameChange,
base::Unretained(this)));
}
ManagedUserSyncService::~ManagedUserSyncService() {
}
// static
void ManagedUserSyncService::RegisterProfilePrefs(
PrefRegistrySyncable* registry) {
registry->RegisterDictionaryPref(prefs::kManagedUsers,
PrefRegistrySyncable::UNSYNCABLE_PREF);
}
void ManagedUserSyncService::AddObserver(
ManagedUserSyncServiceObserver* observer) {
observers_.AddObserver(observer);
}
void ManagedUserSyncService::RemoveObserver(
ManagedUserSyncServiceObserver* observer) {
observers_.RemoveObserver(observer);
}
void ManagedUserSyncService::AddManagedUser(const std::string& id,
const std::string& name,
const std::string& master_key) {
DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
DictionaryValue* dict = update.Get();
DictionaryValue* value = new DictionaryValue;
value->SetString(kName, name);
value->SetString(kMasterKey, master_key);
DCHECK(!dict->HasKey(id));
dict->SetWithoutPathExpansion(id, value);
if (!sync_processor_)
return;
// If we're already syncing, create a new change and upload it.
SyncChangeList change_list;
change_list.push_back(SyncChange(
FROM_HERE,
SyncChange::ACTION_ADD,
CreateLocalSyncData(id, name, false, master_key)));
SyncError error =
sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
DCHECK(!error.IsSet()) << error.ToString();
}
void ManagedUserSyncService::DeleteManagedUser(const std::string& id) {
if (!sync_processor_)
return;
SyncChangeList change_list;
change_list.push_back(SyncChange(
FROM_HERE,
SyncChange::ACTION_DELETE,
SyncData::CreateLocalDelete(id, MANAGED_USERS)));
SyncError sync_error =
sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
DCHECK(!sync_error.IsSet());
}
void ManagedUserSyncService::Shutdown() {
NotifyManagedUsersSyncingStopped();
}
SyncMergeResult ManagedUserSyncService::MergeDataAndStartSyncing(
ModelType type,
const SyncDataList& initial_sync_data,
scoped_ptr<SyncChangeProcessor> sync_processor,
scoped_ptr<SyncErrorFactory> error_handler) {
DCHECK_EQ(MANAGED_USERS, type);
sync_processor_ = sync_processor.Pass();
error_handler_ = error_handler.Pass();
SyncChangeList change_list;
SyncMergeResult result(MANAGED_USERS);
DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
DictionaryValue* dict = update.Get();
result.set_num_items_before_association(dict->size());
std::set<std::string> seen_ids;
int num_items_added = 0;
int num_items_modified = 0;
for (SyncDataList::const_iterator it = initial_sync_data.begin();
it != initial_sync_data.end(); ++it) {
DCHECK_EQ(MANAGED_USERS, it->GetDataType());
const ManagedUserSpecifics& managed_user =
it->GetSpecifics().managed_user();
DictionaryValue* value = new DictionaryValue();
value->SetString(kName, managed_user.name());
value->SetBoolean(kAcknowledged, managed_user.acknowledged());
value->SetString(kMasterKey, managed_user.master_key());
if (dict->HasKey(managed_user.id()))
num_items_modified++;
else
num_items_added++;
dict->SetWithoutPathExpansion(managed_user.id(), value);
seen_ids.insert(managed_user.id());
}
for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
if (seen_ids.find(it.key()) != seen_ids.end())
continue;
const DictionaryValue* dict = NULL;
bool success = it.value().GetAsDictionary(&dict);
DCHECK(success);
bool acknowledged = false;
dict->GetBoolean(kAcknowledged, &acknowledged);
std::string name;
dict->GetString(kName, &name);
std::string master_key;
dict->GetString(kMasterKey, &master_key);
DCHECK(!name.empty());
change_list.push_back(
SyncChange(FROM_HERE, SyncChange::ACTION_ADD,
CreateLocalSyncData(it.key(), name, acknowledged, master_key)));
}
result.set_error(sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
result.set_num_items_modified(num_items_modified);
result.set_num_items_added(num_items_added);
result.set_num_items_after_association(dict->size());
return result;
}
void ManagedUserSyncService::StopSyncing(ModelType type) {
DCHECK_EQ(MANAGED_USERS, type);
// The observers may want to change the Sync data, so notify them before
// resetting the |sync_processor_|.
NotifyManagedUsersSyncingStopped();
sync_processor_.reset();
error_handler_.reset();
}
SyncDataList ManagedUserSyncService::GetAllSyncData(
ModelType type) const {
SyncDataList data;
DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
DictionaryValue* dict = update.Get();
for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
const DictionaryValue* dict = NULL;
bool success = it.value().GetAsDictionary(&dict);
DCHECK(success);
std::string name;
dict->GetString(kName, &name);
std::string master_key;
dict->GetString(kMasterKey, &master_key);
bool acknowledged = false;
dict->GetBoolean(kAcknowledged, &acknowledged);
data.push_back(
CreateLocalSyncData(it.key(), name, acknowledged, master_key));
}
return data;
}
SyncError ManagedUserSyncService::ProcessSyncChanges(
const tracked_objects::Location& from_here,
const SyncChangeList& change_list) {
SyncError error;
DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
DictionaryValue* dict = update.Get();
for (SyncChangeList::const_iterator it = change_list.begin();
it != change_list.end(); ++it) {
SyncData data = it->sync_data();
DCHECK_EQ(MANAGED_USERS, data.GetDataType());
const ManagedUserSpecifics& managed_user =
data.GetSpecifics().managed_user();
switch (it->change_type()) {
case SyncChange::ACTION_ADD:
case SyncChange::ACTION_UPDATE: {
// Every item we get from the server should be acknowledged.
DCHECK(managed_user.acknowledged());
const DictionaryValue* old_value = NULL;
dict->GetDictionaryWithoutPathExpansion(managed_user.id(), &old_value);
// For an update action, the managed user should already exist, for an
// add action, it should not.
DCHECK_EQ(
old_value ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD,
it->change_type());
// If the managed user switched from unacknowledged to acknowledged,
// we might need to continue with a registration.
if (old_value && !old_value->HasKey(kAcknowledged))
NotifyManagedUserAcknowledged(managed_user.id());
DictionaryValue* value = new DictionaryValue;
value->SetString(kName, managed_user.name());
value->SetBoolean(kAcknowledged, managed_user.acknowledged());
value->SetString(kMasterKey, managed_user.master_key());
dict->SetWithoutPathExpansion(managed_user.id(), value);
break;
}
case SyncChange::ACTION_DELETE: {
DCHECK(dict->HasKey(managed_user.id())) << managed_user.id();
dict->RemoveWithoutPathExpansion(managed_user.id(), NULL);
break;
}
case SyncChange::ACTION_INVALID: {
NOTREACHED();
break;
}
}
}
return error;
}
void ManagedUserSyncService::OnLastSignedInUsernameChange() {
DCHECK(!sync_processor_);
// If the last signed in user changes, we clear all data, to avoid managed
// users from one custodian appearing in another one's profile.
prefs_->ClearPref(prefs::kManagedUsers);
}
void ManagedUserSyncService::NotifyManagedUserAcknowledged(
const std::string& managed_user_id) {
FOR_EACH_OBSERVER(ManagedUserSyncServiceObserver, observers_,
OnManagedUserAcknowledged(managed_user_id));
}
void ManagedUserSyncService::NotifyManagedUsersSyncingStopped() {
FOR_EACH_OBSERVER(ManagedUserSyncServiceObserver, observers_,
OnManagedUsersSyncingStopped());
}