use crate::{
access_control::{acl_simple::SimpleAccessController, traits::AccessController},
guardian::error::Result,
log::{
entry::Entry,
identity::{Identity, Signatures},
},
};
use std::{collections::HashMap, sync::Arc};
use tokio;
fn create_test_identity(id: &str, pub_key: &str) -> Identity {
let signatures = Signatures::new(&format!("sig_{}", id), &format!("sig_{}", pub_key));
Identity::new(id, pub_key, signatures)
}
fn create_test_head(hash: &str, identity: Option<Identity>) -> Entry {
use crate::log::{entry::EntryOrHash, lamport_clock::LamportClock};
let clock = LamportClock::new("test_peer").set_time(1);
let next: Vec<EntryOrHash> = Vec::new();
let entry_identity =
identity.unwrap_or_else(|| create_test_identity("default_test_user", "default_test_key"));
Entry::new(
entry_identity,
"test_log",
format!("payload_{}", hash).as_bytes(),
&next,
Some(clock),
)
}
async fn create_test_access_controller() -> Arc<dyn AccessController> {
let mut permissions = HashMap::new();
permissions.insert("read".to_string(), Vec::new());
permissions.insert("write".to_string(), Vec::new());
permissions.insert("admin".to_string(), Vec::new());
Arc::new(SimpleAccessController::new(Some(permissions)))
}
#[tokio::test]
async fn test_verify_heads_permissions_with_authorized_identity() -> Result<()> {
let access_controller = create_test_access_controller().await;
let authorized_identity = create_test_identity("authorized_user", "auth_key_123");
access_controller
.grant("write", authorized_identity.id())
.await?;
let test_heads = vec![create_test_head(
"authorized_head",
Some(authorized_identity),
)];
let result = simulate_permission_check(&test_heads, &access_controller).await?;
assert_eq!(result.len(), 1, "Head autorizado deve ser aceito");
assert_eq!(
result[0].hash, test_heads[0].hash,
"Hash deve corresponder ao original"
);
Ok(())
}
#[tokio::test]
async fn test_verify_heads_permissions_with_unauthorized_identity() -> Result<()> {
let access_controller = create_test_access_controller().await;
let unauthorized_identity = create_test_identity("unauthorized_user", "unauth_key_456");
let test_heads = vec![create_test_head(
"unauthorized_head",
Some(unauthorized_identity),
)];
let result = simulate_permission_check(&test_heads, &access_controller).await?;
assert_eq!(result.len(), 0, "Head não autorizado deve ser rejeitado");
Ok(())
}
#[tokio::test]
async fn test_verify_heads_permissions_with_admin_identity() -> Result<()> {
let access_controller = create_test_access_controller().await;
let admin_identity = create_test_identity("admin_user", "admin_key_789");
access_controller
.grant("admin", admin_identity.id())
.await?;
let test_heads = vec![create_test_head("admin_head", Some(admin_identity))];
let result = simulate_permission_check(&test_heads, &access_controller).await?;
assert_eq!(result.len(), 1, "Head com permissão admin deve ser aceito");
assert_eq!(
result[0].hash, test_heads[0].hash,
"Hash deve corresponder ao original"
);
Ok(())
}
#[tokio::test]
async fn test_verify_heads_permissions_without_identity() -> Result<()> {
let access_controller = create_test_access_controller().await;
let test_heads = vec![create_test_head("no_identity_head", None)];
let result = simulate_permission_check(&test_heads, &access_controller).await?;
assert_eq!(result.len(), 0, "Head sem identidade deve ser rejeitado");
Ok(())
}
#[tokio::test]
async fn test_verify_heads_permissions_with_wildcard() -> Result<()> {
let access_controller = create_test_access_controller().await;
let any_identity = create_test_identity("any_user", "any_key_999");
access_controller.grant("write", "*").await?;
let test_heads = vec![create_test_head("wildcard_head", Some(any_identity))];
let result = simulate_permission_check(&test_heads, &access_controller).await?;
assert_eq!(
result.len(),
1,
"Head deve ser aceito com permissão universal"
);
assert_eq!(
result[0].hash, test_heads[0].hash,
"Hash deve corresponder ao original"
);
Ok(())
}
#[tokio::test]
async fn test_verify_heads_permissions_with_empty_identity() -> Result<()> {
let access_controller = create_test_access_controller().await;
let empty_identity = Identity::new(
"", "", Signatures::new("", ""),
);
let test_heads = vec![create_test_head(
"empty_identity_head",
Some(empty_identity),
)];
let result = simulate_permission_check(&test_heads, &access_controller).await?;
assert_eq!(
result.len(),
0,
"Head com identidade vazia deve ser rejeitado"
);
Ok(())
}
#[tokio::test]
async fn test_verify_heads_permissions_mixed_scenarios() -> Result<()> {
let access_controller = create_test_access_controller().await;
let authorized_identity = create_test_identity("authorized", "auth_key");
let unauthorized_identity = create_test_identity("unauthorized", "unauth_key");
let admin_identity = create_test_identity("admin", "admin_key");
access_controller
.grant("write", authorized_identity.id())
.await?;
access_controller
.grant("admin", admin_identity.id())
.await?;
let test_heads = vec![
create_test_head("head_1", Some(authorized_identity)), create_test_head("head_2", Some(unauthorized_identity)), create_test_head("head_3", Some(admin_identity)), create_test_head("head_4", None), ];
let result = simulate_permission_check(&test_heads, &access_controller).await?;
assert_eq!(result.len(), 2, "Apenas 2 heads devem ser aceitos");
let accepted_ids: Vec<String> = result
.iter()
.filter_map(|h| h.identity.as_ref().map(|id| id.id().to_string()))
.collect();
assert!(
accepted_ids.contains(&"authorized".to_string()),
"Head autorizado deve ser aceito"
);
assert!(
accepted_ids.contains(&"admin".to_string()),
"Head admin deve ser aceito"
);
assert!(
!accepted_ids.contains(&"unauthorized".to_string()),
"Head não autorizado deve ser rejeitado"
);
assert!(
!accepted_ids.contains(&"default_test_user".to_string()),
"Head sem identidade deve ser rejeitado"
);
Ok(())
}
#[tokio::test]
async fn test_verify_heads_permissions_by_public_key() -> Result<()> {
let access_controller = create_test_access_controller().await;
let identity = create_test_identity("user_by_key", "special_key_123");
access_controller.grant("write", identity.pub_key()).await?;
let test_heads = vec![create_test_head("key_based_head", Some(identity))];
let result = simulate_permission_check(&test_heads, &access_controller).await?;
assert_eq!(
result.len(),
1,
"Head autorizado por chave pública deve ser aceito"
);
Ok(())
}
async fn simulate_permission_check(
heads: &[Entry],
access_controller: &Arc<dyn AccessController>,
) -> Result<Vec<Entry>> {
let mut authorized_heads = Vec::new();
for head in heads {
let identity = match &head.identity {
Some(identity) => identity,
None => continue,
};
if identity.id().is_empty() || identity.pub_key().is_empty() {
continue;
}
let identity_key = identity.pub_key();
let has_write_permission = match access_controller.get_authorized_by_role("write").await {
Ok(authorized_keys) => {
authorized_keys.contains(&identity_key.to_string())
|| authorized_keys.contains(&identity.id().to_string())
|| authorized_keys.contains(&"*".to_string())
}
Err(_) => false,
};
let has_admin_permission = match access_controller.get_authorized_by_role("admin").await {
Ok(admin_keys) => {
admin_keys.contains(&identity_key.to_string())
|| admin_keys.contains(&identity.id().to_string())
|| admin_keys.contains(&"*".to_string())
}
Err(_) => false,
};
if has_write_permission || has_admin_permission {
authorized_heads.push(head.clone());
}
}
Ok(authorized_heads)
}
#[tokio::test]
async fn test_access_controller_integration() -> Result<()> {
let access_controller = create_test_access_controller().await;
assert_eq!(
access_controller.get_type(),
"simple",
"Deve ser SimpleAccessController"
);
access_controller.grant("write", "test_key").await?;
let write_keys = access_controller.get_authorized_by_role("write").await?;
assert!(
write_keys.contains(&"test_key".to_string()),
"Chave deve estar autorizada"
);
access_controller.revoke("write", "test_key").await?;
let write_keys_after = access_controller.get_authorized_by_role("write").await?;
assert!(
!write_keys_after.contains(&"test_key".to_string()),
"Chave deve estar revogada"
);
Ok(())
}