Home | History | Annotate | Download | only in managed_mode
      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/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/common/pref_names.h"
     14 #include "components/user_prefs/pref_registry_syncable.h"
     15 #include "sync/api/sync_change.h"
     16 #include "sync/api/sync_data.h"
     17 #include "sync/api/sync_error.h"
     18 #include "sync/api/sync_error_factory.h"
     19 #include "sync/api/sync_merge_result.h"
     20 #include "sync/protocol/sync.pb.h"
     21 
     22 using base::DictionaryValue;
     23 using user_prefs::PrefRegistrySyncable;
     24 using syncer::MANAGED_USERS;
     25 using syncer::ModelType;
     26 using syncer::SyncChange;
     27 using syncer::SyncChangeList;
     28 using syncer::SyncChangeProcessor;
     29 using syncer::SyncData;
     30 using syncer::SyncDataList;
     31 using syncer::SyncError;
     32 using syncer::SyncErrorFactory;
     33 using syncer::SyncMergeResult;
     34 using sync_pb::ManagedUserSpecifics;
     35 
     36 namespace {
     37 
     38 #if defined(OS_CHROMEOS)
     39 const char kChromeOSAvatarPrefix[] = "chromeos-avatar-index:";
     40 #else
     41 const char kChromeAvatarPrefix[] = "chrome-avatar-index:";
     42 #endif
     43 
     44 SyncData CreateLocalSyncData(const std::string& id,
     45                              const std::string& name,
     46                              bool acknowledged,
     47                              const std::string& master_key,
     48                              const std::string& chrome_avatar,
     49                              const std::string& chromeos_avatar) {
     50   ::sync_pb::EntitySpecifics specifics;
     51   specifics.mutable_managed_user()->set_id(id);
     52   specifics.mutable_managed_user()->set_name(name);
     53   if (!chrome_avatar.empty())
     54     specifics.mutable_managed_user()->set_chrome_avatar(chrome_avatar);
     55   if (!chromeos_avatar.empty())
     56     specifics.mutable_managed_user()->set_chromeos_avatar(chromeos_avatar);
     57   if (!master_key.empty())
     58     specifics.mutable_managed_user()->set_master_key(master_key);
     59   if (acknowledged)
     60     specifics.mutable_managed_user()->set_acknowledged(true);
     61   return SyncData::CreateLocalData(id, name, specifics);
     62 }
     63 
     64 SyncData CreateSyncDataFromDictionaryEntry(
     65     const DictionaryValue::Iterator& it) {
     66   const DictionaryValue* dict = NULL;
     67   bool success = it.value().GetAsDictionary(&dict);
     68   DCHECK(success);
     69   bool acknowledged = false;
     70   dict->GetBoolean(ManagedUserSyncService::kAcknowledged, &acknowledged);
     71   std::string name;
     72   dict->GetString(ManagedUserSyncService::kName, &name);
     73   DCHECK(!name.empty());
     74   std::string master_key;
     75   dict->GetString(ManagedUserSyncService::kMasterKey, &master_key);
     76   std::string chrome_avatar;
     77   dict->GetString(ManagedUserSyncService::kChromeAvatar, &chrome_avatar);
     78   std::string chromeos_avatar;
     79   dict->GetString(ManagedUserSyncService::kChromeOsAvatar, &chromeos_avatar);
     80 
     81   return CreateLocalSyncData(it.key(), name, acknowledged, master_key,
     82                              chrome_avatar, chromeos_avatar);
     83 }
     84 
     85 }  // namespace
     86 
     87 const char ManagedUserSyncService::kAcknowledged[] = "acknowledged";
     88 const char ManagedUserSyncService::kChromeAvatar[] = "chromeAvatar";
     89 const char ManagedUserSyncService::kChromeOsAvatar[] = "chromeOsAvatar";
     90 const char ManagedUserSyncService::kMasterKey[] = "masterKey";
     91 const char ManagedUserSyncService::kName[] = "name";
     92 const int ManagedUserSyncService::kNoAvatar = -100;
     93 
     94 ManagedUserSyncService::ManagedUserSyncService(PrefService* prefs)
     95     : prefs_(prefs) {
     96   pref_change_registrar_.Init(prefs_);
     97   pref_change_registrar_.Add(
     98       prefs::kGoogleServicesLastUsername,
     99       base::Bind(&ManagedUserSyncService::OnLastSignedInUsernameChange,
    100                  base::Unretained(this)));
    101 }
    102 
    103 ManagedUserSyncService::~ManagedUserSyncService() {
    104 }
    105 
    106 // static
    107 void ManagedUserSyncService::RegisterProfilePrefs(
    108     PrefRegistrySyncable* registry) {
    109   registry->RegisterDictionaryPref(prefs::kManagedUsers,
    110                                    PrefRegistrySyncable::UNSYNCABLE_PREF);
    111 }
    112 
    113 // static
    114 bool ManagedUserSyncService::GetAvatarIndex(const std::string& avatar_str,
    115                                             int* avatar_index) {
    116   DCHECK(avatar_index);
    117   if (avatar_str.empty()) {
    118     *avatar_index = kNoAvatar;
    119     return true;
    120   }
    121 #if defined(OS_CHROMEOS)
    122   const char* prefix = kChromeOSAvatarPrefix;
    123 #else
    124   const char* prefix = kChromeAvatarPrefix;
    125 #endif
    126   size_t prefix_len = strlen(prefix);
    127   if (avatar_str.size() <= prefix_len ||
    128       avatar_str.substr(0, prefix_len) != prefix) {
    129     return false;
    130   }
    131 
    132   return base::StringToInt(avatar_str.substr(prefix_len), avatar_index);
    133 }
    134 
    135 // static
    136 std::string ManagedUserSyncService::BuildAvatarString(int avatar_index) {
    137 #if defined(OS_CHROMEOS)
    138   const char* prefix = kChromeOSAvatarPrefix;
    139 #else
    140   const char* prefix = kChromeAvatarPrefix;
    141 #endif
    142   return base::StringPrintf("%s%d", prefix, avatar_index);
    143 }
    144 
    145 void ManagedUserSyncService::AddObserver(
    146     ManagedUserSyncServiceObserver* observer) {
    147   observers_.AddObserver(observer);
    148 }
    149 
    150 void ManagedUserSyncService::RemoveObserver(
    151     ManagedUserSyncServiceObserver* observer) {
    152   observers_.RemoveObserver(observer);
    153 }
    154 
    155 void ManagedUserSyncService::AddManagedUser(const std::string& id,
    156                                             const std::string& name,
    157                                             const std::string& master_key,
    158                                             int avatar_index) {
    159   DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
    160   DictionaryValue* dict = update.Get();
    161   DictionaryValue* value = new DictionaryValue;
    162   value->SetString(kName, name);
    163   value->SetString(kMasterKey, master_key);
    164   std::string chrome_avatar;
    165   std::string chromeos_avatar;
    166 #if defined(OS_CHROMEOS)
    167   chromeos_avatar = BuildAvatarString(avatar_index);
    168 #else
    169   chrome_avatar = BuildAvatarString(avatar_index);
    170 #endif
    171   value->SetString(kChromeAvatar, chrome_avatar);
    172   value->SetString(kChromeOsAvatar, chromeos_avatar);
    173   DCHECK(!dict->HasKey(id));
    174   dict->SetWithoutPathExpansion(id, value);
    175 
    176   if (!sync_processor_)
    177     return;
    178 
    179   // If we're already syncing, create a new change and upload it.
    180   SyncChangeList change_list;
    181   change_list.push_back(SyncChange(
    182       FROM_HERE,
    183       SyncChange::ACTION_ADD,
    184       CreateLocalSyncData(id, name, false, master_key,
    185                           chrome_avatar, chromeos_avatar)));
    186   SyncError error =
    187       sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
    188   DCHECK(!error.IsSet()) << error.ToString();
    189 }
    190 
    191 void ManagedUserSyncService::DeleteManagedUser(const std::string& id) {
    192   DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
    193   bool success = update->RemoveWithoutPathExpansion(id, NULL);
    194   DCHECK(success);
    195 
    196   if (!sync_processor_)
    197     return;
    198 
    199   SyncChangeList change_list;
    200   change_list.push_back(SyncChange(
    201       FROM_HERE,
    202       SyncChange::ACTION_DELETE,
    203       SyncData::CreateLocalDelete(id, MANAGED_USERS)));
    204   SyncError sync_error =
    205       sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
    206   DCHECK(!sync_error.IsSet());
    207 }
    208 
    209 const DictionaryValue* ManagedUserSyncService::GetManagedUsers() {
    210   DCHECK(sync_processor_);
    211   return prefs_->GetDictionary(prefs::kManagedUsers);
    212 }
    213 
    214 bool ManagedUserSyncService::UpdateManagedUserAvatarIfNeeded(
    215     const std::string& id,
    216     int avatar_index) {
    217   DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
    218   DictionaryValue* dict = update.Get();
    219   DCHECK(dict->HasKey(id));
    220   DictionaryValue* value = NULL;
    221   bool success = dict->GetDictionaryWithoutPathExpansion(id, &value);
    222   DCHECK(success);
    223 
    224   bool acknowledged = false;
    225   value->GetBoolean(ManagedUserSyncService::kAcknowledged, &acknowledged);
    226   std::string name;
    227   value->GetString(ManagedUserSyncService::kName, &name);
    228   std::string master_key;
    229   value->GetString(ManagedUserSyncService::kMasterKey, &master_key);
    230   std::string chromeos_avatar;
    231   value->GetString(ManagedUserSyncService::kChromeOsAvatar, &chromeos_avatar);
    232   std::string chrome_avatar;
    233   value->GetString(ManagedUserSyncService::kChromeAvatar, &chrome_avatar);
    234   // The following check is just for safety. We want to avoid that the existing
    235   // avatar selection is overwritten. Currently we don't allow the user to
    236   // choose a different avatar in the recreation dialog, anyway, if there is
    237   // already an avatar selected.
    238 #if defined(OS_CHROMEOS)
    239   if (!chromeos_avatar.empty() && avatar_index != kNoAvatar)
    240     return false;
    241 #else
    242   if (!chrome_avatar.empty() && avatar_index != kNoAvatar)
    243     return false;
    244 #endif
    245 
    246   chrome_avatar = avatar_index == kNoAvatar ?
    247       std::string() : BuildAvatarString(avatar_index);
    248 #if defined(OS_CHROMEOS)
    249   value->SetString(kChromeOsAvatar, chrome_avatar);
    250 #else
    251   value->SetString(kChromeAvatar, chrome_avatar);
    252 #endif
    253 
    254   if (!sync_processor_)
    255     return true;
    256 
    257   SyncChangeList change_list;
    258   change_list.push_back(SyncChange(
    259       FROM_HERE,
    260       SyncChange::ACTION_UPDATE,
    261       CreateLocalSyncData(id, name, acknowledged, master_key,
    262                           chrome_avatar, chromeos_avatar)));
    263   SyncError error =
    264       sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
    265   DCHECK(!error.IsSet()) << error.ToString();
    266   return true;
    267 }
    268 
    269 void ManagedUserSyncService::ClearManagedUserAvatar(const std::string& id) {
    270   bool cleared = UpdateManagedUserAvatarIfNeeded(id, kNoAvatar);
    271   DCHECK(cleared);
    272 }
    273 
    274 void ManagedUserSyncService::GetManagedUsersAsync(
    275     const ManagedUsersCallback& callback) {
    276   // If we are already syncing, just run the callback.
    277   if (sync_processor_) {
    278     callback.Run(GetManagedUsers());
    279     return;
    280   }
    281 
    282   // Otherwise queue it up until we start syncing.
    283   callbacks_.push_back(callback);
    284 }
    285 
    286 void ManagedUserSyncService::Shutdown() {
    287   NotifyManagedUsersSyncingStopped();
    288 }
    289 
    290 SyncMergeResult ManagedUserSyncService::MergeDataAndStartSyncing(
    291     ModelType type,
    292     const SyncDataList& initial_sync_data,
    293     scoped_ptr<SyncChangeProcessor> sync_processor,
    294     scoped_ptr<SyncErrorFactory> error_handler) {
    295   DCHECK_EQ(MANAGED_USERS, type);
    296   sync_processor_ = sync_processor.Pass();
    297   error_handler_ = error_handler.Pass();
    298 
    299   SyncChangeList change_list;
    300   SyncMergeResult result(MANAGED_USERS);
    301 
    302   DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
    303   DictionaryValue* dict = update.Get();
    304   result.set_num_items_before_association(dict->size());
    305   std::set<std::string> seen_ids;
    306   int num_items_added = 0;
    307   int num_items_modified = 0;
    308   for (SyncDataList::const_iterator it = initial_sync_data.begin();
    309        it != initial_sync_data.end(); ++it) {
    310     DCHECK_EQ(MANAGED_USERS, it->GetDataType());
    311     const ManagedUserSpecifics& managed_user =
    312         it->GetSpecifics().managed_user();
    313     DictionaryValue* value = new DictionaryValue();
    314     value->SetString(kName, managed_user.name());
    315     value->SetBoolean(kAcknowledged, managed_user.acknowledged());
    316     value->SetString(kMasterKey, managed_user.master_key());
    317     value->SetString(kChromeAvatar, managed_user.chrome_avatar());
    318     value->SetString(kChromeOsAvatar, managed_user.chromeos_avatar());
    319     if (dict->HasKey(managed_user.id()))
    320       num_items_modified++;
    321     else
    322       num_items_added++;
    323     dict->SetWithoutPathExpansion(managed_user.id(), value);
    324     seen_ids.insert(managed_user.id());
    325   }
    326 
    327   for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
    328     if (seen_ids.find(it.key()) != seen_ids.end())
    329       continue;
    330 
    331     change_list.push_back(SyncChange(FROM_HERE, SyncChange::ACTION_ADD,
    332                                      CreateSyncDataFromDictionaryEntry(it)));
    333   }
    334   result.set_error(sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
    335 
    336   result.set_num_items_modified(num_items_modified);
    337   result.set_num_items_added(num_items_added);
    338   result.set_num_items_after_association(dict->size());
    339 
    340   DispatchCallbacks();
    341 
    342   return result;
    343 }
    344 
    345 void ManagedUserSyncService::StopSyncing(ModelType type) {
    346   DCHECK_EQ(MANAGED_USERS, type);
    347   // The observers may want to change the Sync data, so notify them before
    348   // resetting the |sync_processor_|.
    349   NotifyManagedUsersSyncingStopped();
    350   sync_processor_.reset();
    351   error_handler_.reset();
    352 }
    353 
    354 SyncDataList ManagedUserSyncService::GetAllSyncData(
    355     ModelType type) const {
    356   SyncDataList data;
    357   DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
    358   DictionaryValue* dict = update.Get();
    359   for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance())
    360     data.push_back(CreateSyncDataFromDictionaryEntry(it));
    361 
    362   return data;
    363 }
    364 
    365 SyncError ManagedUserSyncService::ProcessSyncChanges(
    366     const tracked_objects::Location& from_here,
    367     const SyncChangeList& change_list) {
    368   SyncError error;
    369   DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
    370   DictionaryValue* dict = update.Get();
    371   for (SyncChangeList::const_iterator it = change_list.begin();
    372        it != change_list.end(); ++it) {
    373     SyncData data = it->sync_data();
    374     DCHECK_EQ(MANAGED_USERS, data.GetDataType());
    375     const ManagedUserSpecifics& managed_user =
    376         data.GetSpecifics().managed_user();
    377     switch (it->change_type()) {
    378       case SyncChange::ACTION_ADD:
    379       case SyncChange::ACTION_UPDATE: {
    380         // Every item we get from the server should be acknowledged.
    381         DCHECK(managed_user.acknowledged());
    382         const DictionaryValue* old_value = NULL;
    383         dict->GetDictionaryWithoutPathExpansion(managed_user.id(), &old_value);
    384 
    385         // For an update action, the managed user should already exist, for an
    386         // add action, it should not.
    387         DCHECK_EQ(
    388             old_value ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD,
    389             it->change_type());
    390 
    391         // If the managed user switched from unacknowledged to acknowledged,
    392         // we might need to continue with a registration.
    393         if (old_value && !old_value->HasKey(kAcknowledged))
    394           NotifyManagedUserAcknowledged(managed_user.id());
    395 
    396         DictionaryValue* value = new DictionaryValue;
    397         value->SetString(kName, managed_user.name());
    398         value->SetBoolean(kAcknowledged, managed_user.acknowledged());
    399         value->SetString(kMasterKey, managed_user.master_key());
    400         value->SetString(kChromeAvatar, managed_user.chrome_avatar());
    401         value->SetString(kChromeOsAvatar, managed_user.chromeos_avatar());
    402         dict->SetWithoutPathExpansion(managed_user.id(), value);
    403         break;
    404       }
    405       case SyncChange::ACTION_DELETE: {
    406         DCHECK(dict->HasKey(managed_user.id())) << managed_user.id();
    407         dict->RemoveWithoutPathExpansion(managed_user.id(), NULL);
    408         break;
    409       }
    410       case SyncChange::ACTION_INVALID: {
    411         NOTREACHED();
    412         break;
    413       }
    414     }
    415   }
    416   return error;
    417 }
    418 
    419 void ManagedUserSyncService::OnLastSignedInUsernameChange() {
    420   DCHECK(!sync_processor_);
    421 
    422   // If the last signed in user changes, we clear all data, to avoid managed
    423   // users from one custodian appearing in another one's profile.
    424   prefs_->ClearPref(prefs::kManagedUsers);
    425 }
    426 
    427 void ManagedUserSyncService::NotifyManagedUserAcknowledged(
    428     const std::string& managed_user_id) {
    429   FOR_EACH_OBSERVER(ManagedUserSyncServiceObserver, observers_,
    430                     OnManagedUserAcknowledged(managed_user_id));
    431 }
    432 
    433 void ManagedUserSyncService::NotifyManagedUsersSyncingStopped() {
    434   FOR_EACH_OBSERVER(ManagedUserSyncServiceObserver, observers_,
    435                     OnManagedUsersSyncingStopped());
    436 }
    437 
    438 void ManagedUserSyncService::DispatchCallbacks() {
    439   const DictionaryValue* managed_users =
    440       prefs_->GetDictionary(prefs::kManagedUsers);
    441   for (std::vector<ManagedUsersCallback>::iterator it = callbacks_.begin();
    442        it != callbacks_.end(); ++it) {
    443     it->Run(managed_users);
    444   }
    445   callbacks_.clear();
    446 }
    447