Commit 445d777b authored by Viktor Semeniuk's avatar Viktor Semeniuk Committed by Commit Bot

Using ACCESS_PASSWORD_COPIED metrics

Used AccessPasswordInSettingsEvent::ACCESS_PASSWORD_COPIED metrics to
count number of times user clicked Copy password button in the password
settings. Added related check to unittests.

Bug: 917337
Change-Id: I4b6172acfaffc0f8b840632cfc320187e242ad7a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2064549
Commit-Queue: Viktor Semeniuk <vsemeniuk@google.com>
Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#743913}
parent 28339f84
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "components/password_manager/core/browser/password_list_sorter.h" #include "components/password_manager/core/browser/password_list_sorter.h"
#include "components/password_manager/core/browser/password_manager_util.h" #include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_ui_utils.h" #include "components/password_manager/core/browser/password_ui_utils.h"
#include "components/password_manager/core/browser/ui/plaintext_reason.h"
#include "components/password_manager/core/common/password_manager_features.h" #include "components/password_manager/core/common/password_manager_features.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
...@@ -223,25 +224,39 @@ void PasswordsPrivateDelegateImpl::RequestPlaintextPassword( ...@@ -223,25 +224,39 @@ void PasswordsPrivateDelegateImpl::RequestPlaintextPassword(
return; return;
} }
if (reason == api::passwords_private::PLAINTEXT_REASON_COPY) { password_manager::PlaintextReason presenter_reason =
// In case of copy we don't need to give password back to UI. callback password_manager::PlaintextReason::kView;
// will receive either empty string in case of success or null otherwise.
// Copying occurs here so javascript doesn't need plaintext password. switch (reason) {
callback = base::BindOnce( case api::passwords_private::PLAINTEXT_REASON_VIEW:
[](PlaintextPasswordCallback callback, break;
base::Optional<base::string16> password) { case api::passwords_private::PLAINTEXT_REASON_COPY:
if (!password) { presenter_reason = password_manager::PlaintextReason::kCopy;
std::move(callback).Run(base::nullopt); // In case of copy we don't need to give password back to UI. callback
return; // will receive either empty string in case of success or null otherwise.
} // Copying occurs here so javascript doesn't need plaintext password.
ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste) callback = base::BindOnce(
.WriteText(*password); [](PlaintextPasswordCallback callback,
std::move(callback).Run(base::string16()); base::Optional<base::string16> password) {
}, if (!password) {
std::move(callback)); std::move(callback).Run(base::nullopt);
return;
}
ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)
.WriteText(*password);
std::move(callback).Run(base::string16());
},
std::move(callback));
break;
case api::passwords_private::PLAINTEXT_REASON_EDIT:
presenter_reason = password_manager::PlaintextReason::kEdit;
break;
case api::passwords_private::PLAINTEXT_REASON_NONE:
NOTREACHED();
break;
} }
password_manager_presenter_->RequestPlaintextPassword(*sort_key, password_manager_presenter_->RequestPlaintextPassword(
std::move(callback)); *sort_key, presenter_reason, std::move(callback));
} }
bool PasswordsPrivateDelegateImpl::OsReauthCall( bool PasswordsPrivateDelegateImpl::OsReauthCall(
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h" #include "base/test/mock_callback.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h" #include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h"
...@@ -25,6 +26,7 @@ ...@@ -25,6 +26,7 @@
#include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile.h"
#include "components/autofill/core/common/password_form.h" #include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/password_list_sorter.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_manager_test_utils.h" #include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/reauth_purpose.h" #include "components/password_manager/core/browser/reauth_purpose.h"
#include "components/password_manager/core/browser/test_password_store.h" #include "components/password_manager/core/browser/test_password_store.h"
...@@ -41,11 +43,12 @@ using ::testing::Eq; ...@@ -41,11 +43,12 @@ using ::testing::Eq;
using ::testing::Ne; using ::testing::Ne;
using ::testing::Return; using ::testing::Return;
using ::testing::StrictMock; using ::testing::StrictMock;
namespace extensions { namespace extensions {
namespace { namespace {
constexpr char kHistogramName[] = "PasswordManager.AccessPasswordInSettings";
using MockPlaintextPasswordCallback = using MockPlaintextPasswordCallback =
base::MockCallback<PasswordsPrivateDelegate::PlaintextPasswordCallback>; base::MockCallback<PasswordsPrivateDelegate::PlaintextPasswordCallback>;
...@@ -117,6 +120,8 @@ class PasswordsPrivateDelegateImplTest : public testing::Test { ...@@ -117,6 +120,8 @@ class PasswordsPrivateDelegateImplTest : public testing::Test {
// PasswordsPrivateEventRouter. // PasswordsPrivateEventRouter.
void SetUpRouters(); void SetUpRouters();
base::HistogramTester& histogram_tester() { return histogram_tester_; }
protected: protected:
content::BrowserTaskEnvironment task_environment_; content::BrowserTaskEnvironment task_environment_;
TestingProfile profile_; TestingProfile profile_;
...@@ -125,6 +130,7 @@ class PasswordsPrivateDelegateImplTest : public testing::Test { ...@@ -125,6 +130,7 @@ class PasswordsPrivateDelegateImplTest : public testing::Test {
ui::TestClipboard::CreateForCurrentThread(); ui::TestClipboard::CreateForCurrentThread();
private: private:
base::HistogramTester histogram_tester_;
DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateDelegateImplTest); DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateDelegateImplTest);
}; };
...@@ -266,6 +272,10 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResult) { ...@@ -266,6 +272,10 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResult) {
base::string16 result; base::string16 result;
test_clipboard_->ReadText(ui::ClipboardBuffer::kCopyPaste, &result); test_clipboard_->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
EXPECT_EQ(form.password_value, result); EXPECT_EQ(form.password_value, result);
histogram_tester().ExpectUniqueSample(
kHistogramName, password_manager::metrics_util::ACCESS_PASSWORD_COPIED,
1);
} }
TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResultFail) { TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResultFail) {
...@@ -292,6 +302,9 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResultFail) { ...@@ -292,6 +302,9 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResultFail) {
test_clipboard_->ReadText(ui::ClipboardBuffer::kCopyPaste, &result); test_clipboard_->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
EXPECT_EQ(base::string16(), result); EXPECT_EQ(base::string16(), result);
EXPECT_EQ(before_call, test_clipboard_->GetLastModifiedTime()); EXPECT_EQ(before_call, test_clipboard_->GetLastModifiedTime());
// Since Reauth had failed password was not copied and metric wasn't recorded
histogram_tester().ExpectTotalCount(kHistogramName, 0);
} }
TEST_F(PasswordsPrivateDelegateImplTest, TestPassedReauthOnView) { TEST_F(PasswordsPrivateDelegateImplTest, TestPassedReauthOnView) {
...@@ -313,6 +326,10 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestPassedReauthOnView) { ...@@ -313,6 +326,10 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestPassedReauthOnView) {
delegate.RequestPlaintextPassword( delegate.RequestPlaintextPassword(
0, api::passwords_private::PLAINTEXT_REASON_VIEW, password_callback.Get(), 0, api::passwords_private::PLAINTEXT_REASON_VIEW, password_callback.Get(),
nullptr); nullptr);
histogram_tester().ExpectUniqueSample(
kHistogramName, password_manager::metrics_util::ACCESS_PASSWORD_VIEWED,
1);
} }
TEST_F(PasswordsPrivateDelegateImplTest, TestFailedReauthOnView) { TEST_F(PasswordsPrivateDelegateImplTest, TestFailedReauthOnView) {
...@@ -334,6 +351,9 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestFailedReauthOnView) { ...@@ -334,6 +351,9 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestFailedReauthOnView) {
delegate.RequestPlaintextPassword( delegate.RequestPlaintextPassword(
0, api::passwords_private::PLAINTEXT_REASON_VIEW, password_callback.Get(), 0, api::passwords_private::PLAINTEXT_REASON_VIEW, password_callback.Get(),
nullptr); nullptr);
// Since Reauth had failed password was not viewed and metric wasn't recorded
histogram_tester().ExpectTotalCount(kHistogramName, 0);
} }
TEST_F(PasswordsPrivateDelegateImplTest, TestReauthOnExport) { TEST_F(PasswordsPrivateDelegateImplTest, TestReauthOnExport) {
......
...@@ -345,6 +345,7 @@ void PasswordManagerPresenter::UndoRemoveSavedPasswordOrException() { ...@@ -345,6 +345,7 @@ void PasswordManagerPresenter::UndoRemoveSavedPasswordOrException() {
#if !defined(OS_ANDROID) // This is never called on Android. #if !defined(OS_ANDROID) // This is never called on Android.
void PasswordManagerPresenter::RequestPlaintextPassword( void PasswordManagerPresenter::RequestPlaintextPassword(
const std::string& sort_key, const std::string& sort_key,
password_manager::PlaintextReason reason,
base::OnceCallback<void(base::Optional<base::string16>)> callback) const { base::OnceCallback<void(base::Optional<base::string16>)> callback) const {
auto it = password_map_.find(sort_key); auto it = password_map_.find(sort_key);
if (it == password_map_.end()) { if (it == password_map_.end()) {
...@@ -369,9 +370,12 @@ void PasswordManagerPresenter::RequestPlaintextPassword( ...@@ -369,9 +370,12 @@ void PasswordManagerPresenter::RequestPlaintextPassword(
// Call back the front end to reveal the password. // Call back the front end to reveal the password.
std::move(callback).Run(form.password_value); std::move(callback).Run(form.password_value);
auto metric_type =
reason == password_manager::PlaintextReason::kCopy
? password_manager::metrics_util::ACCESS_PASSWORD_COPIED
: password_manager::metrics_util::ACCESS_PASSWORD_VIEWED;
UMA_HISTOGRAM_ENUMERATION( UMA_HISTOGRAM_ENUMERATION(
"PasswordManager.AccessPasswordInSettings", "PasswordManager.AccessPasswordInSettings", metric_type,
password_manager::metrics_util::ACCESS_PASSWORD_VIEWED,
password_manager::metrics_util::ACCESS_PASSWORD_COUNT); password_manager::metrics_util::ACCESS_PASSWORD_COUNT);
} }
#endif #endif
......
...@@ -11,14 +11,15 @@ ...@@ -11,14 +11,15 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "build/build_config.h"
#include "components/password_manager/core/browser/password_store.h" #include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/password_store_consumer.h" #include "components/password_manager/core/browser/password_store_consumer.h"
#include "components/password_manager/core/browser/ui/credential_provider_interface.h" #include "components/password_manager/core/browser/ui/credential_provider_interface.h"
#include "components/password_manager/core/browser/ui/plaintext_reason.h"
#include "components/prefs/pref_member.h" #include "components/prefs/pref_member.h"
#include "components/undo/undo_manager.h" #include "components/undo/undo_manager.h"
#include "ui/shell_dialogs/select_file_dialog.h" #include "ui/shell_dialogs/select_file_dialog.h"
...@@ -91,6 +92,7 @@ class PasswordManagerPresenter ...@@ -91,6 +92,7 @@ class PasswordManagerPresenter
// Undoes the last saved password or exception removal. // Undoes the last saved password or exception removal.
void UndoRemoveSavedPasswordOrException(); void UndoRemoveSavedPasswordOrException();
#if !defined(OS_ANDROID)
// Requests to reveal the plain text password corresponding to |sort_key|. If // Requests to reveal the plain text password corresponding to |sort_key|. If
// |sort_key| is a valid key into |password_map_|, runs |callback| with the // |sort_key| is a valid key into |password_map_|, runs |callback| with the
// corresponding value, or nullopt otherwise. // corresponding value, or nullopt otherwise.
...@@ -98,7 +100,9 @@ class PasswordManagerPresenter ...@@ -98,7 +100,9 @@ class PasswordManagerPresenter
// instead. // instead.
void RequestPlaintextPassword( void RequestPlaintextPassword(
const std::string& sort_key, const std::string& sort_key,
password_manager::PlaintextReason reason,
base::OnceCallback<void(base::Optional<base::string16>)> callback) const; base::OnceCallback<void(base::Optional<base::string16>)> callback) const;
#endif
// Wrapper around |PasswordStore::AddLogin| that adds the corresponding undo // Wrapper around |PasswordStore::AddLogin| that adds the corresponding undo
// action to |undo_manager_|. // action to |undo_manager_|.
......
...@@ -15,14 +15,21 @@ ...@@ -15,14 +15,21 @@
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#if !defined(OS_ANDROID)
#include "base/test/metrics/histogram_tester.h"
#endif
#include "base/test/mock_callback.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/password_manager/password_store_factory.h" #include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/ui/passwords/settings/password_ui_view.h" #include "chrome/browser/ui/passwords/settings/password_ui_view.h"
#include "chrome/browser/ui/passwords/settings/password_ui_view_mock.h" #include "chrome/browser/ui/passwords/settings/password_ui_view_mock.h"
#include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile.h"
#include "components/password_manager/core/browser/password_list_sorter.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_manager_test_utils.h" #include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/test_password_store.h" #include "components/password_manager/core/browser/test_password_store.h"
#include "components/password_manager/core/browser/ui/plaintext_reason.h"
#include "content/public/test/browser_task_environment.h" #include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -45,7 +52,9 @@ constexpr char kPassword[] = "pass"; ...@@ -45,7 +52,9 @@ constexpr char kPassword[] = "pass";
constexpr char kPassword2[] = "pass2"; constexpr char kPassword2[] = "pass2";
constexpr char kUsername[] = "user"; constexpr char kUsername[] = "user";
constexpr char kUsername2[] = "user2"; constexpr char kUsername2[] = "user2";
#if !defined(OS_ANDROID)
constexpr char kHistogramName[] = "PasswordManager.AccessPasswordInSettings";
#endif
MATCHER(IsNotBlacklisted, "") { MATCHER(IsNotBlacklisted, "") {
return !arg->blacklisted_by_user; return !arg->blacklisted_by_user;
} }
...@@ -82,9 +91,9 @@ class PasswordManagerPresenterTest : public testing::Test { ...@@ -82,9 +91,9 @@ class PasswordManagerPresenterTest : public testing::Test {
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
} }
void AddPasswordEntry(const GURL& origin, autofill::PasswordForm AddPasswordEntry(const GURL& origin,
base::StringPiece username, base::StringPiece username,
base::StringPiece password) { base::StringPiece password) {
autofill::PasswordForm form; autofill::PasswordForm form;
form.origin = origin; form.origin = origin;
form.signon_realm = origin.GetOrigin().spec(); form.signon_realm = origin.GetOrigin().spec();
...@@ -93,6 +102,7 @@ class PasswordManagerPresenterTest : public testing::Test { ...@@ -93,6 +102,7 @@ class PasswordManagerPresenterTest : public testing::Test {
form.password_element = base::ASCIIToUTF16("Passwd"); form.password_element = base::ASCIIToUTF16("Passwd");
form.password_value = base::ASCIIToUTF16(password); form.password_value = base::ASCIIToUTF16(password);
store_->AddLogin(form); store_->AddLogin(form);
return form;
} }
void AddPasswordException(const GURL& origin) { void AddPasswordException(const GURL& origin) {
...@@ -372,4 +382,28 @@ TEST_F(PasswordManagerPresenterTest, BlacklistDoesNotPreventExporting) { ...@@ -372,4 +382,28 @@ TEST_F(PasswordManagerPresenterTest, BlacklistDoesNotPreventExporting) {
EXPECT_EQ(kSameOrigin, passwords_for_export[0]->origin); EXPECT_EQ(kSameOrigin, passwords_for_export[0]->origin);
} }
#if !defined(OS_ANDROID)
TEST_F(PasswordManagerPresenterTest, TestRequestPlaintextPassword) {
base::HistogramTester histogram_tester_;
autofill::PasswordForm form =
AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1)));
EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
UpdatePasswordLists();
base::MockOnceCallback<void(base::Optional<base::string16>)>
password_callback;
EXPECT_CALL(password_callback,
Run(testing::Eq(base::ASCIIToUTF16(kPassword))));
std::string sort_key = password_manager::CreateSortKey(form);
GetUIController().GetPasswordManagerPresenter()->RequestPlaintextPassword(
sort_key, password_manager::PlaintextReason::kView,
password_callback.Get());
histogram_tester_.ExpectUniqueSample(
kHistogramName, password_manager::metrics_util::ACCESS_PASSWORD_VIEWED,
1);
}
#endif
} // namespace } // namespace
...@@ -224,6 +224,7 @@ jumbo_static_library("browser") { ...@@ -224,6 +224,7 @@ jumbo_static_library("browser") {
"ui/export_flow.h", "ui/export_flow.h",
"ui/export_progress_status.h", "ui/export_progress_status.h",
"ui/import_flow.h", "ui/import_flow.h",
"ui/plaintext_reason.h",
"ui/saved_passwords_presenter.cc", "ui/saved_passwords_presenter.cc",
"ui/saved_passwords_presenter.h", "ui/saved_passwords_presenter.h",
"votes_uploader.cc", "votes_uploader.cc",
......
// Copyright 2020 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_UI_PLAINTEXT_REASON_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_UI_PLAINTEXT_REASON_H_
namespace password_manager {
// Possible reasons why a plaintext password was requested.
enum class PlaintextReason {
kView,
kCopy,
kEdit,
};
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_UI_PLAINTEXT_REASON_H_
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