Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2012 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/sync/glue/password_model_associator.h"
      6 
      7 #include <set>
      8 
      9 #include "base/location.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/stl_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chrome/browser/password_manager/password_store.h"
     14 #include "chrome/browser/sync/profile_sync_service.h"
     15 #include "content/public/common/password_form.h"
     16 #include "net/base/escape.h"
     17 #include "sync/api/sync_error.h"
     18 #include "sync/internal_api/public/read_node.h"
     19 #include "sync/internal_api/public/read_transaction.h"
     20 #include "sync/internal_api/public/write_node.h"
     21 #include "sync/internal_api/public/write_transaction.h"
     22 #include "sync/protocol/password_specifics.pb.h"
     23 
     24 using content::BrowserThread;
     25 
     26 namespace browser_sync {
     27 
     28 const char kPasswordTag[] = "google_chrome_passwords";
     29 
     30 PasswordModelAssociator::PasswordModelAssociator(
     31     ProfileSyncService* sync_service,
     32     PasswordStore* password_store,
     33     DataTypeErrorHandler* error_handler)
     34     : sync_service_(sync_service),
     35       password_store_(password_store),
     36       password_node_id_(syncer::kInvalidId),
     37       abort_association_requested_(false),
     38       expected_loop_(base::MessageLoop::current()),
     39       error_handler_(error_handler) {
     40   DCHECK(sync_service_);
     41 #if defined(OS_MACOSX)
     42   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
     43 #else
     44   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
     45 #endif
     46 }
     47 
     48 PasswordModelAssociator::~PasswordModelAssociator() {
     49   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
     50 }
     51 
     52 syncer::SyncError PasswordModelAssociator::AssociateModels(
     53     syncer::SyncMergeResult* local_merge_result,
     54     syncer::SyncMergeResult* syncer_merge_result) {
     55   DCHECK(expected_loop_ == base::MessageLoop::current());
     56 
     57   // We must not be holding a transaction when we interact with the password
     58   // store, as it can post tasks to the UI thread which can itself be blocked
     59   // on our transaction, resulting in deadlock. (http://crbug.com/70658)
     60   std::vector<content::PasswordForm*> passwords;
     61   if (!password_store_->FillAutofillableLogins(&passwords) ||
     62       !password_store_->FillBlacklistLogins(&passwords)) {
     63     STLDeleteElements(&passwords);
     64 
     65     // Password store often fails to load passwords. Track failures with UMA.
     66     // (http://crbug.com/249000)
     67     UMA_HISTOGRAM_ENUMERATION("Sync.LocalDataFailedToLoad",
     68                               ModelTypeToHistogramInt(syncer::PASSWORDS),
     69                               syncer::MODEL_TYPE_COUNT);
     70     return syncer::SyncError(FROM_HERE,
     71                              syncer::SyncError::DATATYPE_ERROR,
     72                              "Could not get the password entries.",
     73                              model_type());
     74   }
     75 
     76   PasswordVector new_passwords;
     77   PasswordVector updated_passwords;
     78   {
     79     base::AutoLock lock(association_lock_);
     80     if (abort_association_requested_)
     81       return syncer::SyncError();
     82 
     83     std::set<std::string> current_passwords;
     84     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
     85     syncer::ReadNode password_root(&trans);
     86     if (password_root.InitByTagLookup(kPasswordTag) !=
     87             syncer::BaseNode::INIT_OK) {
     88       return error_handler_->CreateAndUploadError(
     89           FROM_HERE,
     90           "Server did not create the top-level password node. We "
     91           "might be running against an out-of-date server.",
     92           model_type());
     93     }
     94 
     95     for (std::vector<content::PasswordForm*>::iterator ix =
     96              passwords.begin();
     97          ix != passwords.end(); ++ix) {
     98       std::string tag = MakeTag(**ix);
     99 
    100       syncer::ReadNode node(&trans);
    101       if (node.InitByClientTagLookup(syncer::PASSWORDS, tag) ==
    102               syncer::BaseNode::INIT_OK) {
    103         const sync_pb::PasswordSpecificsData& password =
    104             node.GetPasswordSpecifics();
    105         DCHECK_EQ(tag, MakeTag(password));
    106 
    107         content::PasswordForm new_password;
    108 
    109         if (MergePasswords(password, **ix, &new_password)) {
    110           syncer::WriteNode write_node(&trans);
    111           if (write_node.InitByClientTagLookup(syncer::PASSWORDS, tag) !=
    112                   syncer::BaseNode::INIT_OK) {
    113             STLDeleteElements(&passwords);
    114             return error_handler_->CreateAndUploadError(
    115                 FROM_HERE,
    116                 "Failed to edit password sync node.",
    117                 model_type());
    118           }
    119           WriteToSyncNode(new_password, &write_node);
    120           updated_passwords.push_back(new_password);
    121         }
    122 
    123         Associate(&tag, node.GetId());
    124       } else {
    125         syncer::WriteNode node(&trans);
    126         syncer::WriteNode::InitUniqueByCreationResult result =
    127             node.InitUniqueByCreation(syncer::PASSWORDS, password_root, tag);
    128         if (result != syncer::WriteNode::INIT_SUCCESS) {
    129           STLDeleteElements(&passwords);
    130           return error_handler_->CreateAndUploadError(
    131               FROM_HERE,
    132               "Failed to create password sync node.",
    133               model_type());
    134         }
    135 
    136         WriteToSyncNode(**ix, &node);
    137 
    138         Associate(&tag, node.GetId());
    139       }
    140 
    141       current_passwords.insert(tag);
    142     }
    143 
    144     STLDeleteElements(&passwords);
    145 
    146     int64 sync_child_id = password_root.GetFirstChildId();
    147     while (sync_child_id != syncer::kInvalidId) {
    148       syncer::ReadNode sync_child_node(&trans);
    149       if (sync_child_node.InitByIdLookup(sync_child_id) !=
    150               syncer::BaseNode::INIT_OK) {
    151         return error_handler_->CreateAndUploadError(
    152             FROM_HERE,
    153             "Failed to fetch child node.",
    154             model_type());
    155       }
    156       const sync_pb::PasswordSpecificsData& password =
    157           sync_child_node.GetPasswordSpecifics();
    158       std::string tag = MakeTag(password);
    159 
    160       // The password only exists on the server.  Add it to the local
    161       // model.
    162       if (current_passwords.find(tag) == current_passwords.end()) {
    163         content::PasswordForm new_password;
    164 
    165         CopyPassword(password, &new_password);
    166         Associate(&tag, sync_child_node.GetId());
    167         new_passwords.push_back(new_password);
    168       }
    169 
    170       sync_child_id = sync_child_node.GetSuccessorId();
    171     }
    172   }
    173 
    174   // We must not be holding a transaction when we interact with the password
    175   // store, as it can post tasks to the UI thread which can itself be blocked
    176   // on our transaction, resulting in deadlock. (http://crbug.com/70658)
    177   return WriteToPasswordStore(&new_passwords,
    178                               &updated_passwords,
    179                               NULL);
    180 }
    181 
    182 bool PasswordModelAssociator::DeleteAllNodes(
    183     syncer::WriteTransaction* trans) {
    184   DCHECK(expected_loop_ == base::MessageLoop::current());
    185   for (PasswordToSyncIdMap::iterator node_id = id_map_.begin();
    186        node_id != id_map_.end(); ++node_id) {
    187     syncer::WriteNode sync_node(trans);
    188     if (sync_node.InitByIdLookup(node_id->second) !=
    189             syncer::BaseNode::INIT_OK) {
    190       LOG(ERROR) << "Typed url node lookup failed.";
    191       return false;
    192     }
    193     sync_node.Tombstone();
    194   }
    195 
    196   id_map_.clear();
    197   id_map_inverse_.clear();
    198   return true;
    199 }
    200 
    201 syncer::SyncError PasswordModelAssociator::DisassociateModels() {
    202   id_map_.clear();
    203   id_map_inverse_.clear();
    204   return syncer::SyncError();
    205 }
    206 
    207 bool PasswordModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
    208   DCHECK(has_nodes);
    209   *has_nodes = false;
    210   int64 password_sync_id;
    211   if (!GetSyncIdForTaggedNode(kPasswordTag, &password_sync_id)) {
    212     LOG(ERROR) << "Server did not create the top-level password node. We "
    213                << "might be running against an out-of-date server.";
    214     return false;
    215   }
    216   syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
    217 
    218   syncer::ReadNode password_node(&trans);
    219   if (password_node.InitByIdLookup(password_sync_id) !=
    220           syncer::BaseNode::INIT_OK) {
    221     LOG(ERROR) << "Server did not create the top-level password node. We "
    222                << "might be running against an out-of-date server.";
    223     return false;
    224   }
    225 
    226   // The sync model has user created nodes if the password folder has any
    227   // children.
    228   *has_nodes = password_node.HasChildren();
    229   return true;
    230 }
    231 
    232 void PasswordModelAssociator::AbortAssociation() {
    233   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    234   base::AutoLock lock(association_lock_);
    235   abort_association_requested_ = true;
    236 }
    237 
    238 bool PasswordModelAssociator::CryptoReadyIfNecessary() {
    239   // We only access the cryptographer while holding a transaction.
    240   syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
    241   // We always encrypt passwords, so no need to check if encryption is enabled.
    242   return sync_service_->IsCryptographerReady(&trans);
    243 }
    244 
    245 const std::string* PasswordModelAssociator::GetChromeNodeFromSyncId(
    246     int64 sync_id) {
    247   return NULL;
    248 }
    249 
    250 bool PasswordModelAssociator::InitSyncNodeFromChromeId(
    251     const std::string& node_id,
    252     syncer::BaseNode* sync_node) {
    253   return false;
    254 }
    255 
    256 int64 PasswordModelAssociator::GetSyncIdFromChromeId(
    257     const std::string& password) {
    258   PasswordToSyncIdMap::const_iterator iter = id_map_.find(password);
    259   return iter == id_map_.end() ? syncer::kInvalidId : iter->second;
    260 }
    261 
    262 void PasswordModelAssociator::Associate(
    263     const std::string* password, int64 sync_id) {
    264   DCHECK(expected_loop_ == base::MessageLoop::current());
    265   DCHECK_NE(syncer::kInvalidId, sync_id);
    266   DCHECK(id_map_.find(*password) == id_map_.end());
    267   DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
    268   id_map_[*password] = sync_id;
    269   id_map_inverse_[sync_id] = *password;
    270 }
    271 
    272 void PasswordModelAssociator::Disassociate(int64 sync_id) {
    273   DCHECK(expected_loop_ == base::MessageLoop::current());
    274   SyncIdToPasswordMap::iterator iter = id_map_inverse_.find(sync_id);
    275   if (iter == id_map_inverse_.end())
    276     return;
    277   CHECK(id_map_.erase(iter->second));
    278   id_map_inverse_.erase(iter);
    279 }
    280 
    281 bool PasswordModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
    282                                                      int64* sync_id) {
    283   syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
    284   syncer::ReadNode sync_node(&trans);
    285   if (sync_node.InitByTagLookup(tag.c_str()) != syncer::BaseNode::INIT_OK)
    286     return false;
    287   *sync_id = sync_node.GetId();
    288   return true;
    289 }
    290 
    291 syncer::SyncError PasswordModelAssociator::WriteToPasswordStore(
    292     const PasswordVector* new_passwords,
    293     const PasswordVector* updated_passwords,
    294     const PasswordVector* deleted_passwords) {
    295   if (new_passwords) {
    296     for (PasswordVector::const_iterator password = new_passwords->begin();
    297          password != new_passwords->end(); ++password) {
    298       password_store_->AddLoginImpl(*password);
    299     }
    300   }
    301 
    302   if (updated_passwords) {
    303     for (PasswordVector::const_iterator password = updated_passwords->begin();
    304          password != updated_passwords->end(); ++password) {
    305       password_store_->UpdateLoginImpl(*password);
    306     }
    307   }
    308 
    309   if (deleted_passwords) {
    310     for (PasswordVector::const_iterator password = deleted_passwords->begin();
    311          password != deleted_passwords->end(); ++password) {
    312       password_store_->RemoveLoginImpl(*password);
    313     }
    314   }
    315 
    316   if (new_passwords || updated_passwords || deleted_passwords) {
    317     // We have to notify password store observers of the change by hand since
    318     // we use internal password store interfaces to make changes synchronously.
    319     password_store_->PostNotifyLoginsChanged();
    320   }
    321   return syncer::SyncError();
    322 }
    323 
    324 // static
    325 void PasswordModelAssociator::CopyPassword(
    326         const sync_pb::PasswordSpecificsData& password,
    327         content::PasswordForm* new_password) {
    328   new_password->scheme =
    329       static_cast<content::PasswordForm::Scheme>(password.scheme());
    330   new_password->signon_realm = password.signon_realm();
    331   new_password->origin = GURL(password.origin());
    332   new_password->action = GURL(password.action());
    333   new_password->username_element =
    334       UTF8ToUTF16(password.username_element());
    335   new_password->password_element =
    336       UTF8ToUTF16(password.password_element());
    337   new_password->username_value =
    338       UTF8ToUTF16(password.username_value());
    339   new_password->password_value =
    340       UTF8ToUTF16(password.password_value());
    341   new_password->ssl_valid = password.ssl_valid();
    342   new_password->preferred = password.preferred();
    343   new_password->date_created =
    344       base::Time::FromInternalValue(password.date_created());
    345   new_password->blacklisted_by_user =
    346       password.blacklisted();
    347 }
    348 
    349 // static
    350 bool PasswordModelAssociator::MergePasswords(
    351         const sync_pb::PasswordSpecificsData& password,
    352         const content::PasswordForm& password_form,
    353         content::PasswordForm* new_password) {
    354   DCHECK(new_password);
    355 
    356   if (password.scheme() == password_form.scheme &&
    357       password_form.signon_realm == password.signon_realm() &&
    358       password_form.origin.spec() == password.origin() &&
    359       password_form.action.spec() == password.action() &&
    360       UTF16ToUTF8(password_form.username_element) ==
    361           password.username_element() &&
    362       UTF16ToUTF8(password_form.password_element) ==
    363           password.password_element() &&
    364       UTF16ToUTF8(password_form.username_value) ==
    365           password.username_value() &&
    366       UTF16ToUTF8(password_form.password_value) ==
    367           password.password_value() &&
    368       password.ssl_valid() == password_form.ssl_valid &&
    369       password.preferred() == password_form.preferred &&
    370       password.date_created() == password_form.date_created.ToInternalValue() &&
    371       password.blacklisted() == password_form.blacklisted_by_user) {
    372     return false;
    373   }
    374 
    375   // If the passwords differ, we take the one that was created more recently.
    376   if (base::Time::FromInternalValue(password.date_created()) <=
    377       password_form.date_created) {
    378     *new_password = password_form;
    379   } else {
    380     CopyPassword(password, new_password);
    381   }
    382 
    383   return true;
    384 }
    385 
    386 // static
    387 void PasswordModelAssociator::WriteToSyncNode(
    388          const content::PasswordForm& password_form,
    389          syncer::WriteNode* node) {
    390   sync_pb::PasswordSpecificsData password;
    391   password.set_scheme(password_form.scheme);
    392   password.set_signon_realm(password_form.signon_realm);
    393   password.set_origin(password_form.origin.spec());
    394   password.set_action(password_form.action.spec());
    395   password.set_username_element(UTF16ToUTF8(password_form.username_element));
    396   password.set_password_element(UTF16ToUTF8(password_form.password_element));
    397   password.set_username_value(UTF16ToUTF8(password_form.username_value));
    398   password.set_password_value(UTF16ToUTF8(password_form.password_value));
    399   password.set_ssl_valid(password_form.ssl_valid);
    400   password.set_preferred(password_form.preferred);
    401   password.set_date_created(password_form.date_created.ToInternalValue());
    402   password.set_blacklisted(password_form.blacklisted_by_user);
    403 
    404   node->SetPasswordSpecifics(password);
    405 }
    406 
    407 // static
    408 std::string PasswordModelAssociator::MakeTag(
    409                 const content::PasswordForm& password) {
    410   return MakeTag(password.origin.spec(),
    411                  UTF16ToUTF8(password.username_element),
    412                  UTF16ToUTF8(password.username_value),
    413                  UTF16ToUTF8(password.password_element),
    414                  password.signon_realm);
    415 }
    416 
    417 // static
    418 std::string PasswordModelAssociator::MakeTag(
    419                 const sync_pb::PasswordSpecificsData& password) {
    420   return MakeTag(password.origin(),
    421                  password.username_element(),
    422                  password.username_value(),
    423                  password.password_element(),
    424                  password.signon_realm());
    425 }
    426 
    427 // static
    428 std::string PasswordModelAssociator::MakeTag(
    429     const std::string& origin_url,
    430     const std::string& username_element,
    431     const std::string& username_value,
    432     const std::string& password_element,
    433     const std::string& signon_realm) {
    434   return net::EscapePath(origin_url) + "|" +
    435          net::EscapePath(username_element) + "|" +
    436          net::EscapePath(username_value) + "|" +
    437          net::EscapePath(password_element) + "|" +
    438          net::EscapePath(signon_realm);
    439 }
    440 
    441 }  // namespace browser_sync
    442