password_store.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_store.h"
6
7#include "base/bind.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/message_loop/message_loop.h"
10#include "base/message_loop/message_loop_proxy.h"
11#include "base/metrics/histogram.h"
12#include "base/stl_util.h"
13#include "components/autofill/core/common/password_form.h"
14#include "components/password_manager/core/browser/password_store_consumer.h"
15
16using autofill::PasswordForm;
17using std::vector;
18
19namespace {
20
21// Calls |consumer| back with the request result, if |consumer| is still alive.
22// Takes ownership of the elements in |result|, passing ownership to |consumer|
23// if it is still alive.
24void MaybeCallConsumerCallback(base::WeakPtr<PasswordStoreConsumer> consumer,
25                               scoped_ptr<vector<PasswordForm*> > result) {
26  if (consumer.get())
27    consumer->OnGetPasswordStoreResults(*result);
28  else
29    STLDeleteElements(result.get());
30}
31
32}  // namespace
33
34PasswordStore::GetLoginsRequest::GetLoginsRequest(
35    PasswordStoreConsumer* consumer)
36    : consumer_weak_(consumer->GetWeakPtr()),
37      result_(new vector<PasswordForm*>()) {
38  DCHECK(thread_checker_.CalledOnValidThread());
39  origin_loop_ = base::MessageLoopProxy::current();
40}
41
42PasswordStore::GetLoginsRequest::~GetLoginsRequest() {
43}
44
45void PasswordStore::GetLoginsRequest::ApplyIgnoreLoginsCutoff() {
46  if (!ignore_logins_cutoff_.is_null()) {
47    // Count down rather than up since we may be deleting elements.
48    // Note that in principle it could be more efficient to copy the whole array
49    // since that's worst-case linear time, but we expect that elements will be
50    // deleted rarely and lists will be small, so this avoids the copies.
51    for (size_t i = result_->size(); i > 0; --i) {
52      if ((*result_)[i - 1]->date_created < ignore_logins_cutoff_) {
53        delete (*result_)[i - 1];
54        result_->erase(result_->begin() + (i - 1));
55      }
56    }
57  }
58}
59
60void PasswordStore::GetLoginsRequest::ForwardResult() {
61  origin_loop_->PostTask(FROM_HERE,
62                         base::Bind(&MaybeCallConsumerCallback,
63                                    consumer_weak_,
64                                    base::Passed(result_.Pass())));
65}
66
67PasswordStore::PasswordStore(
68    scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
69    scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner)
70    : main_thread_runner_(main_thread_runner),
71      db_thread_runner_(db_thread_runner),
72      observers_(new ObserverListThreadSafe<Observer>()),
73      shutdown_called_(false) {}
74
75bool PasswordStore::Init() {
76  ReportMetrics();
77  return true;
78}
79
80void PasswordStore::AddLogin(const PasswordForm& form) {
81  ScheduleTask(
82      base::Bind(&PasswordStore::WrapModificationTask, this,
83                 base::Bind(&PasswordStore::AddLoginImpl, this, form)));
84}
85
86void PasswordStore::UpdateLogin(const PasswordForm& form) {
87  ScheduleTask(
88      base::Bind(&PasswordStore::WrapModificationTask, this,
89                 base::Bind(&PasswordStore::UpdateLoginImpl, this, form)));
90}
91
92void PasswordStore::RemoveLogin(const PasswordForm& form) {
93  ScheduleTask(
94      base::Bind(&PasswordStore::WrapModificationTask, this,
95                 base::Bind(&PasswordStore::RemoveLoginImpl, this, form)));
96}
97
98void PasswordStore::RemoveLoginsCreatedBetween(const base::Time& delete_begin,
99                                               const base::Time& delete_end) {
100  ScheduleTask(
101      base::Bind(&PasswordStore::WrapModificationTask, this,
102                 base::Bind(&PasswordStore::RemoveLoginsCreatedBetweenImpl,
103                            this, delete_begin, delete_end)));
104}
105
106void PasswordStore::GetLogins(
107    const PasswordForm& form,
108    AuthorizationPromptPolicy prompt_policy,
109    PasswordStoreConsumer* consumer) {
110  // Per http://crbug.com/121738, we deliberately ignore saved logins for
111  // http*://www.google.com/ that were stored prior to 2012. (Google now uses
112  // https://accounts.google.com/ for all login forms, so these should be
113  // unused.) We don't delete them just yet, and they'll still be visible in the
114  // password manager, but we won't use them to autofill any forms. This is a
115  // security feature to help minimize damage that can be done by XSS attacks.
116  // TODO(mdm): actually delete them at some point, say M24 or so.
117  base::Time ignore_logins_cutoff;  // the null time
118  if (form.scheme == PasswordForm::SCHEME_HTML &&
119      (form.signon_realm == "http://www.google.com" ||
120       form.signon_realm == "http://www.google.com/" ||
121       form.signon_realm == "https://www.google.com" ||
122       form.signon_realm == "https://www.google.com/")) {
123    static const base::Time::Exploded exploded_cutoff =
124        { 2012, 1, 0, 1, 0, 0, 0, 0 };  // 00:00 Jan 1 2012
125    ignore_logins_cutoff = base::Time::FromUTCExploded(exploded_cutoff);
126  }
127  GetLoginsRequest* request = new GetLoginsRequest(consumer);
128  request->set_ignore_logins_cutoff(ignore_logins_cutoff);
129
130  ConsumerCallbackRunner callback_runner =
131      base::Bind(&PasswordStore::CopyAndForwardLoginsResult,
132                 this, base::Owned(request));
133  ScheduleTask(base::Bind(&PasswordStore::GetLoginsImpl,
134                          this, form, prompt_policy, callback_runner));
135}
136
137void PasswordStore::GetAutofillableLogins(PasswordStoreConsumer* consumer) {
138  Schedule(&PasswordStore::GetAutofillableLoginsImpl, consumer);
139}
140
141void PasswordStore::GetBlacklistLogins(PasswordStoreConsumer* consumer) {
142  Schedule(&PasswordStore::GetBlacklistLoginsImpl, consumer);
143}
144
145void PasswordStore::ReportMetrics() {
146  ScheduleTask(base::Bind(&PasswordStore::ReportMetricsImpl, this));
147}
148
149void PasswordStore::AddObserver(Observer* observer) {
150  observers_->AddObserver(observer);
151}
152
153void PasswordStore::RemoveObserver(Observer* observer) {
154  observers_->RemoveObserver(observer);
155}
156
157void PasswordStore::Shutdown() { shutdown_called_ = true; }
158
159PasswordStore::~PasswordStore() { DCHECK(shutdown_called_); }
160
161bool PasswordStore::ScheduleTask(const base::Closure& task) {
162  scoped_refptr<base::SingleThreadTaskRunner> task_runner(
163      GetBackgroundTaskRunner());
164  if (task_runner.get())
165    return task_runner->PostTask(FROM_HERE, task);
166  return false;
167}
168
169scoped_refptr<base::SingleThreadTaskRunner>
170PasswordStore::GetBackgroundTaskRunner() {
171  return db_thread_runner_;
172}
173
174void PasswordStore::ForwardLoginsResult(GetLoginsRequest* request) {
175  request->ApplyIgnoreLoginsCutoff();
176  request->ForwardResult();
177}
178
179void PasswordStore::CopyAndForwardLoginsResult(
180    PasswordStore::GetLoginsRequest* request,
181    const vector<PasswordForm*>& matched_forms) {
182  // Copy the contents of |matched_forms| into the request. The request takes
183  // ownership of the PasswordForm elements.
184  *(request->result()) = matched_forms;
185  ForwardLoginsResult(request);
186}
187
188void PasswordStore::LogStatsForBulkDeletion(int num_deletions) {
189  UMA_HISTOGRAM_COUNTS("PasswordManager.NumPasswordsDeletedByBulkDelete",
190                       num_deletions);
191}
192
193template<typename BackendFunc>
194void PasswordStore::Schedule(
195    BackendFunc func,
196    PasswordStoreConsumer* consumer) {
197  GetLoginsRequest* request = new GetLoginsRequest(consumer);
198  consumer->cancelable_task_tracker()->PostTask(
199      GetBackgroundTaskRunner(),
200      FROM_HERE,
201      base::Bind(func, this, base::Owned(request)));
202}
203
204void PasswordStore::WrapModificationTask(ModificationTask task) {
205  PasswordStoreChangeList changes = task.Run();
206  NotifyLoginsChanged(changes);
207}
208
209void PasswordStore::NotifyLoginsChanged(
210    const PasswordStoreChangeList& changes) {
211  if (!changes.empty())
212    observers_->Notify(&Observer::OnLoginsChanged, changes);
213}
214