1// Copyright 2013 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/managed_mode/managed_user_sync_service.h"
6
7#include "base/bind.h"
8#include "base/values.h"
9#include "chrome/browser/prefs/scoped_user_pref_update.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/common/pref_names.h"
12#include "components/user_prefs/pref_registry_syncable.h"
13#include "sync/api/sync_change.h"
14#include "sync/api/sync_data.h"
15#include "sync/api/sync_error.h"
16#include "sync/api/sync_error_factory.h"
17#include "sync/api/sync_merge_result.h"
18#include "sync/protocol/sync.pb.h"
19
20using base::DictionaryValue;
21using user_prefs::PrefRegistrySyncable;
22using syncer::MANAGED_USERS;
23using syncer::ModelType;
24using syncer::SyncChange;
25using syncer::SyncChangeList;
26using syncer::SyncChangeProcessor;
27using syncer::SyncData;
28using syncer::SyncDataList;
29using syncer::SyncError;
30using syncer::SyncErrorFactory;
31using syncer::SyncMergeResult;
32using sync_pb::ManagedUserSpecifics;
33
34namespace {
35
36const char kAcknowledged[] = "acknowledged";
37const char kName[] = "name";
38const char kMasterKey[] = "masterKey";
39
40SyncData CreateLocalSyncData(const std::string& id,
41                             const std::string& name,
42                             bool acknowledged,
43                             const std::string& master_key) {
44  ::sync_pb::EntitySpecifics specifics;
45  specifics.mutable_managed_user()->set_id(id);
46  specifics.mutable_managed_user()->set_name(name);
47  if (!master_key.empty())
48    specifics.mutable_managed_user()->set_master_key(master_key);
49  if (acknowledged)
50    specifics.mutable_managed_user()->set_acknowledged(true);
51  return SyncData::CreateLocalData(id, name, specifics);
52}
53
54}  // namespace
55
56ManagedUserSyncService::ManagedUserSyncService(PrefService* prefs)
57    : prefs_(prefs) {
58  pref_change_registrar_.Init(prefs_);
59  pref_change_registrar_.Add(
60      prefs::kGoogleServicesLastUsername,
61      base::Bind(&ManagedUserSyncService::OnLastSignedInUsernameChange,
62                 base::Unretained(this)));
63}
64
65ManagedUserSyncService::~ManagedUserSyncService() {
66}
67
68// static
69void ManagedUserSyncService::RegisterProfilePrefs(
70    PrefRegistrySyncable* registry) {
71  registry->RegisterDictionaryPref(prefs::kManagedUsers,
72                                   PrefRegistrySyncable::UNSYNCABLE_PREF);
73}
74
75void ManagedUserSyncService::AddObserver(
76    ManagedUserSyncServiceObserver* observer) {
77  observers_.AddObserver(observer);
78}
79
80void ManagedUserSyncService::RemoveObserver(
81    ManagedUserSyncServiceObserver* observer) {
82  observers_.RemoveObserver(observer);
83}
84
85void ManagedUserSyncService::AddManagedUser(const std::string& id,
86                                            const std::string& name,
87                                            const std::string& master_key) {
88  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
89  DictionaryValue* dict = update.Get();
90  DictionaryValue* value = new DictionaryValue;
91  value->SetString(kName, name);
92  value->SetString(kMasterKey, master_key);
93  DCHECK(!dict->HasKey(id));
94  dict->SetWithoutPathExpansion(id, value);
95
96  if (!sync_processor_)
97    return;
98
99  // If we're already syncing, create a new change and upload it.
100  SyncChangeList change_list;
101  change_list.push_back(SyncChange(
102      FROM_HERE,
103      SyncChange::ACTION_ADD,
104      CreateLocalSyncData(id, name, false, master_key)));
105  SyncError error =
106      sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
107  DCHECK(!error.IsSet()) << error.ToString();
108}
109
110void ManagedUserSyncService::DeleteManagedUser(const std::string& id) {
111  if (!sync_processor_)
112    return;
113
114  SyncChangeList change_list;
115  change_list.push_back(SyncChange(
116      FROM_HERE,
117      SyncChange::ACTION_DELETE,
118      SyncData::CreateLocalDelete(id, MANAGED_USERS)));
119  SyncError sync_error =
120      sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
121  DCHECK(!sync_error.IsSet());
122}
123
124void ManagedUserSyncService::Shutdown() {
125  NotifyManagedUsersSyncingStopped();
126}
127
128SyncMergeResult ManagedUserSyncService::MergeDataAndStartSyncing(
129    ModelType type,
130    const SyncDataList& initial_sync_data,
131    scoped_ptr<SyncChangeProcessor> sync_processor,
132    scoped_ptr<SyncErrorFactory> error_handler) {
133  DCHECK_EQ(MANAGED_USERS, type);
134  sync_processor_ = sync_processor.Pass();
135  error_handler_ = error_handler.Pass();
136
137  SyncChangeList change_list;
138  SyncMergeResult result(MANAGED_USERS);
139
140  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
141  DictionaryValue* dict = update.Get();
142  result.set_num_items_before_association(dict->size());
143  std::set<std::string> seen_ids;
144  int num_items_added = 0;
145  int num_items_modified = 0;
146  for (SyncDataList::const_iterator it = initial_sync_data.begin();
147       it != initial_sync_data.end(); ++it) {
148    DCHECK_EQ(MANAGED_USERS, it->GetDataType());
149    const ManagedUserSpecifics& managed_user =
150        it->GetSpecifics().managed_user();
151    DictionaryValue* value = new DictionaryValue();
152    value->SetString(kName, managed_user.name());
153    DCHECK(managed_user.acknowledged());
154    value->SetBoolean(kAcknowledged, managed_user.acknowledged());
155    value->SetString(kMasterKey, managed_user.master_key());
156    if (dict->HasKey(managed_user.id()))
157      num_items_modified++;
158    else
159      num_items_added++;
160    dict->SetWithoutPathExpansion(managed_user.id(), value);
161    seen_ids.insert(managed_user.id());
162  }
163
164  for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
165    if (seen_ids.find(it.key()) != seen_ids.end())
166      continue;
167
168    const DictionaryValue* dict = NULL;
169    bool success = it.value().GetAsDictionary(&dict);
170    DCHECK(success);
171    bool acknowledged = false;
172    dict->GetBoolean(kAcknowledged, &acknowledged);
173    std::string name;
174    dict->GetString(kName, &name);
175    std::string master_key;
176    dict->GetString(kMasterKey, &master_key);
177    DCHECK(!name.empty());
178    change_list.push_back(
179        SyncChange(FROM_HERE, SyncChange::ACTION_ADD,
180            CreateLocalSyncData(it.key(), name, acknowledged, master_key)));
181  }
182  result.set_error(sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
183
184  result.set_num_items_modified(num_items_modified);
185  result.set_num_items_added(num_items_added);
186  result.set_num_items_after_association(dict->size());
187
188  return result;
189}
190
191void ManagedUserSyncService::StopSyncing(ModelType type) {
192  DCHECK_EQ(MANAGED_USERS, type);
193  // The observers may want to change the Sync data, so notify them before
194  // resetting the |sync_processor_|.
195  NotifyManagedUsersSyncingStopped();
196  sync_processor_.reset();
197  error_handler_.reset();
198}
199
200SyncDataList ManagedUserSyncService::GetAllSyncData(
201    ModelType type) const {
202  SyncDataList data;
203  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
204  DictionaryValue* dict = update.Get();
205  for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
206    const DictionaryValue* dict = NULL;
207    bool success = it.value().GetAsDictionary(&dict);
208    DCHECK(success);
209    std::string name;
210    dict->GetString(kName, &name);
211    std::string master_key;
212    dict->GetString(kMasterKey, &master_key);
213    bool acknowledged = false;
214    dict->GetBoolean(kAcknowledged, &acknowledged);
215    data.push_back(
216        CreateLocalSyncData(it.key(), name, acknowledged, master_key));
217  }
218  return data;
219}
220
221SyncError ManagedUserSyncService::ProcessSyncChanges(
222    const tracked_objects::Location& from_here,
223    const SyncChangeList& change_list) {
224  SyncError error;
225  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
226  DictionaryValue* dict = update.Get();
227  for (SyncChangeList::const_iterator it = change_list.begin();
228       it != change_list.end(); ++it) {
229    SyncData data = it->sync_data();
230    DCHECK_EQ(MANAGED_USERS, data.GetDataType());
231    const ManagedUserSpecifics& managed_user =
232        data.GetSpecifics().managed_user();
233    switch (it->change_type()) {
234      case SyncChange::ACTION_ADD:
235      case SyncChange::ACTION_UPDATE: {
236        // Every item we get from the server should be acknowledged.
237        DCHECK(managed_user.acknowledged());
238        const DictionaryValue* old_value = NULL;
239        dict->GetDictionaryWithoutPathExpansion(managed_user.id(), &old_value);
240
241        // For an update action, the managed user should already exist, for an
242        // add action, it should not.
243        DCHECK_EQ(
244            old_value ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD,
245            it->change_type());
246
247        // If the managed user switched from unacknowledged to acknowledged,
248        // we might need to continue with a registration.
249        if (old_value && !old_value->HasKey(kAcknowledged))
250          NotifyManagedUserAcknowledged(managed_user.id());
251
252        DictionaryValue* value = new DictionaryValue;
253        value->SetString(kName, managed_user.name());
254        value->SetBoolean(kAcknowledged, managed_user.acknowledged());
255        value->SetString(kMasterKey, managed_user.master_key());
256        dict->SetWithoutPathExpansion(managed_user.id(), value);
257        break;
258      }
259      case SyncChange::ACTION_DELETE: {
260        DCHECK(dict->HasKey(managed_user.id())) << managed_user.id();
261        dict->RemoveWithoutPathExpansion(managed_user.id(), NULL);
262        break;
263      }
264      case SyncChange::ACTION_INVALID: {
265        NOTREACHED();
266        break;
267      }
268    }
269  }
270  return error;
271}
272
273void ManagedUserSyncService::OnLastSignedInUsernameChange() {
274  DCHECK(!sync_processor_);
275
276  // If the last signed in user changes, we clear all data, to avoid managed
277  // users from one custodian appearing in another one's profile.
278  prefs_->ClearPref(prefs::kManagedUsers);
279}
280
281void ManagedUserSyncService::NotifyManagedUserAcknowledged(
282    const std::string& managed_user_id) {
283  FOR_EACH_OBSERVER(ManagedUserSyncServiceObserver, observers_,
284                    OnManagedUserAcknowledged(managed_user_id));
285}
286
287void ManagedUserSyncService::NotifyManagedUsersSyncingStopped() {
288  FOR_EACH_OBSERVER(ManagedUserSyncServiceObserver, observers_,
289                    OnManagedUsersSyncingStopped());
290}
291