Commit 1fb4cc85 authored by Jan Wilken Dörrie's avatar Jan Wilken Dörrie Committed by Commit Bot

[Passwords] Update UI after each checked credential

This change implements updating the Password Check UI after each
credential was checked. It makes sure to account for duplicates with
regard to canonicalization of usernames and passwords and updates the
progress accordingly.

Bug: 1061500
Change-Id: I46d13e7e007f31596b6c67f9f3e56535cbc5e8a2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2106580
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#751128}
parent e08873fb
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORD_CHECK_DELEGATE_H_ #define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORD_CHECK_DELEGATE_H_
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h" #include "base/scoped_observer.h"
#include "chrome/browser/extensions/api/passwords_private/passwords_private_utils.h" #include "chrome/browser/extensions/api/passwords_private/passwords_private_utils.h"
#include "chrome/common/extensions/api/passwords_private.h" #include "chrome/common/extensions/api/passwords_private.h"
...@@ -26,6 +27,10 @@ class PasswordStore; ...@@ -26,6 +27,10 @@ class PasswordStore;
namespace extensions { namespace extensions {
extern const char kPasswordCheckDataKey[];
class PasswordCheckProgress;
// This class handles the part of the passwordsPrivate extension API that deals // This class handles the part of the passwordsPrivate extension API that deals
// with the bulk password check feature. // with the bulk password check feature.
class PasswordCheckDelegate class PasswordCheckDelegate
...@@ -131,8 +136,12 @@ class PasswordCheckDelegate ...@@ -131,8 +136,12 @@ class PasswordCheckDelegate
password_manager::BulkLeakCheckServiceAdapter password_manager::BulkLeakCheckServiceAdapter
bulk_leak_check_service_adapter_; bulk_leak_check_service_adapter_;
// Remembers whether the bulk check is running due to explicit user action. // Remembers the progress of the ongoing check. Null if no check is currently
bool is_bulk_check_running_ = false; // running.
base::WeakPtr<PasswordCheckProgress> password_check_progress_;
// Remembers whether a password check is running right now.
bool is_check_running_ = false;
// A scoped observer for |saved_passwords_presenter_|. // A scoped observer for |saved_passwords_presenter_|.
ScopedObserver<password_manager::SavedPasswordsPresenter, ScopedObserver<password_manager::SavedPasswordsPresenter,
......
...@@ -862,7 +862,7 @@ TEST_F(PasswordCheckDelegateTest, LastTimePasswordCheckCompletedIsSet) { ...@@ -862,7 +862,7 @@ TEST_F(PasswordCheckDelegateTest, LastTimePasswordCheckCompletedIsSet) {
Pointee(std::string("5 minutes ago"))); Pointee(std::string("5 minutes ago")));
} }
// Checks that a tranistion into the idle state after starting a check results // Checks that a transition into the idle state after starting a check results
// in resetting the kLastTimePasswordCheckCompleted pref to the current time. // in resetting the kLastTimePasswordCheckCompleted pref to the current time.
TEST_F(PasswordCheckDelegateTest, LastTimePasswordCheckCompletedReset) { TEST_F(PasswordCheckDelegateTest, LastTimePasswordCheckCompletedReset) {
delegate().StartPasswordCheck(); delegate().StartPasswordCheck();
...@@ -873,4 +873,53 @@ TEST_F(PasswordCheckDelegateTest, LastTimePasswordCheckCompletedReset) { ...@@ -873,4 +873,53 @@ TEST_F(PasswordCheckDelegateTest, LastTimePasswordCheckCompletedReset) {
Pointee(std::string("Just now"))); Pointee(std::string("Just now")));
} }
// Checks that processing a credential by the leak check updates the progress
// correctly and raises the expected event.
TEST_F(PasswordCheckDelegateTest, OnCredentialDoneUpdatesProgress) {
const char* const kEventName =
api::passwords_private::OnPasswordCheckStatusChanged::kEventName;
identity_test_env().MakeAccountAvailable(kTestEmail);
store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1, kPassword1));
store().AddLogin(MakeSavedPassword(kExampleCom, kUsername2, kPassword2));
store().AddLogin(MakeSavedPassword(kExampleOrg, kUsername1, kPassword1));
store().AddLogin(MakeSavedPassword(kExampleOrg, kUsername2, kPassword2));
RunUntilIdle();
const auto event_iter = event_router_observer().events().find(kEventName);
delegate().StartPasswordCheck();
EXPECT_EQ(events::PASSWORDS_PRIVATE_ON_PASSWORD_CHECK_STATUS_CHANGED,
event_iter->second->histogram_value);
auto status = PasswordCheckStatus::FromValue(
event_iter->second->event_args->GetList().front());
EXPECT_EQ(api::passwords_private::PASSWORD_CHECK_STATE_RUNNING,
status->state);
EXPECT_EQ(0, *status->already_processed);
EXPECT_EQ(4, *status->remaining_in_queue);
static_cast<BulkLeakCheckDelegateInterface*>(service())->OnFinishedCredential(
LeakCheckCredential(base::ASCIIToUTF16(kUsername1),
base::ASCIIToUTF16(kPassword1)),
IsLeaked(false));
status = PasswordCheckStatus::FromValue(
event_iter->second->event_args->GetList().front());
EXPECT_EQ(api::passwords_private::PASSWORD_CHECK_STATE_RUNNING,
status->state);
EXPECT_EQ(2, *status->already_processed);
EXPECT_EQ(2, *status->remaining_in_queue);
static_cast<BulkLeakCheckDelegateInterface*>(service())->OnFinishedCredential(
LeakCheckCredential(base::ASCIIToUTF16(kUsername2),
base::ASCIIToUTF16(kPassword2)),
IsLeaked(false));
status = PasswordCheckStatus::FromValue(
event_iter->second->event_args->GetList().front());
EXPECT_EQ(api::passwords_private::PASSWORD_CHECK_STATE_RUNNING,
status->state);
EXPECT_EQ(4, *status->already_processed);
EXPECT_EQ(0, *status->remaining_in_queue);
}
} // namespace extensions } // namespace extensions
...@@ -13,34 +13,12 @@ ...@@ -13,34 +13,12 @@
#include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h" #include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
#include "components/password_manager/core/browser/leak_detection/encryption_utils.h" #include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
#include "components/password_manager/core/browser/leak_detection_delegate.h" #include "components/password_manager/core/browser/leak_detection_delegate.h"
#include "components/password_manager/core/browser/ui/credential_utils.h"
#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h" #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
namespace password_manager { namespace password_manager {
namespace {
using autofill::PasswordForm;
// Simple struct that stores a canonicalized credential. Allows implicit
// constructon from PasswordForm for convenience.
struct CanonicalizedCredential {
CanonicalizedCredential(const PasswordForm& form)
: canonicalized_username(CanonicalizeUsername(form.username_value)),
password(form.password_value) {}
base::string16 canonicalized_username;
base::string16 password;
};
bool operator<(const CanonicalizedCredential& lhs,
const CanonicalizedCredential& rhs) {
return std::tie(lhs.canonicalized_username, lhs.password) <
std::tie(rhs.canonicalized_username, rhs.password);
}
} // namespace
BulkLeakCheckServiceAdapter::BulkLeakCheckServiceAdapter( BulkLeakCheckServiceAdapter::BulkLeakCheckServiceAdapter(
SavedPasswordsPresenter* presenter, SavedPasswordsPresenter* presenter,
BulkLeakCheckService* service, BulkLeakCheckService* service,
...@@ -56,7 +34,9 @@ BulkLeakCheckServiceAdapter::~BulkLeakCheckServiceAdapter() { ...@@ -56,7 +34,9 @@ BulkLeakCheckServiceAdapter::~BulkLeakCheckServiceAdapter() {
presenter_->RemoveObserver(this); presenter_->RemoveObserver(this);
} }
bool BulkLeakCheckServiceAdapter::StartBulkLeakCheck() { bool BulkLeakCheckServiceAdapter::StartBulkLeakCheck(
const void* key,
LeakCheckCredential::Data* data) {
if (service_->state() == BulkLeakCheckService::State::kRunning) if (service_->state() == BulkLeakCheckService::State::kRunning)
return false; return false;
...@@ -75,6 +55,10 @@ bool BulkLeakCheckServiceAdapter::StartBulkLeakCheck() { ...@@ -75,6 +55,10 @@ bool BulkLeakCheckServiceAdapter::StartBulkLeakCheck() {
for (const auto& credential : canonicalized) { for (const auto& credential : canonicalized) {
credentials.emplace_back(credential.canonicalized_username, credentials.emplace_back(credential.canonicalized_username,
credential.password); credential.password);
if (key) {
DCHECK(data);
credentials.back().SetUserData(key, data->Clone());
}
} }
service_->CheckUsernamePasswordPairs(std::move(credentials)); service_->CheckUsernamePasswordPairs(std::move(credentials));
...@@ -94,7 +78,7 @@ size_t BulkLeakCheckServiceAdapter::GetPendingChecksCount() const { ...@@ -94,7 +78,7 @@ size_t BulkLeakCheckServiceAdapter::GetPendingChecksCount() const {
return service_->GetPendingChecksCount(); return service_->GetPendingChecksCount();
} }
void BulkLeakCheckServiceAdapter::OnEdited(const PasswordForm& form) { void BulkLeakCheckServiceAdapter::OnEdited(const autofill::PasswordForm& form) {
if (CanStartLeakCheck(*prefs_)) { if (CanStartLeakCheck(*prefs_)) {
// Here no extra canonicalization is needed, as there are no other forms we // Here no extra canonicalization is needed, as there are no other forms we
// could de-dupe before we pass it on to the service. // could de-dupe before we pass it on to the service.
......
...@@ -29,9 +29,11 @@ class BulkLeakCheckServiceAdapter : public SavedPasswordsPresenter::Observer { ...@@ -29,9 +29,11 @@ class BulkLeakCheckServiceAdapter : public SavedPasswordsPresenter::Observer {
// Instructs the adapter to start a check. This is a no-op in case a check is // Instructs the adapter to start a check. This is a no-op in case a check is
// already running. Otherwise, this will obtain the list of saved passwords // already running. Otherwise, this will obtain the list of saved passwords
// from |presenter_|, perform de-duplication of username and password pairs // from |presenter_|, perform de-duplication of username and password pairs
// and then feed it to the |service_| for checking. // and then feed it to the |service_| for checking. If |key| is present, it
// will append |data->Clone()| to each created LeakCheckCredential.
// Returns whether new check was started. // Returns whether new check was started.
bool StartBulkLeakCheck(); bool StartBulkLeakCheck(const void* key = nullptr,
LeakCheckCredential::Data* data = nullptr);
// This asks |service_| to stop an ongoing check. // This asks |service_| to stop an ongoing check.
void StopBulkLeakCheck(); void StopBulkLeakCheck();
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.h" #include "components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.h"
#include <memory>
#include <tuple> #include <tuple>
#include <vector> #include <vector>
...@@ -164,6 +165,27 @@ TEST_F(BulkLeakCheckServiceAdapterTest, StartBulkLeakCheck) { ...@@ -164,6 +165,27 @@ TEST_F(BulkLeakCheckServiceAdapterTest, StartBulkLeakCheck) {
EXPECT_THAT(credentials, CredentialsAre(std::cref(expected))); EXPECT_THAT(credentials, CredentialsAre(std::cref(expected)));
} }
TEST_F(BulkLeakCheckServiceAdapterTest, StartBulkLeakCheckAttachesData) {
constexpr char kKey[] = "key";
struct UserData : LeakCheckCredential::Data {
std::unique_ptr<Data> Clone() override { return std::make_unique<Data>(); }
} data;
std::vector<PasswordForm> passwords = {
MakeSavedPassword(kExampleCom, kUsername1, kPassword1)};
store().AddLogin(passwords[0]);
RunUntilIdle();
auto leak_check = std::make_unique<NiceMockBulkLeakCheck>();
std::vector<LeakCheckCredential> credentials;
EXPECT_CALL(*leak_check, CheckCredentials).WillOnce(MoveArg(&credentials));
EXPECT_CALL(factory(), TryCreateBulkLeakCheck)
.WillOnce(Return(ByMove(std::move(leak_check))));
adapter().StartBulkLeakCheck(kKey, &data);
EXPECT_NE(nullptr, credentials.at(0).GetUserData(kKey));
}
// Tests that multiple credentials with effectively the same username are // Tests that multiple credentials with effectively the same username are
// correctly deduped before starting the leak check. // correctly deduped before starting the leak check.
TEST_F(BulkLeakCheckServiceAdapterTest, StartBulkLeakCheckDedupes) { TEST_F(BulkLeakCheckServiceAdapterTest, StartBulkLeakCheckDedupes) {
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/template_util.h" #include "base/template_util.h"
#include "components/autofill/core/common/password_form.h" #include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
#include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
namespace password_manager { namespace password_manager {
...@@ -29,6 +31,27 @@ struct CredentialView { ...@@ -29,6 +31,27 @@ struct CredentialView {
base::string16 password; base::string16 password;
}; };
// Simple struct that stores a canonicalized credential. Allows implicit
// constructon from PasswordForm and LeakCheckCredentail for convenience.
struct CanonicalizedCredential {
CanonicalizedCredential(const autofill::PasswordForm& form)
: canonicalized_username(CanonicalizeUsername(form.username_value)),
password(form.password_value) {}
CanonicalizedCredential(const LeakCheckCredential& credential)
: canonicalized_username(CanonicalizeUsername(credential.username())),
password(credential.password()) {}
base::string16 canonicalized_username;
base::string16 password;
};
inline bool operator<(const CanonicalizedCredential& lhs,
const CanonicalizedCredential& rhs) {
return std::tie(lhs.canonicalized_username, lhs.password) <
std::tie(rhs.canonicalized_username, rhs.password);
}
// Transparent comparator that can compare various types like CredentialView or // Transparent comparator that can compare various types like CredentialView or
// CredentialsWithPasswords. // CredentialsWithPasswords.
struct PasswordCredentialLess { struct PasswordCredentialLess {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment