use chrono::Utc;
use zeroize::Zeroize;
use crate::audit::{AuditLog, AuditRecord};
use crate::cell::Cell;
use crate::error::HexvaultError;
use crate::keys::PartitionKey;
use crate::stack::{Layer, LayerContext};
pub struct TraversalRequest<'a> {
pub source_partition_key: &'a PartitionKey,
pub dest_partition_key: &'a PartitionKey,
pub source: &'a Cell,
pub dest: &'a mut Cell,
pub key: &'a str,
pub target_layer: Layer,
pub source_ctx: &'a LayerContext,
pub dest_ctx: &'a LayerContext,
}
pub fn traverse(audit: &mut AuditLog, req: TraversalRequest) -> Result<(), HexvaultError> {
let mut plaintext = req
.source
.retrieve(req.source_partition_key, req.key, req.source_ctx)?;
let seal_result = req.dest.store(
req.dest_partition_key,
req.key,
&plaintext,
req.target_layer,
req.dest_ctx,
);
plaintext.zeroize();
seal_result?;
let record = AuditRecord {
source_cell_id: req.source.id().to_string(),
dest_cell_id: req.dest.id().to_string(),
layer: req.target_layer,
timestamp: Utc::now(),
entry_hash: String::new(),
};
audit.append(record);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::keys::{self, MasterKey};
#[test]
fn test_traverse_audit() {
let master = MasterKey::from_bytes([2u8; 32]);
let partition = keys::derive_partition_key(&master, "p1").unwrap();
let mut cell_a = Cell::new("cell-a".to_string());
let mut cell_b = Cell::new("cell-b".to_string());
let mut audit = AuditLog::new();
let ctx = LayerContext::default();
cell_a
.store(&partition, "secret", b"move me", Layer::AtRest, &ctx)
.unwrap();
traverse(
&mut audit,
TraversalRequest {
source_partition_key: &partition,
dest_partition_key: &partition,
source: &cell_a,
dest: &mut cell_b,
key: "secret",
target_layer: Layer::AtRest,
source_ctx: &ctx,
dest_ctx: &ctx,
},
)
.unwrap();
let retrieved = cell_b.retrieve(&partition, "secret", &ctx).unwrap();
assert_eq!(retrieved, b"move me");
assert_eq!(audit.len(), 1);
let record = audit.iter().next().unwrap();
assert_eq!(record.source_cell_id, "cell-a");
assert_eq!(record.dest_cell_id, "cell-b");
}
}