Skip to content

Commit

Permalink
Add dialog for password leak detection
Browse files Browse the repository at this point in the history
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/+/1738459
Reviewed-by: Vasilii Sukhanov <vasilii@chromium.org>
Commit-Queue: Rafał Godlewski <rgod@google.com>
Cr-Commit-Position: refs/heads/master@{#686062}
  • Loading branch information
Rafał Godlewski authored and Commit Bot committed Aug 12, 2019
1 parent 01bf391 commit d9c691a
Show file tree
Hide file tree
Showing 16 changed files with 353 additions and 1 deletion.
16 changes: 16 additions & 0 deletions chrome/app/generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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">
Expand Down
11 changes: 11 additions & 0 deletions chrome/browser/password_manager/chrome_password_manager_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions chrome/browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
17 changes: 17 additions & 0 deletions chrome/browser/ui/passwords/credential_leak_dialog_controller.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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_
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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_
13 changes: 13 additions & 0 deletions chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Expand Down
7 changes: 7 additions & 0 deletions chrome/browser/ui/passwords/manage_passwords_ui_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ class PasswordFormManagerForUI;
class AccountChooserPrompt;
struct AccountInfo;
class AutoSigninFirstRunPrompt;
class CredentialLeakPrompt;
class ManagePasswordsIconView;
class CredentialLeakDialogController;
class CredentialManagerDialogController;
class PasswordBaseDialogController;

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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;

Expand Down
20 changes: 20 additions & 0 deletions chrome/browser/ui/passwords/password_dialog_prompts.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace content {
class WebContents;
}

class CredentialLeakDialogController;
class CredentialManagerDialogController;

// The default inset from BubbleFrameView.
Expand Down Expand Up @@ -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,
Expand All @@ -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_
4 changes: 4 additions & 0 deletions chrome/browser/ui/passwords/passwords_client_ui_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
119 changes: 119 additions & 0 deletions chrome/browser/ui/views/passwords/credential_leak_dialog_view.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// 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);
}

0 comments on commit d9c691a

Please sign in to comment.