use super::{CryptoExt, GoogleCryptoKms, IOSCrypto, MongoStore};
use crate::{
prelude::secret::Secret, secrets::SecretsConfig, IntegrationOSError, InternalError,
SecretVersion,
};
use async_trait::async_trait;
use bson::doc;
use secrecy::ExposeSecret;
use serde_json::Value;
#[async_trait]
pub trait SecretExt {
async fn get(&self, id: &str, buildable_id: &str) -> Result<Secret, IntegrationOSError>;
async fn create(
&self,
secret: &Value,
buildable_id: &str,
) -> Result<Secret, IntegrationOSError>;
}
#[derive(Debug, Clone)]
pub struct IOSKms {
storage: MongoStore<Secret>,
crypto: IOSCrypto,
}
impl IOSKms {
pub async fn new(
secrets_config: &SecretsConfig,
storage: MongoStore<Secret>,
) -> Result<Self, IntegrationOSError> {
let crypto = IOSCrypto::new(secrets_config.clone())?;
Ok(Self { crypto, storage })
}
}
#[async_trait]
impl SecretExt for IOSKms {
async fn get(&self, id: &str, buildable_id: &str) -> Result<Secret, IntegrationOSError> {
let secret = self
.storage
.get_one(doc! { "_id": id, "buildableId": buildable_id })
.await?
.ok_or_else(|| InternalError::key_not_found("Secret", None))?;
let encrypted_secret = secret.encrypted_secret().expose_secret().to_owned();
let version = secret.version();
let decrypted_secret = self.crypto.decrypt(encrypted_secret, version).await?;
Ok(Secret::new(
decrypted_secret,
secret.version(),
secret.buildable_id(),
Some(secret.created_at()),
))
}
async fn create(
&self,
secret: &Value,
buildable_id: &str,
) -> Result<Secret, IntegrationOSError> {
let string = serde_json::to_string(&secret).map_err(|_| {
InternalError::serialize_error("The provided value is not a valid UTF-8 string", None)
})?;
let encrypted_secret = self.crypto.encrypt(string).await?;
let secret = Secret::new(
encrypted_secret,
Some(SecretVersion::V2),
buildable_id.to_owned(),
None,
);
self.storage
.create_one(&secret)
.await
.map_err(|e| InternalError::io_err(e.as_ref(), None))?;
Ok(secret)
}
}
#[derive(Debug, Clone)]
pub struct GoogleKms {
storage: MongoStore<Secret>,
crypto: GoogleCryptoKms,
}
impl GoogleKms {
pub async fn new(
secrets_config: &SecretsConfig,
storage: MongoStore<Secret>,
) -> Result<Self, IntegrationOSError> {
let crypto = GoogleCryptoKms::new(secrets_config).await?;
Ok(Self { crypto, storage })
}
}
#[async_trait]
impl SecretExt for GoogleKms {
async fn get(&self, id: &str, buildable_id: &str) -> Result<Secret, IntegrationOSError> {
let secret = self
.storage
.get_one(doc! { "_id": id, "buildableId": buildable_id })
.await?
.ok_or_else(|| InternalError::key_not_found("Secret", None))?;
let encrypted_secret = secret.encrypted_secret().expose_secret().to_owned();
let version = secret.version();
let decrypted_secret = self.crypto.decrypt(encrypted_secret, version).await?;
Ok(Secret::new(
decrypted_secret,
secret.version(),
secret.buildable_id(),
Some(secret.created_at()),
))
}
async fn create(
&self,
secret: &Value,
buildable_id: &str,
) -> Result<Secret, IntegrationOSError> {
let string = serde_json::to_string(&secret).map_err(|_| {
InternalError::serialize_error("The provided value is not a valid UTF-8 string", None)
})?;
let encrypted_secret = self.crypto.encrypt(string).await?;
let secret = Secret::new(
encrypted_secret,
Some(SecretVersion::V2),
buildable_id.to_owned(),
None,
);
self.storage
.create_one(&secret)
.await
.map_err(|e| InternalError::io_err(e.as_ref(), None))?;
Ok(secret)
}
}