blob: f2c2893a39e4036b0b46c0d567c9ee073d648b5a [file] [log] [blame]
// Copyright 2013 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/ui/passwords/password_manager_presenter.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/user_metrics_action.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
#include "chrome/browser/ui/passwords/password_ui_view.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "components/autofill/core/common/password_form.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/password_manager/core/browser/affiliation_utils.h"
#include "components/password_manager/core/browser/import/password_importer.h"
#include "components/password_manager/core/browser/password_ui_utils.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/password_manager/sync/browser/password_sync_util.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#if defined(OS_WIN)
#include "chrome/browser/password_manager/password_manager_util_win.h"
#elif defined(OS_MACOSX)
#include "chrome/browser/password_manager/password_manager_util_mac.h"
#endif
using base::StringPiece;
using password_manager::PasswordStore;
namespace {
const char kSortKeyPartsSeparator = ' ';
// The character that is added to a sort key if there is no federation.
// Note: to separate the entries w/ federation and the entries w/o federation,
// this character should be alphabetically smaller than real federations.
const char kSortKeyNoFederationSymbol = '-';
// Helper function that returns the type of the entry (non-Android credentials,
// Android w/ affiliated web realm (i.e. clickable) or w/o web realm).
std::string GetEntryTypeCode(bool is_android_uri, bool is_clickable) {
if (!is_android_uri)
return "0";
if (is_clickable)
return "1";
return "2";
}
// Creates key for sorting password or password exception entries. The key is
// eTLD+1 followed by the reversed list of domains (e.g.
// secure.accounts.example.com => example.com.com.example.accounts.secure) and
// the scheme. If |entry_type == SAVED|, username, password and federation are
// appended to the key. The entry type code (non-Android, Android w/ or w/o
// affiliated web realm) is also appended to the key.
std::string CreateSortKey(const autofill::PasswordForm& form,
PasswordEntryType entry_type) {
bool is_android_uri = false;
bool is_clickable = false;
GURL link_url;
std::string origin = password_manager::GetShownOriginAndLinkUrl(
form, &is_android_uri, &link_url, &is_clickable);
if (!is_clickable) // e.g. android://com.example.r => r.example.com.
origin = password_manager::StripAndroidAndReverse(origin);
std::string site_name =
net::registry_controlled_domains::GetDomainAndRegistry(
origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
if (site_name.empty()) // e.g. localhost.
site_name = origin;
std::string key = site_name + password_manager::SplitByDotAndReverse(origin);
if (entry_type == PasswordEntryType::SAVED) {
key += kSortKeyPartsSeparator + base::UTF16ToUTF8(form.username_value) +
kSortKeyPartsSeparator + base::UTF16ToUTF8(form.password_value);
key += kSortKeyPartsSeparator;
if (!form.federation_origin.unique())
key += form.federation_origin.host();
else
key += kSortKeyNoFederationSymbol;
}
// To separate HTTP/HTTPS credentials, add the scheme to the key.
key += kSortKeyPartsSeparator + link_url.scheme();
// Since Android and non-Android entries shouldn't be merged into one entry,
// add the entry type code to the sort key.
key +=
kSortKeyPartsSeparator + GetEntryTypeCode(is_android_uri, is_clickable);
return key;
}
// Finds duplicates of |form| in |duplicates|, removes them from |store| and
// from |duplicates|.
void RemoveDuplicates(const autofill::PasswordForm& form,
DuplicatesMap* duplicates,
PasswordStore* store,
PasswordEntryType entry_type) {
std::string key = CreateSortKey(form, entry_type);
std::pair<DuplicatesMap::iterator, DuplicatesMap::iterator> dups =
duplicates->equal_range(key);
for (DuplicatesMap::iterator it = dups.first; it != dups.second; ++it)
store->RemoveLogin(*it->second);
duplicates->erase(key);
}
} // namespace
PasswordManagerPresenter::PasswordManagerPresenter(
PasswordUIView* password_view)
: populater_(this),
exception_populater_(this),
password_view_(password_view) {
DCHECK(password_view_);
}
PasswordManagerPresenter::~PasswordManagerPresenter() {
PasswordStore* store = GetPasswordStore();
if (store)
store->RemoveObserver(this);
}
void PasswordManagerPresenter::Initialize() {
PasswordStore* store = GetPasswordStore();
if (store)
store->AddObserver(this);
}
void PasswordManagerPresenter::OnLoginsChanged(
const password_manager::PasswordStoreChangeList& changes) {
// Entire list is updated for convenience.
UpdatePasswordLists();
}
PasswordStore* PasswordManagerPresenter::GetPasswordStore() {
return PasswordStoreFactory::GetForProfile(password_view_->GetProfile(),
ServiceAccessType::EXPLICIT_ACCESS)
.get();
}
void PasswordManagerPresenter::UpdatePasswordLists() {
// Reset so that showing a password will require re-authentication.
last_authentication_time_ = base::TimeTicks();
// Reset the current lists.
password_list_.clear();
password_duplicates_.clear();
password_exception_list_.clear();
password_exception_duplicates_.clear();
populater_.Populate();
exception_populater_.Populate();
}
void PasswordManagerPresenter::RemoveSavedPassword(size_t index) {
if (index >= password_list_.size()) {
// |index| out of bounds might come from a compromised renderer
// (http://crbug.com/362054), or the user removed a password while a request
// to the store is in progress (i.e. |password_list_| is empty).
// Don't let it crash the browser.
return;
}
PasswordStore* store = GetPasswordStore();
if (!store)
return;
RemoveDuplicates(*password_list_[index], &password_duplicates_, store,
PasswordEntryType::SAVED);
store->RemoveLogin(*password_list_[index]);
content::RecordAction(
base::UserMetricsAction("PasswordManager_RemoveSavedPassword"));
}
void PasswordManagerPresenter::RemovePasswordException(size_t index) {
if (index >= password_exception_list_.size()) {
// |index| out of bounds might come from a compromised renderer
// (http://crbug.com/362054), or the user removed a password exception while
// a request to the store is in progress (i.e. |password_exception_list_|
// is empty). Don't let it crash the browser.
return;
}
PasswordStore* store = GetPasswordStore();
if (!store)
return;
RemoveDuplicates(*password_exception_list_[index],
&password_exception_duplicates_, store,
PasswordEntryType::BLACKLISTED);
store->RemoveLogin(*password_exception_list_[index]);
content::RecordAction(
base::UserMetricsAction("PasswordManager_RemovePasswordException"));
}
void PasswordManagerPresenter::RequestShowPassword(size_t index) {
#if !defined(OS_ANDROID) // This is never called on Android.
if (index >= password_list_.size()) {
// |index| out of bounds might come from a compromised renderer
// (http://crbug.com/362054), or the user requested to show a password while
// a request to the store is in progress (i.e. |password_list_|
// is empty). Don't let it crash the browser.
return;
}
if (!IsUserAuthenticated()) {
return;
}
syncer::SyncService* sync_service = nullptr;
if (ProfileSyncServiceFactory::HasProfileSyncService(
password_view_->GetProfile())) {
sync_service =
ProfileSyncServiceFactory::GetForProfile(password_view_->GetProfile());
}
if (password_manager::sync_util::IsSyncAccountCredential(
*password_list_[index], sync_service,
SigninManagerFactory::GetForProfile(password_view_->GetProfile()))) {
content::RecordAction(
base::UserMetricsAction("PasswordManager_SyncCredentialShown"));
}
// Call back the front end to reveal the password.
std::string origin_url = password_manager::GetHumanReadableOrigin(
*password_list_[index]);
password_view_->ShowPassword(
index,
origin_url,
base::UTF16ToUTF8(password_list_[index]->username_value),
password_list_[index]->password_value);
#endif
}
std::vector<std::unique_ptr<autofill::PasswordForm>>
PasswordManagerPresenter::GetAllPasswords() {
std::vector<std::unique_ptr<autofill::PasswordForm>> ret_val;
for (const auto& form : password_list_) {
ret_val.push_back(base::MakeUnique<autofill::PasswordForm>(*form));
}
return ret_val;
}
const autofill::PasswordForm* PasswordManagerPresenter::GetPassword(
size_t index) {
if (index >= password_list_.size()) {
// |index| out of bounds might come from a compromised renderer
// (http://crbug.com/362054), or the user requested to get a password while
// a request to the store is in progress (i.e. |password_list_|
// is empty). Don't let it crash the browser.
return NULL;
}
return password_list_[index].get();
}
const autofill::PasswordForm* PasswordManagerPresenter::GetPasswordException(
size_t index) {
if (index >= password_exception_list_.size()) {
// |index| out of bounds might come from a compromised renderer
// (http://crbug.com/362054), or the user requested to get a password
// exception while a request to the store is in progress (i.e.
// |password_exception_list_| is empty). Don't let it crash the browser.
return NULL;
}
return password_exception_list_[index].get();
}
void PasswordManagerPresenter::SetPasswordList() {
password_view_->SetPasswordList(password_list_);
}
void PasswordManagerPresenter::SetPasswordExceptionList() {
password_view_->SetPasswordExceptionList(password_exception_list_);
}
void PasswordManagerPresenter::SortEntriesAndHideDuplicates(
std::vector<std::unique_ptr<autofill::PasswordForm>>* list,
DuplicatesMap* duplicates,
PasswordEntryType entry_type) {
std::vector<std::pair<std::string, std::unique_ptr<autofill::PasswordForm>>>
pairs;
pairs.reserve(list->size());
for (auto& form : *list) {
pairs.push_back(
std::make_pair(CreateSortKey(*form, entry_type), std::move(form)));
}
std::sort(
pairs.begin(), pairs.end(),
[](const std::pair<std::string, std::unique_ptr<autofill::PasswordForm>>&
left,
const std::pair<std::string, std::unique_ptr<autofill::PasswordForm>>&
right) { return left.first < right.first; });
list->clear();
duplicates->clear();
std::string previous_key;
for (auto& pair : pairs) {
if (pair.first != previous_key) {
list->push_back(std::move(pair.second));
previous_key = pair.first;
} else {
duplicates->insert(std::make_pair(previous_key, std::move(pair.second)));
}
}
}
bool PasswordManagerPresenter::IsUserAuthenticated() {
#if defined(OS_ANDROID)
NOTREACHED();
#endif
if (base::TimeTicks::Now() - last_authentication_time_ >
base::TimeDelta::FromSeconds(60)) {
bool authenticated = true;
#if defined(OS_WIN)
authenticated = password_manager_util_win::AuthenticateUser(
password_view_->GetNativeWindow());
#elif defined(OS_MACOSX)
authenticated = password_manager_util_mac::AuthenticateUser();
#endif
if (authenticated)
last_authentication_time_ = base::TimeTicks::Now();
return authenticated;
}
return true;
}
PasswordManagerPresenter::ListPopulater::ListPopulater(
PasswordManagerPresenter* page) : page_(page) {
}
PasswordManagerPresenter::ListPopulater::~ListPopulater() {
}
PasswordManagerPresenter::PasswordListPopulater::PasswordListPopulater(
PasswordManagerPresenter* page) : ListPopulater(page) {
}
void PasswordManagerPresenter::PasswordListPopulater::Populate() {
PasswordStore* store = page_->GetPasswordStore();
if (store != NULL) {
cancelable_task_tracker()->TryCancelAll();
store->GetAutofillableLoginsWithAffiliatedRealms(this);
} else {
LOG(ERROR) << "No password store! Cannot display passwords.";
}
}
void PasswordManagerPresenter::PasswordListPopulater::OnGetPasswordStoreResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
page_->password_list_ = std::move(results);
page_->SortEntriesAndHideDuplicates(&page_->password_list_,
&page_->password_duplicates_,
PasswordEntryType::SAVED);
page_->SetPasswordList();
}
PasswordManagerPresenter::PasswordExceptionListPopulater::
PasswordExceptionListPopulater(PasswordManagerPresenter* page)
: ListPopulater(page) {
}
void PasswordManagerPresenter::PasswordExceptionListPopulater::Populate() {
PasswordStore* store = page_->GetPasswordStore();
if (store != NULL) {
cancelable_task_tracker()->TryCancelAll();
store->GetBlacklistLoginsWithAffiliatedRealms(this);
} else {
LOG(ERROR) << "No password store! Cannot display exceptions.";
}
}
void PasswordManagerPresenter::PasswordExceptionListPopulater::
OnGetPasswordStoreResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
page_->password_exception_list_ = std::move(results);
page_->SortEntriesAndHideDuplicates(&page_->password_exception_list_,
&page_->password_exception_duplicates_,
PasswordEntryType::BLACKLISTED);
page_->SetPasswordExceptionList();
}