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