Commit d9c691ae authored by Rafał Godlewski's avatar Rafał Godlewski Committed by Commit Bot

Add dialog for password leak detection

Implements dialog for desktop that should pop up when user used
leaked credential. The picture used is a placeholder and the dialog
itself is not called anywhere. At the moment it only covers the case
for user who is not saving passwords in Chrome or Google Account.

Bug: 986317
Change-Id: Ia725c1282110b4b6657001d3c58943569b77cc15
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1738459Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Commit-Queue: Rafał Godlewski <rgod@google.com>
Cr-Commit-Position: refs/heads/master@{#686062}
parent 01bf3913
......@@ -8304,6 +8304,12 @@ Please help our engineers fix this problem. Tell us what happened right before y
<message name="IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE_MD" desc="The title of the auto-signin toast in Material Design mode.">
Signing in as
</message>
<message name="IDS_CREDENTIAL_LEAK_TITLE" desc="The title of the dialog for leaked credentials.">
Check your passwords
</message>
<message name="IDS_CREDENTIAL_LEAK_MESSAGE" desc="The text that is used in credentials leak detection dialog.">
The password you entered was part of an online data breach. Chrome can help you to protect your saved passwords.
</message>
<message name="IDS_AUTO_SIGNIN_FIRST_RUN_TITLE_MANY_DEVICES" desc="The title of the dialog during the autosign-in first run experience for the Chrome syncing users.">
Sign in easily across devices
</message>
......@@ -8323,6 +8329,16 @@ Please help our engineers fix this problem. Tell us what happened right before y
OK, got it
</message>
</if>
<if expr="use_titlecase">
<message name="IDS_CREDENTIAL_LEAK_OK" desc="The text of the OK button in the dialog for credentials leak detection.">
Check Passwords
</message>
</if>
<if expr="not use_titlecase">
<message name="IDS_CREDENTIAL_LEAK_OK" desc="The text of the OK button in the dialog for credentials leak detection.">
Check passwords
</message>
</if>
<!-- Extra Mac UI Strings -->
<if expr="is_macosx">
......
......@@ -504,6 +504,17 @@ void ChromePasswordManagerClient::AutofillHttpAuth(
form_manager->GetOrigin(), nullptr);
}
void ChromePasswordManagerClient::NotifyUserCredentialsWereLeaked(
const GURL& origin) {
#if defined(OS_ANDROID)
// TODO(crbug.com/986317): Implement Android part of the feature.
#else // !defined(OS_ANDROID)
PasswordsClientUIDelegate* manage_passwords_ui_controller =
PasswordsClientUIDelegateFromWebContents(web_contents());
manage_passwords_ui_controller->OnCredentialLeak(origin);
#endif // defined(OS_ANDROID)
}
bool ChromePasswordManagerClient::IsIsolationForPasswordSitesEnabled() const {
// TODO(crbug.com/862989): Move the following function (and the feature) to
// the password component. Then remove IsIsolationForPasswordsSitesEnabled()
......
......@@ -116,6 +116,7 @@ class ChromePasswordManagerClient
void AutofillHttpAuth(
const autofill::PasswordForm& preferred_match,
const password_manager::PasswordFormManagerForUI* form_manager) override;
void NotifyUserCredentialsWereLeaked(const GURL& origin) override;
bool IsIsolationForPasswordSitesEnabled() const override;
PrefService* GetPrefs() const override;
......
......@@ -1009,6 +1009,9 @@ jumbo_split_static_library("ui") {
"page_info/page_info_infobar_delegate.h",
"page_info/permission_menu_model.cc",
"page_info/permission_menu_model.h",
"passwords/credential_leak_dialog_controller.h",
"passwords/credential_leak_dialog_controller_impl.cc",
"passwords/credential_leak_dialog_controller_impl.h",
"passwords/credential_manager_dialog_controller.h",
"passwords/credential_manager_dialog_controller_impl.cc",
"passwords/credential_manager_dialog_controller_impl.h",
......@@ -2901,6 +2904,8 @@ jumbo_split_static_library("ui") {
"views/passwords/account_chooser_dialog_view.h",
"views/passwords/auto_signin_first_run_dialog_view.cc",
"views/passwords/auto_signin_first_run_dialog_view.h",
"views/passwords/credential_leak_dialog_view.cc",
"views/passwords/credential_leak_dialog_view.h",
"views/passwords/credentials_item_view.cc",
"views/passwords/credentials_item_view.h",
"views/passwords/manage_passwords_icon_views.cc",
......
// 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 CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_CONTROLLER_H_
#define CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_CONTROLLER_H_
#include "chrome/browser/ui/passwords/password_base_dialog_controller.h"
// An interface used by the credential leak dialog for setting and retrieving
// the state.
class CredentialLeakDialogController : public PasswordBaseDialogController {
protected:
~CredentialLeakDialogController() override = default;
};
#endif // CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_CONTROLLER_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 "chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.h"
#include "base/logging.h"
#include "chrome/browser/ui/passwords/password_dialog_prompts.h"
CredentialLeakDialogControllerImpl::CredentialLeakDialogControllerImpl()
: credential_leak_dialog_(nullptr) {}
CredentialLeakDialogControllerImpl::~CredentialLeakDialogControllerImpl() =
default;
void CredentialLeakDialogControllerImpl::ShowCredentialLeakPrompt(
CredentialLeakPrompt* dialog) {
DCHECK(dialog);
credential_leak_dialog_ = dialog;
credential_leak_dialog_->ShowCredentialLeakPrompt();
}
bool CredentialLeakDialogControllerImpl::IsShowingAccountChooser() const {
return false;
}
// 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 CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_CONTROLLER_IMPL_H_
#define CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_CONTROLLER_IMPL_H_
#include "base/macros.h"
#include "chrome/browser/ui/passwords/credential_leak_dialog_controller.h"
class CredentialLeakPrompt;
// A UI controller responsible for the credential leak dialog.
class CredentialLeakDialogControllerImpl
: public CredentialLeakDialogController {
public:
CredentialLeakDialogControllerImpl();
~CredentialLeakDialogControllerImpl() override;
// Pop up the credential leak dialog.
void ShowCredentialLeakPrompt(CredentialLeakPrompt* dialog);
// CredentialLeakDialogController:
bool IsShowingAccountChooser() const override;
private:
CredentialLeakPrompt* credential_leak_dialog_;
DISALLOW_COPY_AND_ASSIGN(CredentialLeakDialogControllerImpl);
};
#endif // CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_CONTROLLER_IMPL_H_
......@@ -25,6 +25,7 @@
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/page_action/page_action_icon_container.h"
#include "chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.h"
#include "chrome/browser/ui/passwords/credential_manager_dialog_controller_impl.h"
#include "chrome/browser/ui/passwords/manage_passwords_icon_view.h"
#include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
......@@ -242,6 +243,13 @@ void ManagePasswordsUIController::OnPasswordAutofilled(
}
}
void ManagePasswordsUIController::OnCredentialLeak(const GURL& origin) {
auto* raw_controller = new CredentialLeakDialogControllerImpl();
dialog_controller_.reset(raw_controller);
raw_controller->ShowCredentialLeakPrompt(
CreateCredentialLeakPrompt(raw_controller));
}
void ManagePasswordsUIController::OnLoginsChanged(
const password_manager::PasswordStoreChangeList& changes) {
password_manager::ui::State current_state = GetState();
......@@ -521,6 +529,11 @@ AutoSigninFirstRunPrompt* ManagePasswordsUIController::CreateAutoSigninPrompt(
return CreateAutoSigninPromptView(controller, web_contents());
}
CredentialLeakPrompt* ManagePasswordsUIController::CreateCredentialLeakPrompt(
CredentialLeakDialogController* controller) {
return CreateCredentialLeakPromptView(controller, web_contents());
}
bool ManagePasswordsUIController::HasBrowserWindow() const {
return chrome::FindBrowserWithWebContents(web_contents()) != nullptr;
}
......
......@@ -36,7 +36,9 @@ class PasswordFormManagerForUI;
class AccountChooserPrompt;
struct AccountInfo;
class AutoSigninFirstRunPrompt;
class CredentialLeakPrompt;
class ManagePasswordsIconView;
class CredentialLeakDialogController;
class CredentialManagerDialogController;
class PasswordBaseDialogController;
......@@ -85,6 +87,7 @@ class ManagePasswordsUIController
const GURL& origin,
const std::vector<const autofill::PasswordForm*>* federated_matches)
override;
void OnCredentialLeak(const GURL& origin) override;
// PasswordStore::Observer:
void OnLoginsChanged(
......@@ -165,6 +168,10 @@ class ManagePasswordsUIController
virtual AutoSigninFirstRunPrompt* CreateAutoSigninPrompt(
CredentialManagerDialogController* controller);
// Called to create the credentials leaked dialog.
virtual CredentialLeakPrompt* CreateCredentialLeakPrompt(
CredentialLeakDialogController* controller);
// Check if |web_contents()| is attached to some Browser. Mocked in tests.
virtual bool HasBrowserWindow() const;
......
......@@ -11,6 +11,7 @@ namespace content {
class WebContents;
}
class CredentialLeakDialogController;
class CredentialManagerDialogController;
// The default inset from BubbleFrameView.
......@@ -48,6 +49,20 @@ class AutoSigninFirstRunPrompt {
virtual ~AutoSigninFirstRunPrompt() = default;
};
// A platform-independent interface for the credentials leaked prompt.
class CredentialLeakPrompt {
public:
// Shows the dialog.
virtual void ShowCredentialLeakPrompt() = 0;
// Notifies the UI element that its controller is no longer managing the UI
// element. The dialog should close.
virtual void ControllerGone() = 0;
protected:
virtual ~CredentialLeakPrompt() = default;
};
// Factory function for AccountChooserPrompt on desktop platforms.
AccountChooserPrompt* CreateAccountChooserPromptView(
CredentialManagerDialogController* controller,
......@@ -58,4 +73,9 @@ AutoSigninFirstRunPrompt* CreateAutoSigninPromptView(
CredentialManagerDialogController* controller,
content::WebContents* web_contents);
// Factory function for CredentialsLeakedPrompt on desktop platforms.
CredentialLeakPrompt* CreateCredentialLeakPromptView(
CredentialLeakDialogController* controller,
content::WebContents* web_contents);
#endif // CHROME_BROWSER_UI_PASSWORDS_PASSWORD_DIALOG_PROMPTS_H_
......@@ -89,6 +89,10 @@ class PasswordsClientUIDelegate {
const GURL& origin,
const std::vector<const autofill::PasswordForm*>* federated_matches) = 0;
// Called when user credentials were leaked. This triggers the UI to prompt
// the user whether they would like to check their passwords.
virtual void OnCredentialLeak(const GURL& origin) = 0;
protected:
virtual ~PasswordsClientUIDelegate() = default;
};
......
// 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 "chrome/browser/ui/views/passwords/credential_leak_dialog_view.h"
#include "build/build_config.h"
#include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/theme_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/views/border.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/layout/box_layout.h"
using views::BoxLayout;
namespace {
// Creates the illustration which is rendered on top of the dialog.
std::unique_ptr<views::View> CreateIllustration() {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
auto image_view = std::make_unique<NonAccessibleImageView>();
// TODO(crbug.com/986317): Replace with a proper image.
image_view->SetImage(
*rb.GetNativeImageNamed(IDR_PROFILES_DICE_TURN_ON_SYNC).ToImageSkia());
return image_view;
}
// Creates the content containing the title and description for the dialog
// rendered below the illustration.
std::unique_ptr<views::View> CreateContent() {
auto content = std::make_unique<views::View>();
content->SetLayoutManager(std::make_unique<BoxLayout>(
BoxLayout::Orientation::kVertical, gfx::Insets(),
views::LayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_UNRELATED_CONTROL_VERTICAL)));
content->SetBorder(views::CreateEmptyBorder(
views::LayoutProvider::Get()->GetDialogInsetsForContentType(
views::CONTROL, views::CONTROL)));
auto title_label = std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_CREDENTIAL_LEAK_TITLE),
views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_PRIMARY);
title_label->SetMultiLine(true);
title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
content->AddChildView(std::move(title_label));
auto description_label = std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_CREDENTIAL_LEAK_MESSAGE),
views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT);
description_label->SetMultiLine(true);
description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
content->AddChildView(std::move(description_label));
return content;
}
} // namespace
CredentialLeakDialogView::CredentialLeakDialogView(
CredentialLeakDialogController* controller,
content::WebContents* web_contents)
: controller_(controller), web_contents_(web_contents) {
DCHECK(controller);
DCHECK(web_contents);
}
CredentialLeakDialogView::~CredentialLeakDialogView() = default;
void CredentialLeakDialogView::ShowCredentialLeakPrompt() {
InitWindow();
constrained_window::ShowWebModalDialogViews(this, web_contents_);
}
void CredentialLeakDialogView::ControllerGone() {
// During Widget::Close() phase some accessibility event may occur. Thus,
// |controller_| should be kept around.
GetWidget()->Close();
controller_ = nullptr;
}
ui::ModalType CredentialLeakDialogView::GetModalType() const {
return ui::MODAL_TYPE_CHILD;
}
gfx::Size CredentialLeakDialogView::CalculatePreferredSize() const {
const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) -
margins().width();
return gfx::Size(width, GetHeightForWidth(width));
}
base::string16 CredentialLeakDialogView::GetDialogButtonLabel(
ui::DialogButton button) const {
return l10n_util::GetStringUTF16(
button == ui::DIALOG_BUTTON_OK ? IDS_CREDENTIAL_LEAK_OK : IDS_CLOSE);
}
void CredentialLeakDialogView::InitWindow() {
SetLayoutManager(std::make_unique<BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
0 /* between_child_spacing */));
std::unique_ptr<views::View> illustration = CreateIllustration();
std::unique_ptr<views::View> content = CreateContent();
AddChildView(std::move(illustration));
AddChildView(std::move(content));
}
CredentialLeakPrompt* CreateCredentialLeakPromptView(
CredentialLeakDialogController* controller,
content::WebContents* web_contents) {
return new CredentialLeakDialogView(controller, web_contents);
}
// 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 CHROME_BROWSER_UI_VIEWS_PASSWORDS_CREDENTIAL_LEAK_DIALOG_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_PASSWORDS_CREDENTIAL_LEAK_DIALOG_VIEW_H_
#include "base/macros.h"
#include "chrome/browser/ui/passwords/password_dialog_prompts.h"
#include "ui/views/window/dialog_delegate.h"
namespace content {
class WebContents;
}
class CredentialLeakDialogController;
class CredentialLeakDialogView : public views::DialogDelegateView,
public CredentialLeakPrompt {
public:
CredentialLeakDialogView(CredentialLeakDialogController* controller,
content::WebContents* web_contents);
~CredentialLeakDialogView() override;
// CredentialsLeakedPrompt:
void ShowCredentialLeakPrompt() override;
void ControllerGone() override;
private:
// views::DialogDelegateView:
ui::ModalType GetModalType() const override;
gfx::Size CalculatePreferredSize() const override;
base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
// Sets up the child views.
void InitWindow();
// A weak pointer to the controller.
CredentialLeakDialogController* controller_;
content::WebContents* const web_contents_;
DISALLOW_COPY_AND_ASSIGN(CredentialLeakDialogView);
};
#endif // CHROME_BROWSER_UI_VIEWS_PASSWORDS_CREDENTIAL_LEAK_DIALOG_VIEW_H_
......@@ -19,6 +19,7 @@
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/passwords/account_chooser_dialog_view.h"
#include "chrome/browser/ui/views/passwords/auto_signin_first_run_dialog_view.h"
#include "chrome/browser/ui/views/passwords/credential_leak_dialog_view.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/password_manager/core/browser/password_bubble_experiment.h"
......@@ -49,6 +50,8 @@ class TestManagePasswordsUIController : public ManagePasswordsUIController {
CredentialManagerDialogController* controller) override;
AutoSigninFirstRunPrompt* CreateAutoSigninPrompt(
CredentialManagerDialogController* controller) override;
CredentialLeakPrompt* CreateCredentialLeakPrompt(
CredentialLeakDialogController* controller) override;
AccountChooserDialogView* current_account_chooser() const {
return static_cast<AccountChooserDialogView*>(current_account_chooser_);
......@@ -59,11 +62,17 @@ class TestManagePasswordsUIController : public ManagePasswordsUIController {
current_autosignin_prompt_);
}
CredentialLeakDialogView* current_credential_leak_prompt() const {
return static_cast<CredentialLeakDialogView*>(
current_credential_leak_prompt_);
}
MOCK_METHOD0(OnDialogClosed, void());
private:
AccountChooserPrompt* current_account_chooser_;
AutoSigninFirstRunPrompt* current_autosignin_prompt_;
CredentialLeakPrompt* current_credential_leak_prompt_;
DISALLOW_COPY_AND_ASSIGN(TestManagePasswordsUIController);
};
......@@ -72,7 +81,8 @@ TestManagePasswordsUIController::TestManagePasswordsUIController(
content::WebContents* web_contents)
: ManagePasswordsUIController(web_contents),
current_account_chooser_(nullptr),
current_autosignin_prompt_(nullptr) {
current_autosignin_prompt_(nullptr),
current_credential_leak_prompt_(nullptr) {
// Attach TestManagePasswordsUIController to |web_contents| so the default
// ManagePasswordsUIController isn't created.
// Do not silently replace an existing ManagePasswordsUIController because it
......@@ -101,6 +111,14 @@ TestManagePasswordsUIController::CreateAutoSigninPrompt(
return current_autosignin_prompt_;
}
CredentialLeakPrompt*
TestManagePasswordsUIController::CreateCredentialLeakPrompt(
CredentialLeakDialogController* controller) {
current_credential_leak_prompt_ =
ManagePasswordsUIController::CreateCredentialLeakPrompt(controller);
return current_credential_leak_prompt_;
}
class PasswordDialogViewTest : public DialogBrowserTest {
public:
// DialogBrowserTest:
......@@ -405,6 +423,19 @@ IN_PROC_BROWSER_TEST_F(PasswordDialogViewTest, PopupAutoSigninPrompt) {
browser()->profile()->GetPrefs()));
}
IN_PROC_BROWSER_TEST_F(PasswordDialogViewTest, PopupCredentialsLeakedPrompt) {
GURL origin("https://example.com");
controller()->OnCredentialLeak(origin);
ASSERT_TRUE(controller()->current_credential_leak_prompt());
EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
CredentialLeakDialogView* dialog =
controller()->current_credential_leak_prompt();
views::test::WidgetClosingObserver bubble_observer(dialog->GetWidget());
ui::Accelerator esc(ui::VKEY_ESCAPE, 0);
EXPECT_TRUE(dialog->GetWidget()->client_view()->AcceleratorPressed(esc));
EXPECT_TRUE(bubble_observer.widget_closed());
}
IN_PROC_BROWSER_TEST_F(PasswordDialogViewTest,
PopupAutoSigninPromptAfterBlockedZeroclick) {
EXPECT_TRUE(
......
......@@ -45,6 +45,9 @@ void PasswordManagerClient::AutofillHttpAuth(
const autofill::PasswordForm& preferred_match,
const PasswordFormManagerForUI* form_manager) {}
void PasswordManagerClient::NotifyUserCredentialsWereLeaked(
const GURL& origin) {}
SyncState PasswordManagerClient::GetPasswordSyncState() const {
return NOT_SYNCING;
}
......
......@@ -213,6 +213,9 @@ class PasswordManagerClient {
virtual void AutofillHttpAuth(const autofill::PasswordForm& preferred_match,
const PasswordFormManagerForUI* form_manager);
// Informs the embedder that user credentials were leaked.
virtual void NotifyUserCredentialsWereLeaked(const GURL& origin);
// Gets prefs associated with this embedder.
virtual PrefService* GetPrefs() const = 0;
......
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