use passkey_types::ctap2::{
Ctap2Error,
make_credential::{PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity},
};
use crate::passkey::PasskeyAccessor;
#[cfg(any(test, feature = "testable", doc))]
use passkey_types::Passkey;
#[cfg(doc)]
use crate::Authenticator;
#[derive(Debug, Clone, PartialEq)]
pub enum UiHint<'a, P> {
InformExcludedCredentialFound(&'a P),
InformNoCredentialsFound,
RequestNewCredential(
&'a PublicKeyCredentialUserEntity,
&'a PublicKeyCredentialRpEntity,
),
RequestExistingCredential(&'a P),
}
#[derive(Clone, Copy, PartialEq)]
pub struct UserCheck {
pub presence: bool,
pub verification: bool,
}
#[cfg_attr(any(test, feature = "testable"), mockall::automock(type PasskeyItem = Passkey;))]
#[async_trait::async_trait]
pub trait UserValidationMethod {
type PasskeyItem: PasskeyAccessor + Send + Sync;
async fn check_user<'a>(
&self,
hint: UiHint<'a, Self::PasskeyItem>,
presence: bool,
verification: bool,
) -> Result<UserCheck, Ctap2Error>;
fn is_presence_enabled(&self) -> bool;
fn is_verification_enabled(&self) -> Option<bool>;
}
#[cfg(any(test, feature = "testable"))]
#[derive(Debug, Clone)]
pub enum MockUiHint {
InformExcludedCredentialFound(Passkey),
InformNoCredentialsFound,
RequestNewCredential(PublicKeyCredentialUserEntity, PublicKeyCredentialRpEntity),
RequestExistingCredential(Passkey),
}
#[cfg(any(test, feature = "testable"))]
impl MockUserValidationMethod {
pub fn verified_user(times: usize) -> Self {
let mut user_mock = MockUserValidationMethod::new();
user_mock.expect_is_presence_enabled().returning(|| true);
user_mock
.expect_is_verification_enabled()
.returning(|| Some(true))
.times(..);
user_mock.expect_is_presence_enabled().returning(|| true);
user_mock
.expect_check_user()
.with(
mockall::predicate::always(),
mockall::predicate::eq(true),
mockall::predicate::eq(true),
)
.returning(|_, _, _| {
Ok(UserCheck {
presence: true,
verification: true,
})
})
.times(times);
user_mock
}
pub fn verified_user_with_hint(times: usize, expected_hint: MockUiHint) -> Self {
let mut user_mock = MockUserValidationMethod::new();
user_mock
.expect_is_verification_enabled()
.returning(|| Some(true))
.times(..);
user_mock
.expect_is_presence_enabled()
.returning(|| true)
.times(..);
user_mock
.expect_check_user()
.withf(move |actual_hint, presence, verification| {
*presence
&& *verification
&& match &expected_hint {
MockUiHint::InformExcludedCredentialFound(p) => {
actual_hint == &UiHint::InformExcludedCredentialFound(p)
}
MockUiHint::InformNoCredentialsFound => {
matches!(actual_hint, UiHint::InformNoCredentialsFound)
}
MockUiHint::RequestNewCredential(user, rp) => {
actual_hint == &UiHint::RequestNewCredential(user, rp)
}
MockUiHint::RequestExistingCredential(p) => {
actual_hint == &UiHint::RequestExistingCredential(p)
}
}
})
.returning(|_, _, _| {
Ok(UserCheck {
presence: true,
verification: true,
})
})
.times(times);
user_mock
}
}