use async_trait::async_trait;
use keyring::Entry;
use super::{PersistedTokens, TokenKey, TokenStore, TokenStoreError};
pub struct KeyringTokenStore {
service_name: String,
}
impl KeyringTokenStore {
pub fn new(service_name: impl Into<String>) -> Self {
Self {
service_name: service_name.into(),
}
}
pub fn service_name(&self) -> &str {
&self.service_name
}
fn entry_for(&self, key: &TokenKey) -> Result<Entry, TokenStoreError> {
Entry::new(&self.service_name, &key.keyring_account())
.map_err(|e| TokenStoreError::KeyringUnavailable(e.to_string()))
}
}
#[async_trait]
impl TokenStore for KeyringTokenStore {
async fn load(&self, key: &TokenKey) -> Result<Option<PersistedTokens>, TokenStoreError> {
let entry = self.entry_for(key)?;
let json = match entry.get_password() {
Ok(s) => s,
Err(keyring::Error::NoEntry) => return Ok(None),
Err(e) => return Err(TokenStoreError::KeyringUnavailable(e.to_string())),
};
let tokens: PersistedTokens = serde_json::from_str(&json)?;
Ok(Some(tokens))
}
async fn save(&self, key: &TokenKey, tokens: &PersistedTokens) -> Result<(), TokenStoreError> {
let entry = self.entry_for(key)?;
let json = serde_json::to_string(tokens)?;
entry
.set_password(&json)
.map_err(|e| TokenStoreError::KeyringUnavailable(e.to_string()))?;
Ok(())
}
async fn clear(&self, key: &TokenKey) -> Result<(), TokenStoreError> {
let entry = self.entry_for(key)?;
match entry.delete_credential() {
Ok(()) | Err(keyring::Error::NoEntry) => Ok(()),
Err(e) => Err(TokenStoreError::KeyringUnavailable(e.to_string())),
}
}
async fn list(&self) -> Result<Vec<TokenKey>, TokenStoreError> {
Err(TokenStoreError::Unavailable(
"keyring backend does not support enumeration".into(),
))
}
fn backend_name(&self) -> &'static str {
"keyring"
}
}