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.
4
5#include "chrome/browser/supervised_user/supervised_user_sync_service.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/prefs/scoped_user_pref_update.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/stringprintf.h"
12#include "base/values.h"
13#include "chrome/browser/profiles/profile_avatar_icon_util.h"
14#include "chrome/common/pref_names.h"
15#include "components/pref_registry/pref_registry_syncable.h"
16#include "sync/api/sync_change.h"
17#include "sync/api/sync_data.h"
18#include "sync/api/sync_error.h"
19#include "sync/api/sync_error_factory.h"
20#include "sync/api/sync_merge_result.h"
21#include "sync/protocol/sync.pb.h"
22
23#if defined(OS_CHROMEOS)
24#include "components/user_manager/user_image/default_user_images.h"
25#endif
26
27using base::DictionaryValue;
28using user_prefs::PrefRegistrySyncable;
29using syncer::SUPERVISED_USERS;
30using syncer::ModelType;
31using syncer::SyncChange;
32using syncer::SyncChangeList;
33using syncer::SyncChangeProcessor;
34using syncer::SyncData;
35using syncer::SyncDataList;
36using syncer::SyncError;
37using syncer::SyncErrorFactory;
38using syncer::SyncMergeResult;
39using sync_pb::ManagedUserSpecifics;
40
41namespace {
42
43#if defined(OS_CHROMEOS)
44const char kChromeOSAvatarPrefix[] = "chromeos-avatar-index:";
45#else
46const char kChromeAvatarPrefix[] = "chrome-avatar-index:";
47#endif
48
49SyncData CreateLocalSyncData(const std::string& id,
50                             const std::string& name,
51                             bool acknowledged,
52                             const std::string& master_key,
53                             const std::string& chrome_avatar,
54                             const std::string& chromeos_avatar,
55                             const std::string& password_signature_key,
56                             const std::string& password_encryption_key) {
57  ::sync_pb::EntitySpecifics specifics;
58  specifics.mutable_managed_user()->set_id(id);
59  specifics.mutable_managed_user()->set_name(name);
60  if (!chrome_avatar.empty())
61    specifics.mutable_managed_user()->set_chrome_avatar(chrome_avatar);
62  if (!chromeos_avatar.empty())
63    specifics.mutable_managed_user()->set_chromeos_avatar(chromeos_avatar);
64  if (!master_key.empty())
65    specifics.mutable_managed_user()->set_master_key(master_key);
66  if (acknowledged)
67    specifics.mutable_managed_user()->set_acknowledged(true);
68  if (!password_signature_key.empty()) {
69    specifics.mutable_managed_user()->
70        set_password_signature_key(password_signature_key);
71  }
72  if (!password_encryption_key.empty()) {
73    specifics.mutable_managed_user()->
74        set_password_encryption_key(password_encryption_key);
75  }
76  return SyncData::CreateLocalData(id, name, specifics);
77}
78
79SyncData CreateSyncDataFromDictionaryEntry(const std::string& id,
80                                           const base::Value& value) {
81  const base::DictionaryValue* dict = NULL;
82  bool success = value.GetAsDictionary(&dict);
83  DCHECK(success);
84  bool acknowledged = false;
85  dict->GetBoolean(SupervisedUserSyncService::kAcknowledged, &acknowledged);
86  std::string name;
87  dict->GetString(SupervisedUserSyncService::kName, &name);
88  DCHECK(!name.empty());
89  std::string master_key;
90  dict->GetString(SupervisedUserSyncService::kMasterKey, &master_key);
91  std::string chrome_avatar;
92  dict->GetString(SupervisedUserSyncService::kChromeAvatar, &chrome_avatar);
93  std::string chromeos_avatar;
94  dict->GetString(SupervisedUserSyncService::kChromeOsAvatar, &chromeos_avatar);
95  std::string signature;
96  dict->GetString(SupervisedUserSyncService::kPasswordSignatureKey, &signature);
97  std::string encryption;
98  dict->GetString(SupervisedUserSyncService::kPasswordEncryptionKey,
99                  &encryption);
100
101  return CreateLocalSyncData(id,
102                             name,
103                             acknowledged,
104                             master_key,
105                             chrome_avatar,
106                             chromeos_avatar,
107                             signature,
108                             encryption);
109}
110
111}  // namespace
112
113const char SupervisedUserSyncService::kAcknowledged[] = "acknowledged";
114const char SupervisedUserSyncService::kChromeAvatar[] = "chromeAvatar";
115const char SupervisedUserSyncService::kChromeOsAvatar[] = "chromeOsAvatar";
116const char SupervisedUserSyncService::kMasterKey[] = "masterKey";
117const char SupervisedUserSyncService::kName[] = "name";
118const char SupervisedUserSyncService::kPasswordSignatureKey[] =
119    "passwordSignatureKey";
120const char SupervisedUserSyncService::kPasswordEncryptionKey[] =
121    "passwordEncryptionKey";
122const int SupervisedUserSyncService::kNoAvatar = -100;
123
124SupervisedUserSyncService::SupervisedUserSyncService(PrefService* prefs)
125    : prefs_(prefs) {
126  pref_change_registrar_.Init(prefs_);
127  pref_change_registrar_.Add(
128      prefs::kGoogleServicesLastUsername,
129      base::Bind(&SupervisedUserSyncService::OnLastSignedInUsernameChange,
130                 base::Unretained(this)));
131}
132
133SupervisedUserSyncService::~SupervisedUserSyncService() {
134}
135
136// static
137void SupervisedUserSyncService::RegisterProfilePrefs(
138    PrefRegistrySyncable* registry) {
139  registry->RegisterDictionaryPref(prefs::kSupervisedUsers,
140                                   PrefRegistrySyncable::UNSYNCABLE_PREF);
141}
142
143// static
144bool SupervisedUserSyncService::GetAvatarIndex(const std::string& avatar_str,
145                                               int* avatar_index) {
146  DCHECK(avatar_index);
147  if (avatar_str.empty()) {
148    *avatar_index = kNoAvatar;
149    return true;
150  }
151#if defined(OS_CHROMEOS)
152  const char* prefix = kChromeOSAvatarPrefix;
153#else
154  const char* prefix = kChromeAvatarPrefix;
155#endif
156  size_t prefix_len = strlen(prefix);
157  if (avatar_str.size() <= prefix_len ||
158      avatar_str.substr(0, prefix_len) != prefix) {
159    return false;
160  }
161
162  if (!base::StringToInt(avatar_str.substr(prefix_len), avatar_index))
163    return false;
164
165  const int kChromeOSDummyAvatarIndex = -111;
166
167#if defined(OS_CHROMEOS)
168  return (*avatar_index == kChromeOSDummyAvatarIndex ||
169          (*avatar_index >= user_manager::kFirstDefaultImageIndex &&
170           *avatar_index < user_manager::kDefaultImagesCount));
171#else
172  // Check if the Chrome avatar index is set to a dummy value. Some early
173  // supervised user profiles on ChromeOS stored a dummy avatar index as a
174  // Chrome Avatar before there was logic to sync the ChromeOS avatar
175  // separately. Handle this as if the Chrome Avatar was not chosen yet (which
176  // is correct for these profiles).
177  if (*avatar_index == kChromeOSDummyAvatarIndex)
178    *avatar_index = kNoAvatar;
179  return (*avatar_index == kNoAvatar ||
180          (*avatar_index >= 0 &&
181           static_cast<size_t>(*avatar_index) <
182               profiles::GetDefaultAvatarIconCount()));
183#endif
184}
185
186// static
187std::string SupervisedUserSyncService::BuildAvatarString(int avatar_index) {
188#if defined(OS_CHROMEOS)
189  const char* prefix = kChromeOSAvatarPrefix;
190#else
191  const char* prefix = kChromeAvatarPrefix;
192#endif
193  return base::StringPrintf("%s%d", prefix, avatar_index);
194}
195
196void SupervisedUserSyncService::AddObserver(
197    SupervisedUserSyncServiceObserver* observer) {
198  observers_.AddObserver(observer);
199}
200
201void SupervisedUserSyncService::RemoveObserver(
202    SupervisedUserSyncServiceObserver* observer) {
203  observers_.RemoveObserver(observer);
204}
205
206scoped_ptr<base::DictionaryValue> SupervisedUserSyncService::CreateDictionary(
207    const std::string& name,
208    const std::string& master_key,
209    const std::string& signature_key,
210    const std::string& encryption_key,
211    int avatar_index) {
212  scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
213  result->SetString(kName, name);
214  result->SetString(kMasterKey, master_key);
215  result->SetString(kPasswordSignatureKey, signature_key);
216  result->SetString(kPasswordEncryptionKey, encryption_key);
217  // TODO(akuegel): Get rid of the avatar stuff here when Chrome OS switches
218  // to the avatar index that is stored as a shared setting.
219  std::string chrome_avatar;
220  std::string chromeos_avatar;
221#if defined(OS_CHROMEOS)
222  chromeos_avatar = BuildAvatarString(avatar_index);
223#else
224  chrome_avatar = BuildAvatarString(avatar_index);
225#endif
226  result->SetString(kChromeAvatar, chrome_avatar);
227  result->SetString(kChromeOsAvatar, chromeos_avatar);
228  return result.Pass();
229}
230
231void SupervisedUserSyncService::AddSupervisedUser(
232    const std::string& id,
233    const std::string& name,
234    const std::string& master_key,
235    const std::string& signature_key,
236    const std::string& encryption_key,
237    int avatar_index) {
238  UpdateSupervisedUserImpl(id,
239                           name,
240                           master_key,
241                           signature_key,
242                           encryption_key,
243                           avatar_index,
244                           true /* add */);
245}
246
247void SupervisedUserSyncService::UpdateSupervisedUser(
248    const std::string& id,
249    const std::string& name,
250    const std::string& master_key,
251    const std::string& signature_key,
252    const std::string& encryption_key,
253    int avatar_index) {
254  UpdateSupervisedUserImpl(id,
255                           name,
256                           master_key,
257                           signature_key,
258                           encryption_key,
259                           avatar_index,
260                           false /* update */);
261}
262
263void SupervisedUserSyncService::UpdateSupervisedUserImpl(
264    const std::string& id,
265    const std::string& name,
266    const std::string& master_key,
267    const std::string& signature_key,
268    const std::string& encryption_key,
269    int avatar_index,
270    bool add_user) {
271  DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUsers);
272  base::DictionaryValue* dict = update.Get();
273  scoped_ptr<base::DictionaryValue> value = CreateDictionary(
274      name, master_key, signature_key, encryption_key, avatar_index);
275
276  DCHECK_EQ(add_user, !dict->HasKey(id));
277  base::DictionaryValue* entry = value.get();
278  dict->SetWithoutPathExpansion(id, value.release());
279
280  if (!sync_processor_)
281    return;
282
283  // If we're already syncing, create a new change and upload it.
284  SyncChangeList change_list;
285  change_list.push_back(
286      SyncChange(FROM_HERE,
287                 add_user ? SyncChange::ACTION_ADD : SyncChange::ACTION_UPDATE,
288                 CreateSyncDataFromDictionaryEntry(id, *entry)));
289  SyncError error =
290      sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
291  DCHECK(!error.IsSet()) << error.ToString();
292}
293
294void SupervisedUserSyncService::DeleteSupervisedUser(const std::string& id) {
295  DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUsers);
296  bool success = update->RemoveWithoutPathExpansion(id, NULL);
297  DCHECK(success);
298
299  if (!sync_processor_)
300    return;
301
302  SyncChangeList change_list;
303  change_list.push_back(SyncChange(
304      FROM_HERE,
305      SyncChange::ACTION_DELETE,
306      SyncData::CreateLocalDelete(id, SUPERVISED_USERS)));
307  SyncError sync_error =
308      sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
309  DCHECK(!sync_error.IsSet());
310}
311
312const base::DictionaryValue* SupervisedUserSyncService::GetSupervisedUsers() {
313  DCHECK(sync_processor_);
314  return prefs_->GetDictionary(prefs::kSupervisedUsers);
315}
316
317bool SupervisedUserSyncService::UpdateSupervisedUserAvatarIfNeeded(
318    const std::string& id,
319    int avatar_index) {
320  DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUsers);
321  base::DictionaryValue* dict = update.Get();
322  DCHECK(dict->HasKey(id));
323  base::DictionaryValue* value = NULL;
324  bool success = dict->GetDictionaryWithoutPathExpansion(id, &value);
325  DCHECK(success);
326
327  bool acknowledged = false;
328  value->GetBoolean(SupervisedUserSyncService::kAcknowledged, &acknowledged);
329  std::string name;
330  value->GetString(SupervisedUserSyncService::kName, &name);
331  std::string master_key;
332  value->GetString(SupervisedUserSyncService::kMasterKey, &master_key);
333  std::string signature;
334  value->GetString(SupervisedUserSyncService::kPasswordSignatureKey,
335                   &signature);
336  std::string encryption;
337  value->GetString(SupervisedUserSyncService::kPasswordEncryptionKey,
338                   &encryption);
339  std::string chromeos_avatar;
340  value->GetString(SupervisedUserSyncService::kChromeOsAvatar,
341                   &chromeos_avatar);
342  std::string chrome_avatar;
343  value->GetString(SupervisedUserSyncService::kChromeAvatar, &chrome_avatar);
344  // The following check is just for safety. We want to avoid that the existing
345  // avatar selection is overwritten. Currently we don't allow the user to
346  // choose a different avatar in the recreation dialog, anyway, if there is
347  // already an avatar selected.
348#if defined(OS_CHROMEOS)
349  if (!chromeos_avatar.empty() && avatar_index != kNoAvatar)
350    return false;
351#else
352  if (!chrome_avatar.empty() && avatar_index != kNoAvatar)
353    return false;
354#endif
355
356  chrome_avatar = avatar_index == kNoAvatar ?
357      std::string() : BuildAvatarString(avatar_index);
358#if defined(OS_CHROMEOS)
359  value->SetString(kChromeOsAvatar, chrome_avatar);
360#else
361  value->SetString(kChromeAvatar, chrome_avatar);
362#endif
363
364  if (!sync_processor_)
365    return true;
366
367  SyncChangeList change_list;
368  change_list.push_back(SyncChange(
369      FROM_HERE,
370      SyncChange::ACTION_UPDATE,
371      CreateLocalSyncData(id, name, acknowledged, master_key,
372                          chrome_avatar, chromeos_avatar,
373                          signature, encryption)));
374  SyncError error =
375      sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
376  DCHECK(!error.IsSet()) << error.ToString();
377  return true;
378}
379
380void SupervisedUserSyncService::ClearSupervisedUserAvatar(
381    const std::string& id) {
382  bool cleared = UpdateSupervisedUserAvatarIfNeeded(id, kNoAvatar);
383  DCHECK(cleared);
384}
385
386void SupervisedUserSyncService::GetSupervisedUsersAsync(
387    const SupervisedUsersCallback& callback) {
388  // If we are already syncing, just run the callback.
389  if (sync_processor_) {
390    callback.Run(GetSupervisedUsers());
391    return;
392  }
393
394  // Otherwise queue it up until we start syncing.
395  callbacks_.push_back(callback);
396}
397
398void SupervisedUserSyncService::Shutdown() {
399  NotifySupervisedUsersSyncingStopped();
400}
401
402SyncMergeResult SupervisedUserSyncService::MergeDataAndStartSyncing(
403    ModelType type,
404    const SyncDataList& initial_sync_data,
405    scoped_ptr<SyncChangeProcessor> sync_processor,
406    scoped_ptr<SyncErrorFactory> error_handler) {
407  DCHECK_EQ(SUPERVISED_USERS, type);
408  sync_processor_ = sync_processor.Pass();
409  error_handler_ = error_handler.Pass();
410
411  SyncChangeList change_list;
412  SyncMergeResult result(SUPERVISED_USERS);
413
414  DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUsers);
415  base::DictionaryValue* dict = update.Get();
416  result.set_num_items_before_association(dict->size());
417  std::set<std::string> seen_ids;
418  int num_items_added = 0;
419  int num_items_modified = 0;
420  for (SyncDataList::const_iterator it = initial_sync_data.begin();
421       it != initial_sync_data.end(); ++it) {
422    DCHECK_EQ(SUPERVISED_USERS, it->GetDataType());
423    const ManagedUserSpecifics& supervised_user =
424        it->GetSpecifics().managed_user();
425    base::DictionaryValue* value = new base::DictionaryValue();
426    value->SetString(kName, supervised_user.name());
427    value->SetBoolean(kAcknowledged, supervised_user.acknowledged());
428    value->SetString(kMasterKey, supervised_user.master_key());
429    value->SetString(kChromeAvatar, supervised_user.chrome_avatar());
430    value->SetString(kChromeOsAvatar, supervised_user.chromeos_avatar());
431    value->SetString(kPasswordSignatureKey,
432                     supervised_user.password_signature_key());
433    value->SetString(kPasswordEncryptionKey,
434                     supervised_user.password_encryption_key());
435    if (dict->HasKey(supervised_user.id()))
436      num_items_modified++;
437    else
438      num_items_added++;
439    dict->SetWithoutPathExpansion(supervised_user.id(), value);
440    seen_ids.insert(supervised_user.id());
441  }
442
443  for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
444    if (seen_ids.find(it.key()) != seen_ids.end())
445      continue;
446
447    change_list.push_back(
448        SyncChange(FROM_HERE,
449                   SyncChange::ACTION_ADD,
450                   CreateSyncDataFromDictionaryEntry(it.key(), it.value())));
451  }
452  result.set_error(sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
453
454  result.set_num_items_modified(num_items_modified);
455  result.set_num_items_added(num_items_added);
456  result.set_num_items_after_association(dict->size());
457
458  DispatchCallbacks();
459
460  return result;
461}
462
463void SupervisedUserSyncService::StopSyncing(ModelType type) {
464  DCHECK_EQ(SUPERVISED_USERS, type);
465  // The observers may want to change the Sync data, so notify them before
466  // resetting the |sync_processor_|.
467  NotifySupervisedUsersSyncingStopped();
468  sync_processor_.reset();
469  error_handler_.reset();
470}
471
472SyncDataList SupervisedUserSyncService::GetAllSyncData(
473    ModelType type) const {
474  SyncDataList data;
475  DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUsers);
476  base::DictionaryValue* dict = update.Get();
477  for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance())
478    data.push_back(CreateSyncDataFromDictionaryEntry(it.key(), it.value()));
479
480  return data;
481}
482
483SyncError SupervisedUserSyncService::ProcessSyncChanges(
484    const tracked_objects::Location& from_here,
485    const SyncChangeList& change_list) {
486  SyncError error;
487  DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUsers);
488  base::DictionaryValue* dict = update.Get();
489  for (SyncChangeList::const_iterator it = change_list.begin();
490       it != change_list.end(); ++it) {
491    SyncData data = it->sync_data();
492    DCHECK_EQ(SUPERVISED_USERS, data.GetDataType());
493    const ManagedUserSpecifics& supervised_user =
494        data.GetSpecifics().managed_user();
495    switch (it->change_type()) {
496      case SyncChange::ACTION_ADD:
497      case SyncChange::ACTION_UPDATE: {
498        // Every item we get from the server should be acknowledged.
499        DCHECK(supervised_user.acknowledged());
500        const base::DictionaryValue* old_value = NULL;
501        dict->GetDictionaryWithoutPathExpansion(supervised_user.id(),
502                                                &old_value);
503
504        // For an update action, the supervised user should already exist, for
505        // an add action, it should not.
506        DCHECK_EQ(
507            old_value ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD,
508            it->change_type());
509
510        // If the supervised user switched from unacknowledged to acknowledged,
511        // we might need to continue with a registration.
512        if (old_value && !old_value->HasKey(kAcknowledged))
513          NotifySupervisedUserAcknowledged(supervised_user.id());
514
515        base::DictionaryValue* value = new base::DictionaryValue;
516        value->SetString(kName, supervised_user.name());
517        value->SetBoolean(kAcknowledged, supervised_user.acknowledged());
518        value->SetString(kMasterKey, supervised_user.master_key());
519        value->SetString(kChromeAvatar, supervised_user.chrome_avatar());
520        value->SetString(kChromeOsAvatar, supervised_user.chromeos_avatar());
521        value->SetString(kPasswordSignatureKey,
522                         supervised_user.password_signature_key());
523        value->SetString(kPasswordEncryptionKey,
524                         supervised_user.password_encryption_key());
525        dict->SetWithoutPathExpansion(supervised_user.id(), value);
526
527        NotifySupervisedUsersChanged();
528        break;
529      }
530      case SyncChange::ACTION_DELETE: {
531        DCHECK(dict->HasKey(supervised_user.id())) << supervised_user.id();
532        dict->RemoveWithoutPathExpansion(supervised_user.id(), NULL);
533        break;
534      }
535      case SyncChange::ACTION_INVALID: {
536        NOTREACHED();
537        break;
538      }
539    }
540  }
541  return error;
542}
543
544void SupervisedUserSyncService::OnLastSignedInUsernameChange() {
545  DCHECK(!sync_processor_);
546
547  // If the last signed in user changes, we clear all data, to avoid supervised
548  // users from one custodian appearing in another one's profile.
549  prefs_->ClearPref(prefs::kSupervisedUsers);
550}
551
552void SupervisedUserSyncService::NotifySupervisedUserAcknowledged(
553    const std::string& supervised_user_id) {
554  FOR_EACH_OBSERVER(SupervisedUserSyncServiceObserver, observers_,
555                    OnSupervisedUserAcknowledged(supervised_user_id));
556}
557
558void SupervisedUserSyncService::NotifySupervisedUsersSyncingStopped() {
559  FOR_EACH_OBSERVER(SupervisedUserSyncServiceObserver, observers_,
560                    OnSupervisedUsersSyncingStopped());
561}
562
563void SupervisedUserSyncService::NotifySupervisedUsersChanged() {
564  FOR_EACH_OBSERVER(SupervisedUserSyncServiceObserver,
565                    observers_,
566                    OnSupervisedUsersChanged());
567}
568
569void SupervisedUserSyncService::DispatchCallbacks() {
570  const base::DictionaryValue* supervised_users =
571      prefs_->GetDictionary(prefs::kSupervisedUsers);
572  for (std::vector<SupervisedUsersCallback>::iterator it = callbacks_.begin();
573       it != callbacks_.end(); ++it) {
574    it->Run(supervised_users);
575  }
576  callbacks_.clear();
577}
578