Add Local Authentication Dialog for re-auth session.
- Implement LocalAuthenticationdRequestView and related Widget and Controller - Add unittest - Add pixeltest - Add a button that can use the dialog with a debug view Change-Id: I2301ca4eea6ad21cb07a7f22a94de3af9e2ba748 Bug: b:291811391 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4819967 Reviewed-by: Denis Kuznetsov <antrim@chromium.org> Reviewed-by: Scott Violet <sky@chromium.org> Commit-Queue: Istvan Nagy <iscsi@google.com> Cr-Commit-Position: refs/heads/main@{#1217272}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
533025a949
commit
93bfdf55e7
ash
BUILD.gnash_strings.grd
ash_strings_grd
IDS_ASH_LOGIN_LOCAL_AUTHENTICATION_REQUEST_DESCRIPTION.png.sha1IDS_ASH_LOGIN_LOCAL_AUTHENTICATION_REQUEST_TITLE.png.sha1
login
mock_login_screen_client.h
ui
local_authentication_request_controller_impl.cclocal_authentication_request_controller_impl.hlocal_authentication_request_controller_impl_pixeltest.cclocal_authentication_request_controller_impl_unittest.cclocal_authentication_request_view.cclocal_authentication_request_view.hlocal_authentication_request_widget.cclocal_authentication_request_widget.hlock_debug_view.cc
public
shell.ccshell.h
24
ash/BUILD.gn
24
ash/BUILD.gn
@ -794,6 +794,12 @@ component("ash") {
|
||||
"login/ui/image_parser.h",
|
||||
"login/ui/kiosk_app_default_message.cc",
|
||||
"login/ui/kiosk_app_default_message.h",
|
||||
"login/ui/local_authentication_request_controller_impl.cc",
|
||||
"login/ui/local_authentication_request_controller_impl.h",
|
||||
"login/ui/local_authentication_request_view.cc",
|
||||
"login/ui/local_authentication_request_view.h",
|
||||
"login/ui/local_authentication_request_widget.cc",
|
||||
"login/ui/local_authentication_request_widget.h",
|
||||
"login/ui/lock_contents_view.cc",
|
||||
"login/ui/lock_contents_view.h",
|
||||
"login/ui/lock_contents_view_constants.h",
|
||||
@ -2842,6 +2848,8 @@ component("ash") {
|
||||
"//chromeos/ash/components/assistant:buildflags",
|
||||
"//chromeos/ash/components/audio",
|
||||
"//chromeos/ash/components/auth_panel",
|
||||
"//chromeos/ash/components/cryptohome:cryptohome",
|
||||
"//chromeos/ash/components/cryptohome:public",
|
||||
"//chromeos/ash/components/dbus/audio",
|
||||
"//chromeos/ash/components/dbus/biod",
|
||||
"//chromeos/ash/components/dbus/dlcservice",
|
||||
@ -2860,6 +2868,7 @@ component("ash") {
|
||||
"//chromeos/ash/components/dbus/typecd",
|
||||
"//chromeos/ash/components/dbus/update_engine",
|
||||
"//chromeos/ash/components/dbus/usb",
|
||||
"//chromeos/ash/components/dbus/userdataauth:userdataauth",
|
||||
"//chromeos/ash/components/early_prefs",
|
||||
"//chromeos/ash/components/feature_usage",
|
||||
"//chromeos/ash/components/fwupd",
|
||||
@ -3291,8 +3300,6 @@ test("ash_unittests") {
|
||||
"lock_screen_action/lock_screen_note_display_state_handler_unittest.cc",
|
||||
"lock_screen_action/lock_screen_note_launcher_unittest.cc",
|
||||
"login/login_screen_controller_unittest.cc",
|
||||
"login/mock_login_screen_client.cc",
|
||||
"login/mock_login_screen_client.h",
|
||||
"login/security_token_request_controller_unittest.cc",
|
||||
"login/ui/access_code_input.cc",
|
||||
"login/ui/access_code_input.h",
|
||||
@ -3305,6 +3312,7 @@ test("ash_unittests") {
|
||||
"login/ui/fake_smart_lock_auth_factor_model.cc",
|
||||
"login/ui/fake_smart_lock_auth_factor_model.h",
|
||||
"login/ui/fingerprint_auth_factor_model_unittest.cc",
|
||||
"login/ui/local_authentication_request_controller_impl_unittest.cc",
|
||||
"login/ui/lock_contents_view_unittest.cc",
|
||||
"login/ui/lock_screen_media_controls_view_unittest.cc",
|
||||
"login/ui/lock_screen_media_view_unittest.cc",
|
||||
@ -3873,8 +3881,11 @@ test("ash_unittests") {
|
||||
"//chromeos/ash/components/assistant:buildflags",
|
||||
"//chromeos/ash/components/audio",
|
||||
"//chromeos/ash/components/cryptohome",
|
||||
"//chromeos/ash/components/cryptohome:cryptohome",
|
||||
"//chromeos/ash/components/dbus:test_support",
|
||||
"//chromeos/ash/components/dbus/audio",
|
||||
"//chromeos/ash/components/dbus/cryptohome:cryptohome",
|
||||
"//chromeos/ash/components/dbus/cryptohome:cryptohome_proto",
|
||||
"//chromeos/ash/components/dbus/dlcservice",
|
||||
"//chromeos/ash/components/dbus/dlcservice:dlcservice_proto",
|
||||
"//chromeos/ash/components/dbus/fwupd",
|
||||
@ -4066,6 +4077,7 @@ test("ash_pixeltests") {
|
||||
"glanceables/glanceables_pixeltest.cc",
|
||||
"glanceables/tasks/glanceables_task_view_pixeltest.cc",
|
||||
"in_session_auth/auth_dialog_contents_view_pixeltest.cc",
|
||||
"login/ui/local_authentication_request_controller_impl_pixeltest.cc",
|
||||
"shelf/login_shelf_view_pixeltest.cc",
|
||||
"shelf/scrollable_shelf_view_pixeltest.cc",
|
||||
"shelf/shelf_layout_manager_pixeltest.cc",
|
||||
@ -4115,7 +4127,11 @@ test("ash_pixeltests") {
|
||||
"//ash/public/cpp:test_support",
|
||||
"//base/test:test_support",
|
||||
"//chromeos/ash/components/audio",
|
||||
"//chromeos/ash/components/cryptohome:cryptohome",
|
||||
"//chromeos/ash/components/dbus/audio",
|
||||
"//chromeos/ash/components/dbus/cryptohome:cryptohome",
|
||||
"//chromeos/ash/components/dbus/cryptohome:cryptohome_proto",
|
||||
"//chromeos/ash/components/dbus/userdataauth",
|
||||
"//chromeos/ash/services/assistant/public/cpp",
|
||||
"//chromeos/ash/services/bluetooth_config:test_support",
|
||||
"//chromeos/dbus/power:power",
|
||||
@ -4123,6 +4139,8 @@ test("ash_pixeltests") {
|
||||
"//chromeos/services/network_config/public/cpp:test_support",
|
||||
"//components/user_education/common",
|
||||
"//components/user_education/views",
|
||||
"//components/user_manager",
|
||||
"//components/user_manager:test_support",
|
||||
"//components/viz/test:test_support",
|
||||
"//google_apis/calendar:test_support",
|
||||
"//mojo/core/embedder:embedder",
|
||||
@ -4291,6 +4309,8 @@ static_library("test_support") {
|
||||
"lock_screen_action/test_lock_screen_action_background_controller.cc",
|
||||
"lock_screen_action/test_lock_screen_action_background_controller.h",
|
||||
"login/login_screen_test_api.cc",
|
||||
"login/mock_login_screen_client.cc",
|
||||
"login/mock_login_screen_client.h",
|
||||
"login/ui/login_test_base.cc",
|
||||
"login/ui/login_test_base.h",
|
||||
"login/ui/login_test_utils.cc",
|
||||
|
@ -4800,6 +4800,12 @@ Some features are limited to increase battery life.
|
||||
<message name="IDS_ASH_LOGIN_PIN_REQUEST_NEXT_NUMBER_PROMPT" desc="Accessible prompt read when next access code input field has been focused. Asks user to enter next piece of the access code.">
|
||||
Next number
|
||||
</message>
|
||||
<message name="IDS_ASH_LOGIN_LOCAL_AUTHENTICATION_REQUEST_TITLE" desc="Title of the local authentication input dialog that allows the user to login during the reauthenticate session.">
|
||||
Finish verifying your identity
|
||||
</message>
|
||||
<message name="IDS_ASH_LOGIN_LOCAL_AUTHENTICATION_REQUEST_DESCRIPTION" desc="Description shown on the local authentication input dialog. Explains local authentication credentials should be used to unlock the device.">
|
||||
Enter device password for <ph name="EMAIL">$1<ex>john.doe@example.com</ex></ph>
|
||||
</message>
|
||||
<message name="IDS_ASH_LOGIN_SECURITY_TOKEN_REQUEST_DIALOG_TITLE" desc="Title of the PIN dialog to unlock the device with a smart card.">
|
||||
Smart card PIN
|
||||
</message>
|
||||
|
@ -0,0 +1 @@
|
||||
5d68d9ee4e421920f0866383b06fb1fc6bbd6b3e
|
@ -0,0 +1 @@
|
||||
ca2a3ba9a0c6a226f293dbb8943414468b65f553
|
@ -9,7 +9,6 @@
|
||||
#include "ash/public/cpp/login_screen_client.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/time/time.h"
|
||||
#include "components/password_manager/core/browser/hash_password_manager.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
|
||||
namespace ash {
|
||||
|
57
ash/login/ui/local_authentication_request_controller_impl.cc
Normal file
57
ash/login/ui/local_authentication_request_controller_impl.cc
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/login/ui/local_authentication_request_controller_impl.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "ash/login/ui/local_authentication_request_view.h"
|
||||
#include "ash/login/ui/local_authentication_request_widget.h"
|
||||
#include "ash/session/session_controller_impl.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "base/check.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "chromeos/ash/components/login/auth/public/user_context.h"
|
||||
#include "components/account_id/account_id.h"
|
||||
#include "components/session_manager/session_manager_types.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
LocalAuthenticationRequestControllerImpl::
|
||||
LocalAuthenticationRequestControllerImpl() = default;
|
||||
|
||||
LocalAuthenticationRequestControllerImpl::
|
||||
~LocalAuthenticationRequestControllerImpl() = default;
|
||||
|
||||
void LocalAuthenticationRequestControllerImpl::OnClose() {}
|
||||
|
||||
bool LocalAuthenticationRequestControllerImpl::ShowWidget(
|
||||
OnLocalAuthenticationCompleted on_local_authentication_completed,
|
||||
std::unique_ptr<UserContext> user_context) {
|
||||
if (LocalAuthenticationRequestWidget::Get()) {
|
||||
LOG(ERROR) << "LocalAuthenticationRequestWidget is already shown.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const AccountId& account_id = user_context->GetAccountId();
|
||||
|
||||
const std::string& user_email = account_id.GetUserEmail();
|
||||
|
||||
const std::u16string desc = l10n_util::GetStringFUTF16(
|
||||
IDS_ASH_LOGIN_LOCAL_AUTHENTICATION_REQUEST_DESCRIPTION,
|
||||
base::UTF8ToUTF16(user_email));
|
||||
|
||||
LocalAuthenticationRequestWidget::Show(
|
||||
std::move(on_local_authentication_completed),
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_ASH_LOGIN_LOCAL_AUTHENTICATION_REQUEST_TITLE),
|
||||
desc, this, std::move(user_context));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ash
|
47
ash/login/ui/local_authentication_request_controller_impl.h
Normal file
47
ash/login/ui/local_authentication_request_controller_impl.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_LOGIN_UI_LOCAL_AUTHENTICATION_REQUEST_CONTROLLER_IMPL_H_
|
||||
#define ASH_LOGIN_UI_LOCAL_AUTHENTICATION_REQUEST_CONTROLLER_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ash/login/ui/local_authentication_request_view.h"
|
||||
#include "ash/login/ui/local_authentication_request_widget.h"
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
class UserContext;
|
||||
|
||||
// Implementation of LocalAuthenticationRequestController. It serves to finalize
|
||||
// the re-auth session with local authentication.
|
||||
class ASH_EXPORT LocalAuthenticationRequestControllerImpl
|
||||
: public LocalAuthenticationRequestController,
|
||||
public LocalAuthenticationRequestView::Delegate {
|
||||
public:
|
||||
LocalAuthenticationRequestControllerImpl();
|
||||
LocalAuthenticationRequestControllerImpl(
|
||||
const LocalAuthenticationRequestControllerImpl&) = delete;
|
||||
LocalAuthenticationRequestControllerImpl& operator=(
|
||||
const LocalAuthenticationRequestControllerImpl&) = delete;
|
||||
~LocalAuthenticationRequestControllerImpl() override;
|
||||
|
||||
// LocalAuthenticationRequestView::Delegate:
|
||||
void OnClose() override;
|
||||
|
||||
// LocalAuthenticationRequestController:
|
||||
bool ShowWidget(
|
||||
OnLocalAuthenticationCompleted on_local_authentication_completed,
|
||||
std::unique_ptr<UserContext> user_context) override;
|
||||
|
||||
private:
|
||||
base::WeakPtrFactory<LocalAuthenticationRequestControllerImpl> weak_factory_{
|
||||
this};
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_LOGIN_UI_LOCAL_AUTHENTICATION_REQUEST_CONTROLLER_IMPL_H_
|
@ -0,0 +1,253 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/login/ui/local_authentication_request_controller_impl.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "ash/login/ui/local_authentication_request_view.h"
|
||||
#include "ash/login/ui/local_authentication_request_widget.h"
|
||||
#include "ash/login/ui/login_button.h"
|
||||
#include "ash/login/ui/login_test_base.h"
|
||||
#include "ash/login/ui/views_utils.h"
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/style/dark_light_mode_controller_impl.h"
|
||||
#include "ash/test/pixel/ash_pixel_differ.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "chromeos/ash/components/cryptohome/system_salt_getter.h"
|
||||
#include "chromeos/ash/components/dbus/cryptohome/account_identifier_operators.h"
|
||||
#include "chromeos/ash/components/dbus/cryptohome/key.pb.h"
|
||||
#include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h"
|
||||
#include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h"
|
||||
#include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
|
||||
#include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h"
|
||||
#include "chromeos/ash/components/login/auth/public/key.h"
|
||||
#include "chromeos/ash/components/login/auth/public/user_context.h"
|
||||
#include "components/session_manager/session_manager_types.h"
|
||||
#include "components/user_manager/fake_user_manager.h"
|
||||
#include "components/user_manager/scoped_user_manager.h"
|
||||
#include "ui/chromeos/resources/grit/ui_chromeos_resources.h"
|
||||
#include "ui/events/base_event_utils.h"
|
||||
#include "ui/views/controls/button/label_button.h"
|
||||
#include "ui/views/controls/textfield/textfield_test_api.h"
|
||||
#include "ui/views/test/button_test_api.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::cryptohome::KeyLabel;
|
||||
|
||||
const char kTestAccount[] = "user@test.com";
|
||||
const char kExpectedPassword[] = "qwerty";
|
||||
|
||||
class LocalAuthenticationRequestControllerImplPixelTest : public AshTestBase {
|
||||
public:
|
||||
LocalAuthenticationRequestControllerImplPixelTest(
|
||||
const LocalAuthenticationRequestControllerImplPixelTest&) = delete;
|
||||
LocalAuthenticationRequestControllerImplPixelTest& operator=(
|
||||
const LocalAuthenticationRequestControllerImplPixelTest&) = delete;
|
||||
|
||||
protected:
|
||||
LocalAuthenticationRequestControllerImplPixelTest() = default;
|
||||
~LocalAuthenticationRequestControllerImplPixelTest() override = default;
|
||||
|
||||
absl::optional<pixel_test::InitParams> CreatePixelTestInitParams()
|
||||
const override {
|
||||
return pixel_test::InitParams();
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
AshTestBase::SetUp();
|
||||
UpdateDisplay("600x800");
|
||||
auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get();
|
||||
dark_light_mode_controller->SetAutoScheduleEnabled(false);
|
||||
// Test Base should setup the dark mode.
|
||||
EXPECT_EQ(dark_light_mode_controller->IsDarkModeEnabled(), true);
|
||||
|
||||
CryptohomeMiscClient::InitializeFake();
|
||||
FakeCryptohomeMiscClient::Get()->SetServiceIsAvailable(true);
|
||||
FakeCryptohomeMiscClient::Get()->set_system_salt(
|
||||
FakeCryptohomeMiscClient::GetStubSystemSalt());
|
||||
UserDataAuthClient::InitializeFake();
|
||||
SystemSaltGetter::Initialize();
|
||||
|
||||
test_account_id_ = AccountId::FromUserEmail(kTestAccount);
|
||||
|
||||
SetExpectedCredentialsWithDbusClient(test_account_id_, kExpectedPassword);
|
||||
auto fake_user_manager = std::make_unique<user_manager::FakeUserManager>();
|
||||
fake_user_manager->AddUser(test_account_id_);
|
||||
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
|
||||
std::move(fake_user_manager));
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// If the test did not explicitly dismissed the widget, destroy it now.
|
||||
LocalAuthenticationRequestWidget* local_authentication_request_widget =
|
||||
LocalAuthenticationRequestWidget::Get();
|
||||
if (local_authentication_request_widget) {
|
||||
local_authentication_request_widget->Close(
|
||||
false /* validation success */);
|
||||
}
|
||||
scoped_user_manager_.reset();
|
||||
SystemSaltGetter::Shutdown();
|
||||
UserDataAuthClient::Shutdown();
|
||||
CryptohomeMiscClient::Shutdown();
|
||||
AshTestBase::TearDown();
|
||||
}
|
||||
|
||||
void SetExpectedCredentialsWithDbusClient(const AccountId& account_id,
|
||||
const std::string& password) {
|
||||
auto* test_api = FakeUserDataAuthClient::TestApi::Get();
|
||||
test_api->set_enable_auth_check(true);
|
||||
|
||||
const auto cryptohome_id =
|
||||
cryptohome::CreateAccountIdentifierFromAccountId(account_id);
|
||||
Key key{password};
|
||||
key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF,
|
||||
SystemSaltGetter::ConvertRawSaltToHexString(
|
||||
FakeCryptohomeMiscClient::GetStubSystemSalt()));
|
||||
|
||||
cryptohome::Key cryptohome_key;
|
||||
cryptohome_key.mutable_data()->set_label(kCryptohomeLocalPasswordKeyLabel);
|
||||
cryptohome_key.set_secret(key.GetSecret());
|
||||
|
||||
test_api->AddExistingUser(cryptohome_id);
|
||||
test_api->AddKey(cryptohome_id, cryptohome_key);
|
||||
session_ids_ = test_api->AddSession(cryptohome_id, false);
|
||||
}
|
||||
|
||||
// Simulates mouse press event on a |button|.
|
||||
void SimulateButtonPress(views::Button* button) {
|
||||
ui::MouseEvent event(/*type=*/ui::ET_MOUSE_PRESSED,
|
||||
/*location=*/gfx::Point(),
|
||||
/*root_location=*/gfx::Point(),
|
||||
/*time_stamp=*/ui::EventTimeForNow(),
|
||||
/*flags=*/0,
|
||||
/*changed_button_flags=*/0);
|
||||
views::test::ButtonTestApi(button).NotifyClick(event);
|
||||
}
|
||||
|
||||
// Called when LocalAuthenticationRequestView finished processing.
|
||||
void OnFinished(bool access_granted) {
|
||||
access_granted ? ++successful_validation_ : ++close_action_;
|
||||
}
|
||||
|
||||
// Starts local authentication validation.
|
||||
void StartLocalAuthenticationRequest() {
|
||||
// Configure the user context.
|
||||
std::unique_ptr<UserContext> user_context = std::make_unique<UserContext>(
|
||||
user_manager::USER_TYPE_REGULAR, test_account_id_);
|
||||
|
||||
user_context->SetAuthSessionIds(session_ids_.first, session_ids_.second);
|
||||
|
||||
// Add local password as an auth factor.
|
||||
std::vector<cryptohome::AuthFactor> factors;
|
||||
cryptohome::AuthFactorRef ref(cryptohome::AuthFactorType::kPassword,
|
||||
KeyLabel(kCryptohomeLocalPasswordKeyLabel));
|
||||
|
||||
cryptohome::AuthFactor factor(ref, cryptohome::AuthFactorCommonMetadata());
|
||||
factors.push_back(factor);
|
||||
SessionAuthFactors data(factors);
|
||||
user_context->SetSessionAuthFactors(data);
|
||||
|
||||
user_context->SetKey(Key(kExpectedPassword));
|
||||
user_context->SetPasswordKey(Key(kExpectedPassword));
|
||||
|
||||
Shell::Get()->local_authentication_request_controller()->ShowWidget(
|
||||
base::BindOnce(
|
||||
&LocalAuthenticationRequestControllerImplPixelTest::OnFinished,
|
||||
base::Unretained(this)),
|
||||
std::move(user_context));
|
||||
}
|
||||
|
||||
// Simulates entering a password. |success| determines whether the code will
|
||||
// be accepted.
|
||||
void SimulateValidation(bool success) {
|
||||
// Submit password.
|
||||
for (char c : kExpectedPassword) {
|
||||
ui::KeyboardCode code =
|
||||
static_cast<ui::KeyboardCode>(ui::KeyboardCode::VKEY_A + (c - 'a'));
|
||||
PressAndReleaseKey(code);
|
||||
}
|
||||
if (!success) {
|
||||
PressAndReleaseKey(ui::KeyboardCode::VKEY_A);
|
||||
}
|
||||
PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
|
||||
base::RunLoop().RunUntilIdle();
|
||||
}
|
||||
|
||||
// Number of times the view was dismissed with close button.
|
||||
int close_action_ = 0;
|
||||
|
||||
// Number of times the view was dismissed after successful validation.
|
||||
int successful_validation_ = 0;
|
||||
|
||||
// Test account id.
|
||||
AccountId test_account_id_;
|
||||
|
||||
// Auth session ids.
|
||||
std::pair<std::string, std::string> session_ids_;
|
||||
|
||||
// Container object for the fake user manager for tests.
|
||||
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
|
||||
};
|
||||
|
||||
// Tests local authentication dialog showing/hiding and focus behavior for
|
||||
// password field
|
||||
TEST_F(LocalAuthenticationRequestControllerImplPixelTest, FailedValidation) {
|
||||
EXPECT_FALSE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
StartLocalAuthenticationRequest();
|
||||
|
||||
LocalAuthenticationRequestView* view =
|
||||
LocalAuthenticationRequestWidget::GetViewForTesting();
|
||||
ASSERT_NE(view, nullptr);
|
||||
LocalAuthenticationRequestView::TestApi view_test_api(view);
|
||||
|
||||
// Hide the textfield cursor to avoid the flakiness due to the blinking.
|
||||
views::TextfieldTestApi(view_test_api.GetInputTextfield())
|
||||
.SetCursorLayerOpacity(0.f);
|
||||
|
||||
ASSERT_TRUE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
// Verify the UI.
|
||||
EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
|
||||
"Ready", /*revision_number=*/0, view));
|
||||
|
||||
SimulateValidation(false);
|
||||
// Verify the UI.
|
||||
EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
|
||||
"Fail", /*revision_number=*/0, view));
|
||||
}
|
||||
|
||||
// Tests local authentication dialog theme change
|
||||
TEST_F(LocalAuthenticationRequestControllerImplPixelTest, ThemeChange) {
|
||||
EXPECT_FALSE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
StartLocalAuthenticationRequest();
|
||||
LocalAuthenticationRequestView* view =
|
||||
LocalAuthenticationRequestWidget::GetViewForTesting();
|
||||
ASSERT_NE(view, nullptr);
|
||||
LocalAuthenticationRequestView::TestApi view_test_api(view);
|
||||
|
||||
// Hide the textfield cursor to avoid the flakiness due to the blinking.
|
||||
views::TextfieldTestApi(view_test_api.GetInputTextfield())
|
||||
.SetCursorLayerOpacity(0.f);
|
||||
|
||||
ASSERT_TRUE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
DarkLightModeControllerImpl::Get()->SetDarkModeEnabledForTest(false);
|
||||
// Verify the UI.
|
||||
EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
|
||||
"Light", /*revision_number=*/0, view));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ash
|
@ -0,0 +1,269 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/login/ui/local_authentication_request_controller_impl.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "ash/login/ui/local_authentication_request_view.h"
|
||||
#include "ash/login/ui/local_authentication_request_widget.h"
|
||||
#include "ash/login/ui/login_button.h"
|
||||
#include "ash/login/ui/login_test_base.h"
|
||||
#include "ash/login/ui/views_utils.h"
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
#include "ash/shell.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "chromeos/ash/components/cryptohome/system_salt_getter.h"
|
||||
#include "chromeos/ash/components/dbus/cryptohome/account_identifier_operators.h"
|
||||
#include "chromeos/ash/components/dbus/cryptohome/key.pb.h"
|
||||
#include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h"
|
||||
#include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h"
|
||||
#include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
|
||||
#include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h"
|
||||
#include "chromeos/ash/components/login/auth/public/key.h"
|
||||
#include "chromeos/ash/components/login/auth/public/user_context.h"
|
||||
#include "components/session_manager/session_manager_types.h"
|
||||
#include "components/user_manager/fake_user_manager.h"
|
||||
#include "components/user_manager/scoped_user_manager.h"
|
||||
#include "ui/events/base_event_utils.h"
|
||||
#include "ui/views/controls/button/label_button.h"
|
||||
#include "ui/views/test/button_test_api.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::cryptohome::KeyLabel;
|
||||
|
||||
const char kTestAccount[] = "user@test.com";
|
||||
const char kExpectedPassword[] = "qwerty";
|
||||
|
||||
class LocalAuthenticationRequestControllerImplTest : public LoginTestBase {
|
||||
public:
|
||||
LocalAuthenticationRequestControllerImplTest(
|
||||
const LocalAuthenticationRequestControllerImplTest&) = delete;
|
||||
LocalAuthenticationRequestControllerImplTest& operator=(
|
||||
const LocalAuthenticationRequestControllerImplTest&) = delete;
|
||||
|
||||
protected:
|
||||
LocalAuthenticationRequestControllerImplTest() = default;
|
||||
~LocalAuthenticationRequestControllerImplTest() override = default;
|
||||
|
||||
// LoginScreenTest:
|
||||
void SetUp() override {
|
||||
LoginTestBase::SetUp();
|
||||
|
||||
CryptohomeMiscClient::InitializeFake();
|
||||
FakeCryptohomeMiscClient::Get()->SetServiceIsAvailable(true);
|
||||
FakeCryptohomeMiscClient::Get()->set_system_salt(
|
||||
FakeCryptohomeMiscClient::GetStubSystemSalt());
|
||||
UserDataAuthClient::InitializeFake();
|
||||
SystemSaltGetter::Initialize();
|
||||
test_account_id_ = AccountId::FromUserEmail(kTestAccount);
|
||||
|
||||
SetExpectedCredentialsWithDbusClient(test_account_id_, kExpectedPassword);
|
||||
auto fake_user_manager = std::make_unique<user_manager::FakeUserManager>();
|
||||
fake_user_manager->AddUser(test_account_id_);
|
||||
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
|
||||
std::move(fake_user_manager));
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
LoginTestBase::TearDown();
|
||||
|
||||
// If the test did not explicitly dismissed the widget, destroy it now.
|
||||
LocalAuthenticationRequestWidget* local_authentication_request_widget =
|
||||
LocalAuthenticationRequestWidget::Get();
|
||||
if (local_authentication_request_widget) {
|
||||
local_authentication_request_widget->Close(
|
||||
false /* validation success */);
|
||||
}
|
||||
scoped_user_manager_.reset();
|
||||
SystemSaltGetter::Shutdown();
|
||||
UserDataAuthClient::Shutdown();
|
||||
CryptohomeMiscClient::Shutdown();
|
||||
}
|
||||
|
||||
void SetExpectedCredentialsWithDbusClient(const AccountId& account_id,
|
||||
const std::string& password) {
|
||||
auto* test_api = FakeUserDataAuthClient::TestApi::Get();
|
||||
test_api->set_enable_auth_check(true);
|
||||
|
||||
const auto cryptohome_id =
|
||||
cryptohome::CreateAccountIdentifierFromAccountId(account_id);
|
||||
Key key{password};
|
||||
key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF,
|
||||
SystemSaltGetter::ConvertRawSaltToHexString(
|
||||
FakeCryptohomeMiscClient::GetStubSystemSalt()));
|
||||
|
||||
cryptohome::Key cryptohome_key;
|
||||
cryptohome_key.mutable_data()->set_label(kCryptohomeLocalPasswordKeyLabel);
|
||||
cryptohome_key.set_secret(key.GetSecret());
|
||||
|
||||
test_api->AddExistingUser(cryptohome_id);
|
||||
test_api->AddKey(cryptohome_id, cryptohome_key);
|
||||
session_ids_ = test_api->AddSession(cryptohome_id, false);
|
||||
}
|
||||
|
||||
// Simulates mouse press event on a |button|.
|
||||
void SimulateButtonPress(views::Button* button) {
|
||||
ui::MouseEvent event(/*type=*/ui::ET_MOUSE_PRESSED,
|
||||
/*location=*/gfx::Point(),
|
||||
/*root_location=*/gfx::Point(),
|
||||
/*time_stamp=*/ui::EventTimeForNow(),
|
||||
/*flags=*/0,
|
||||
/*changed_button_flags=*/0);
|
||||
views::test::ButtonTestApi(button).NotifyClick(event);
|
||||
}
|
||||
|
||||
// Called when LocalAuthenticationRequestView finished processing.
|
||||
void OnFinished(bool access_granted) {
|
||||
access_granted ? ++successful_validation_ : ++close_action_;
|
||||
}
|
||||
|
||||
// Starts local authentication validation.
|
||||
void StartLocalAuthenticationRequest() {
|
||||
// Configure the user context.
|
||||
std::unique_ptr<UserContext> user_context = std::make_unique<UserContext>(
|
||||
user_manager::USER_TYPE_REGULAR, test_account_id_);
|
||||
|
||||
user_context->SetAuthSessionIds(session_ids_.first, session_ids_.second);
|
||||
|
||||
// Add local password as an auth factor.
|
||||
std::vector<cryptohome::AuthFactor> factors;
|
||||
cryptohome::AuthFactorRef ref(cryptohome::AuthFactorType::kPassword,
|
||||
KeyLabel(kCryptohomeLocalPasswordKeyLabel));
|
||||
|
||||
cryptohome::AuthFactor factor(ref, cryptohome::AuthFactorCommonMetadata());
|
||||
factors.push_back(factor);
|
||||
SessionAuthFactors data(factors);
|
||||
user_context->SetSessionAuthFactors(data);
|
||||
|
||||
user_context->SetKey(Key(kExpectedPassword));
|
||||
user_context->SetPasswordKey(Key(kExpectedPassword));
|
||||
|
||||
Shell::Get()->local_authentication_request_controller()->ShowWidget(
|
||||
base::BindOnce(
|
||||
&LocalAuthenticationRequestControllerImplTest::OnFinished,
|
||||
base::Unretained(this)),
|
||||
std::move(user_context));
|
||||
}
|
||||
|
||||
// Simulates entering a password. |success| determines whether the code will
|
||||
// be accepted.
|
||||
void SimulateValidation(bool success) {
|
||||
// Submit password.
|
||||
for (char c : kExpectedPassword) {
|
||||
ui::KeyboardCode code =
|
||||
static_cast<ui::KeyboardCode>(ui::KeyboardCode::VKEY_A + (c - 'a'));
|
||||
PressAndReleaseKey(code);
|
||||
}
|
||||
if (!success) {
|
||||
PressAndReleaseKey(ui::KeyboardCode::VKEY_A);
|
||||
}
|
||||
PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
|
||||
base::RunLoop().RunUntilIdle();
|
||||
}
|
||||
|
||||
// Number of times the view was dismissed with close button.
|
||||
int close_action_ = 0;
|
||||
|
||||
// Number of times the view was dismissed after successful validation.
|
||||
int successful_validation_ = 0;
|
||||
|
||||
// Test account id.
|
||||
AccountId test_account_id_;
|
||||
|
||||
// Auth session ids.
|
||||
std::pair<std::string, std::string> session_ids_;
|
||||
|
||||
// Container object for the fake user manager for tests.
|
||||
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
|
||||
};
|
||||
|
||||
// Tests local authentication dialog showing/hiding and focus behavior for
|
||||
// password field
|
||||
TEST_F(LocalAuthenticationRequestControllerImplTest,
|
||||
LocalAuthenticationRequestDialogFocus) {
|
||||
EXPECT_FALSE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
StartLocalAuthenticationRequest();
|
||||
|
||||
LocalAuthenticationRequestView* view =
|
||||
LocalAuthenticationRequestWidget::GetViewForTesting();
|
||||
ASSERT_NE(view, nullptr);
|
||||
|
||||
ASSERT_TRUE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
LoginPasswordView* login_password_view =
|
||||
LocalAuthenticationRequestView::TestApi(view).login_password_view();
|
||||
EXPECT_TRUE(login_views_utils::HasFocusInAnyChildView(login_password_view));
|
||||
|
||||
LocalAuthenticationRequestWidget::Get()->Close(
|
||||
false /* validation success */);
|
||||
|
||||
EXPECT_FALSE(LocalAuthenticationRequestWidget::Get());
|
||||
}
|
||||
|
||||
// Tests successful authentication flow.
|
||||
TEST_F(LocalAuthenticationRequestControllerImplTest,
|
||||
LocalAuthenticationRequestSuccessfulValidation) {
|
||||
EXPECT_FALSE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
StartLocalAuthenticationRequest();
|
||||
SimulateValidation(true);
|
||||
EXPECT_EQ(1, successful_validation_);
|
||||
EXPECT_EQ(0, close_action_);
|
||||
|
||||
// Widget closed after successful validation
|
||||
EXPECT_FALSE(LocalAuthenticationRequestWidget::Get());
|
||||
}
|
||||
|
||||
// Tests failed authentication flow.
|
||||
TEST_F(LocalAuthenticationRequestControllerImplTest,
|
||||
LocalAuthenticationRequestFailedValidation) {
|
||||
EXPECT_FALSE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
StartLocalAuthenticationRequest();
|
||||
SimulateValidation(false);
|
||||
|
||||
EXPECT_EQ(0, successful_validation_);
|
||||
EXPECT_EQ(0, close_action_);
|
||||
|
||||
// Widget still exists despite the auth error.
|
||||
EXPECT_TRUE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
LocalAuthenticationRequestView* view =
|
||||
LocalAuthenticationRequestWidget::GetViewForTesting();
|
||||
ASSERT_NE(view, nullptr);
|
||||
LocalAuthenticationRequestView::TestApi view_test_api(view);
|
||||
EXPECT_EQ(view_test_api.state(), LocalAuthenticationRequestViewState::kError);
|
||||
}
|
||||
|
||||
// Tests close button successfully close the widget.
|
||||
TEST_F(LocalAuthenticationRequestControllerImplTest,
|
||||
LocalAuthenticationRequestClose) {
|
||||
EXPECT_FALSE(LocalAuthenticationRequestWidget::Get());
|
||||
|
||||
StartLocalAuthenticationRequest();
|
||||
LocalAuthenticationRequestView* view =
|
||||
LocalAuthenticationRequestWidget::GetViewForTesting();
|
||||
ASSERT_NE(view, nullptr);
|
||||
|
||||
views::Button* close_button =
|
||||
LocalAuthenticationRequestView::TestApi(view).close_button();
|
||||
SimulateButtonPress(close_button);
|
||||
|
||||
EXPECT_EQ(0, successful_validation_);
|
||||
EXPECT_EQ(1, close_action_);
|
||||
|
||||
EXPECT_FALSE(LocalAuthenticationRequestWidget::Get());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ash
|
409
ash/login/ui/local_authentication_request_view.cc
Normal file
409
ash/login/ui/local_authentication_request_view.cc
Normal file
@ -0,0 +1,409 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/login/ui/local_authentication_request_view.h"
|
||||
|
||||
#include "ash/accessibility/accessibility_controller_impl.h"
|
||||
#include "ash/keyboard/ui/keyboard_ui_controller.h"
|
||||
#include "ash/login/ui/arrow_button_view.h"
|
||||
#include "ash/login/ui/local_authentication_request_widget.h"
|
||||
#include "ash/login/ui/non_accessible_view.h"
|
||||
#include "ash/public/cpp/accessibility_controller.h"
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
#include "ash/public/cpp/login/login_utils.h"
|
||||
#include "ash/public/cpp/session/user_info.h"
|
||||
#include "ash/public/cpp/shelf_config.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/style/ash_color_id.h"
|
||||
#include "ash/style/ash_color_provider.h"
|
||||
#include "ash/style/icon_button.h"
|
||||
#include "ash/style/system_shadow.h"
|
||||
#include "ash/wallpaper/wallpaper_controller_impl.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "chromeos/ash/components/cryptohome/auth_factor.h"
|
||||
#include "chromeos/ash/components/cryptohome/common_types.h"
|
||||
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
|
||||
#include "chromeos/ash/components/login/auth/public/session_auth_factors.h"
|
||||
#include "chromeos/ash/components/login/auth/public/user_context.h"
|
||||
#include "chromeos/constants/chromeos_features.h"
|
||||
#include "components/account_id/account_id.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/models/image_model.h"
|
||||
#include "ui/chromeos/resources/grit/ui_chromeos_resources.h"
|
||||
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
#include "ui/views/highlight_border.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
#include "ui/views/layout/fill_layout.h"
|
||||
#include "ui/views/vector_icons.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(b/304754895): move the pin request view shared constants to
|
||||
// ash/login/ui/login_constants.h
|
||||
constexpr int kLocalAuthenticationRequestViewVerticalInsetDp = 8;
|
||||
// Minimum inset (= back button inset).
|
||||
constexpr int kLocalAuthenticationRequestViewHorizontalInsetDp = 8;
|
||||
constexpr int kLocalAuthenticationRequestViewRoundedCornerRadiusDp = 8;
|
||||
constexpr int kLocalAuthenticationRequestViewWidthDp = 340;
|
||||
constexpr int kLocalAuthenticationRequestViewHeightDp = 300;
|
||||
|
||||
constexpr int kIconToTitleDistanceDp = 24;
|
||||
constexpr int kTitleToDescriptionDistanceDp = 8;
|
||||
constexpr int kDescriptionToAccessCodeDistanceDp = 32;
|
||||
constexpr int kSubmitButtonBottomMarginDp = 28;
|
||||
|
||||
constexpr int kTitleFontSizeDeltaDp = 4;
|
||||
constexpr int kTitleLineWidthDp = 268;
|
||||
constexpr int kTitleLineHeightDp = 24;
|
||||
constexpr int kTitleMaxLines = 4;
|
||||
constexpr int kDescriptionFontSizeDeltaDp = 0;
|
||||
constexpr int kDescriptionLineWidthDp = 268;
|
||||
constexpr int kDescriptionTextLineHeightDp = 18;
|
||||
constexpr int kDescriptionMaxLines = 4;
|
||||
|
||||
constexpr int kAvatarSizeDp = 36;
|
||||
|
||||
constexpr int kCrossSizeDp = 20;
|
||||
constexpr int kBackButtonSizeDp = 36;
|
||||
constexpr int kLockIconSizeDp = 24;
|
||||
constexpr int kBackButtonLockIconVerticalOverlapDp = 8;
|
||||
constexpr int kHeaderHeightDp =
|
||||
kBackButtonSizeDp + kLockIconSizeDp - kBackButtonLockIconVerticalOverlapDp;
|
||||
|
||||
} // namespace
|
||||
|
||||
LocalAuthenticationRequestView::TestApi::TestApi(
|
||||
LocalAuthenticationRequestView* view)
|
||||
: view_(view) {
|
||||
CHECK(view_);
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestView::TestApi::~TestApi() {
|
||||
view_ = nullptr;
|
||||
}
|
||||
|
||||
LoginButton* LocalAuthenticationRequestView::TestApi::close_button() {
|
||||
return view_->close_button_;
|
||||
}
|
||||
|
||||
views::Label* LocalAuthenticationRequestView::TestApi::title_label() {
|
||||
return view_->title_label_;
|
||||
}
|
||||
|
||||
views::Label* LocalAuthenticationRequestView::TestApi::description_label() {
|
||||
return view_->description_label_;
|
||||
}
|
||||
|
||||
LoginPasswordView*
|
||||
LocalAuthenticationRequestView::TestApi::login_password_view() {
|
||||
return view_->login_password_view_;
|
||||
}
|
||||
|
||||
views::Textfield* LocalAuthenticationRequestView::TestApi::GetInputTextfield()
|
||||
const {
|
||||
return LoginPasswordView::TestApi(view_->login_password_view_).textfield();
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestViewState
|
||||
LocalAuthenticationRequestView::TestApi::state() const {
|
||||
return view_->state_;
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestView::LocalAuthenticationRequestView(
|
||||
OnLocalAuthenticationCompleted on_local_authentication_completed,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description,
|
||||
Delegate* delegate,
|
||||
std::unique_ptr<UserContext> user_context)
|
||||
: on_local_authentication_completed_(
|
||||
std::move(on_local_authentication_completed)),
|
||||
delegate_(delegate),
|
||||
default_title_(title),
|
||||
default_description_(description),
|
||||
auth_performer_(UserDataAuthClient::Get()),
|
||||
user_context_(std::move(user_context)) {
|
||||
// MODAL_TYPE_SYSTEM is used to get a semi-transparent background behind the
|
||||
// local authentication request view, when it is used directly on a widget.
|
||||
// The overlay consumes all the inputs from the user, so that they can only
|
||||
// interact with the local authentication request view while it is visible.
|
||||
SetModalType(ui::MODAL_TYPE_SYSTEM);
|
||||
const bool is_jelly = chromeos::features::IsJellyEnabled();
|
||||
|
||||
// Main view contains all other views aligned vertically and centered.
|
||||
auto layout = std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical,
|
||||
gfx::Insets::VH(kLocalAuthenticationRequestViewVerticalInsetDp,
|
||||
kLocalAuthenticationRequestViewHorizontalInsetDp),
|
||||
0);
|
||||
layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
|
||||
layout->set_cross_axis_alignment(
|
||||
views::BoxLayout::CrossAxisAlignment::kCenter);
|
||||
SetLayoutManager(std::move(layout));
|
||||
|
||||
// Set Backgground color and shape.
|
||||
SetPaintToLayer();
|
||||
layer()->SetBackgroundBlur(ShelfConfig::Get()->shelf_blur_radius());
|
||||
ui::ColorId background_color_id =
|
||||
is_jelly ? cros_tokens::kCrosSysSystemBaseElevated
|
||||
: static_cast<ui::ColorId>(kColorAshShieldAndBase80);
|
||||
SetBackground(views::CreateThemedRoundedRectBackground(
|
||||
background_color_id,
|
||||
kLocalAuthenticationRequestViewRoundedCornerRadiusDp));
|
||||
|
||||
// Set Border and shadow.
|
||||
SetBorder(std::make_unique<views::HighlightBorder>(
|
||||
kLocalAuthenticationRequestViewRoundedCornerRadiusDp,
|
||||
views::HighlightBorder::Type::kHighlightBorder1));
|
||||
shadow_ = SystemShadow::CreateShadowOnNinePatchLayerForView(
|
||||
this, SystemShadow::Type::kElevation12);
|
||||
shadow_->SetRoundedCornerRadius(
|
||||
kLocalAuthenticationRequestViewRoundedCornerRadiusDp);
|
||||
|
||||
// Header view which contains the back button that is aligned top right and
|
||||
// the lock icon which is in the bottom center.
|
||||
auto header_layout = std::make_unique<views::FillLayout>();
|
||||
auto* header = new NonAccessibleView();
|
||||
header->SetLayoutManager(std::move(header_layout));
|
||||
AddChildView(header);
|
||||
auto* header_spacer = new NonAccessibleView();
|
||||
header_spacer->SetPreferredSize(gfx::Size(0, kHeaderHeightDp));
|
||||
header->AddChildView(header_spacer);
|
||||
|
||||
// Main view user avatar.
|
||||
auto* icon_view = new NonAccessibleView();
|
||||
icon_view->SetPreferredSize(gfx::Size(0, kHeaderHeightDp));
|
||||
auto icon_layout = std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0);
|
||||
icon_layout->set_main_axis_alignment(
|
||||
views::BoxLayout::MainAxisAlignment::kEnd);
|
||||
icon_layout->set_cross_axis_alignment(
|
||||
views::BoxLayout::CrossAxisAlignment::kCenter);
|
||||
icon_view->SetLayoutManager(std::move(icon_layout));
|
||||
header->AddChildView(icon_view);
|
||||
|
||||
auto* avatar_view =
|
||||
icon_view->AddChildView(std::make_unique<AnimatedRoundedImageView>(
|
||||
gfx::Size(kAvatarSizeDp, kAvatarSizeDp),
|
||||
kAvatarSizeDp / 2 /*corner_radius*/));
|
||||
|
||||
const UserAvatar avatar =
|
||||
BuildAshUserAvatarForAccountId(user_context_->GetAccountId());
|
||||
|
||||
avatar_view->SetImage(avatar.image);
|
||||
|
||||
// Close button. Note that it should be the last view added to |header| in
|
||||
// order to be clickable.
|
||||
auto* close_button_view = new NonAccessibleView();
|
||||
close_button_view->SetPreferredSize(
|
||||
gfx::Size(kLocalAuthenticationRequestViewWidthDp -
|
||||
2 * kLocalAuthenticationRequestViewHorizontalInsetDp,
|
||||
kHeaderHeightDp));
|
||||
auto close_button_layout = std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0);
|
||||
close_button_layout->set_main_axis_alignment(
|
||||
views::BoxLayout::MainAxisAlignment::kEnd);
|
||||
close_button_layout->set_cross_axis_alignment(
|
||||
views::BoxLayout::CrossAxisAlignment::kStart);
|
||||
close_button_view->SetLayoutManager(std::move(close_button_layout));
|
||||
header->AddChildView(close_button_view);
|
||||
|
||||
close_button_ = new LoginButton(base::BindRepeating(
|
||||
&LocalAuthenticationRequestView::OnClose, base::Unretained(this)));
|
||||
close_button_->SetPreferredSize(
|
||||
gfx::Size(kBackButtonSizeDp, kBackButtonSizeDp));
|
||||
const ui::ColorId icon_color_id =
|
||||
is_jelly ? static_cast<ui::ColorId>(cros_tokens::kCrosSysOnSurface)
|
||||
: kColorAshIconColorPrimary;
|
||||
close_button_->SetImageModel(
|
||||
views::Button::STATE_NORMAL,
|
||||
ui::ImageModel::FromVectorIcon(views::kIcCloseIcon, icon_color_id,
|
||||
kCrossSizeDp));
|
||||
close_button_->SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
|
||||
close_button_->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
|
||||
close_button_->SetAccessibleName(
|
||||
l10n_util::GetStringUTF16(IDS_ASH_LOGIN_BACK_BUTTON_ACCESSIBLE_NAME));
|
||||
close_button_->SetFocusBehavior(FocusBehavior::ALWAYS);
|
||||
close_button_view->AddChildView(close_button_.get());
|
||||
|
||||
auto add_spacer = [&](int height) {
|
||||
auto* spacer = new NonAccessibleView();
|
||||
spacer->SetPreferredSize(gfx::Size(0, height));
|
||||
AddChildView(spacer);
|
||||
};
|
||||
|
||||
add_spacer(kIconToTitleDistanceDp);
|
||||
|
||||
auto decorate_label = [](views::Label* label) {
|
||||
label->SetSubpixelRenderingEnabled(false);
|
||||
label->SetAutoColorReadabilityEnabled(false);
|
||||
|
||||
const ui::ColorId text_color_id =
|
||||
chromeos::features::IsJellyEnabled()
|
||||
? static_cast<ui::ColorId>(cros_tokens::kCrosSysOnSurface)
|
||||
: kColorAshTextColorPrimary;
|
||||
label->SetEnabledColorId(text_color_id);
|
||||
label->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
|
||||
};
|
||||
|
||||
// Main view title.
|
||||
title_label_ = new views::Label(default_title_, views::style::CONTEXT_LABEL,
|
||||
views::style::STYLE_PRIMARY);
|
||||
title_label_->SetMultiLine(true);
|
||||
title_label_->SetMaxLines(kTitleMaxLines);
|
||||
title_label_->SizeToFit(kTitleLineWidthDp);
|
||||
title_label_->SetLineHeight(kTitleLineHeightDp);
|
||||
title_label_->SetFontList(gfx::FontList().Derive(
|
||||
kTitleFontSizeDeltaDp, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
|
||||
decorate_label(title_label_);
|
||||
AddChildView(title_label_.get());
|
||||
|
||||
add_spacer(kTitleToDescriptionDistanceDp);
|
||||
|
||||
// Main view description.
|
||||
description_label_ =
|
||||
new views::Label(default_description_, views::style::CONTEXT_LABEL,
|
||||
views::style::STYLE_PRIMARY);
|
||||
description_label_->SetMultiLine(true);
|
||||
description_label_->SetMaxLines(kDescriptionMaxLines);
|
||||
description_label_->SizeToFit(kDescriptionLineWidthDp);
|
||||
description_label_->SetLineHeight(kDescriptionTextLineHeightDp);
|
||||
description_label_->SetFontList(
|
||||
gfx::FontList().Derive(kDescriptionFontSizeDeltaDp, gfx::Font::NORMAL,
|
||||
gfx::Font::Weight::NORMAL));
|
||||
decorate_label(description_label_);
|
||||
AddChildView(description_label_.get());
|
||||
|
||||
add_spacer(kDescriptionToAccessCodeDistanceDp);
|
||||
|
||||
login_password_view_ = AddChildView(std::make_unique<LoginPasswordView>());
|
||||
|
||||
login_password_view_->SetPaintToLayer();
|
||||
login_password_view_->layer()->SetFillsBoundsOpaquely(false);
|
||||
login_password_view_->SetDisplayPasswordButtonVisible(true);
|
||||
login_password_view_->SetEnabledOnEmptyPassword(false);
|
||||
login_password_view_->SetFocusEnabledForTextfield(true);
|
||||
|
||||
login_password_view_->SetPlaceholderText(
|
||||
l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_PASSWORD_PLACEHOLDER));
|
||||
login_password_view_->Init(
|
||||
base::BindRepeating(&LocalAuthenticationRequestView::OnAuthSubmit,
|
||||
base::Unretained(this),
|
||||
/*authenticated_by_pin=*/false),
|
||||
base::BindRepeating(&LocalAuthenticationRequestView::OnInputTextChanged,
|
||||
base::Unretained(this)));
|
||||
|
||||
add_spacer(kSubmitButtonBottomMarginDp);
|
||||
|
||||
SetPreferredSize(GetLocalAuthenticationRequestViewSize());
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestView::~LocalAuthenticationRequestView() = default;
|
||||
|
||||
void LocalAuthenticationRequestView::RequestFocus() {
|
||||
login_password_view_->RequestFocus();
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestView::SetInputEnabled(bool input_enabled) {
|
||||
login_password_view_->SetReadOnly(!input_enabled);
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestView::ClearInput() {
|
||||
login_password_view_->Reset();
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestView::UpdateState(
|
||||
LocalAuthenticationRequestViewState state,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description) {
|
||||
state_ = state;
|
||||
ui::ColorId color_id = state == LocalAuthenticationRequestViewState::kNormal
|
||||
? cros_tokens::kCrosSysSystemBaseElevated
|
||||
: cros_tokens::kCrosSysError;
|
||||
title_label_->SetText(title);
|
||||
description_label_->SetText(description);
|
||||
description_label_->SetEnabledColorId(color_id);
|
||||
}
|
||||
|
||||
gfx::Size LocalAuthenticationRequestView::CalculatePreferredSize() const {
|
||||
return GetLocalAuthenticationRequestViewSize();
|
||||
}
|
||||
|
||||
views::View* LocalAuthenticationRequestView::GetInitiallyFocusedView() {
|
||||
return login_password_view_;
|
||||
}
|
||||
|
||||
std::u16string LocalAuthenticationRequestView::GetAccessibleWindowTitle()
|
||||
const {
|
||||
return default_title_;
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestView::OnClose() {
|
||||
delegate_->OnClose();
|
||||
if (LocalAuthenticationRequestWidget::Get()) {
|
||||
LocalAuthenticationRequestWidget::Get()->Close(false /* success */);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestView::UpdatePreferredSize() {
|
||||
SetPreferredSize(CalculatePreferredSize());
|
||||
if (GetWidget()) {
|
||||
GetWidget()->CenterWindow(GetPreferredSize());
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Size
|
||||
LocalAuthenticationRequestView::GetLocalAuthenticationRequestViewSize() const {
|
||||
return gfx::Size(kLocalAuthenticationRequestViewWidthDp,
|
||||
kLocalAuthenticationRequestViewHeightDp);
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestView::OnAuthSubmit(
|
||||
bool authenticated_by_pin,
|
||||
const std::u16string& password) {
|
||||
CHECK(!authenticated_by_pin);
|
||||
SetInputEnabled(false);
|
||||
|
||||
const auto& auth_factors = user_context_->GetAuthFactorsData();
|
||||
const cryptohome::AuthFactor* local_password_factor =
|
||||
auth_factors.FindLocalPasswordFactor();
|
||||
CHECK_NE(local_password_factor, nullptr);
|
||||
|
||||
const cryptohome::KeyLabel& key_label = local_password_factor->ref().label();
|
||||
|
||||
// Create a copy of `user_context_` so that we don't lose it to std::move
|
||||
// for future auth attempts
|
||||
auth_performer_.AuthenticateWithPassword(
|
||||
key_label.value(), base::UTF16ToUTF8(password),
|
||||
std::make_unique<UserContext>(*user_context_),
|
||||
base::BindOnce(&LocalAuthenticationRequestView::OnAuthComplete,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestView::OnAuthComplete(
|
||||
std::unique_ptr<UserContext> user_context,
|
||||
absl::optional<AuthenticationError> authentication_error) {
|
||||
if (authentication_error.has_value()) {
|
||||
LOG(ERROR) << "An error happened during the attempt to validate "
|
||||
"the password: "
|
||||
<< authentication_error.value().get_cryptohome_code();
|
||||
|
||||
UpdateState(
|
||||
LocalAuthenticationRequestViewState::kError, default_title_,
|
||||
l10n_util::GetStringUTF16(IDS_ASH_LOGIN_ERROR_AUTHENTICATING_PWD));
|
||||
ClearInput();
|
||||
SetInputEnabled(true);
|
||||
} else {
|
||||
LocalAuthenticationRequestWidget::Get()->Close(true /* success */);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestView::OnInputTextChanged(bool is_empty) {}
|
||||
|
||||
} // namespace ash
|
160
ash/login/ui/local_authentication_request_view.h
Normal file
160
ash/login/ui/local_authentication_request_view.h
Normal file
@ -0,0 +1,160 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_LOGIN_UI_LOCAL_AUTHENTICATION_REQUEST_VIEW_H_
|
||||
#define ASH_LOGIN_UI_LOCAL_AUTHENTICATION_REQUEST_VIEW_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "ash/login/ui/access_code_input.h"
|
||||
#include "ash/login/ui/login_button.h"
|
||||
#include "ash/login/ui/login_password_view.h"
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
#include "ash/public/cpp/login_types.h"
|
||||
#include "ash/public/cpp/tablet_mode_observer.h"
|
||||
#include "ash/style/system_shadow.h"
|
||||
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "chromeos/ash/components/login/auth/auth_performer.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
#include "ui/gfx/geometry/size.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
#include "ui/views/window/dialog_delegate.h"
|
||||
|
||||
namespace views {
|
||||
class Label;
|
||||
} // namespace views
|
||||
|
||||
namespace ash {
|
||||
|
||||
class UserContext;
|
||||
|
||||
// State of the LocalAuthenticationView.
|
||||
enum class LocalAuthenticationRequestViewState {
|
||||
kNormal,
|
||||
kError,
|
||||
};
|
||||
|
||||
// The view that allows for input of local authentication to authorize certain
|
||||
// actions.
|
||||
class ASH_EXPORT LocalAuthenticationRequestView
|
||||
: public views::DialogDelegateView {
|
||||
public:
|
||||
using OnLocalAuthenticationRequestDone =
|
||||
base::OnceCallback<void(bool success)>;
|
||||
|
||||
class Delegate {
|
||||
public:
|
||||
virtual void OnClose() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~Delegate() = default;
|
||||
};
|
||||
|
||||
class ASH_EXPORT TestApi {
|
||||
public:
|
||||
explicit TestApi(LocalAuthenticationRequestView* view);
|
||||
~TestApi();
|
||||
|
||||
LoginButton* close_button();
|
||||
views::Label* title_label();
|
||||
views::Label* description_label();
|
||||
LoginPasswordView* login_password_view();
|
||||
views::Textfield* GetInputTextfield() const;
|
||||
LocalAuthenticationRequestViewState state() const;
|
||||
|
||||
private:
|
||||
raw_ptr<LocalAuthenticationRequestView, ExperimentalAsh> view_;
|
||||
};
|
||||
|
||||
// Creates local authentication request view that will enable the user to
|
||||
// authenticate with a local authentication.
|
||||
LocalAuthenticationRequestView(
|
||||
OnLocalAuthenticationCompleted on_local_authentication_completed,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description,
|
||||
Delegate* delegate,
|
||||
std::unique_ptr<UserContext> user_context);
|
||||
|
||||
LocalAuthenticationRequestView(const LocalAuthenticationRequestView&) =
|
||||
delete;
|
||||
LocalAuthenticationRequestView& operator=(
|
||||
const LocalAuthenticationRequestView&) = delete;
|
||||
|
||||
~LocalAuthenticationRequestView() override;
|
||||
|
||||
// views::DialogDelegateView:
|
||||
void RequestFocus() override;
|
||||
gfx::Size CalculatePreferredSize() const override;
|
||||
views::View* GetInitiallyFocusedView() override;
|
||||
std::u16string GetAccessibleWindowTitle() const override;
|
||||
|
||||
// Sets whether the user can enter a local authentication. Other buttons
|
||||
// (back, submit etc.) are unaffected.
|
||||
void SetInputEnabled(bool input_enabled);
|
||||
|
||||
// Clears previously entered local authentication from the input field.
|
||||
void ClearInput();
|
||||
|
||||
// Updates state of the view.
|
||||
void UpdateState(LocalAuthenticationRequestViewState state,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description);
|
||||
|
||||
private:
|
||||
void OnAuthSubmit(bool authenticated_by_pin, const std::u16string& password);
|
||||
|
||||
void OnAuthComplete(std::unique_ptr<UserContext> user_context,
|
||||
absl::optional<AuthenticationError> authentication_error);
|
||||
|
||||
void OnInputTextChanged(bool is_empty);
|
||||
|
||||
void OnVisibilityChanged();
|
||||
|
||||
// Closes the view.
|
||||
void OnClose();
|
||||
|
||||
// Updates view's preferred size.
|
||||
void UpdatePreferredSize();
|
||||
|
||||
// Callback to close the UI.
|
||||
OnLocalAuthenticationCompleted on_local_authentication_completed_ =
|
||||
base::NullCallback();
|
||||
|
||||
// Returns the view dimensions.
|
||||
gfx::Size GetLocalAuthenticationRequestViewSize() const;
|
||||
|
||||
// Unowned pointer to the delegate. The delegate should outlive this instance.
|
||||
raw_ptr<Delegate, ExperimentalAsh> delegate_;
|
||||
|
||||
// Strings as on view construction to enable restoring the original state.
|
||||
std::u16string default_title_;
|
||||
std::u16string default_description_;
|
||||
|
||||
// Correspononding labels and other UI elements.
|
||||
raw_ptr<views::Label, ExperimentalAsh> title_label_ = nullptr;
|
||||
raw_ptr<views::Label, ExperimentalAsh> description_label_ = nullptr;
|
||||
raw_ptr<LoginButton, ExperimentalAsh> close_button_ = nullptr;
|
||||
raw_ptr<LoginPasswordView, ExperimentalAsh> login_password_view_ = nullptr;
|
||||
std::unique_ptr<SystemShadow> shadow_;
|
||||
|
||||
// Current local authentication state.
|
||||
LocalAuthenticationRequestViewState state_ =
|
||||
LocalAuthenticationRequestViewState::kNormal;
|
||||
|
||||
AuthPerformer auth_performer_;
|
||||
|
||||
// Current user context.
|
||||
std::unique_ptr<UserContext> user_context_;
|
||||
|
||||
base::WeakPtrFactory<LocalAuthenticationRequestView> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_LOGIN_UI_LOCAL_AUTHENTICATION_REQUEST_VIEW_H_
|
135
ash/login/ui/local_authentication_request_widget.cc
Normal file
135
ash/login/ui/local_authentication_request_widget.cc
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/login/ui/local_authentication_request_widget.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "ash/keyboard/keyboard_controller_impl.h"
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
#include "ash/public/cpp/shell_window_ids.h"
|
||||
#include "ash/session/session_controller_impl.h"
|
||||
#include "ash/shell.h"
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "chromeos/ash/components/login/auth/public/user_context.h"
|
||||
#include "components/session_manager/session_manager_types.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
LocalAuthenticationRequestWidget* g_instance = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void LocalAuthenticationRequestWidget::Show(
|
||||
OnLocalAuthenticationCompleted on_local_authentication_completed,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description,
|
||||
LocalAuthenticationRequestView::Delegate* delegate,
|
||||
std::unique_ptr<UserContext> user_context) {
|
||||
CHECK(!g_instance);
|
||||
|
||||
g_instance = new LocalAuthenticationRequestWidget(
|
||||
std::move(on_local_authentication_completed), title, description,
|
||||
delegate, std::move(user_context));
|
||||
}
|
||||
|
||||
// static
|
||||
LocalAuthenticationRequestWidget* LocalAuthenticationRequestWidget::Get() {
|
||||
return g_instance;
|
||||
}
|
||||
|
||||
// static
|
||||
void LocalAuthenticationRequestWidget::UpdateState(
|
||||
LocalAuthenticationRequestViewState state,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description) {
|
||||
CHECK_EQ(g_instance, this);
|
||||
GetView()->UpdateState(state, title, description);
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestWidget::SetInputEnabled(bool enabled) {
|
||||
CHECK_EQ(g_instance, this);
|
||||
GetView()->SetInputEnabled(enabled);
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestWidget::ClearInput() {
|
||||
CHECK_EQ(g_instance, this);
|
||||
GetView()->ClearInput();
|
||||
}
|
||||
|
||||
void LocalAuthenticationRequestWidget::Close(bool success) {
|
||||
CHECK_EQ(g_instance, this);
|
||||
LocalAuthenticationRequestWidget* instance = g_instance;
|
||||
g_instance = nullptr;
|
||||
std::move(on_local_authentication_completed_).Run(success);
|
||||
widget_->Close();
|
||||
delete instance;
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestWidget::LocalAuthenticationRequestWidget(
|
||||
OnLocalAuthenticationCompleted on_local_authentication_completed,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description,
|
||||
LocalAuthenticationRequestView::Delegate* delegate,
|
||||
std::unique_ptr<UserContext> user_context)
|
||||
: on_local_authentication_completed_(
|
||||
std::move(on_local_authentication_completed)) {
|
||||
views::Widget::InitParams widget_params;
|
||||
// Using window frameless to be able to get focus on the view input fields,
|
||||
// which does not work with popup type.
|
||||
widget_params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
|
||||
widget_params.ownership =
|
||||
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
|
||||
widget_params.opacity =
|
||||
views::Widget::InitParams::WindowOpacity::kTranslucent;
|
||||
widget_params.accept_events = true;
|
||||
|
||||
ShellWindowId parent_window_id =
|
||||
Shell::Get()->session_controller()->GetSessionState() ==
|
||||
session_manager::SessionState::ACTIVE
|
||||
? kShellWindowId_SystemModalContainer
|
||||
: kShellWindowId_LockSystemModalContainer;
|
||||
widget_params.parent =
|
||||
Shell::GetPrimaryRootWindow()->GetChildById(parent_window_id);
|
||||
widget_params.delegate = new LocalAuthenticationRequestView(
|
||||
base::BindOnce(&LocalAuthenticationRequestWidget::Close,
|
||||
weak_factory_.GetWeakPtr()),
|
||||
title, description, delegate, std::move(user_context));
|
||||
|
||||
widget_ = std::make_unique<views::Widget>();
|
||||
widget_->Init(std::move(widget_params));
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestWidget::~LocalAuthenticationRequestWidget() = default;
|
||||
|
||||
void LocalAuthenticationRequestWidget::Show() {
|
||||
CHECK(widget_);
|
||||
widget_->Show();
|
||||
}
|
||||
|
||||
// static
|
||||
LocalAuthenticationRequestView*
|
||||
LocalAuthenticationRequestWidget::GetViewForTesting() {
|
||||
LocalAuthenticationRequestWidget* widget =
|
||||
LocalAuthenticationRequestWidget::Get();
|
||||
return widget ? widget->GetView() : nullptr;
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestView* LocalAuthenticationRequestWidget::GetView() {
|
||||
return static_cast<LocalAuthenticationRequestView*>(
|
||||
widget_->widget_delegate());
|
||||
}
|
||||
|
||||
} // namespace ash
|
104
ash/login/ui/local_authentication_request_widget.h
Normal file
104
ash/login/ui/local_authentication_request_widget.h
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_LOGIN_UI_LOCAL_AUTHENTICATION_REQUEST_WIDGET_H_
|
||||
#define ASH_LOGIN_UI_LOCAL_AUTHENTICATION_REQUEST_WIDGET_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "ash/login/ui/local_authentication_request_view.h"
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
|
||||
namespace views {
|
||||
class Widget;
|
||||
}
|
||||
|
||||
namespace ash {
|
||||
|
||||
enum class LocalAuthenticationRequestReason;
|
||||
enum class LocalAuthenticationRequestViewState;
|
||||
class UserContext;
|
||||
|
||||
// Widget to display the Local Password Request View in a standalone container.
|
||||
class ASH_EXPORT LocalAuthenticationRequestWidget {
|
||||
public:
|
||||
class ASH_EXPORT TestApi {
|
||||
public:
|
||||
TestApi();
|
||||
~TestApi();
|
||||
|
||||
static LocalAuthenticationRequestView* GetView();
|
||||
};
|
||||
|
||||
LocalAuthenticationRequestWidget(const LocalAuthenticationRequestWidget&) =
|
||||
delete;
|
||||
LocalAuthenticationRequestWidget& operator=(
|
||||
const LocalAuthenticationRequestWidget&) = delete;
|
||||
|
||||
// Creates and shows the instance of LocalAuthenticationRequestWidget.
|
||||
// This widget is modal and only one instance can be created at a time. It
|
||||
// will be destroyed when dismissed.
|
||||
static void Show(
|
||||
OnLocalAuthenticationCompleted on_local_authentication_completed,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description,
|
||||
LocalAuthenticationRequestView::Delegate* delegate,
|
||||
std::unique_ptr<UserContext> user_context);
|
||||
|
||||
// Returns the instance of LocalAuthenticationRequestWidget or nullptr if it
|
||||
// does not exits.
|
||||
static LocalAuthenticationRequestWidget* Get();
|
||||
|
||||
// Toggles showing an error state and updates displayed strings.
|
||||
void UpdateState(LocalAuthenticationRequestViewState state,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description);
|
||||
|
||||
// Enables or disables input textfield.
|
||||
void SetInputEnabled(bool enabled);
|
||||
|
||||
// Clears previously entered password.
|
||||
void ClearInput();
|
||||
|
||||
// Closes the widget.
|
||||
// |success| describes whether the validation was successful and is passed to
|
||||
// |on_local_authentication_request_done_|.
|
||||
void Close(bool success);
|
||||
|
||||
// Returns the associated view for testing purposes.
|
||||
static LocalAuthenticationRequestView* GetViewForTesting();
|
||||
|
||||
private:
|
||||
LocalAuthenticationRequestWidget(
|
||||
LocalAuthenticationRequestView::OnLocalAuthenticationRequestDone
|
||||
on_local_authentication_request_done,
|
||||
const std::u16string& title,
|
||||
const std::u16string& description,
|
||||
LocalAuthenticationRequestView::Delegate* delegate,
|
||||
std::unique_ptr<UserContext> user_context);
|
||||
~LocalAuthenticationRequestWidget();
|
||||
|
||||
// Shows the |widget_|.
|
||||
void Show();
|
||||
|
||||
// Returns the associated view.
|
||||
LocalAuthenticationRequestView* GetView();
|
||||
|
||||
// Callback invoked when closing the widget.
|
||||
LocalAuthenticationRequestView::OnLocalAuthenticationRequestDone
|
||||
on_local_authentication_completed_;
|
||||
|
||||
std::unique_ptr<views::Widget> widget_;
|
||||
|
||||
base::WeakPtrFactory<LocalAuthenticationRequestWidget> weak_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_LOGIN_UI_LOCAL_AUTHENTICATION_REQUEST_WIDGET_H_
|
@ -15,6 +15,7 @@
|
||||
#include "ash/detachable_base/detachable_base_pairing_status.h"
|
||||
#include "ash/ime/ime_controller_impl.h"
|
||||
#include "ash/login/login_screen_controller.h"
|
||||
#include "ash/login/ui/local_authentication_request_controller_impl.h"
|
||||
#include "ash/login/ui/lock_contents_view.h"
|
||||
#include "ash/login/ui/lock_screen.h"
|
||||
#include "ash/login/ui/login_data_dispatcher.h"
|
||||
@ -22,6 +23,7 @@
|
||||
#include "ash/login/ui/non_accessible_view.h"
|
||||
#include "ash/login/ui/views_utils.h"
|
||||
#include "ash/public/cpp/kiosk_app_menu.h"
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
#include "ash/public/cpp/login_types.h"
|
||||
#include "ash/public/cpp/smartlock_state.h"
|
||||
#include "ash/public/cpp/style/dark_light_mode_controller.h"
|
||||
@ -36,11 +38,13 @@
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chromeos/ash/components/login/auth/public/user_context.h"
|
||||
#include "components/account_id/account_id.h"
|
||||
#include "components/user_manager/known_user.h"
|
||||
#include "components/user_manager/multi_user/multi_user_sign_in_policy.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
#include "ui/base/ime/ash/ime_keyboard.h"
|
||||
#include "ui/chromeos/resources/grit/ui_chromeos_resources.h"
|
||||
#include "ui/gfx/image/image_skia.h"
|
||||
#include "ui/views/controls/button/md_text_button.h"
|
||||
#include "ui/views/controls/scroll_view.h"
|
||||
@ -381,6 +385,20 @@ class LockDebugView::DebugDataDispatcherTransformer
|
||||
debug_user->account_id, debug_user->enable_tap_to_unlock);
|
||||
}
|
||||
|
||||
// Activates authentication request dialog for the user at |user_index|.
|
||||
void AuthRequestForUserIndex(size_t user_index) {
|
||||
DCHECK(user_index >= 0 && user_index < debug_users_.size());
|
||||
UserMetadata* debug_user = &debug_users_[user_index];
|
||||
const AccountId account_id = debug_user->account_id;
|
||||
|
||||
std::unique_ptr<ash::UserContext> user_context =
|
||||
std::make_unique<ash::UserContext>(user_manager::USER_TYPE_REGULAR,
|
||||
account_id);
|
||||
|
||||
Shell::Get()->local_authentication_request_controller()->ShowWidget(
|
||||
base::BindOnce([](bool bla) {}), std::move(user_context));
|
||||
}
|
||||
|
||||
// Cycles fingerprint state for the user at |user_index|.
|
||||
void CycleFingerprintStateForUserIndex(size_t user_index) {
|
||||
DCHECK(user_index >= 0 && user_index < debug_users_.size());
|
||||
@ -1229,6 +1247,12 @@ void LockDebugView::UpdatePerUserActionContainer() {
|
||||
base::Unretained(debug_data_dispatcher_.get()), i),
|
||||
row);
|
||||
|
||||
AddButton("Show local authentication request",
|
||||
base::BindRepeating(
|
||||
&DebugDataDispatcherTransformer::AuthRequestForUserIndex,
|
||||
base::Unretained(debug_data_dispatcher_.get()), i),
|
||||
row);
|
||||
|
||||
if (debug_detachable_base_model_->debugging_pairing_state() &&
|
||||
debug_detachable_base_model_->GetPairingStatus() ==
|
||||
DetachableBasePairingStatus::kAuthenticated) {
|
||||
|
@ -200,6 +200,8 @@ component("cpp") {
|
||||
"locale_update_controller.h",
|
||||
"lock_screen_widget_factory.cc",
|
||||
"lock_screen_widget_factory.h",
|
||||
"login/local_authentication_request_controller.cc",
|
||||
"login/local_authentication_request_controller.h",
|
||||
"login/login_utils.cc",
|
||||
"login/login_utils.h",
|
||||
"login_accelerators.cc",
|
||||
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
|
||||
#include "base/check_op.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
LocalAuthenticationRequestController* g_instance = nullptr;
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestController::LocalAuthenticationRequestController() {
|
||||
CHECK_EQ(nullptr, g_instance);
|
||||
g_instance = this;
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestController::~LocalAuthenticationRequestController() {
|
||||
CHECK_EQ(this, g_instance);
|
||||
g_instance = nullptr;
|
||||
}
|
||||
|
||||
LocalAuthenticationRequestController*
|
||||
LocalAuthenticationRequestController::Get() {
|
||||
return g_instance;
|
||||
}
|
||||
} // namespace ash
|
@ -0,0 +1,40 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_PUBLIC_CPP_LOGIN_LOCAL_AUTHENTICATION_REQUEST_CONTROLLER_H_
|
||||
#define ASH_PUBLIC_CPP_LOGIN_LOCAL_AUTHENTICATION_REQUEST_CONTROLLER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ash/public/cpp/ash_public_export.h"
|
||||
#include "base/functional/callback_forward.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
class UserContext;
|
||||
|
||||
using OnLocalAuthenticationCompleted = base::OnceCallback<void(bool success)>;
|
||||
|
||||
// LocalAuthenticationRequestController serves local authentication requests
|
||||
// regarding the re-auth session. It takes care of showing and hiding the UI.
|
||||
class ASH_PUBLIC_EXPORT LocalAuthenticationRequestController {
|
||||
public:
|
||||
static LocalAuthenticationRequestController* Get();
|
||||
|
||||
// Shows a standalone local authentication dialog.
|
||||
// |callback| is invoked when the widget is closed e.g with the back button
|
||||
// or the correct code is entered.
|
||||
// Returns whether opening the dialog was successful. Will fail if another
|
||||
// dialog is already opened.
|
||||
|
||||
virtual bool ShowWidget(OnLocalAuthenticationCompleted callback,
|
||||
std::unique_ptr<UserContext> user_context) = 0;
|
||||
|
||||
protected:
|
||||
LocalAuthenticationRequestController();
|
||||
virtual ~LocalAuthenticationRequestController();
|
||||
};
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_PUBLIC_CPP_LOGIN_LOCAL_AUTHENTICATION_REQUEST_CONTROLLER_H_
|
@ -86,6 +86,7 @@
|
||||
#include "ash/keyboard/keyboard_controller_impl.h"
|
||||
#include "ash/keyboard/ui/keyboard_ui_factory.h"
|
||||
#include "ash/login/login_screen_controller.h"
|
||||
#include "ash/login/ui/local_authentication_request_controller_impl.h"
|
||||
#include "ash/login_status.h"
|
||||
#include "ash/media/media_controller_impl.h"
|
||||
#include "ash/metrics/feature_discovery_duration_reporter_impl.h"
|
||||
@ -98,6 +99,7 @@
|
||||
#include "ash/public/cpp/accelerator_keycode_lookup_cache.h"
|
||||
#include "ash/public/cpp/ash_prefs.h"
|
||||
#include "ash/public/cpp/holding_space/holding_space_controller.h"
|
||||
#include "ash/public/cpp/login/local_authentication_request_controller.h"
|
||||
#include "ash/public/cpp/nearby_share_delegate.h"
|
||||
#include "ash/public/cpp/saved_desk_delegate.h"
|
||||
#include "ash/public/cpp/shelf_config.h"
|
||||
@ -664,6 +666,8 @@ Shell::Shell(std::unique_ptr<ShellDelegate> shell_delegate)
|
||||
std::make_unique<KeyboardBrightnessController>()),
|
||||
locale_update_controller_(std::make_unique<LocaleUpdateControllerImpl>()),
|
||||
parent_access_controller_(std::make_unique<ParentAccessControllerImpl>()),
|
||||
local_authentication_request_controller_(
|
||||
std::make_unique<LocalAuthenticationRequestControllerImpl>()),
|
||||
session_controller_(std::make_unique<SessionControllerImpl>()),
|
||||
feature_discover_reporter_(
|
||||
std::make_unique<FeatureDiscoveryDurationReporterImpl>(
|
||||
|
@ -197,6 +197,7 @@ class NightLightControllerImpl;
|
||||
class OcclusionTrackerPauser;
|
||||
class OverviewController;
|
||||
class ParentAccessController;
|
||||
class LocalAuthenticationRequestControllerImpl;
|
||||
class PartialMagnifierController;
|
||||
class PciePeripheralNotificationController;
|
||||
class UsbPeripheralNotificationController;
|
||||
@ -603,6 +604,10 @@ class ASH_EXPORT Shell : public SessionObserver,
|
||||
LocaleUpdateControllerImpl* locale_update_controller() {
|
||||
return locale_update_controller_.get();
|
||||
}
|
||||
LocalAuthenticationRequestControllerImpl*
|
||||
local_authentication_request_controller() {
|
||||
return local_authentication_request_controller_.get();
|
||||
}
|
||||
LoginScreenController* login_screen_controller() {
|
||||
return login_screen_controller_.get();
|
||||
}
|
||||
@ -1029,6 +1034,8 @@ class ASH_EXPORT Shell : public SessionObserver,
|
||||
std::unique_ptr<NearbyShareControllerImpl> nearby_share_controller_;
|
||||
std::unique_ptr<NearbyShareDelegate> nearby_share_delegate_;
|
||||
std::unique_ptr<ParentAccessController> parent_access_controller_;
|
||||
std::unique_ptr<LocalAuthenticationRequestControllerImpl>
|
||||
local_authentication_request_controller_;
|
||||
std::unique_ptr<PciePeripheralNotificationController>
|
||||
pcie_peripheral_notification_controller_;
|
||||
std::unique_ptr<PrivacyHubController> privacy_hub_controller_;
|
||||
|
Reference in New Issue
Block a user