blob: f3f5b48ccab15705940a418d20a603b5160561c6 [file] [log] [blame]
// Copyright 2019 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 "components/autofill_assistant/browser/website_login_manager_impl.h"
#include "base/containers/cxx20_erase.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/password_manager/content/browser/content_password_manager_driver.h"
#include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
#include "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/form_parsing/form_parser.h"
#include "components/password_manager/core/browser/form_saver.h"
#include "components/password_manager/core/browser/form_saver_impl.h"
#include "components/password_manager/core/browser/password_form_metrics_recorder.h"
#include "components/password_manager/core/browser/password_generation_frame_helper.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_save_manager_impl.h"
#include "components/password_manager/core/browser/votes_uploader.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace autofill_assistant {
namespace {
// Creates a |PasswordForm| with minimal initialization (origin, username,
// password).
password_manager::PasswordForm CreatePasswordForm(
const WebsiteLoginManager::Login& login,
const std::string& password) {
password_manager::PasswordForm form;
form.url = login.origin.DeprecatedGetOriginAsURL();
form.signon_realm = password_manager::GetSignonRealm(form.url);
form.username_value = base::UTF8ToUTF16(login.username);
form.password_value = base::UTF8ToUTF16(password);
return form;
}
// Returns the first match that has same username.
const password_manager::PasswordForm* FindPasswordForLogin(
const std::vector<const password_manager::PasswordForm*>& matches,
const WebsiteLoginManager::Login& login) {
auto result =
std::find_if(matches.begin(), matches.end(), [&](const auto& match) {
return base::UTF16ToUTF8(match->username_value) == login.username;
});
if (result == matches.end()) {
return nullptr;
}
return *result;
}
} // namespace
// Represents a pending form fetcher request which will notify the
// |WebsiteLoginManagerImpl| when finished.
class WebsiteLoginManagerImpl::PendingRequest
: public password_manager::FormFetcher::Consumer {
public:
PendingRequest(
const password_manager::PasswordFormDigest& form_digest,
const password_manager::PasswordManagerClient* client,
base::OnceCallback<void(const PendingRequest*)> notify_finished_callback)
: form_fetcher_(
std::make_unique<password_manager::FormFetcherImpl>(form_digest,
client,
true)),
notify_finished_callback_(std::move(notify_finished_callback)),
weak_ptr_factory_(this) {}
PendingRequest(const PendingRequest&) = delete;
PendingRequest& operator=(const PendingRequest&) = delete;
~PendingRequest() override = default;
void Start() {
// Note: Currently, |FormFetcherImpl| has the default state NOT_WAITING.
// This has the unfortunate side effect that new consumers are immediately
// notified with an empty result. As a workaround to avoid this first
// notification, we register as consumer *after* we call |Fetch|.
form_fetcher_->Fetch();
form_fetcher_->AddConsumer(this);
}
protected:
// From password_manager::FormFetcher::Consumer:
// This implementation should be called by subclasses when they are finished.
void OnFetchCompleted() override {
// This needs to be done asynchronously, because it will lead to the
// destruction of |this|, which needs to happen *after* this call has
// returned.
if (notify_finished_callback_) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&PendingRequest::NotifyFinished,
weak_ptr_factory_.GetWeakPtr(),
std::move(notify_finished_callback_)));
}
}
std::unique_ptr<password_manager::FormFetcher> form_fetcher_;
private:
void NotifyFinished(
base::OnceCallback<void(const PendingRequest*)> callback) {
form_fetcher_->RemoveConsumer(this);
std::move(callback).Run(this);
}
base::OnceCallback<void(const PendingRequest*)> notify_finished_callback_;
base::WeakPtrFactory<PendingRequest> weak_ptr_factory_;
};
// A pending request to fetch all logins that match the specified |form_digest|.
class WebsiteLoginManagerImpl::PendingFetchLoginsRequest
: public WebsiteLoginManagerImpl::PendingRequest {
public:
PendingFetchLoginsRequest(
const password_manager::PasswordFormDigest& form_digest,
const password_manager::PasswordManagerClient* client,
base::OnceCallback<void(std::vector<Login>)> callback,
base::OnceCallback<void(const PendingRequest*)> notify_finished_callback)
: PendingRequest(form_digest,
client,
std::move(notify_finished_callback)),
callback_(std::move(callback)) {}
protected:
// From PendingRequest:
void OnFetchCompleted() override {
std::vector<Login> logins;
for (const auto* match : form_fetcher_->GetBestMatches()) {
logins.emplace_back(match->url.DeprecatedGetOriginAsURL(),
base::UTF16ToUTF8(match->username_value));
}
std::move(callback_).Run(logins);
PendingRequest::OnFetchCompleted();
}
private:
base::OnceCallback<void(std::vector<Login>)> callback_;
};
// A pending request to fetch the password for the specified |login|.
class WebsiteLoginManagerImpl::PendingFetchPasswordRequest
: public WebsiteLoginManagerImpl::PendingRequest {
public:
PendingFetchPasswordRequest(
const password_manager::PasswordFormDigest& form_digest,
const password_manager::PasswordManagerClient* client,
const Login& login,
base::OnceCallback<void(bool, std::string)> callback,
base::OnceCallback<void(const PendingRequest*)> notify_finished_callback)
: PendingRequest(form_digest,
client,
std::move(notify_finished_callback)),
login_(login),
callback_(std::move(callback)) {}
protected:
// From PendingRequest:
void OnFetchCompleted() override {
std::vector<const password_manager::PasswordForm*> matches =
form_fetcher_->GetNonFederatedMatches();
const auto* match = FindPasswordForLogin(matches, login_);
if (match != nullptr) {
std::move(callback_).Run(true, base::UTF16ToUTF8(match->password_value));
PendingRequest::OnFetchCompleted();
return;
}
std::move(callback_).Run(false, std::string());
PendingRequest::OnFetchCompleted();
}
private:
Login login_;
base::OnceCallback<void(bool, std::string)> callback_;
};
// A pending request to fetch the last time a password was used for the
// specified |login|.
class WebsiteLoginManagerImpl::PendingFetchLastTimePasswordUseRequest
: public WebsiteLoginManagerImpl::PendingRequest {
public:
PendingFetchLastTimePasswordUseRequest(
const password_manager::PasswordFormDigest& form_digest,
const password_manager::PasswordManagerClient* client,
const Login& login,
base::OnceCallback<void(absl::optional<base::Time>)> callback,
base::OnceCallback<void(const PendingRequest*)> notify_finished_callback)
: PendingRequest(form_digest,
client,
std::move(notify_finished_callback)),
login_(login),
callback_(std::move(callback)) {}
protected:
// From PendingRequest:
void OnFetchCompleted() override {
std::vector<const password_manager::PasswordForm*> matches =
form_fetcher_->GetNonFederatedMatches();
const auto* match = FindPasswordForLogin(matches, login_);
if (!match) {
std::move(callback_).Run(absl::nullopt);
PendingRequest::OnFetchCompleted();
return;
}
std::move(callback_).Run(match->date_last_used);
PendingRequest::OnFetchCompleted();
}
private:
Login login_;
base::OnceCallback<void(absl::optional<base::Time>)> callback_;
};
// A pending request to delete the password for the specified |login|.
class WebsiteLoginManagerImpl::PendingDeletePasswordRequest
: public WebsiteLoginManagerImpl::PendingRequest {
public:
PendingDeletePasswordRequest(
const password_manager::PasswordFormDigest& form_digest,
const password_manager::PasswordManagerClient* client,
const Login& login,
base::OnceCallback<void(bool)> callback,
base::OnceCallback<void(const PendingRequest*)> notify_finished_callback)
: PendingRequest(form_digest,
client,
std::move(notify_finished_callback)),
login_(login),
callback_(std::move(callback)),
store_(client->GetProfilePasswordStore()) {}
protected:
// From PendingRequest:
void OnFetchCompleted() override {
std::vector<const password_manager::PasswordForm*> matches =
form_fetcher_->GetNonFederatedMatches();
const auto* match = FindPasswordForLogin(matches, login_);
if (match != nullptr) {
store_->RemoveLogin(*match);
}
std::move(callback_).Run(match != nullptr);
PendingRequest::OnFetchCompleted();
}
private:
Login login_;
base::OnceCallback<void(bool)> callback_;
raw_ptr<password_manager::PasswordStoreInterface> store_;
};
// A pending request to edit the password for the specified |login|.
class WebsiteLoginManagerImpl::PendingEditPasswordRequest
: public WebsiteLoginManagerImpl::PendingRequest {
public:
PendingEditPasswordRequest(
const password_manager::PasswordFormDigest& form_digest,
const password_manager::PasswordManagerClient* client,
const Login& login,
const std::string& new_password,
base::OnceCallback<void(bool)> callback,
base::OnceCallback<void(const PendingRequest*)> notify_finished_callback)
: PendingRequest(form_digest,
client,
std::move(notify_finished_callback)),
login_(login),
new_password_(new_password),
callback_(std::move(callback)),
form_saver_(std::make_unique<password_manager::FormSaverImpl>(
client->GetProfilePasswordStore())) {}
protected:
// From PendingRequest:
void OnFetchCompleted() override {
std::vector<const password_manager::PasswordForm*> matches =
form_fetcher_->GetNonFederatedMatches();
const auto* old_password_form = FindPasswordForLogin(matches, login_);
if (old_password_form != nullptr) {
password_manager::PasswordForm new_password_form = *old_password_form;
new_password_form.password_value = base::UTF8ToUTF16(new_password_);
form_saver_->Update(new_password_form, matches,
/*old_password=*/old_password_form->password_value);
}
std::move(callback_).Run(old_password_form != nullptr);
PendingRequest::OnFetchCompleted();
}
private:
Login login_;
std::string new_password_;
base::OnceCallback<void(bool)> callback_;
// Using form saver instead store itself gives us benefit of updating
// password for all matches (i.e. m.domain.com and www.domain.com' passwords
// will stay synced)
std::unique_ptr<password_manager::FormSaver> form_saver_;
};
// A request to update store with new password for a login.
class WebsiteLoginManagerImpl::UpdatePasswordRequest
: public password_manager::FormFetcher::Consumer {
public:
UpdatePasswordRequest(const Login& login,
const std::string& password,
const autofill::FormData& form_data,
password_manager::PasswordManagerClient* client,
base::OnceCallback<void()> presaving_completed_callback)
: password_form_(CreatePasswordForm(login, password)),
form_data_(form_data),
client_(client),
presaving_completed_callback_(std::move(presaving_completed_callback)),
password_save_manager_(
std::make_unique<password_manager::PasswordSaveManagerImpl>(
client)),
metrics_recorder_(
base::MakeRefCounted<password_manager::PasswordFormMetricsRecorder>(
client->IsCommittedMainFrameSecure(),
client->GetUkmSourceId(),
client->GetPrefs())),
votes_uploader_(client, true /* is_possible_change_password_form */) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
password_manager::PasswordFormDigest digest(
password_manager::PasswordForm::Scheme::kHtml,
password_form_.signon_realm, password_form_.url);
form_fetcher_ = std::make_unique<password_manager::FormFetcherImpl>(
digest, client, true);
}
// Password will be presaved when |form_fetcher_| completes fetching.
void FetchAndPresave() {
form_fetcher_->Fetch();
form_fetcher_->AddConsumer(this);
}
void SaveGeneratedPassword() {
password_save_manager_->Save(&form_data_ /* observed_form */,
password_form_);
}
// password_manager::FormFetcher::Consumer
void OnFetchCompleted() override {
password_save_manager_->Init(client_, form_fetcher_.get(),
metrics_recorder_, &votes_uploader_);
password_save_manager_->PresaveGeneratedPassword(password_form_);
password_save_manager_->CreatePendingCredentials(
password_form_, &form_data_ /* observed_form */,
form_data_ /* submitted_form */, false /* is_http_auth */,
false /* is_credential_api_save */);
if (presaving_completed_callback_) {
std::move(presaving_completed_callback_).Run();
}
}
private:
const password_manager::PasswordForm password_form_;
const autofill::FormData form_data_;
const raw_ptr<password_manager::PasswordManagerClient> client_ = nullptr;
// This callback will execute when presaving is completed.
base::OnceCallback<void()> presaving_completed_callback_;
const std::unique_ptr<password_manager::PasswordSaveManager>
password_save_manager_;
scoped_refptr<password_manager::PasswordFormMetricsRecorder>
metrics_recorder_;
password_manager::VotesUploader votes_uploader_;
std::unique_ptr<password_manager::FormFetcher> form_fetcher_;
};
WebsiteLoginManagerImpl::WebsiteLoginManagerImpl(
password_manager::PasswordManagerClient* client,
content::WebContents* web_contents)
: client_(client),
web_contents_(web_contents),
leak_delegate_(
std::make_unique<SavePasswordLeakDetectionDelegate>(client_)),
weak_ptr_factory_(this) {}
WebsiteLoginManagerImpl::~WebsiteLoginManagerImpl() = default;
void WebsiteLoginManagerImpl::GetLoginsForUrl(
const GURL& url,
base::OnceCallback<void(std::vector<Login>)> callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
password_manager::PasswordFormDigest digest(
password_manager::PasswordForm::Scheme::kHtml,
url.DeprecatedGetOriginAsURL().spec(), GURL());
pending_requests_.emplace_back(std::make_unique<PendingFetchLoginsRequest>(
digest, client_, std::move(callback),
base::BindOnce(&WebsiteLoginManagerImpl::OnRequestFinished,
weak_ptr_factory_.GetWeakPtr())));
pending_requests_.back()->Start();
}
void WebsiteLoginManagerImpl::GetPasswordForLogin(
const Login& login,
base::OnceCallback<void(bool, std::string)> callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
password_manager::PasswordFormDigest digest(
password_manager::PasswordForm::Scheme::kHtml, login.origin.spec(),
GURL());
pending_requests_.emplace_back(std::make_unique<PendingFetchPasswordRequest>(
digest, client_, login, std::move(callback),
base::BindOnce(&WebsiteLoginManagerImpl::OnRequestFinished,
weak_ptr_factory_.GetWeakPtr())));
pending_requests_.back()->Start();
}
void WebsiteLoginManagerImpl::DeletePasswordForLogin(
const Login& login,
base::OnceCallback<void(bool)> callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
password_manager::PasswordFormDigest digest(
password_manager::PasswordForm::Scheme::kHtml, login.origin.spec(),
GURL());
pending_requests_.push_back(std::make_unique<PendingDeletePasswordRequest>(
digest, client_, login, std::move(callback),
base::BindOnce(&WebsiteLoginManagerImpl::OnRequestFinished,
weak_ptr_factory_.GetWeakPtr())));
pending_requests_.back()->Start();
}
void WebsiteLoginManagerImpl::GetGetLastTimePasswordUsed(
const Login& login,
base::OnceCallback<void(absl::optional<base::Time>)> callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
password_manager::PasswordFormDigest digest(
password_manager::PasswordForm::Scheme::kHtml, login.origin.spec(),
GURL());
pending_requests_.emplace_back(
std::make_unique<PendingFetchLastTimePasswordUseRequest>(
digest, client_, login, std::move(callback),
base::BindOnce(&WebsiteLoginManagerImpl::OnRequestFinished,
weak_ptr_factory_.GetWeakPtr())));
pending_requests_.back()->Start();
}
void WebsiteLoginManagerImpl::EditPasswordForLogin(
const Login& login,
const std::string& new_password,
base::OnceCallback<void(bool)> callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
password_manager::PasswordFormDigest digest(
password_manager::PasswordForm::Scheme::kHtml, login.origin.spec(),
GURL());
pending_requests_.push_back(std::make_unique<PendingEditPasswordRequest>(
digest, client_, login, new_password, std::move(callback),
base::BindOnce(&WebsiteLoginManagerImpl::OnRequestFinished,
weak_ptr_factory_.GetWeakPtr())));
pending_requests_.back()->Start();
}
absl::optional<std::string> WebsiteLoginManagerImpl::GeneratePassword(
autofill::FormSignature form_signature,
autofill::FieldSignature field_signature,
uint64_t max_length) {
auto* factory =
password_manager::ContentPasswordManagerDriverFactory::FromWebContents(
web_contents_);
DCHECK(factory);
// TODO(crbug.com/1043132): Add support for non-main frames. If another
// frame has a different origin than the main frame, passwords-related
// features may not work.
auto* driver = factory->GetDriverForFrame(web_contents_->GetMainFrame());
if (!driver) {
return absl::nullopt;
}
return base::UTF16ToUTF8(
driver->GetPasswordGenerationHelper()->GeneratePassword(
driver->GetLastCommittedURL(), form_signature, field_signature,
max_length));
}
void WebsiteLoginManagerImpl::PresaveGeneratedPassword(
const Login& login,
const std::string& password,
const autofill::FormData& form_data,
base::OnceCallback<void()> callback) {
DCHECK(!update_password_request_);
update_password_request_ = std::make_unique<UpdatePasswordRequest>(
login, password, form_data, client_, std::move(callback));
update_password_request_->FetchAndPresave();
}
bool WebsiteLoginManagerImpl::ReadyToSaveGeneratedPassword() {
return update_password_request_ != nullptr;
}
void WebsiteLoginManagerImpl::SaveGeneratedPassword() {
DCHECK(update_password_request_);
update_password_request_->SaveGeneratedPassword();
update_password_request_.reset();
}
void WebsiteLoginManagerImpl::ResetPendingCredentials() {
client_->GetPasswordManager()->ResetPendingCredentials();
}
bool WebsiteLoginManagerImpl::ReadyToSaveSubmittedPassword() {
return client_->GetPasswordManager()->HasSubmittedManager();
}
bool WebsiteLoginManagerImpl::SubmittedPasswordIsSame() {
return client_->GetPasswordManager()->HasSubmittedManagerWithSamePassword();
}
void WebsiteLoginManagerImpl::CheckWhetherSubmittedCredentialIsLeaked(
SavePasswordLeakDetectionDelegate::Callback callback,
base::TimeDelta timeout) {
absl::optional<password_manager::PasswordForm> credentials =
client_->GetPasswordManager()->GetSubmittedCredentials();
if (!credentials.has_value()) {
std::move(callback).Run(
LeakDetectionStatus(LeakDetectionStatusCode::NO_USERNAME), false);
return;
}
leak_delegate_->StartLeakCheck(credentials.value(), std::move(callback),
timeout);
}
bool WebsiteLoginManagerImpl::SaveSubmittedPassword() {
if (!ReadyToSaveSubmittedPassword()) {
return false;
}
client_->GetPasswordManager()->SaveSubmittedManager();
return true;
}
void WebsiteLoginManagerImpl::OnRequestFinished(const PendingRequest* request) {
base::EraseIf(pending_requests_, [request](const auto& candidate_request) {
return candidate_request.get() == request;
});
}
} // namespace autofill_assistant