Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(17)

Side by Side Diff: chrome/browser/password_manager/password_manager_util.cc

Issue 2714543006: Clean Obsolete HTTP Data from the Password Store (Closed)
Patch Set: Actually do the deletion. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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/password_manager/password_manager_util.h"
6
7 #include <algorithm>
8 #include <iterator>
9 #include <memory>
10 #include <tuple>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/password_manager/password_store_factory.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "components/autofill/core/common/password_form.h"
21 #include "components/keyed_service/core/service_access_type.h"
22 #include "components/password_manager/core/browser/password_store.h"
23 #include "components/password_manager/core/browser/password_store_consumer.h"
24 #include "components/password_manager/core/browser/statistics_table.h"
25 #include "components/password_manager/core/common/password_manager_pref_names.h"
26 #include "components/prefs/pref_service.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "net/http/transport_security_state.h"
29 #include "net/url_request/url_request_context.h"
30 #include "net/url_request/url_request_context_getter.h"
31 #include "url/gurl.h"
32
33 using autofill::PasswordForm;
34
35 namespace password_manager_util {
36
37 namespace {
38
39 // This class removes obsolete HTTP data from a password store. HTTP data is
40 // obsolete, if the corresponding host migrated to HTTPS and has HSTS enabled.
41 class ObsoleteHttpCleaner : public password_manager::PasswordStoreConsumer {
42 public:
43 // Constructing a ObsoleteHttpCleaner will result in issuing the clean up
44 // tasks already.
45 explicit ObsoleteHttpCleaner(Profile* profile);
46 ~ObsoleteHttpCleaner() override;
47
48 // PasswordStoreConsumer:
49 // This will be called for both autofillable logins as well as blacklisted
50 // logins. Blacklisted logins are removed iff the scheme is HTTP and HSTS is
51 // enabled for the host.
52 // Autofillable logins are removed iff the scheme is HTTP and there exists
53 // another HTTPS login with active HSTS that has the same host as well as the
54 // same username and password.
55 void OnGetPasswordStoreResults(
56 std::vector<std::unique_ptr<PasswordForm>> results) override;
57
58 // This will remove all stats for HTTP sites for which HSTS is active.
59 void OnGetSiteStatistics(
60 std::vector<password_manager::InteractionsStats> stats) override;
61
62 Profile* profile() { return profile_; }
63
64 // Returns the PasswordStore associated with |profile_|.
65 password_manager::PasswordStore* GetPasswordStore() const;
66
67 private:
68 Profile* profile_;
69
70 DISALLOW_COPY_AND_ASSIGN(ObsoleteHttpCleaner);
71 };
72
73 // Utility function that moves all elements from a specified iterator into a new
74 // vector and returns it. The moved elements are deleted from the original
75 // vector.
76 std::vector<std::unique_ptr<PasswordForm>> SplitFormsFrom(
77 std::vector<std::unique_ptr<PasswordForm>>::iterator from,
78 std::vector<std::unique_ptr<PasswordForm>>* forms) {
79 const auto end_forms = std::end(*forms);
80 std::vector<std::unique_ptr<PasswordForm>> result;
81 result.reserve(std::distance(from, end_forms));
82 std::move(from, end_forms, std::back_inserter(result));
83 forms->erase(from, end_forms);
84 return result;
85 }
86
87 ObsoleteHttpCleaner::ObsoleteHttpCleaner(Profile* profile) : profile_(profile) {
88 DCHECK(profile_);
89 GetPasswordStore()->GetBlacklistLogins(this);
90 GetPasswordStore()->GetAutofillableLogins(this);
91 GetPasswordStore()->GetAllSiteStats(this);
92 }
93
94 ObsoleteHttpCleaner::~ObsoleteHttpCleaner() = default;
95
96 void ObsoleteHttpCleaner::OnGetPasswordStoreResults(
97 std::vector<std::unique_ptr<PasswordForm>> results) {
98 // Non HTTP or HTTPS credentials are ignored.
99 results.erase(std::remove_if(std::begin(results), std::end(results),
100 [](const std::unique_ptr<PasswordForm>& form) {
101 return !form->origin.SchemeIsHTTPOrHTTPS();
102 }),
103 std::end(results));
104
105 // Move HTTPS forms into their own container.
106 auto https_forms = SplitFormsFrom(
107 std::partition(std::begin(results), std::end(results),
108 [](const std::unique_ptr<PasswordForm>& form) {
109 return form->origin.SchemeIs(url::kHttpScheme);
110 }),
111 &results);
112
113 // Move blacklisted HTTP forms into their own container.
114 const auto blacklisted_http_forms = SplitFormsFrom(
115 std::partition(std::begin(results), std::end(results),
116 [](const std::unique_ptr<PasswordForm>& form) {
117 return !form->blacklisted_by_user;
118 }),
119 &results);
120
121 // Remove blacklisted HTTP forms from the password store when HSTS is active
122 // for the given host.
123 for (const auto& form : blacklisted_http_forms) {
124 if (IsHSTSActiveForProfileAndHost(profile_, form->origin))
125 GetPasswordStore()->RemoveLogin(*form);
126 }
127
128 // Return early if there are no non-blacklisted HTTP forms.
129 if (results.empty())
130 return;
131
132 // Ignore non HSTS forms.
133 https_forms.erase(
134 std::remove_if(std::begin(https_forms), std::end(https_forms),
135 [this](const std::unique_ptr<PasswordForm>& form) {
136 return IsHSTSActiveForProfileAndHost(profile_,
137 form->origin);
138 }),
139 std::end(https_forms));
140
141 // Sort HSTS forms according to custom comparison function. Consider two forms
142 // equivalent if they have the same host, as well as the same username and
143 // password.
144 const auto form_cmp = [](const std::unique_ptr<PasswordForm>& lhs,
145 const std::unique_ptr<PasswordForm>& rhs) {
146 return std::forward_as_tuple(lhs->origin.host_piece(), lhs->username_value,
147 lhs->password_value) <
148 std::forward_as_tuple(rhs->origin.host_piece(), rhs->username_value,
149 rhs->password_value);
150 };
151
152 std::sort(std::begin(https_forms), std::end(https_forms), form_cmp);
153
154 // Iterate through HTTP forms and remove them from the password store if there
155 // exists an equivalent HSTS form.
156 for (const auto& form : results) {
157 if (std::binary_search(std::begin(https_forms), std::end(https_forms), form,
158 form_cmp))
159 GetPasswordStore()->RemoveLogin(*form);
160 }
161 }
162
163 void ObsoleteHttpCleaner::OnGetSiteStatistics(
164 std::vector<password_manager::InteractionsStats> stats) {
165 for (const auto& stat : stats) {
166 if (stat.origin_domain.SchemeIs(url::kHttpScheme) &&
167 IsHSTSActiveForProfileAndHost(profile_, stat.origin_domain))
168 GetPasswordStore()->RemoveSiteStats(stat.origin_domain);
169 }
170 }
171
172 password_manager::PasswordStore* ObsoleteHttpCleaner::GetPasswordStore() const {
173 return PasswordStoreFactory::GetForProfile(profile_,
174 ServiceAccessType::EXPLICIT_ACCESS)
175 .get();
176 }
177
178 void PostToDBThread(std::unique_ptr<ObsoleteHttpCleaner> cleaner) {
vasilii 2017/02/24 17:27:04 It's not necessary a DB thread. The function dese
jdoerrie 2017/02/27 10:53:21 I took the naming from |password_store_factory|: h
vasilii 2017/02/27 12:28:26 PostToPasswordStoreThread
jdoerrie 2017/03/23 15:42:12 Acknowledged.
179 if (cleaner->HasWeakPtrs()) {
180 // The presence of weak ptrs implies that the cleaning tasks have not
181 // completed yet. In this case we post a task to the background thread to
182 // call this method again on the UI thread. Eventually there won't be any
183 // weak pointers to cleaner left and this method returns, destroying the
184 // cleaner.
185 const auto post_to_ui_thread =
186 [](std::unique_ptr<ObsoleteHttpCleaner> cleaner) {
187 content::BrowserThread::PostTask(
188 content::BrowserThread::UI, FROM_HERE,
189 base::Bind(&PostToDBThread, base::Passed(std::move(cleaner))));
vasilii 2017/02/24 17:27:03 Doesn't it work without std::move?
jdoerrie 2017/02/27 10:53:21 Not sure, base::Passed's documentation mentions st
jdoerrie 2017/03/23 15:42:12 Removing std::move results in a compilation error
190 };
191
192 content::BrowserThread::PostTask(
193 content::BrowserThread::DB, FROM_HERE,
vasilii 2017/02/24 17:27:04 PasswordStore may run on another thread. It still
jdoerrie 2017/02/27 10:53:21 Under what circumstances does this actually happen
vasilii 2017/02/27 12:28:26 You can use PasswordStore::ScheduleTask. On Mac th
jdoerrie 2017/03/23 15:42:11 Done.
194 base::Bind(post_to_ui_thread, base::Passed(std::move(cleaner))));
195 }
196
197 cleaner->profile()->GetPrefs()->SetBoolean(
198 password_manager::prefs::kWasObsoleteHttpDataCleaned, true);
199 }
200
201 } // namespace
202
203 bool IsHSTSActiveForProfileAndHost(Profile* profile, const GURL& origin) {
204 if (!origin.is_valid())
205 return false;
206
207 net::TransportSecurityState* security_state =
208 profile->GetRequestContext()
209 ->GetURLRequestContext()
210 ->transport_security_state();
211
212 if (!security_state)
213 return false;
214
215 return security_state->ShouldUpgradeToSSL(origin.host());
216 }
217
218 void CleanObsoleteHttpDataForProfile(Profile* profile) {
219 if (!profile->GetPrefs()->GetBoolean(
220 password_manager::prefs::kWasObsoleteHttpDataCleaned)) {
221 PostToDBThread(base::MakeUnique<ObsoleteHttpCleaner>(profile));
222 }
223 }
224
225 void DelayCleanObsoleteHttpDataForProfile(Profile* profile) {
226 content::BrowserThread::PostDelayedTask(
227 content::BrowserThread::UI, FROM_HERE,
228 base::Bind(&CleanObsoleteHttpDataForProfile, profile),
229 base::TimeDelta::FromSeconds(40));
vasilii 2017/02/24 17:27:04 magic number to the constants in the begin of the
jdoerrie 2017/03/23 15:42:12 Done.
230 }
231
232 } // namespace password_manager_util
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698