use keyring::credential::{
Credential, CredentialApi, CredentialBuilder, CredentialBuilderApi, CredentialPersistence,
};
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
type Store = Arc<Mutex<HashMap<(String, String), Vec<u8>>>>;
struct SharedMockCredential {
store: Store,
key: (String, String),
}
impl CredentialApi for SharedMockCredential {
fn set_secret(&self, password: &[u8]) -> keyring::Result<()> {
self.store
.lock()
.unwrap()
.insert(self.key.clone(), password.to_vec());
Ok(())
}
fn get_secret(&self) -> keyring::Result<Vec<u8>> {
self.store
.lock()
.unwrap()
.get(&self.key)
.cloned()
.ok_or(keyring::Error::NoEntry)
}
fn delete_credential(&self) -> keyring::Result<()> {
self.store
.lock()
.unwrap()
.remove(&self.key)
.map(|_| ())
.ok_or(keyring::Error::NoEntry)
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct SharedMockBuilder {
store: Store,
}
impl CredentialBuilderApi for SharedMockBuilder {
fn build(
&self,
_target: Option<&str>,
service: &str,
user: &str,
) -> keyring::Result<Box<Credential>> {
Ok(Box::new(SharedMockCredential {
store: self.store.clone(),
key: (service.to_string(), user.to_string()),
}))
}
fn as_any(&self) -> &dyn Any {
self
}
fn persistence(&self) -> CredentialPersistence {
CredentialPersistence::ProcessOnly
}
}
static MOCK_LOCK: AsyncMutex<()> = AsyncMutex::const_new(());
pub(super) async fn env_lock() -> AsyncMutexGuard<'static, ()> {
MOCK_LOCK.lock().await
}
pub(super) async fn install_mock(
initial: Option<(&str, &str, &str)>,
) -> (AsyncMutexGuard<'static, ()>, crate::test_env::EnvGuard) {
let g = MOCK_LOCK.lock().await;
let env = crate::test_env::EnvGuard::set([(super::ENV_KEYCHAIN_ALLOW, "1")]);
let store: Store = Arc::new(Mutex::new(HashMap::new()));
if let Some((svc, user, pw)) = initial {
store
.lock()
.unwrap()
.insert((svc.to_string(), user.to_string()), pw.as_bytes().to_vec());
}
let builder: Box<CredentialBuilder> = Box::new(SharedMockBuilder { store });
keyring::set_default_credential_builder(builder);
(g, env)
}