blob: 30345cc8caacb5708a6edfe41fab803677279be6 [file] [log] [blame]
// 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 "chrome/browser/ui/webauthn/sheet_models.h"
#include <string>
#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
#include "chrome/browser/webauthn/authenticator_transport.h"
#include "chrome/grit/generated_resources.h"
#include "device/fido/fido_types.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
using Mechanism = AuthenticatorRequestDialogModel::Mechanism;
using MechanismVisibility =
AuthenticatorGenericErrorSheetModel::OtherMechanismButtonVisibility;
class AuthenticatorSheetBaseTest : public testing::Test {};
class TestAuthenticatorSheetModel : public AuthenticatorSheetModelBase {
public:
TestAuthenticatorSheetModel(
AuthenticatorRequestDialogModel* dialog_model,
OtherMechanismButtonVisibility other_mechanism_button_visibility)
: AuthenticatorSheetModelBase(dialog_model,
other_mechanism_button_visibility) {
vector_illustrations_.emplace(kPasskeyUsbDarkIcon, kPasskeyUsbDarkIcon);
}
std::u16string GetStepTitle() const override { return u"Step title"; }
std::u16string GetStepDescription() const override {
return u"Step description";
}
bool IsOtherMechanismButtonVisible() const override {
return AuthenticatorSheetModelBase::IsOtherMechanismButtonVisible();
}
};
TEST_F(AuthenticatorSheetBaseTest, IsOtherMechanismButtonVisible) {
auto dialog_model = base::MakeRefCounted<AuthenticatorRequestDialogModel>(
/*render_frame_host=*/nullptr);
// No mechanisms present.
{
TestAuthenticatorSheetModel sheet_model(dialog_model.get(),
MechanismVisibility::kVisible);
dialog_model->mechanisms.clear();
EXPECT_FALSE(sheet_model.IsOtherMechanismButtonVisible());
}
// Two mechanisms present.
{
TestAuthenticatorSheetModel sheet_model(dialog_model.get(),
MechanismVisibility::kVisible);
dialog_model->mechanisms.clear();
dialog_model->mechanisms.emplace_back(Mechanism::Phone("phone"), u"phone",
u"ph", kPasskeyAoaIcon,
base::DoNothing());
dialog_model->mechanisms.emplace_back(
Mechanism::Transport(AuthenticatorTransport::kUsbHumanInterfaceDevice),
u"security key", u"usb", kPasskeyAoaIcon, base::DoNothing());
EXPECT_TRUE(sheet_model.IsOtherMechanismButtonVisible());
}
// Hidden button.
{
TestAuthenticatorSheetModel sheet_model(dialog_model.get(),
MechanismVisibility::kHidden);
dialog_model->mechanisms.clear();
dialog_model->mechanisms.emplace_back(Mechanism::Phone("phone"), u"phone",
u"ph", kPasskeyAoaIcon,
base::DoNothing());
EXPECT_FALSE(sheet_model.IsOtherMechanismButtonVisible());
}
}
// Regression test for crbug.com/1408492.
TEST_F(AuthenticatorSheetBaseTest,
IsOtherMechanismButtonVisible_NoDialogModel) {
auto dialog_model = base::MakeRefCounted<AuthenticatorRequestDialogModel>(
/*render_frame_host=*/nullptr);
TestAuthenticatorSheetModel sheet_model(dialog_model.get(),
MechanismVisibility::kVisible);
dialog_model.reset();
EXPECT_FALSE(sheet_model.IsOtherMechanismButtonVisible());
}
class AuthenticatorMultiSourcePickerSheetModelTest : public testing::Test {};
constexpr char16_t kPasskeyName1[] = u"yuki";
constexpr char16_t kPasskeyName2[] = u"kodai";
constexpr char16_t kPhoneName[] = u"pixel 7";
TEST_F(AuthenticatorMultiSourcePickerSheetModelTest, GPMPasskeysOnly) {
auto dialog_model = base::MakeRefCounted<AuthenticatorRequestDialogModel>(
/*render_frame_host=*/nullptr);
dialog_model->paired_phone_names = {base::UTF16ToUTF8(kPhoneName)};
dialog_model->priority_phone_name = dialog_model->paired_phone_names.at(0);
dialog_model->mechanisms.emplace_back(
Mechanism::Credential({device::AuthenticatorType::kPhone, {0}}),
kPasskeyName1, kPasskeyName1, kPasskeyPhoneIcon, base::DoNothing());
dialog_model->mechanisms.emplace_back(
Mechanism::Credential({device::AuthenticatorType::kPhone, {1}}),
kPasskeyName2, kPasskeyName2, kPasskeyPhoneIcon, base::DoNothing());
dialog_model->mechanisms.emplace_back(
Mechanism::Transport(AuthenticatorTransport::kUsbHumanInterfaceDevice),
u"security key", u"usb", kPasskeyAoaIcon, base::DoNothing());
AuthenticatorMultiSourcePickerSheetModel model(dialog_model.get());
EXPECT_THAT(model.primary_passkey_indices(), testing::ElementsAre(0, 1));
EXPECT_THAT(model.secondary_passkey_indices(), testing::ElementsAre(2));
EXPECT_EQ(
model.primary_passkeys_label(),
l10n_util::GetStringFUTF16(IDS_WEBAUTHN_FROM_PHONE_LABEL, kPhoneName));
}
TEST_F(AuthenticatorMultiSourcePickerSheetModelTest,
GPMPasskeysAndLocalPasskeys) {
auto dialog_model = base::MakeRefCounted<AuthenticatorRequestDialogModel>(
/*render_frame_host=*/nullptr);
dialog_model->paired_phone_names = {base::UTF16ToUTF8(kPhoneName)};
dialog_model->priority_phone_name = dialog_model->paired_phone_names.at(0);
dialog_model->mechanisms.emplace_back(
Mechanism::Credential({device::AuthenticatorType::kPhone, {0}}),
kPasskeyName1, kPasskeyName1, kPasskeyPhoneIcon, base::DoNothing());
dialog_model->mechanisms.emplace_back(
Mechanism::Credential({device::AuthenticatorType::kTouchID, {1}}),
kPasskeyName2, kPasskeyName2, kPasskeyAoaIcon, base::DoNothing());
dialog_model->mechanisms.emplace_back(
Mechanism::Transport(AuthenticatorTransport::kUsbHumanInterfaceDevice),
u"security key", u"usb", kPasskeyAoaIcon, base::DoNothing());
AuthenticatorMultiSourcePickerSheetModel model(dialog_model.get());
EXPECT_THAT(model.primary_passkey_indices(), testing::ElementsAre(1));
EXPECT_THAT(model.secondary_passkey_indices(), testing::ElementsAre(0, 2));
EXPECT_EQ(model.primary_passkeys_label(),
l10n_util::GetStringUTF16(IDS_WEBAUTHN_THIS_DEVICE_LABEL));
}
TEST_F(AuthenticatorMultiSourcePickerSheetModelTest, GPMMechanismAndPhones) {
auto dialog_model = base::MakeRefCounted<AuthenticatorRequestDialogModel>(
/*render_frame_host=*/nullptr);
dialog_model->paired_phone_names = {base::UTF16ToUTF8(kPhoneName)};
dialog_model->priority_phone_name = dialog_model->paired_phone_names.at(0);
dialog_model->mechanisms.emplace_back(
Mechanism::Credential({device::AuthenticatorType::kPhone, {0}}),
kPasskeyName1, kPasskeyName1, kPasskeyPhoneIcon, base::DoNothing());
dialog_model->mechanisms.emplace_back(Mechanism::Enclave(), u"enclave",
u"enclave", kPasskeyAoaIcon,
base::DoNothing());
AuthenticatorMultiSourcePickerSheetModel model(dialog_model.get());
EXPECT_THAT(model.primary_passkey_indices(), testing::ElementsAre(1));
EXPECT_THAT(model.secondary_passkey_indices(), testing::ElementsAre(0));
EXPECT_EQ(model.primary_passkeys_label(),
l10n_util::GetStringUTF16(IDS_WEBAUTHN_THIS_DEVICE_LABEL));
}
TEST_F(AuthenticatorMultiSourcePickerSheetModelTest, NoDiscoveredPasskeys) {
auto dialog_model = base::MakeRefCounted<AuthenticatorRequestDialogModel>(
/*render_frame_host=*/nullptr);
dialog_model->mechanisms.emplace_back(
Mechanism::Transport(AuthenticatorTransport::kUsbHumanInterfaceDevice),
u"security key", u"usb", kPasskeyAoaIcon, base::DoNothing());
AuthenticatorMultiSourcePickerSheetModel model(dialog_model.get());
EXPECT_TRUE(model.primary_passkey_indices().empty());
EXPECT_THAT(model.secondary_passkey_indices(), testing::ElementsAre(0));
EXPECT_EQ(model.primary_passkeys_label(), u"");
}
} // namespace