#[cfg(feature = "rotate_aws_sdk")]
mod aws_sdk;
#[cfg(feature = "rotate_rusoto")]
mod rusoto;
mod smc;
pub use smc::{SecretContainer, Smc};
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "rotate_rusoto", feature = "rotate_aws_sdk")))
)]
#[derive(Clone, serde::Deserialize)]
pub struct Event<Secret> {
#[serde(rename = "ClientRequestToken")]
pub client_request_token: String,
#[serde(rename = "SecretId")]
pub secret_id: String,
#[serde(rename = "Step")]
pub step: Step,
#[doc(hidden)]
#[serde(skip)]
pub _m: std::marker::PhantomData<Secret>,
}
impl<Secret> std::fmt::Debug for Event<Secret> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Event")
.field("client_request_token", &self.client_request_token)
.field("secret_id", &self.secret_id)
.field("step", &self.step)
.finish()
}
}
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "rotate_rusoto", feature = "rotate_aws_sdk")))
)]
#[derive(Debug, Copy, Clone, serde::Deserialize)]
pub enum Step {
#[serde(rename = "createSecret")]
Create,
#[serde(rename = "setSecret")]
Set,
#[serde(rename = "testSecret")]
Test,
#[serde(rename = "finishSecret")]
Finish,
}
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "rotate_rusoto", feature = "rotate_aws_sdk")))
)]
#[async_trait::async_trait]
pub trait RotateRunner<'a, Shared, Secret>
where
Shared: Send + Sync + 'a,
Secret: 'static + Send,
{
async fn setup(region: &'a str) -> anyhow::Result<Shared>;
async fn create(
shared: &'a Shared,
secret_cur: SecretContainer<Secret>,
smc: &Smc,
) -> anyhow::Result<SecretContainer<Secret>>;
async fn set(
shared: &'a Shared,
secret_cur: SecretContainer<Secret>,
secret_new: SecretContainer<Secret>,
) -> anyhow::Result<()>;
async fn test(shared: &'a Shared, secret_new: SecretContainer<Secret>) -> anyhow::Result<()>;
async fn finish(
_shared: &'a Shared,
_secret_cur: SecretContainer<Secret>,
_secret_new: SecretContainer<Secret>,
) -> anyhow::Result<()> {
Ok(())
}
}
#[async_trait::async_trait]
impl<'a, Type, Shared, Sec> super::Runner<'a, Shared, Event<Sec>, ()> for Type
where
Shared: Send + Sync + 'a,
Sec: 'static + Send + Sync + Clone + serde::de::DeserializeOwned + serde::Serialize,
Type: 'static + RotateRunner<'a, Shared, Sec>,
{
async fn setup(region: &'a str) -> anyhow::Result<Shared> {
Self::setup(region).await
}
async fn run(
shared: &'a Shared,
event: crate::LambdaEvent<'a, Event<Sec>>,
) -> anyhow::Result<()> {
let smc = Smc::new(event.region).await?;
log::info!("{:?}", event.event.step);
match event.event.step {
Step::Create => {
let secret_cur = smc
.get_secret_value_current::<Sec>(&event.event.secret_id)
.await?;
let secret_new = smc
.get_secret_value_pending::<Sec>(&event.event.secret_id)
.await;
if let Ok(secret_new) = secret_new {
if secret_new.version_id != secret_cur.version_id {
log::info!("Found existing pending value.");
return Ok(());
}
}
log::info!("Creating new secret value.");
let secret = Self::create(shared, secret_cur.inner, &smc).await?;
smc.put_secret_value_pending(
&event.event.secret_id,
Some(&event.event.client_request_token),
&secret,
)
.await?;
Ok(())
}
Step::Set => {
log::info!("Setting secret on remote system.");
let secret_new = smc
.get_secret_value_pending(&event.event.secret_id)
.await?
.inner;
if Self::test(shared, SecretContainer::clone(&secret_new))
.await
.is_err()
{
let secret_cur = smc
.get_secret_value_current(&event.event.secret_id)
.await?
.inner;
Self::set(shared, secret_cur, secret_new).await?;
} else {
log::info!("Password already set in remote system.");
}
Ok(())
}
Step::Test => {
log::info!("Testing secret on remote system.");
let secret = smc
.get_secret_value_pending(&event.event.secret_id)
.await?
.inner;
Self::test(shared, secret).await?;
Ok(())
}
Step::Finish => {
log::info!("Finishing secret deployment.");
let secret_current: smc::Secret<Sec> =
smc.get_secret_value_current(&event.event.secret_id).await?;
let secret_pending: smc::Secret<Sec> =
smc.get_secret_value_pending(&event.event.secret_id).await?;
Self::finish(shared, secret_current.inner, secret_pending.inner).await?;
smc.set_pending_secret_value_to_current(
secret_current.arn,
secret_current.version_id,
secret_pending.version_id,
)
.await?;
Ok(())
}
}
}
}