Commit 2e211eda authored by Matthias Körber's avatar Matthias Körber Committed by Commit Bot

[PasswordManager] |LeakDetectionDelegate| determines |CredentialLeakType|.

The |LeakDetectionDelegate| uses a helper class to asynchronously
determine the |CredentialLeakType| for choosing which dialog to show.

Change-Id: If8790ff4fda2fe74bc527816daa2efbc6b5b24ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1768428Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Commit-Queue: Matthias Körber <koerber@google.com>
Cr-Commit-Position: refs/heads/master@{#690451}
parent a4a7e453
......@@ -258,6 +258,8 @@ jumbo_static_library("browser") {
"http_credentials_cleaner.h",
"leak_detection_delegate.cc",
"leak_detection_delegate.h",
"leak_detection_delegate_helper.cc",
"leak_detection_delegate_helper.h",
"leak_detection_dialog_utils.cc",
"leak_detection_dialog_utils.h",
]
......@@ -535,6 +537,7 @@ source_set("unit_tests") {
sources += [
"hsts_query_unittest.cc",
"http_credentials_cleaner_unittest.cc",
"leak_detection_delegate_helper_unittest.cc",
"leak_detection_delegate_unittest.cc",
]
}
......
......@@ -9,6 +9,8 @@
#include "components/autofill/core/common/save_password_progress_logger.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_check.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory_impl.h"
#include "components/password_manager/core/browser/leak_detection_delegate_helper.h"
#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
......@@ -56,17 +58,37 @@ void LeakDetectionDelegate::OnLeakDetectionDone(bool is_leaked,
logger.LogBoolean(Logger::STRING_LEAK_DETECTION_FINISHED, is_leaked);
}
if (is_leaked) {
DCHECK(is_leaked_timer_);
base::UmaHistogramTimes(
"PasswordManager.LeakDetection.NotifyIsLeakedTime",
std::exchange(is_leaked_timer_, nullptr)->Elapsed());
client_->NotifyUserCredentialsWereLeaked(
password_manager::CreateLeakTypeFromBools(
/*is_saved=*/false, /*is_reused=*/false, /*is_syncing=*/false),
url);
if (client_->GetPasswordSyncState() != SYNCING_NORMAL_ENCRYPTION) {
// If the credentials are not synced, the |CredentialLeakType| needed to
// show the correct notification is already determined.
OnShowLeakDetectionNotifiction(
CreateLeakTypeFromBools(/*is_saved=*/false, /*is_reused=*/false,
/*is_synced=*/false),
std::move(url), std::move(username));
} else {
// Otherwise query the helper to asynchronously determine the
// |CredentialLeakType|.
helper_ = std::make_unique<LeakDetectionDelegateHelper>(std::move(
base::BindOnce(&LeakDetectionDelegate::OnShowLeakDetectionNotifiction,
base::Unretained(this))));
helper_->GetCredentialLeakType(client_->GetPasswordStore(),
std::move(url), std::move(username),
std::move(password));
}
}
}
void LeakDetectionDelegate::OnShowLeakDetectionNotifiction(
CredentialLeakType leak_type,
GURL url,
base::string16 username) {
DCHECK(is_leaked_timer_);
base::UmaHistogramTimes("PasswordManager.LeakDetection.NotifyIsLeakedTime",
std::exchange(is_leaked_timer_, nullptr)->Elapsed());
helper_.reset();
client_->NotifyUserCredentialsWereLeaked(leak_type, url);
}
void LeakDetectionDelegate::OnError(LeakDetectionError error) {
leak_check_.reset();
......
......@@ -6,10 +6,12 @@
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DELEGATE_H_
#include <memory>
#include <utility>
#include "base/timer/elapsed_timer.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_delegate_interface.h"
#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
namespace autofill {
struct PasswordForm;
......@@ -18,9 +20,10 @@ struct PasswordForm;
namespace password_manager {
class LeakDetectionCheck;
class LeakDetectionDelegateHelper;
class PasswordManagerClient;
// The helper class that incapsulates the requests and their processing.
// The helper class that encapsulates the requests and their processing.
class LeakDetectionDelegate : public LeakDetectionDelegateInterface {
public:
explicit LeakDetectionDelegate(PasswordManagerClient* client);
......@@ -48,6 +51,14 @@ class LeakDetectionDelegate : public LeakDetectionDelegateInterface {
GURL url,
base::string16 username,
base::string16 password) override;
// Initiates the showing of the leak detection notification. If the account is
// synced, it is called by |helper_| after the |leak_type| was asynchronously
// determined.
void OnShowLeakDetectionNotifiction(CredentialLeakType leak_type,
GURL url,
base::string16 username);
void OnError(LeakDetectionError error) override;
PasswordManagerClient* client_;
......@@ -60,6 +71,10 @@ class LeakDetectionDelegate : public LeakDetectionDelegateInterface {
// Timer measuring the time it takes from StartLeakCheck() until a call to
// OnLeakDetectionDone() with is_leaked = true.
std::unique_ptr<base::ElapsedTimer> is_leaked_timer_;
// Helper class to asynchronously determine |CredentialLeakType| for leaked
// credentials.
std::unique_ptr<LeakDetectionDelegateHelper> helper_;
};
} // namespace password_manager
......
// 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/password_manager/core/browser/leak_detection_delegate_helper.h"
#include "components/password_manager/core/browser/password_store.h"
namespace password_manager {
LeakDetectionDelegateHelper::LeakDetectionDelegateHelper(LeakTypeReply callback)
: callback_(std::move(callback)) {}
LeakDetectionDelegateHelper::~LeakDetectionDelegateHelper() = default;
void LeakDetectionDelegateHelper::GetCredentialLeakType(
PasswordStore* store,
GURL url,
base::string16 username,
base::string16 password) {
DCHECK(store);
url_ = std::move(url);
username_ = std::move(username);
password_ = std::move(password);
store->GetLoginsByPassword(password_, this);
}
void LeakDetectionDelegateHelper::OnGetPasswordStoreResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
bool is_saved = false;
for (const auto& form : results) {
if (form->origin == url_ && form->username_value == username_) {
is_saved = true;
break;
}
}
bool is_reused = results.size() > (is_saved ? 1 : 0);
CredentialLeakType leak_type =
CreateLeakTypeFromBools(is_saved, is_reused, /*is_synced=*/true);
std::move(callback_).Run(std::move(leak_type), std::move(url_),
std::move(username_));
}
} // namespace password_manager
// 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.
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DELEGATE_HELPER_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DELEGATE_HELPER_H_
#include <memory>
#include <utility>
#include <vector>
#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "url/gurl.h"
namespace password_manager {
class LeakDetectionCheck;
class PasswordStore;
// Helper class to asynchronously requests all credentials with
// a specific password from the |PasswordStore|.
class LeakDetectionDelegateHelper : public PasswordStoreConsumer {
public:
// Type alias for |callback_|.
using LeakTypeReply = base::OnceCallback<void(int, GURL, base::string16)>;
explicit LeakDetectionDelegateHelper(LeakTypeReply callback);
~LeakDetectionDelegateHelper() override;
// Request all credentials with |password| from |store|.
// Results are password to |OnGetPasswordStoreResults|.
void GetCredentialLeakType(PasswordStore* store,
GURL url,
base::string16 username,
base::string16 password);
private:
// PasswordStoreConsumer:
// Is called by the |PasswordStore| once all credentials with the specific
// password are retrieved. Determine the |CredentialLeakType and invokes
// |callback_| when done.
void OnGetPasswordStoreResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
LeakTypeReply callback_;
GURL url_;
base::string16 username_;
base::string16 password_;
// Instances should be neither copyable nor assignable.
DISALLOW_COPY_AND_ASSIGN(LeakDetectionDelegateHelper);
};
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DELEGATE_HELPER_H_
// 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/password_manager/core/browser/leak_detection_delegate_helper.h"
#include <memory>
#include "base/strings/utf_string_conversions.h"
#include "base/test/mock_callback.h"
#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using autofill::PasswordForm;
using base::ASCIIToUTF16;
using base::BindOnce;
using base::MockCallback;
using base::Unretained;
using testing::_;
using testing::StrictMock;
using testing::WithArg;
namespace password_manager {
namespace {
constexpr char kLeakedPassword[] = "leaked_password";
constexpr char kLeakedUsername[] = "leaked_username";
constexpr char kOtherUsername[] = "other_username";
constexpr char kLeakedOrigin[] = "https://www.leaked_origin.de/login";
constexpr char kOtherOrigin[] = "https://www.other_origin.de/login";
// Creates a |PasswordForm| with the supplied |origin| and |username|. The
// password is always set to |kLeakedPassword|.
PasswordForm CreateForm(const char* origin, const char* username) {
PasswordForm form;
form.origin = GURL(ASCIIToUTF16(origin));
form.username_value = std::move(ASCIIToUTF16(username));
form.password_value = ASCIIToUTF16(kLeakedPassword);
form.signon_realm = form.origin.GetOrigin().spec();
return form;
}
// Used to mimic the callback of the |PasswordStore|. Converts the vector of
// |PasswordForm|s to a vector of unique pointers to |PasswordForm|s.
ACTION_P(InvokeConsumerWithPasswordForms, forms) {
std::vector<std::unique_ptr<autofill::PasswordForm>> results;
for (const auto& form : forms) {
results.push_back(std::make_unique<PasswordForm>(form));
}
arg0->OnGetPasswordStoreResults(std::move(results));
}
} // namespace
class LeakDetectionDelegateHelperTest : public testing::Test {
public:
LeakDetectionDelegateHelperTest() = default;
~LeakDetectionDelegateHelperTest() override = default;
protected:
void SetUp() override {
store_ = new testing::StrictMock<MockPasswordStore>;
delegate_helper_ =
std::make_unique<LeakDetectionDelegateHelper>(callback_.Get());
}
void TearDown() override {
store_->ShutdownOnUIThread();
store_ = nullptr;
}
// Initiates determining the credential leak type.
void InitiateGetCredentialLeakType() {
delegate_helper_->GetCredentialLeakType(store_.get(), GURL(kLeakedOrigin),
ASCIIToUTF16(kLeakedUsername),
ASCIIToUTF16(kLeakedPassword));
}
// Sets the |PasswordForm|s which are retrieve from the |PasswordStore|.
void SetGetLoginByPasswordConsumerInvocation(
std::vector<PasswordForm> password_forms) {
EXPECT_CALL(*store_.get(), GetLoginsByPassword(_, _))
.WillRepeatedly(WithArg<1>(
InvokeConsumerWithPasswordForms(std::move(password_forms))));
}
// Set the expectation for the |CredentialLeakType| in the callback_.
void SetOnShowLeakDetectionNotificationExpectation(bool is_saved,
bool is_reused) {
EXPECT_CALL(
callback_,
Run(CreateLeakTypeFromBools(is_saved, is_reused, /*is_synced=*/true),
GURL(kLeakedOrigin), ASCIIToUTF16(kLeakedUsername)))
.Times(1);
}
MockCallback<LeakDetectionDelegateHelper::LeakTypeReply> callback_;
scoped_refptr<MockPasswordStore> store_;
std::unique_ptr<LeakDetectionDelegateHelper> delegate_helper_;
};
// Credentials are neither saved nor is the password reused.
TEST_F(LeakDetectionDelegateHelperTest, NeitherSaveNotReused) {
std::vector<PasswordForm> password_forms;
SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/false,
/*is_reused=*/false);
InitiateGetCredentialLeakType();
}
// Credentials are saved but the password is not reused.
TEST_F(LeakDetectionDelegateHelperTest, SavedLeakedCredentials) {
std::vector<PasswordForm> password_forms = {
CreateForm(kLeakedOrigin, kLeakedUsername)};
SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/true,
/*is_reused=*/false);
InitiateGetCredentialLeakType();
}
// Credentials are saved and the password is reused on a different origin.
TEST_F(LeakDetectionDelegateHelperTest,
SavedCredentialsAndReusedPasswordOnOtherOrigin) {
std::vector<PasswordForm> password_forms = {
CreateForm(kLeakedOrigin, kLeakedUsername),
CreateForm(kOtherOrigin, kLeakedUsername)};
SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/true,
/*is_reused=*/true);
InitiateGetCredentialLeakType();
}
// Credentials are saved and the password is reused on the same origin with
// a different username.
TEST_F(LeakDetectionDelegateHelperTest,
SavedCredentialsAndReusedPasswordWithOtherUsername) {
std::vector<PasswordForm> password_forms = {
CreateForm(kLeakedOrigin, kLeakedUsername),
CreateForm(kLeakedOrigin, kOtherUsername)};
SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/true,
/*is_reused=*/true);
InitiateGetCredentialLeakType();
}
// Credentials are not saved but the password is reused.
TEST_F(LeakDetectionDelegateHelperTest, ReusedPasswordWithOtherUsername) {
std::vector<PasswordForm> password_forms = {
CreateForm(kLeakedOrigin, kOtherUsername)};
SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/false,
/*is_reused=*/true);
InitiateGetCredentialLeakType();
}
// Credentials are not saved but the password is reused on a different origin.
TEST_F(LeakDetectionDelegateHelperTest, ReusedPasswordOnOtherOrigin) {
std::vector<PasswordForm> password_forms = {
CreateForm(kOtherOrigin, kLeakedUsername)};
SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/false,
/*is_reused=*/true);
InitiateGetCredentialLeakType();
}
// Credentials are not saved but the password is reused with a different
// username on a different origin.
TEST_F(LeakDetectionDelegateHelperTest, ReusedPassword) {
std::vector<PasswordForm> password_forms = {
CreateForm(kOtherOrigin, kOtherUsername)};
SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(/*is_saved=*/false,
/*is_reused=*/true);
InitiateGetCredentialLeakType();
}
} // namespace password_manager
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