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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/password_manager/password_manager_util.cc
diff --git a/chrome/browser/password_manager/password_manager_util.cc b/chrome/browser/password_manager/password_manager_util.cc
new file mode 100644
index 0000000000000000000000000000000000000000..815777af81410870c8d39781c0346e41c5b2b340
--- /dev/null
+++ b/chrome/browser/password_manager/password_manager_util.cc
@@ -0,0 +1,232 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/password_manager_util.h"
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <tuple>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/keyed_service/core/service_access_type.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+#include "components/password_manager/core/browser/statistics_table.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/http/transport_security_state.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "url/gurl.h"
+
+using autofill::PasswordForm;
+
+namespace password_manager_util {
+
+namespace {
+
+// This class removes obsolete HTTP data from a password store. HTTP data is
+// obsolete, if the corresponding host migrated to HTTPS and has HSTS enabled.
+class ObsoleteHttpCleaner : public password_manager::PasswordStoreConsumer {
+ public:
+ // Constructing a ObsoleteHttpCleaner will result in issuing the clean up
+ // tasks already.
+ explicit ObsoleteHttpCleaner(Profile* profile);
+ ~ObsoleteHttpCleaner() override;
+
+ // PasswordStoreConsumer:
+ // This will be called for both autofillable logins as well as blacklisted
+ // logins. Blacklisted logins are removed iff the scheme is HTTP and HSTS is
+ // enabled for the host.
+ // Autofillable logins are removed iff the scheme is HTTP and there exists
+ // another HTTPS login with active HSTS that has the same host as well as the
+ // same username and password.
+ void OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<PasswordForm>> results) override;
+
+ // This will remove all stats for HTTP sites for which HSTS is active.
+ void OnGetSiteStatistics(
+ std::vector<password_manager::InteractionsStats> stats) override;
+
+ Profile* profile() { return profile_; }
+
+ // Returns the PasswordStore associated with |profile_|.
+ password_manager::PasswordStore* GetPasswordStore() const;
+
+ private:
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(ObsoleteHttpCleaner);
+};
+
+// Utility function that moves all elements from a specified iterator into a new
+// vector and returns it. The moved elements are deleted from the original
+// vector.
+std::vector<std::unique_ptr<PasswordForm>> SplitFormsFrom(
+ std::vector<std::unique_ptr<PasswordForm>>::iterator from,
+ std::vector<std::unique_ptr<PasswordForm>>* forms) {
+ const auto end_forms = std::end(*forms);
+ std::vector<std::unique_ptr<PasswordForm>> result;
+ result.reserve(std::distance(from, end_forms));
+ std::move(from, end_forms, std::back_inserter(result));
+ forms->erase(from, end_forms);
+ return result;
+}
+
+ObsoleteHttpCleaner::ObsoleteHttpCleaner(Profile* profile) : profile_(profile) {
+ DCHECK(profile_);
+ GetPasswordStore()->GetBlacklistLogins(this);
+ GetPasswordStore()->GetAutofillableLogins(this);
+ GetPasswordStore()->GetAllSiteStats(this);
+}
+
+ObsoleteHttpCleaner::~ObsoleteHttpCleaner() = default;
+
+void ObsoleteHttpCleaner::OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<PasswordForm>> results) {
+ // Non HTTP or HTTPS credentials are ignored.
+ results.erase(std::remove_if(std::begin(results), std::end(results),
+ [](const std::unique_ptr<PasswordForm>& form) {
+ return !form->origin.SchemeIsHTTPOrHTTPS();
+ }),
+ std::end(results));
+
+ // Move HTTPS forms into their own container.
+ auto https_forms = SplitFormsFrom(
+ std::partition(std::begin(results), std::end(results),
+ [](const std::unique_ptr<PasswordForm>& form) {
+ return form->origin.SchemeIs(url::kHttpScheme);
+ }),
+ &results);
+
+ // Move blacklisted HTTP forms into their own container.
+ const auto blacklisted_http_forms = SplitFormsFrom(
+ std::partition(std::begin(results), std::end(results),
+ [](const std::unique_ptr<PasswordForm>& form) {
+ return !form->blacklisted_by_user;
+ }),
+ &results);
+
+ // Remove blacklisted HTTP forms from the password store when HSTS is active
+ // for the given host.
+ for (const auto& form : blacklisted_http_forms) {
+ if (IsHSTSActiveForProfileAndHost(profile_, form->origin))
+ GetPasswordStore()->RemoveLogin(*form);
+ }
+
+ // Return early if there are no non-blacklisted HTTP forms.
+ if (results.empty())
+ return;
+
+ // Ignore non HSTS forms.
+ https_forms.erase(
+ std::remove_if(std::begin(https_forms), std::end(https_forms),
+ [this](const std::unique_ptr<PasswordForm>& form) {
+ return IsHSTSActiveForProfileAndHost(profile_,
+ form->origin);
+ }),
+ std::end(https_forms));
+
+ // Sort HSTS forms according to custom comparison function. Consider two forms
+ // equivalent if they have the same host, as well as the same username and
+ // password.
+ const auto form_cmp = [](const std::unique_ptr<PasswordForm>& lhs,
+ const std::unique_ptr<PasswordForm>& rhs) {
+ return std::forward_as_tuple(lhs->origin.host_piece(), lhs->username_value,
+ lhs->password_value) <
+ std::forward_as_tuple(rhs->origin.host_piece(), rhs->username_value,
+ rhs->password_value);
+ };
+
+ std::sort(std::begin(https_forms), std::end(https_forms), form_cmp);
+
+ // Iterate through HTTP forms and remove them from the password store if there
+ // exists an equivalent HSTS form.
+ for (const auto& form : results) {
+ if (std::binary_search(std::begin(https_forms), std::end(https_forms), form,
+ form_cmp))
+ GetPasswordStore()->RemoveLogin(*form);
+ }
+}
+
+void ObsoleteHttpCleaner::OnGetSiteStatistics(
+ std::vector<password_manager::InteractionsStats> stats) {
+ for (const auto& stat : stats) {
+ if (stat.origin_domain.SchemeIs(url::kHttpScheme) &&
+ IsHSTSActiveForProfileAndHost(profile_, stat.origin_domain))
+ GetPasswordStore()->RemoveSiteStats(stat.origin_domain);
+ }
+}
+
+password_manager::PasswordStore* ObsoleteHttpCleaner::GetPasswordStore() const {
+ return PasswordStoreFactory::GetForProfile(profile_,
+ ServiceAccessType::EXPLICIT_ACCESS)
+ .get();
+}
+
+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.
+ if (cleaner->HasWeakPtrs()) {
+ // The presence of weak ptrs implies that the cleaning tasks have not
+ // completed yet. In this case we post a task to the background thread to
+ // call this method again on the UI thread. Eventually there won't be any
+ // weak pointers to cleaner left and this method returns, destroying the
+ // cleaner.
+ const auto post_to_ui_thread =
+ [](std::unique_ptr<ObsoleteHttpCleaner> cleaner) {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ 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
+ };
+
+ content::BrowserThread::PostTask(
+ 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.
+ base::Bind(post_to_ui_thread, base::Passed(std::move(cleaner))));
+ }
+
+ cleaner->profile()->GetPrefs()->SetBoolean(
+ password_manager::prefs::kWasObsoleteHttpDataCleaned, true);
+}
+
+} // namespace
+
+bool IsHSTSActiveForProfileAndHost(Profile* profile, const GURL& origin) {
+ if (!origin.is_valid())
+ return false;
+
+ net::TransportSecurityState* security_state =
+ profile->GetRequestContext()
+ ->GetURLRequestContext()
+ ->transport_security_state();
+
+ if (!security_state)
+ return false;
+
+ return security_state->ShouldUpgradeToSSL(origin.host());
+}
+
+void CleanObsoleteHttpDataForProfile(Profile* profile) {
+ if (!profile->GetPrefs()->GetBoolean(
+ password_manager::prefs::kWasObsoleteHttpDataCleaned)) {
+ PostToDBThread(base::MakeUnique<ObsoleteHttpCleaner>(profile));
+ }
+}
+
+void DelayCleanObsoleteHttpDataForProfile(Profile* profile) {
+ content::BrowserThread::PostDelayedTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&CleanObsoleteHttpDataForProfile, profile),
+ 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.
+}
+
+} // namespace password_manager_util

Powered by Google App Engine
This is Rietveld 408576698