use crate::passkey::errors::PasskeyError;
use crate::passkey::types::{CredentialId, PublicKeyCredentialUserEntity, StoredOptions};
use crate::passkey::{PasskeyCredential, PasskeyStore};
use crate::session::UserId;
use crate::storage::{CacheData, CacheErrorConversion, CacheKey, CachePrefix, GENERIC_CACHE_STORE};
use crate::userdb::{User, UserStore};
use chrono::Utc;
use std::time::SystemTime;
pub struct TestCredentialData {
pub credential_id: String,
pub user_id: String,
pub user_handle: String,
pub name: String,
pub display_name: String,
pub public_key: String,
pub aaguid: String,
pub counter: u32,
}
impl TestCredentialData {
#[allow(clippy::too_many_arguments)]
pub fn new(
credential_id: &str,
user_id: &str,
user_handle: &str,
name: &str,
display_name: &str,
public_key: &str,
aaguid: &str,
counter: u32,
) -> Self {
Self {
credential_id: credential_id.to_string(),
user_id: user_id.to_string(),
user_handle: user_handle.to_string(),
name: name.to_string(),
display_name: display_name.to_string(),
public_key: public_key.to_string(),
aaguid: aaguid.to_string(),
counter,
}
}
}
pub async fn insert_test_user(
user_id: UserId,
account: &str,
label: &str,
is_admin: bool,
) -> Result<User, PasskeyError> {
let user = User {
sequence_number: None,
id: user_id.as_str().to_string(),
account: account.to_string(),
label: label.to_string(),
is_admin,
created_at: Utc::now(),
updated_at: Utc::now(),
};
UserStore::upsert_user(user)
.await
.map_err(|e| PasskeyError::Storage(e.to_string()))
}
pub async fn insert_test_credential(data: TestCredentialData) -> Result<(), PasskeyError> {
let now = Utc::now();
let credential = PasskeyCredential {
sequence_number: None,
credential_id: data.credential_id.clone(),
user_id: data.user_id,
public_key: data.public_key,
aaguid: data.aaguid,
rp_id: "localhost".to_string(),
counter: data.counter,
user: PublicKeyCredentialUserEntity {
user_handle: data.user_handle,
name: data.name,
display_name: data.display_name,
},
created_at: now,
updated_at: now,
last_used_at: now,
};
PasskeyStore::store_credential(
CredentialId::new(data.credential_id).expect("Valid credential ID"),
credential,
)
.await
}
pub async fn insert_test_user_and_credential(data: TestCredentialData) -> Result<(), PasskeyError> {
UserStore::init()
.await
.map_err(|e| PasskeyError::Storage(e.to_string()))?;
PasskeyStore::init().await?;
insert_test_user(
UserId::new(data.user_id.clone()).expect("Valid user ID"),
&data.name,
&data.display_name,
false,
)
.await?;
insert_test_credential(data).await
}
pub async fn delete_test_credential(
credential_id: crate::passkey::CredentialId,
) -> Result<(), PasskeyError> {
PasskeyStore::delete_credential_by(crate::passkey::CredentialSearchField::CredentialId(
credential_id,
))
.await
}
pub async fn remove_from_cache(
cache_prefix: CachePrefix,
cache_key: CacheKey,
) -> Result<(), PasskeyError> {
GENERIC_CACHE_STORE
.lock()
.await
.remove(cache_prefix, cache_key)
.await
.map_err(PasskeyError::convert_storage_error)
}
pub async fn cleanup_test_credential(
credential_id: crate::passkey::CredentialId,
) -> Result<(), PasskeyError> {
delete_test_credential(credential_id).await
}
pub async fn create_test_challenge(
challenge_type: &str,
id: &str,
challenge: &str,
user_handle: &str,
name: &str,
display_name: &str,
ttl: u64,
) -> Result<(), PasskeyError> {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let stored_options = StoredOptions {
challenge: challenge.to_string(),
user: PublicKeyCredentialUserEntity {
user_handle: user_handle.to_string(),
name: name.to_string(),
display_name: display_name.to_string(),
},
timestamp: now,
ttl,
};
let cache_data = CacheData {
value: serde_json::to_string(&stored_options)
.map_err(|e| PasskeyError::Storage(e.to_string()))?,
};
let cache_prefix = CachePrefix::new(challenge_type.to_string())
.map_err(PasskeyError::convert_storage_error)?;
let cache_key = CacheKey::new(id.to_string()).map_err(PasskeyError::convert_storage_error)?;
GENERIC_CACHE_STORE
.lock()
.await
.put_with_ttl(cache_prefix, cache_key, cache_data, ttl as usize)
.await
.map_err(PasskeyError::convert_storage_error)
}
pub async fn check_cache_exists(cache_prefix: CachePrefix, cache_key: CacheKey) -> bool {
matches!(
GENERIC_CACHE_STORE
.lock()
.await
.get(cache_prefix, cache_key)
.await,
Ok(Some(_))
)
}