0

ash: In session auth dialog shows the pin lockout status message.

1. When existing PINs lock out, the delay is indefintely so we reuse the
   existing PIN lockout message. We tweak it a bit to make it clear
   that it is about PIN.
2. On the new flexible PIN lockout, the delay is converted from
   PinStatus and formatted into a new error message.
3. Because PinStatus is from AuthFactorStatusUpdate, which periodically
   emits from cryptohomed and processed by the controller. The error
   message will be updated every 30 seconds or immediately after
   a pin is locked out.

BUG=b:341733466
TEST=Unit tests and VM manual tests.

Change-Id: Ib95c9f02003c40b74f17cedfd6cca89c1657ed28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5796520
Reviewed-by: Elie Maamari <emaamari@google.com>
Commit-Queue: Hardik Goyal <hardikgoyal@chromium.org>
Reviewed-by: Hardik Goyal <hardikgoyal@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1347228}
This commit is contained in:
Zi Lin
2024-08-27 09:11:14 +00:00
committed by Chromium LUCI CQ
parent 23d865acda
commit 1a87efe31d
7 changed files with 101 additions and 4 deletions

@ -5333,7 +5333,10 @@ No devices connected.
Incorrect PIN
</message>
<message name="IDS_ASH_IN_SESSION_AUTH_PIN_TOO_MANY_ATTEMPTS" desc="Text shown in the auth dialog to tell the user that PIN auth has reached maximum attempts">
Too many attempts
Too many PIN attempts
</message>
<message name="IDS_ASH_IN_SESSION_AUTH_PIN_DELAY_REQUIRED" desc="Text shown in the auth dialog to tell the user that PIN auth has reached many attempts and a rate limiting delay is in effect">
Too many PIN attempts. Wait <ph name="TIME_LEFT">$1<ex>1 hour, 15 minutes, 10 seconds</ex></ph> and try again
</message>
<message name="IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_AVAILABLE" desc="Text shown in the auth dialog to remind user that fingerprint auth is supported">
Touch the fingerprint sensor

@ -0,0 +1 @@
dbf477759685f9b17ffc0f9a36c7659caeed5d0c

@ -1 +1 @@
825fd9414614f6d0534cb3a1136c921a11aac1b1
a4d4d2f8559bedff7af4c33f69ac1e5a2cba3c21

@ -19,6 +19,7 @@
#include "base/check.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/i18n/time_formatting.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/utf_string_conversions.h"
@ -118,6 +119,28 @@ const char* ActiveSessionAuthStateToString(
NOTREACHED();
}
std::u16string BuildPinStatusMessage(const cryptohome::PinStatus& pin_status) {
if (!pin_status.IsLockedFactor()) {
return u"";
}
if (pin_status.AvailableAt() == base::Time::Max()) {
return l10n_util::GetStringUTF16(
IDS_ASH_IN_SESSION_AUTH_PIN_TOO_MANY_ATTEMPTS);
} else {
base::TimeDelta delta = pin_status.AvailableAt() - base::Time::Now();
std::u16string time_left_message;
if (base::TimeDurationCompactFormatWithSeconds(
delta, base::DurationFormatWidth::DURATION_WIDTH_WIDE,
&time_left_message)) {
return l10n_util::GetStringFUTF16(
IDS_ASH_IN_SESSION_AUTH_PIN_DELAY_REQUIRED, time_left_message);
} else {
return l10n_util::GetStringUTF16(
IDS_ASH_IN_SESSION_AUTH_PIN_TOO_MANY_ATTEMPTS);
}
}
}
} // namespace
ActiveSessionAuthControllerImpl::TestApi::TestApi(
@ -141,6 +164,16 @@ void ActiveSessionAuthControllerImpl::TestApi::SubmitPin(
controller_->OnPinSubmit(base::UTF8ToUTF16(pin));
}
void ActiveSessionAuthControllerImpl::TestApi::DisplayPinStatusMessage(
const cryptohome::PinStatus pin_status) {
controller_->DisplayPinStatusMessage(pin_status);
}
const std::u16string&
ActiveSessionAuthControllerImpl::TestApi::GetPinStatusMessage() const {
return controller_->pin_status_message_;
}
void ActiveSessionAuthControllerImpl::TestApi::Close() {
controller_->Close();
}
@ -410,12 +443,20 @@ void ActiveSessionAuthControllerImpl::ProcessAuthFactorStatusUpdate(
/*fallback_type=*/cryptohome::AuthFactorType::kPassword);
if (auth_factor.ref().type() == cryptohome::AuthFactorType::kPin) {
CHECK(contents_view_);
bool pin_enabled = !auth_factor.GetPinStatus().IsLockedFactor();
auto pin_status = auth_factor.GetPinStatus();
bool pin_enabled = !pin_status.IsLockedFactor();
// Only need to update the auth view because |available_factors_| is only
// used to initialize the view.
contents_view_->SetHasPin(pin_enabled);
DisplayPinStatusMessage(pin_status);
}
}
}
void ActiveSessionAuthControllerImpl::DisplayPinStatusMessage(
const cryptohome::PinStatus pin_status) {
pin_status_message_ = BuildPinStatusMessage(pin_status);
contents_view_->SetPinStatus(pin_status_message_);
}
} // namespace ash

@ -16,6 +16,7 @@
#include "ash/public/cpp/auth/active_session_auth_controller.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/ash/components/cryptohome/auth_factor.h"
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
#include "chromeos/ash/components/login/auth/auth_factor_editor.h"
#include "chromeos/ash/components/login/auth/auth_performer.h"
@ -59,6 +60,11 @@ class ASH_EXPORT ActiveSessionAuthControllerImpl
// manually entered it.
void SubmitPin(const std::string& pin);
// Simulate building and displaying a pin status message.
void DisplayPinStatusMessage(const cryptohome::PinStatus pin_status);
const std::u16string& GetPinStatusMessage() const;
void Close();
private:
@ -129,6 +135,10 @@ class ASH_EXPORT ActiveSessionAuthControllerImpl
// called after auth factors configuration is updated.
bool IsPinLocked() const;
// Show a PinStatus based error message, only if |pin_status| represents
// a locked factor.
void DisplayPinStatusMessage(const cryptohome::PinStatus pin_status);
std::unique_ptr<views::Widget> widget_;
base::ScopedObservation<views::View, ViewObserver> contents_view_observer_{
@ -139,6 +149,7 @@ class ASH_EXPORT ActiveSessionAuthControllerImpl
AccountId account_id_;
std::u16string title_;
std::u16string description_;
std::u16string pin_status_message_;
std::unique_ptr<AuthFactorEditor> auth_factor_editor_;
std::unique_ptr<AuthPerformer> auth_performer_;

@ -6,7 +6,9 @@
#include "ash/constants/ash_pref_names.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/test/ash_test_base.h"
#include "base/test/run_until.h"
#include "base/test/test_future.h"
#include "chromeos/ash/components/cryptohome/system_salt_getter.h"
#include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h"
@ -19,6 +21,7 @@
#include "components/user_manager/known_user.h"
#include "components/user_manager/user_manager.h"
#include "third_party/cros_system_api/dbus/cryptohome/dbus-constants.h"
#include "ui/base/l10n/l10n_util.h"
namespace ash {
@ -412,6 +415,44 @@ TEST_F(ActiveSessionAuthControllerTest, BadPinThenGoodPassword) {
EXPECT_EQ(future.Get<bool>(), true);
}
// Check the format and content of pin lockout status message.
TEST_F(ActiveSessionAuthControllerTest, PinLockoutMessage) {
AddGaiaPassword(account_id_, kExpectedPassword);
AddCryptohomePin(account_id_, kExpectedPin);
const std::string bad_pin = "bad_pin";
user_manager::KnownUser known_user(Shell::Get()->local_state());
known_user.SetStringPref(account_id_, prefs::kQuickUnlockPinSalt,
kExpectedSalt);
auto* controller = static_cast<ActiveSessionAuthControllerImpl*>(
Shell::Get()->active_session_auth_controller());
auto test_api = ActiveSessionAuthControllerImpl::TestApi(controller);
OnAuthComplete future;
Shell::Get()->active_session_auth_controller()->ShowAuthDialog(
std::make_unique<SettingsAuthRequest>(future.GetCallback()));
// Await show.
ASSERT_TRUE(base::test::RunUntil([&]() { return controller->IsShown(); }));
const base::TimeDelta in_a_while = base::Seconds(60);
cryptohome::PinStatus soft_lockout{in_a_while};
test_api.DisplayPinStatusMessage(soft_lockout);
EXPECT_EQ(
test_api.GetPinStatusMessage(),
l10n_util::GetStringFUTF16(IDS_ASH_IN_SESSION_AUTH_PIN_DELAY_REQUIRED,
u"1 minute, 0 seconds"));
cryptohome::PinStatus hard_lockout{base::TimeDelta::Max()};
test_api.DisplayPinStatusMessage(hard_lockout);
EXPECT_EQ(
test_api.GetPinStatusMessage(),
l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_PIN_TOO_MANY_ATTEMPTS));
}
// Tests that the OnAuthCancel callback is called with the correct
// parameters.
TEST_F(ActiveSessionAuthControllerTest, OnAuthCancel) {

@ -262,7 +262,7 @@ TEST_F(AuthDialogContentsViewPixelTest, PinAndFingerprintWithPinFail) {
// Verify the UI.
EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
"pin_fingerprint_pin_fail", /*revision_number=*/3, widget.get()));
"pin_fingerprint_pin_fail", /*revision_number=*/4, widget.get()));
}
TEST_F(AuthDialogContentsViewPixelTest,