Home | History | Annotate | Download | only in browser
      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 "components/password_manager/core/browser/password_syncable_service.h"
      6 
      7 #include "base/auto_reset.h"
      8 #include "base/location.h"
      9 #include "base/memory/scoped_vector.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "components/autofill/core/common/password_form.h"
     13 #include "components/password_manager/core/browser/password_store_sync.h"
     14 #include "net/base/escape.h"
     15 #include "sync/api/sync_error_factory.h"
     16 
     17 namespace password_manager {
     18 
     19 namespace {
     20 
     21 // Describes the result of merging sync and local passwords.
     22 enum MergeResult {
     23   IDENTICAL,
     24   SYNC,
     25   LOCAL,
     26 };
     27 
     28 // Merges the local and sync passwords and outputs the entry into
     29 // |new_password_form|. Returns MergeResult value describing the content of
     30 // |new_password_form|.
     31 MergeResult MergeLocalAndSyncPasswords(
     32     const sync_pb::PasswordSpecificsData& password_specifics,
     33     const autofill::PasswordForm& password_form,
     34     autofill::PasswordForm* new_password_form) {
     35   DCHECK(new_password_form);
     36   if (password_form.scheme == password_specifics.scheme() &&
     37       password_form.signon_realm == password_specifics.signon_realm() &&
     38       password_form.origin.spec() == password_specifics.origin() &&
     39       password_form.action.spec() == password_specifics.action() &&
     40       base::UTF16ToUTF8(password_form.username_element) ==
     41           password_specifics.username_element() &&
     42       base::UTF16ToUTF8(password_form.password_element) ==
     43           password_specifics.password_element() &&
     44       base::UTF16ToUTF8(password_form.username_value) ==
     45           password_specifics.username_value() &&
     46       base::UTF16ToUTF8(password_form.password_value) ==
     47           password_specifics.password_value() &&
     48       password_form.ssl_valid == password_specifics.ssl_valid() &&
     49       password_form.preferred == password_specifics.preferred() &&
     50       password_form.date_created.ToInternalValue() ==
     51           password_specifics.date_created() &&
     52       password_form.blacklisted_by_user == password_specifics.blacklisted() &&
     53       password_form.type == password_specifics.type() &&
     54       password_form.times_used == password_specifics.times_used()) {
     55     return IDENTICAL;
     56   }
     57 
     58   // If the passwords differ, take the one that was created more recently.
     59   if (base::Time::FromInternalValue(password_specifics.date_created()) <
     60           password_form.date_created) {
     61     *new_password_form = password_form;
     62     return LOCAL;
     63   }
     64 
     65   PasswordFromSpecifics(password_specifics, new_password_form);
     66   return SYNC;
     67 }
     68 
     69 std::string MakePasswordSyncTag(const std::string& origin_url,
     70                                 const std::string& username_element,
     71                                 const std::string& username_value,
     72                                 const std::string& password_element,
     73                                 const std::string& signon_realm) {
     74   return net::EscapePath(origin_url) + "|" +
     75          net::EscapePath(username_element) + "|" +
     76          net::EscapePath(username_value) + "|" +
     77          net::EscapePath(password_element) + "|" +
     78          net::EscapePath(signon_realm);
     79 }
     80 
     81 std::string MakePasswordSyncTag(const autofill::PasswordForm& password) {
     82   return MakePasswordSyncTag(password.origin.spec(),
     83                              base::UTF16ToUTF8(password.username_element),
     84                              base::UTF16ToUTF8(password.username_value),
     85                              base::UTF16ToUTF8(password.password_element),
     86                              password.signon_realm);
     87 }
     88 
     89 syncer::SyncChange::SyncChangeType GetSyncChangeType(
     90     PasswordStoreChange::Type type) {
     91   switch (type) {
     92     case PasswordStoreChange::ADD:
     93       return syncer::SyncChange::ACTION_ADD;
     94     case PasswordStoreChange::UPDATE:
     95       return syncer::SyncChange::ACTION_UPDATE;
     96     case PasswordStoreChange::REMOVE:
     97       return syncer::SyncChange::ACTION_DELETE;
     98   }
     99   NOTREACHED();
    100   return syncer::SyncChange::ACTION_INVALID;
    101 }
    102 
    103 void AppendChanges(const PasswordStoreChangeList& new_changes,
    104                    PasswordStoreChangeList* all_changes) {
    105   all_changes->insert(all_changes->end(),
    106                       new_changes.begin(),
    107                       new_changes.end());
    108 }
    109 
    110 }  // namespace
    111 
    112 PasswordSyncableService::PasswordSyncableService(
    113     PasswordStoreSync* password_store)
    114     : password_store_(password_store),
    115       is_processing_sync_changes_(false) {
    116 }
    117 
    118 PasswordSyncableService::~PasswordSyncableService() {}
    119 
    120 syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
    121     syncer::ModelType type,
    122     const syncer::SyncDataList& initial_sync_data,
    123     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
    124     scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
    125   DCHECK(CalledOnValidThread());
    126   DCHECK_EQ(syncer::PASSWORDS, type);
    127   base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true);
    128   syncer::SyncMergeResult merge_result(type);
    129   sync_error_factory_ = sync_error_factory.Pass();
    130   sync_processor_ = sync_processor.Pass();
    131 
    132   // We add all the db entries as |new_local_entries| initially. During model
    133   // association entries that match a sync entry will be removed and this list
    134   // will only contain entries that are not in sync.
    135   ScopedVector<autofill::PasswordForm> password_entries;
    136   PasswordEntryMap new_local_entries;
    137   if (!ReadFromPasswordStore(&password_entries, &new_local_entries)) {
    138     DCHECK(sync_error_factory_);
    139     merge_result.set_error(sync_error_factory_->CreateAndUploadError(
    140         FROM_HERE,
    141         "Failed to get passwords from store."));
    142     return merge_result;
    143   }
    144 
    145   merge_result.set_num_items_before_association(new_local_entries.size());
    146 
    147   // List that contains the entries that are known only to sync.
    148   ScopedVector<autofill::PasswordForm> new_sync_entries;
    149 
    150   // List that contains the entries that are known to both sync and db but
    151   // have updates in sync. They need to be updated in the passwords db.
    152   ScopedVector<autofill::PasswordForm> updated_sync_entries;
    153 
    154   // Changes from password db that need to be propagated to sync.
    155   syncer::SyncChangeList updated_db_entries;
    156   for (syncer::SyncDataList::const_iterator sync_iter =
    157            initial_sync_data.begin();
    158        sync_iter != initial_sync_data.end(); ++sync_iter) {
    159     CreateOrUpdateEntry(*sync_iter,
    160                         &new_local_entries,
    161                         &new_sync_entries,
    162                         &updated_sync_entries,
    163                         &updated_db_entries);
    164   }
    165 
    166   WriteToPasswordStore(new_sync_entries.get(),
    167                        updated_sync_entries.get(),
    168                        PasswordForms());
    169 
    170   merge_result.set_num_items_after_association(
    171       merge_result.num_items_before_association() + new_sync_entries.size());
    172 
    173   merge_result.set_num_items_added(new_sync_entries.size());
    174 
    175   merge_result.set_num_items_modified(updated_sync_entries.size());
    176 
    177   for (PasswordEntryMap::iterator it = new_local_entries.begin();
    178        it != new_local_entries.end();
    179        ++it) {
    180     updated_db_entries.push_back(
    181         syncer::SyncChange(FROM_HERE,
    182                            syncer::SyncChange::ACTION_ADD,
    183                            SyncDataFromPassword(*it->second)));
    184   }
    185 
    186   merge_result.set_error(
    187       sync_processor_->ProcessSyncChanges(FROM_HERE, updated_db_entries));
    188   return merge_result;
    189 }
    190 
    191 void PasswordSyncableService::StopSyncing(syncer::ModelType type) {
    192   DCHECK(CalledOnValidThread());
    193   DCHECK_EQ(syncer::PASSWORDS, type);
    194 
    195   sync_processor_.reset();
    196   sync_error_factory_.reset();
    197 }
    198 
    199 syncer::SyncDataList PasswordSyncableService::GetAllSyncData(
    200     syncer::ModelType type) const {
    201   DCHECK(CalledOnValidThread());
    202   DCHECK_EQ(syncer::PASSWORDS, type);
    203   ScopedVector<autofill::PasswordForm> password_entries;
    204   ReadFromPasswordStore(&password_entries, NULL);
    205 
    206   syncer::SyncDataList sync_data;
    207   for (PasswordForms::iterator it = password_entries.begin();
    208        it != password_entries.end(); ++it) {
    209     sync_data.push_back(SyncDataFromPassword(**it));
    210   }
    211   return sync_data;
    212 }
    213 
    214 syncer::SyncError PasswordSyncableService::ProcessSyncChanges(
    215     const tracked_objects::Location& from_here,
    216     const syncer::SyncChangeList& change_list) {
    217   DCHECK(CalledOnValidThread());
    218   base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true);
    219   // The |db_entries_map| and associated vectors are filled only in case of
    220   // update change.
    221   ScopedVector<autofill::PasswordForm> new_sync_entries;
    222   ScopedVector<autofill::PasswordForm> updated_sync_entries;
    223   ScopedVector<autofill::PasswordForm> deleted_entries;
    224   base::Time time_now = base::Time::Now();
    225 
    226   for (syncer::SyncChangeList::const_iterator it = change_list.begin();
    227        it != change_list.end();
    228        ++it) {
    229     const sync_pb::EntitySpecifics& specifics = it->sync_data().GetSpecifics();
    230     scoped_ptr<autofill::PasswordForm> form(new autofill::PasswordForm);
    231     PasswordFromSpecifics(specifics.password().client_only_encrypted_data(),
    232                           form.get());
    233     switch (it->change_type()) {
    234       case syncer::SyncChange::ACTION_ADD: {
    235         form->date_synced = time_now;
    236         new_sync_entries.push_back(form.release());
    237         break;
    238       }
    239       case syncer::SyncChange::ACTION_UPDATE: {
    240         form->date_synced = time_now;
    241         updated_sync_entries.push_back(form.release());
    242         break;
    243       }
    244 
    245       case syncer::SyncChange::ACTION_DELETE: {
    246         deleted_entries.push_back(form.release());
    247         break;
    248       }
    249       case syncer::SyncChange::ACTION_INVALID:
    250         return sync_error_factory_->CreateAndUploadError(
    251             FROM_HERE,
    252             "Failed to process sync changes for passwords datatype.");
    253     }
    254   }
    255   WriteToPasswordStore(new_sync_entries.get(),
    256                        updated_sync_entries.get(),
    257                        deleted_entries.get());
    258   return syncer::SyncError();
    259 }
    260 
    261 void PasswordSyncableService::ActOnPasswordStoreChanges(
    262     const PasswordStoreChangeList& local_changes) {
    263   DCHECK(CalledOnValidThread());
    264 
    265   if (!sync_processor_) {
    266     if (!flare_.is_null()) {
    267       flare_.Run(syncer::PASSWORDS);
    268       flare_.Reset();
    269     }
    270     return;
    271   }
    272 
    273   // ActOnPasswordStoreChanges() can be called from ProcessSyncChanges(). Do
    274   // nothing in this case.
    275   if (is_processing_sync_changes_)
    276     return;
    277   syncer::SyncChangeList sync_changes;
    278   for (PasswordStoreChangeList::const_iterator it = local_changes.begin();
    279        it != local_changes.end();
    280        ++it) {
    281     syncer::SyncData data = (it->type() == PasswordStoreChange::REMOVE ?
    282         syncer::SyncData::CreateLocalDelete(MakePasswordSyncTag(it->form()),
    283                                             syncer::PASSWORDS) :
    284         SyncDataFromPassword(it->form()));
    285     sync_changes.push_back(
    286         syncer::SyncChange(FROM_HERE, GetSyncChangeType(it->type()), data));
    287   }
    288   sync_processor_->ProcessSyncChanges(FROM_HERE, sync_changes);
    289 }
    290 
    291 void PasswordSyncableService::InjectStartSyncFlare(
    292     const syncer::SyncableService::StartSyncFlare& flare) {
    293   DCHECK(CalledOnValidThread());
    294   flare_ = flare;
    295 }
    296 
    297 bool PasswordSyncableService::ReadFromPasswordStore(
    298     ScopedVector<autofill::PasswordForm>* password_entries,
    299     PasswordEntryMap* passwords_entry_map) const {
    300   DCHECK(password_entries);
    301   if (!password_store_->FillAutofillableLogins(&password_entries->get()) ||
    302       !password_store_->FillBlacklistLogins(&password_entries->get())) {
    303     // Password store often fails to load passwords. Track failures with UMA.
    304     // (http://crbug.com/249000)
    305     UMA_HISTOGRAM_ENUMERATION("Sync.LocalDataFailedToLoad",
    306                               ModelTypeToHistogramInt(syncer::PASSWORDS),
    307                               syncer::MODEL_TYPE_COUNT);
    308     return false;
    309   }
    310 
    311   if (!passwords_entry_map)
    312     return true;
    313 
    314   for (PasswordForms::iterator it = password_entries->begin();
    315        it != password_entries->end(); ++it) {
    316      autofill::PasswordForm* password_form = *it;
    317      passwords_entry_map->insert(
    318          std::make_pair(MakePasswordSyncTag(*password_form), password_form));
    319   }
    320 
    321   return true;
    322 }
    323 
    324 void PasswordSyncableService::WriteToPasswordStore(
    325     const PasswordForms& new_entries,
    326     const PasswordForms& updated_entries,
    327     const PasswordForms& deleted_entries) {
    328   PasswordStoreChangeList changes;
    329   for (std::vector<autofill::PasswordForm*>::const_iterator it =
    330            new_entries.begin();
    331        it != new_entries.end();
    332        ++it) {
    333     AppendChanges(password_store_->AddLoginImpl(**it), &changes);
    334   }
    335 
    336   for (std::vector<autofill::PasswordForm*>::const_iterator it =
    337            updated_entries.begin();
    338        it != updated_entries.end();
    339        ++it) {
    340     AppendChanges(password_store_->UpdateLoginImpl(**it), &changes);
    341   }
    342 
    343   for (std::vector<autofill::PasswordForm*>::const_iterator it =
    344            deleted_entries.begin();
    345        it != deleted_entries.end();
    346        ++it) {
    347     AppendChanges(password_store_->RemoveLoginImpl(**it), &changes);
    348   }
    349 
    350   // We have to notify password store observers of the change by hand since
    351   // we use internal password store interfaces to make changes synchronously.
    352   NotifyPasswordStoreOfLoginChanges(changes);
    353 }
    354 
    355 void PasswordSyncableService::NotifyPasswordStoreOfLoginChanges(
    356     const PasswordStoreChangeList& changes) {
    357   password_store_->NotifyLoginsChanged(changes);
    358 }
    359 
    360 void PasswordSyncableService::CreateOrUpdateEntry(
    361     const syncer::SyncData& data,
    362     PasswordEntryMap* umatched_data_from_password_db,
    363     ScopedVector<autofill::PasswordForm>* new_sync_entries,
    364     ScopedVector<autofill::PasswordForm>* updated_sync_entries,
    365     syncer::SyncChangeList* updated_db_entries) {
    366   const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
    367   const sync_pb::PasswordSpecificsData& password_specifics(
    368       specifics.password().client_only_encrypted_data());
    369   std::string tag = MakePasswordSyncTag(password_specifics);
    370 
    371   // Check whether the data from sync is already in the password store.
    372   PasswordEntryMap::iterator existing_local_entry_iter =
    373       umatched_data_from_password_db->find(tag);
    374   base::Time time_now = base::Time::Now();
    375   if (existing_local_entry_iter == umatched_data_from_password_db->end()) {
    376     // The sync data is not in the password store, so we need to create it in
    377     // the password store. Add the entry to the new_entries list.
    378     scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm);
    379     new_password->date_synced = time_now;
    380     PasswordFromSpecifics(password_specifics, new_password.get());
    381     new_sync_entries->push_back(new_password.release());
    382   } else {
    383     // The entry is in password store. If the entries are not identical, then
    384     // the entries need to be merged.
    385     scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm);
    386     switch (MergeLocalAndSyncPasswords(password_specifics,
    387                                        *existing_local_entry_iter->second,
    388                                        new_password.get())) {
    389       case IDENTICAL:
    390         break;
    391       case SYNC:
    392         new_password->date_synced = time_now;
    393         updated_sync_entries->push_back(new_password.release());
    394         break;
    395       case LOCAL:
    396         updated_db_entries->push_back(
    397             syncer::SyncChange(FROM_HERE,
    398                                syncer::SyncChange::ACTION_UPDATE,
    399                                SyncDataFromPassword(*new_password)));
    400         break;
    401     }
    402     // Remove the entry from the entry map to indicate a match has been found.
    403     // Entries that remain in the map at the end of associating all sync entries
    404     // will be treated as additions that need to be propagated to sync.
    405     umatched_data_from_password_db->erase(existing_local_entry_iter);
    406   }
    407 }
    408 
    409 syncer::SyncData SyncDataFromPassword(
    410     const autofill::PasswordForm& password_form) {
    411   sync_pb::EntitySpecifics password_data;
    412   sync_pb::PasswordSpecificsData* password_specifics =
    413       password_data.mutable_password()->mutable_client_only_encrypted_data();
    414   password_specifics->set_scheme(password_form.scheme);
    415   password_specifics->set_signon_realm(password_form.signon_realm);
    416   password_specifics->set_origin(password_form.origin.spec());
    417   password_specifics->set_action(password_form.action.spec());
    418   password_specifics->set_username_element(
    419       base::UTF16ToUTF8(password_form.username_element));
    420   password_specifics->set_password_element(
    421       base::UTF16ToUTF8(password_form.password_element));
    422   password_specifics->set_username_value(
    423       base::UTF16ToUTF8(password_form.username_value));
    424   password_specifics->set_password_value(
    425       base::UTF16ToUTF8(password_form.password_value));
    426   password_specifics->set_ssl_valid(password_form.ssl_valid);
    427   password_specifics->set_preferred(password_form.preferred);
    428   password_specifics->set_date_created(
    429       password_form.date_created.ToInternalValue());
    430   password_specifics->set_blacklisted(password_form.blacklisted_by_user);
    431   password_specifics->set_type(password_form.type);
    432   password_specifics->set_times_used(password_form.times_used);
    433 
    434   std::string tag = MakePasswordSyncTag(*password_specifics);
    435   return syncer::SyncData::CreateLocalData(tag, tag, password_data);
    436 }
    437 
    438 void PasswordFromSpecifics(const sync_pb::PasswordSpecificsData& password,
    439                            autofill::PasswordForm* new_password) {
    440   new_password->scheme =
    441       static_cast<autofill::PasswordForm::Scheme>(password.scheme());
    442   new_password->signon_realm = password.signon_realm();
    443   new_password->origin = GURL(password.origin());
    444   new_password->action = GURL(password.action());
    445   new_password->username_element =
    446       base::UTF8ToUTF16(password.username_element());
    447   new_password->password_element =
    448       base::UTF8ToUTF16(password.password_element());
    449   new_password->username_value = base::UTF8ToUTF16(password.username_value());
    450   new_password->password_value = base::UTF8ToUTF16(password.password_value());
    451   new_password->ssl_valid = password.ssl_valid();
    452   new_password->preferred = password.preferred();
    453   new_password->date_created =
    454       base::Time::FromInternalValue(password.date_created());
    455   new_password->blacklisted_by_user = password.blacklisted();
    456   new_password->type =
    457       static_cast<autofill::PasswordForm::Type>(password.type());
    458   new_password->times_used = password.times_used();
    459 }
    460 
    461 std::string MakePasswordSyncTag(
    462     const sync_pb::PasswordSpecificsData& password) {
    463   return MakePasswordSyncTag(password.origin(),
    464                              password.username_element(),
    465                              password.username_value(),
    466                              password.password_element(),
    467                              password.signon_realm());
    468 }
    469 
    470 }  // namespace password_manager
    471