Home | History | Annotate | Download | only in supervised_user
      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 
     27 using base::DictionaryValue;
     28 using user_prefs::PrefRegistrySyncable;
     29 using syncer::SUPERVISED_USERS;
     30 using syncer::ModelType;
     31 using syncer::SyncChange;
     32 using syncer::SyncChangeList;
     33 using syncer::SyncChangeProcessor;
     34 using syncer::SyncData;
     35 using syncer::SyncDataList;
     36 using syncer::SyncError;
     37 using syncer::SyncErrorFactory;
     38 using syncer::SyncMergeResult;
     39 using sync_pb::ManagedUserSpecifics;
     40 
     41 namespace {
     42 
     43 #if defined(OS_CHROMEOS)
     44 const char kChromeOSAvatarPrefix[] = "chromeos-avatar-index:";
     45 #else
     46 const char kChromeAvatarPrefix[] = "chrome-avatar-index:";
     47 #endif
     48 
     49 SyncData 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 
     79 SyncData 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 
    113 const char SupervisedUserSyncService::kAcknowledged[] = "acknowledged";
    114 const char SupervisedUserSyncService::kChromeAvatar[] = "chromeAvatar";
    115 const char SupervisedUserSyncService::kChromeOsAvatar[] = "chromeOsAvatar";
    116 const char SupervisedUserSyncService::kMasterKey[] = "masterKey";
    117 const char SupervisedUserSyncService::kName[] = "name";
    118 const char SupervisedUserSyncService::kPasswordSignatureKey[] =
    119     "passwordSignatureKey";
    120 const char SupervisedUserSyncService::kPasswordEncryptionKey[] =
    121     "passwordEncryptionKey";
    122 const int SupervisedUserSyncService::kNoAvatar = -100;
    123 
    124 SupervisedUserSyncService::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 
    133 SupervisedUserSyncService::~SupervisedUserSyncService() {
    134 }
    135 
    136 // static
    137 void SupervisedUserSyncService::RegisterProfilePrefs(
    138     PrefRegistrySyncable* registry) {
    139   registry->RegisterDictionaryPref(prefs::kSupervisedUsers,
    140                                    PrefRegistrySyncable::UNSYNCABLE_PREF);
    141 }
    142 
    143 // static
    144 bool 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
    187 std::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 
    196 void SupervisedUserSyncService::AddObserver(
    197     SupervisedUserSyncServiceObserver* observer) {
    198   observers_.AddObserver(observer);
    199 }
    200 
    201 void SupervisedUserSyncService::RemoveObserver(
    202     SupervisedUserSyncServiceObserver* observer) {
    203   observers_.RemoveObserver(observer);
    204 }
    205 
    206 scoped_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 
    231 void 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 
    247 void 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 
    263 void 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 
    294 void 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 
    312 const base::DictionaryValue* SupervisedUserSyncService::GetSupervisedUsers() {
    313   DCHECK(sync_processor_);
    314   return prefs_->GetDictionary(prefs::kSupervisedUsers);
    315 }
    316 
    317 bool 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 
    380 void SupervisedUserSyncService::ClearSupervisedUserAvatar(
    381     const std::string& id) {
    382   bool cleared = UpdateSupervisedUserAvatarIfNeeded(id, kNoAvatar);
    383   DCHECK(cleared);
    384 }
    385 
    386 void 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 
    398 void SupervisedUserSyncService::Shutdown() {
    399   NotifySupervisedUsersSyncingStopped();
    400 }
    401 
    402 SyncMergeResult 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 
    463 void 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 
    472 SyncDataList 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 
    483 SyncError 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 
    544 void 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 
    552 void SupervisedUserSyncService::NotifySupervisedUserAcknowledged(
    553     const std::string& supervised_user_id) {
    554   FOR_EACH_OBSERVER(SupervisedUserSyncServiceObserver, observers_,
    555                     OnSupervisedUserAcknowledged(supervised_user_id));
    556 }
    557 
    558 void SupervisedUserSyncService::NotifySupervisedUsersSyncingStopped() {
    559   FOR_EACH_OBSERVER(SupervisedUserSyncServiceObserver, observers_,
    560                     OnSupervisedUsersSyncingStopped());
    561 }
    562 
    563 void SupervisedUserSyncService::NotifySupervisedUsersChanged() {
    564   FOR_EACH_OBSERVER(SupervisedUserSyncServiceObserver,
    565                     observers_,
    566                     OnSupervisedUsersChanged());
    567 }
    568 
    569 void 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