rusty_vault 0.2.1

RustyVault is a powerful identity-based secrets management software, providing features such as cryptographic key management, encryption as a service, public key cryptography, certificates management, identity credentials management and so forth. RustyVault's RESTful API is designed to be fully compatible with Hashicorp Vault.
Documentation
use std::{sync::Arc, time::Duration};

use derive_more::{Deref, DerefMut};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

use super::{lease::Lease, Backend, Request, Response};
use crate::errors::RvError;

type SecretOperationHandler = dyn Fn(&dyn Backend, &mut Request) -> Result<Option<Response>, RvError> + Send + Sync;

#[derive(Debug, Clone, Serialize, Deserialize, Deref, DerefMut)]
pub struct SecretData {
    #[deref]
    #[deref_mut]
    #[serde(flatten)]
    pub lease: Lease,
    pub lease_id: String,
    #[serde(skip)]
    pub internal_data: Map<String, Value>,
}

pub struct Secret {
    pub secret_type: String,
    pub default_duration: Duration,
    pub renew_handler: Option<Arc<SecretOperationHandler>>,
    pub revoke_handler: Option<Arc<SecretOperationHandler>>,
}

impl Secret {
    pub fn renewable(&self) -> bool {
        self.renew_handler.is_some()
    }

    pub fn response(&self, data: Option<Map<String, Value>>, internal: Option<Map<String, Value>>) -> Response {
        let mut lease = Lease::default();
        lease.ttl = self.default_duration;
        lease.renewable = self.renewable();

        let mut secret = SecretData { lease, lease_id: String::new(), internal_data: Map::new() };

        if internal.is_some() {
            secret.internal_data = internal.as_ref().unwrap().clone();
        }

        secret.internal_data.insert("secret_type".to_owned(), Value::String(self.secret_type.clone()));

        let mut resp = Response::default();
        resp.data = data;
        resp.secret = Some(secret);
        resp
    }

    pub fn renew(&self, backend: &dyn Backend, req: &mut Request) -> Result<Option<Response>, RvError> {
        if !self.renewable() || self.renew_handler.is_none() {
            return Err(RvError::ErrLogicalOperationUnsupported);
        }

        (self.renew_handler.as_ref().unwrap())(backend, req)
    }

    pub fn revoke(&self, backend: &dyn Backend, req: &mut Request) -> Result<Option<Response>, RvError> {
        if self.revoke_handler.is_none() {
            return Err(RvError::ErrLogicalOperationUnsupported);
        }

        (self.revoke_handler.as_ref().unwrap())(backend, req)
    }
}

#[macro_export]
macro_rules! new_secret {
    ($($tt:tt)*) => {
        new_secret_internal!($($tt)*)
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! new_secret_internal {
    (@object $object:ident () {}) => {
    };
    (@object $object:ident () {secret_type: $secret_type:expr, $($rest:tt)*}) => {
        $object.secret_type = $secret_type.to_string();
        new_secret_internal!(@object $object () {$($rest)*});
    };
    (@object $object:ident () {default_duration: $duration:expr, $($rest:tt)*}) => {
        $object.default_duration = Duration::new($duration, 0);
        new_secret_internal!(@object $object () {$($rest)*});
    };
    (@object $object:ident () {renew_handler: $handler_obj:ident$(.$handler_method:ident)*, $($rest:tt)*}) => {
        $object.renew_handler = Some(Arc::new(move |backend: &dyn Backend, req: &mut Request| -> Result<Option<Response>, RvError> {
            $handler_obj$(.$handler_method)*(backend, req)
        }));
        new_secret_internal!(@object $object () {$($rest)*});
    };
    (@object $object:ident () {revoke_handler: $handler_obj:ident$(.$handler_method:ident)*, $($rest:tt)*}) => {
        $object.revoke_handler = Some(Arc::new(move |backend: &dyn Backend, req: &mut Request| -> Result<Option<Response>, RvError> {
            $handler_obj$(.$handler_method)*(backend, req)
        }));
        new_secret_internal!(@object $object () {$($rest)*});
    };
    ({ $($tt:tt)+ }) => {
        {
            let mut secret = Secret {
                secret_type: String::new(),
                default_duration: Duration::new(0, 0),
                renew_handler: None,
                revoke_handler: None,
            };
            new_secret_internal!(@object secret () {$($tt)+});
            secret
        }
    };
}

#[cfg(test)]
mod test {
    use super::*;

    struct MyTest;

    impl MyTest {
        pub fn new() -> Self {
            MyTest
        }

        pub fn noop(&self, _backend: &dyn Backend, _req: &mut Request) -> Result<Option<Response>, RvError> {
            Ok(None)
        }
    }

    pub fn noop(_backend: &dyn Backend, _req: &mut Request) -> Result<Option<Response>, RvError> {
        Ok(None)
    }

    #[test]
    fn test_logical_secret() {
        let t = MyTest::new();

        let secret: Secret = new_secret!({
            secret_type: "kv",
            default_duration: 60,
            renew_handler: t.noop,
            revoke_handler: noop,
        });

        assert_eq!(&secret.secret_type, "kv");
        assert_eq!(secret.default_duration, Duration::new(60, 0));
        assert!(secret.renew_handler.is_some());
        assert!(secret.revoke_handler.is_some());
    }
}