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;

use super::{barrier::SecurityBarrier, Storage, StorageEntry};
use crate::errors::RvError;

pub struct BarrierView {
    barrier: Arc<dyn SecurityBarrier>,
    prefix: String,
}

impl Storage for BarrierView {
    fn list(&self, prefix: &str) -> Result<Vec<String>, RvError> {
        self.sanity_check(prefix)?;
        self.barrier.list(self.expand_key(prefix).as_str())
    }

    fn get(&self, key: &str) -> Result<Option<StorageEntry>, RvError> {
        self.sanity_check(key)?;
        let storage_entry = self.barrier.get(self.expand_key(key).as_str())?;
        if let Some(entry) = storage_entry {
            Ok(Some(StorageEntry { key: self.truncate_key(entry.key.as_str()), value: entry.value }))
        } else {
            Ok(None)
        }
    }

    fn put(&self, entry: &StorageEntry) -> Result<(), RvError> {
        self.sanity_check(entry.key.as_str())?;
        let nested = StorageEntry { key: self.expand_key(entry.key.as_str()), value: entry.value.clone() };
        self.barrier.put(&nested)
    }

    fn delete(&self, key: &str) -> Result<(), RvError> {
        self.sanity_check(key)?;
        self.barrier.delete(self.expand_key(key).as_str())
    }
}

impl BarrierView {
    pub fn new(barrier: Arc<dyn SecurityBarrier>, prefix: &str) -> Self {
        Self { barrier, prefix: prefix.to_string() }
    }

    pub fn new_sub_view(&self, prefix: &str) -> Self {
        Self { barrier: Arc::clone(&self.barrier), prefix: self.expand_key(prefix) }
    }

    pub fn get_keys(&self) -> Result<Vec<String>, RvError> {
        let mut paths = vec!["".to_string()];
        let mut keys = Vec::new();
        while !paths.is_empty() {
            let n = paths.len();
            let curr = paths[n - 1].to_owned();
            paths.pop();

            let items = self.list(curr.as_str())?;
            for p in items {
                let path = format!("{}{}", curr, p);
                if p.ends_with("/") {
                    paths.push(path);
                } else {
                    keys.push(path.to_owned());
                }
            }
        }
        Ok(keys)
    }

    pub fn clear(&self) -> Result<(), RvError> {
        let keys = self.get_keys()?;
        for key in keys {
            self.delete(key.as_str())?
        }
        Ok(())
    }

    pub fn as_storage(&self) -> &dyn Storage {
        self
    }

    fn sanity_check(&self, key: &str) -> Result<(), RvError> {
        if key.contains("..") || key.starts_with("/") {
            Err(RvError::ErrBarrierKeySanityCheckFailed)
        } else {
            Ok(())
        }
    }

    fn expand_key(&self, suffix: &str) -> String {
        format!("{}{}", self.prefix, suffix)
    }

    fn truncate_key(&self, full: &str) -> String {
        if let Some(result) = full.strip_prefix(self.prefix.as_str()) {
            return result.to_string();
        } else {
            return full.to_string();
        }
    }
}

#[cfg(test)]
mod test {
    use std::sync::Arc;

    use rand::{thread_rng, Rng};

    use super::{super::*, *};
    use crate::test_utils::test_backend;

    #[test]
    fn test_new_barrier_view() {
        let backend = test_backend("test_new_barrier_view");

        let mut key = vec![0u8; 32];
        thread_rng().fill(key.as_mut_slice());

        let aes_gcm_view = barrier_aes_gcm::AESGCMBarrier::new(Arc::clone(&backend));

        let init = aes_gcm_view.init(key.as_slice());
        assert!(init.is_ok());

        let view = barrier_view::BarrierView::new(Arc::new(aes_gcm_view), "test");
        assert_eq!(view.expand_key("foo"), "testfoo");
        assert!(view.sanity_check("foo").is_ok());
        assert!(view.sanity_check("../foo").is_err());
        assert!(view.sanity_check("foo/../").is_err());
    }
}