//! Credential storage (access/secret key metadata) use std::sync::Arc; use iam_types::{CredentialRecord, Result}; use crate::backend::{Backend, CasResult, JsonStore, StorageBackend}; /// Store for credentials (S3/API keys) pub struct CredentialStore { backend: Arc, } impl JsonStore for CredentialStore { fn backend(&self) -> &Backend { &self.backend } } impl CredentialStore { pub fn new(backend: Arc) -> Self { Self { backend } } pub async fn put(&self, record: &CredentialRecord) -> Result { let key = CredentialRecord::storage_key(&record.access_key_id); self.put_json(key.as_bytes(), record).await } pub async fn get(&self, access_key_id: &str) -> Result> { let key = CredentialRecord::storage_key(access_key_id); self.get_json(key.as_bytes()).await } pub async fn list_for_principal( &self, principal_id: &str, limit: u32, ) -> Result> { let prefix = b"iam/credentials/"; let items = self.backend.scan_prefix(prefix, limit).await?; let mut credentials = Vec::new(); for pair in items { let record: CredentialRecord = serde_json::from_slice(&pair.value) .map_err(|e| iam_types::Error::Serialization(e.to_string()))?; if record.principal_id == principal_id { credentials.push(record); } } Ok(credentials) } pub async fn revoke(&self, access_key_id: &str) -> Result { let key = CredentialRecord::storage_key(access_key_id); let current = self.get_json::(key.as_bytes()).await?; let (mut record, version) = match current { Some(v) => v, None => return Ok(false), }; if record.revoked { return Ok(false); } record.revoked = true; match self.cas_json(key.as_bytes(), version, &record).await? { CasResult::Success(_) => Ok(true), CasResult::Conflict { .. } => Ok(false), CasResult::NotFound => Ok(false), } } }