• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PasswordModelAssociator(ProfileSyncService * sync_service,PasswordStore * password_store)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 
~PasswordModelAssociator()39 PasswordModelAssociator::~PasswordModelAssociator() {}
40 
AssociateModels()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 
DeleteAllNodes(sync_api::WriteTransaction * trans)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 
DisassociateModels()171 bool PasswordModelAssociator::DisassociateModels() {
172   id_map_.clear();
173   id_map_inverse_.clear();
174   return true;
175 }
176 
SyncModelHasUserCreatedNodes(bool * has_nodes)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 
AbortAssociation()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 
CryptoReadyIfNecessary()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 
GetChromeNodeFromSyncId(int64 sync_id)214 const std::string* PasswordModelAssociator::GetChromeNodeFromSyncId(
215     int64 sync_id) {
216   return NULL;
217 }
218 
InitSyncNodeFromChromeId(const std::string & node_id,sync_api::BaseNode * sync_node)219 bool PasswordModelAssociator::InitSyncNodeFromChromeId(
220     const std::string& node_id,
221     sync_api::BaseNode* sync_node) {
222   return false;
223 }
224 
IsAbortPending()225 bool PasswordModelAssociator::IsAbortPending() {
226   base::AutoLock lock(abort_association_pending_lock_);
227   return abort_association_pending_;
228 }
229 
GetSyncIdFromChromeId(const std::string & password)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 
Associate(const std::string * password,int64 sync_id)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 
Disassociate(int64 sync_id)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 
GetSyncIdForTaggedNode(const std::string & tag,int64 * sync_id)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 
WriteToPasswordStore(const PasswordVector * new_passwords,const PasswordVector * updated_passwords,const PasswordVector * deleted_passwords)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
CopyPassword(const sync_pb::PasswordSpecificsData & password,webkit_glue::PasswordForm * new_password)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
MergePasswords(const sync_pb::PasswordSpecificsData & password,const webkit_glue::PasswordForm & password_form,webkit_glue::PasswordForm * new_password)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
WriteToSyncNode(const webkit_glue::PasswordForm & password_form,sync_api::WriteNode * node)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
MakeTag(const webkit_glue::PasswordForm & password)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
MakeTag(const sync_pb::PasswordSpecificsData & password)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
MakeTag(const std::string & origin_url,const std::string & username_element,const std::string & username_value,const std::string & password_element,const std::string & signon_realm)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