blob: 2fbd040f2a913170e75bf9873e17dc705e739c6c [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 <iterator>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.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/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/identity_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/password_list_sorter.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_sync_util.h"
#include "components/password_manager/core/browser/password_ui_utils.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/undo/undo_operation.h"
#include "content/public/browser/browser_thread.h"
#if !defined(OS_ANDROID)
#include "chrome/browser/extensions/api/passwords_private/passwords_private_utils.h"
#endif
using base::StringPiece;
using password_manager::PasswordStore;
namespace {
// Convenience typedef for the commonly used vector of PasswordForm pointers.
using FormVector = std::vector<std::unique_ptr<autofill::PasswordForm>>;
const autofill::PasswordForm* TryGetPasswordForm(
const std::map<std::string, FormVector>& map,
size_t index) {
// |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. |forms| is empty). Don't let it crash
// the browser.
if (map.size() <= index)
return nullptr;
// Android tries to obtain a PasswordForm corresponding to a specific index,
// and does not know about sort keys. In order to efficiently obtain the n'th
// element in the map we make use of std::next() here.
const auto& forms = std::next(map.begin(), index)->second;
DCHECK(!forms.empty());
return forms[0].get();
}
// Processes |map| and returns a FormVector where each equivalence class in
// |map| is represented by exactly one entry in the result.
FormVector GetEntryList(const std::map<std::string, FormVector>& map) {
FormVector result;
result.reserve(map.size());
for (const auto& pair : map) {
DCHECK(!pair.second.empty());
result.push_back(std::make_unique<autofill::PasswordForm>(*pair.second[0]));
}
return result;
}
class RemovePasswordOperation : public UndoOperation {
public:
RemovePasswordOperation(PasswordManagerPresenter* page,
const autofill::PasswordForm& form);
~RemovePasswordOperation() override;
// UndoOperation:
void Undo() override;
int GetUndoLabelId() const override;
int GetRedoLabelId() const override;
private:
PasswordManagerPresenter* page_;
autofill::PasswordForm form_;
DISALLOW_COPY_AND_ASSIGN(RemovePasswordOperation);
};
RemovePasswordOperation::RemovePasswordOperation(
PasswordManagerPresenter* page,
const autofill::PasswordForm& form)
: page_(page), form_(form) {}
RemovePasswordOperation::~RemovePasswordOperation() = default;
void RemovePasswordOperation::Undo() {
page_->AddLogin(form_);
}
int RemovePasswordOperation::GetUndoLabelId() const {
return 0;
}
int RemovePasswordOperation::GetRedoLabelId() const {
return 0;
}
class AddPasswordOperation : public UndoOperation {
public:
AddPasswordOperation(PasswordManagerPresenter* page,
const autofill::PasswordForm& password_form);
~AddPasswordOperation() override;
// UndoOperation:
void Undo() override;
int GetUndoLabelId() const override;
int GetRedoLabelId() const override;
private:
PasswordManagerPresenter* page_;
autofill::PasswordForm form_;
DISALLOW_COPY_AND_ASSIGN(AddPasswordOperation);
};
AddPasswordOperation::AddPasswordOperation(PasswordManagerPresenter* page,
const autofill::PasswordForm& form)
: page_(page), form_(form) {}
AddPasswordOperation::~AddPasswordOperation() = default;
void AddPasswordOperation::Undo() {
page_->RemoveLogin(form_);
}
int AddPasswordOperation::GetUndoLabelId() const {
return 0;
}
int AddPasswordOperation::GetRedoLabelId() const {
return 0;
}
} // namespace
PasswordManagerPresenter::PasswordManagerPresenter(
PasswordUIView* password_view)
: 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 maps are updated for convenience.
UpdatePasswordLists();
}
void PasswordManagerPresenter::UpdatePasswordLists() {
// Reset the current maps.
password_map_.clear();
exception_map_.clear();
PasswordStore* store = GetPasswordStore();
if (!store)
return;
CancelAllRequests();
store->GetAllLoginsWithAffiliationAndBrandingInformation(this);
}
const autofill::PasswordForm* PasswordManagerPresenter::GetPassword(
size_t index) const {
return TryGetPasswordForm(password_map_, index);
}
FormVector PasswordManagerPresenter::GetAllPasswords() {
FormVector ret_val;
for (const auto& pair : password_map_) {
for (const auto& form : pair.second) {
ret_val.push_back(std::make_unique<autofill::PasswordForm>(*form));
}
}
return ret_val;
}
const autofill::PasswordForm* PasswordManagerPresenter::GetPasswordException(
size_t index) const {
return TryGetPasswordForm(exception_map_, index);
}
void PasswordManagerPresenter::RemoveSavedPassword(size_t index) {
if (TryRemovePasswordEntries(&password_map_, index)) {
base::RecordAction(
base::UserMetricsAction("PasswordManager_RemoveSavedPassword"));
}
}
void PasswordManagerPresenter::RemoveSavedPassword(
const std::string& sort_key) {
if (TryRemovePasswordEntries(&password_map_, sort_key)) {
base::RecordAction(
base::UserMetricsAction("PasswordManager_RemoveSavedPassword"));
}
}
void PasswordManagerPresenter::RemovePasswordException(size_t index) {
if (TryRemovePasswordEntries(&exception_map_, index)) {
base::RecordAction(
base::UserMetricsAction("PasswordManager_RemovePasswordException"));
}
}
void PasswordManagerPresenter::RemovePasswordException(
const std::string& sort_key) {
if (TryRemovePasswordEntries(&exception_map_, sort_key)) {
base::RecordAction(
base::UserMetricsAction("PasswordManager_RemovePasswordException"));
}
}
void PasswordManagerPresenter::UndoRemoveSavedPasswordOrException() {
undo_manager_.Undo();
}
void PasswordManagerPresenter::RequestShowPassword(
const std::string& sort_key) {
#if !defined(OS_ANDROID) // This is never called on Android.
auto it = password_map_.find(sort_key);
if (it == password_map_.end())
return;
DCHECK(!it->second.empty());
const auto& form = *it->second[0];
syncer::SyncService* sync_service = nullptr;
if (ProfileSyncServiceFactory::HasProfileSyncService(
password_view_->GetProfile())) {
sync_service =
ProfileSyncServiceFactory::GetForProfile(password_view_->GetProfile());
}
if (password_manager::sync_util::IsSyncAccountCredential(
form, sync_service,
IdentityManagerFactory::GetForProfile(
password_view_->GetProfile()))) {
base::RecordAction(
base::UserMetricsAction("PasswordManager_SyncCredentialShown"));
}
// Call back the front end to reveal the password.
password_view_->ShowPassword(sort_key, form.password_value);
UMA_HISTOGRAM_ENUMERATION(
"PasswordManager.AccessPasswordInSettings",
password_manager::metrics_util::ACCESS_PASSWORD_VIEWED,
password_manager::metrics_util::ACCESS_PASSWORD_COUNT);
#endif
}
void PasswordManagerPresenter::AddLogin(const autofill::PasswordForm& form) {
PasswordStore* store = GetPasswordStore();
if (!store)
return;
undo_manager_.AddUndoOperation(
std::make_unique<AddPasswordOperation>(this, form));
store->AddLogin(form);
}
void PasswordManagerPresenter::RemoveLogin(const autofill::PasswordForm& form) {
PasswordStore* store = GetPasswordStore();
if (!store)
return;
undo_manager_.AddUndoOperation(
std::make_unique<RemovePasswordOperation>(this, form));
store->RemoveLogin(form);
}
bool PasswordManagerPresenter::TryRemovePasswordEntries(
PasswordFormMap* form_map,
size_t index) {
if (form_map->size() <= index)
return false;
// Android tries to obtain a PasswordForm corresponding to a specific index,
// and does not know about sort keys. In order to efficiently obtain the n'th
// element in the map we make use of std::next() here.
return TryRemovePasswordEntries(form_map,
std::next(form_map->cbegin(), index));
}
bool PasswordManagerPresenter::TryRemovePasswordEntries(
PasswordFormMap* form_map,
const std::string& sort_key) {
auto it = form_map->find(sort_key);
if (it == form_map->end())
return false;
return TryRemovePasswordEntries(form_map, it);
}
bool PasswordManagerPresenter::TryRemovePasswordEntries(
PasswordFormMap* form_map,
PasswordFormMap::const_iterator forms_iter) {
PasswordStore* store = GetPasswordStore();
if (!store)
return false;
const FormVector& forms = forms_iter->second;
DCHECK(!forms.empty());
undo_manager_.AddUndoOperation(
std::make_unique<RemovePasswordOperation>(this, *forms[0]));
for (const auto& form : forms)
store->RemoveLogin(*form);
form_map->erase(forms_iter);
return true;
}
void PasswordManagerPresenter::OnGetPasswordStoreResults(FormVector results) {
for (auto& form : results) {
auto& form_map = form->blacklisted_by_user ? exception_map_ : password_map_;
form_map[password_manager::CreateSortKey(*form)].push_back(std::move(form));
}
SetPasswordList();
SetPasswordExceptionList();
}
void PasswordManagerPresenter::SetPasswordList() {
// TODO(https://crbug.com/892260): Creating a copy of the elements is
// wasteful. Consider updating PasswordUIView to take a PasswordFormMap
// instead.
password_view_->SetPasswordList(GetEntryList(password_map_));
}
void PasswordManagerPresenter::SetPasswordExceptionList() {
// TODO(https://crbug.com/892260): Creating a copy of the elements is
// wasteful. Consider updating PasswordUIView to take a PasswordFormMap
// instead.
password_view_->SetPasswordExceptionList(GetEntryList(exception_map_));
}
PasswordStore* PasswordManagerPresenter::GetPasswordStore() {
return PasswordStoreFactory::GetForProfile(password_view_->GetProfile(),
ServiceAccessType::EXPLICIT_ACCESS)
.get();
}