use crate::access_control::manifest::CreateAccessControllerOptions;
use crate::access_control::manifest::ManifestParams;
use crate::address::Address;
use crate::guardian::error::{GuardianError, Result};
use crate::log::{access_control, identity_provider::IdentityProvider};
use crate::p2p::{Emitter, EventBus};
use crate::traits::{CreateDBOptions, GuardianDBKVStoreProvider, KeyValueStore};
use iroh_blobs::Hash;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{Span, debug, instrument, warn};
type KVStoreType =
RwLock<Option<Arc<tokio::sync::Mutex<Box<dyn KeyValueStore<Error = GuardianError>>>>>>;
#[derive(Debug, Clone)]
struct StringAddress(String);
impl std::fmt::Display for StringAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Address for StringAddress {
fn get_root(&self) -> Hash {
Hash::from_bytes([0u8; 32]) }
fn get_path(&self) -> &str {
&self.0
}
fn equals(&self, other: &dyn Address) -> bool {
format!("{}", self) == format!("{}", other)
}
}
#[derive(Debug, Clone)]
pub struct EventUpdated {
pub controller_type: String,
pub address: String,
pub action: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
impl EventUpdated {
pub fn new(controller_type: String, address: String, action: String) -> Self {
Self {
controller_type,
address,
action,
timestamp: chrono::Utc::now(),
}
}
}
pub struct GuardianDBAccessController {
event_bus: EventBus,
event_emitter: Arc<tokio::sync::Mutex<Option<Emitter<EventUpdated>>>>,
guardian_db: Arc<dyn GuardianDBKVStoreProvider<Error = GuardianError>>,
kv_store: KVStoreType,
options: Box<dyn ManifestParams>,
span: Span,
}
impl GuardianDBAccessController {
pub fn span(&self) -> &Span {
&self.span
}
pub fn get_type(&self) -> &'static str {
"GuardianDB"
}
pub async fn address(&self) -> Option<Box<dyn Address>> {
let store_guard = self.kv_store.read().await;
if let Some(store_arc) = store_guard.as_ref() {
let store = store_arc.lock().await;
let addr = store.address();
let addr_string = format!("{}", addr);
Some(Box::new(StringAddress(addr_string)) as Box<dyn Address>)
} else {
None
}
}
pub async fn get_authorized_by_role(&self, role: &str) -> Result<Vec<String>> {
let authorizations = self.get_authorizations().await?;
Ok(authorizations.get(role).cloned().unwrap_or_default())
}
async fn get_authorizations(&self) -> Result<HashMap<String, Vec<String>>> {
let mut authorizations_set: HashMap<String, HashSet<String>> = HashMap::new();
let store_guard = self.kv_store.read().await;
let store = match store_guard.as_ref() {
Some(s) => s,
None => return Ok(HashMap::new()),
};
let store_lock = store.lock().await;
let all_data = store_lock.all();
for (role, key_bytes) in all_data {
let authorized_keys: Vec<String> =
crate::guardian::serializer::deserialize(&key_bytes)?;
let entry = authorizations_set.entry(role).or_default();
for key in authorized_keys {
entry.insert(key);
}
}
if let Some(write_keys) = authorizations_set.get("write").cloned() {
let admin_keys = authorizations_set.entry("admin".to_string()).or_default();
for key in write_keys.iter() {
admin_keys.insert(key.clone());
}
}
let authorizations_list = authorizations_set
.into_iter()
.map(|(permission, keys)| (permission, keys.into_iter().collect()))
.collect();
Ok(authorizations_list)
}
#[instrument(skip(self, entry, identity_provider, _additional_context))]
pub async fn can_append(
&self,
entry: &dyn access_control::LogEntry,
identity_provider: &dyn IdentityProvider,
_additional_context: &dyn access_control::CanAppendAdditionalContext,
) -> Result<()> {
let write_access = self.get_authorized_by_role("write").await?;
let admin_access = self.get_authorized_by_role("admin").await?;
let access: HashSet<String> = write_access
.into_iter()
.chain(admin_access.into_iter())
.collect();
let entry_id = entry.get_identity().id();
if access.contains(entry_id) || access.contains("*") {
identity_provider
.verify_identity(entry.get_identity())
.await?;
return Ok(());
}
Err(GuardianError::Store("Não autorizado".to_string()))
}
#[allow(dead_code)]
#[instrument(skip(self), fields(capability = %capability, key_id = %key_id))]
pub async fn grant(&self, capability: &str, key_id: &str) -> Result<()> {
{
let store_guard = self.kv_store.read().await;
let store_arc = store_guard
.as_ref()
.ok_or_else(|| GuardianError::Store("kv_store não inicializado".to_string()))?;
let mut capabilities: HashSet<String> = self
.get_authorized_by_role(capability)
.await?
.into_iter()
.collect();
capabilities.insert(key_id.to_string());
let capabilities_vec: Vec<String> = capabilities.into_iter().collect();
let capabilities_bytes = crate::guardian::serializer::serialize(&capabilities_vec)?;
let store = store_arc.lock().await;
store
.put(capability, capabilities_bytes)
.await
.map_err(|e| GuardianError::Store(format!("Erro ao salvar no store: {}", e)))?;
}
self.on_update("grant", capability, key_id).await;
Ok(())
}
#[allow(dead_code)]
#[instrument(skip(self), fields(capability = %capability, key_id = %key_id))]
pub async fn revoke(&self, capability: &str, key_id: &str) -> Result<()> {
{
let store_guard = self.kv_store.read().await;
let store_arc = store_guard
.as_ref()
.ok_or_else(|| GuardianError::Store("kv_store não inicializado".to_string()))?;
let mut capabilities: Vec<String> = self.get_authorized_by_role(capability).await?;
capabilities.retain(|id| id != key_id);
let store = store_arc.lock().await;
if !capabilities.is_empty() {
let capabilities_bytes = crate::guardian::serializer::serialize(&capabilities)?;
store
.put(capability, capabilities_bytes)
.await
.map_err(|e| {
GuardianError::Store(format!("Erro ao persistir permissões: {}", e))
})?;
} else {
store.delete(capability).await.map_err(|e| {
GuardianError::Store(format!("Erro ao remover permissões: {}", e))
})?;
}
}
self.on_update("revoke", capability, key_id).await;
Ok(())
}
#[instrument(skip(self), fields(address = %address))]
pub async fn load(&self, address: &str) -> Result<()> {
let mut store_guard = self.kv_store.write().await;
if let Some(_store) = store_guard.take() {
}
let write_access = self.options.get_access("admin");
let write_access = match write_access {
Some(access) if !access.is_empty() => access,
_ => {
vec!["*".to_string()] }
};
let db_address = crate::access_control::ensure_address(address);
let mut store_options = CreateDBOptions::default();
let iroh_ac_params = CreateAccessControllerOptions::new_simple("iroh".to_string(), {
let mut access = HashMap::new();
access.insert("write".to_string(), write_access);
access
});
store_options.access_controller = Some(Box::new(iroh_ac_params));
let store = self
.guardian_db
.key_value(&db_address, &mut store_options)
.await
.map_err(|e| GuardianError::Store(format!("Erro ao abrir key-value store: {}", e)))?;
*store_guard = Some(Arc::new(tokio::sync::Mutex::new(store)));
Ok(())
}
#[instrument(skip(self))]
pub async fn save(&self) -> Result<Box<dyn ManifestParams>> {
let store_guard = self.kv_store.read().await;
let store_arc = store_guard
.as_ref()
.ok_or_else(|| GuardianError::Store("kv_store não inicializado".to_string()))?;
let store = store_arc.lock().await;
let addr = store.address();
let addr_string = format!("{}", addr);
debug!(target: "access_controller", address = %addr_string, "Save executado para o store");
let hash = iroh_blobs::Hash::from([0u8; 32]);
let params = CreateAccessControllerOptions::new(hash, false, "GuardianDB".to_string());
Ok(Box::new(params))
}
#[instrument(skip(self))]
pub async fn close(&self) -> Result<()> {
let mut store_guard = self.kv_store.write().await;
if let Some(store_arc) = store_guard.take() {
let store = store_arc.lock().await;
match store.close().await {
Ok(_) => debug!(target: "access_controller", "Store fechado com sucesso"),
Err(e) => warn!(target: "access_controller", error = %e, "Erro ao fechar o store"),
}
}
Ok(())
}
async fn on_update(&self, action: &str, capability: &str, key_id: &str) {
let mut emitter_guard = self.event_emitter.lock().await;
if emitter_guard.is_none() {
match self.event_bus.emitter::<EventUpdated>().await {
Ok(emitter) => {
*emitter_guard = Some(emitter);
}
Err(e) => {
warn!(target: "GuardianDB::ac", error = %e, "Falha ao inicializar event emitter");
return;
}
}
}
if let Some(emitter) = emitter_guard.as_ref() {
let address = self
.address()
.await
.map(|addr| format!("{}", addr))
.unwrap_or_else(|| "unknown".to_string());
let event = EventUpdated::new(
"guardian".to_string(),
address,
format!("{}:{}:{}", action, capability, key_id),
);
if let Err(e) = emitter.emit(event) {
warn!(target: "GuardianDB::ac", error = %e, "Falha ao emitir evento de atualização");
} else {
debug!(target: "GuardianDB::ac", action = %action, capability = %capability, key_id = %key_id, "Evento emitido com sucesso");
}
}
}
#[instrument(skip(guardian_db, params))]
pub async fn new(
guardian_db: Arc<dyn GuardianDBKVStoreProvider<Error = GuardianError>>,
params: Box<dyn crate::access_control::manifest::ManifestParams>,
) -> std::result::Result<Self, GuardianError> {
let kv_provider = guardian_db;
let addr_str = if !params.get_name().is_empty() {
params.get_name().to_string()
} else {
format!(
"test-ac-{}",
chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
)
};
let mut opts = CreateDBOptions {
create: Some(true),
overwrite: Some(params.skip_manifest()),
access_controller: None,
store_type: Some("keyvalue".to_string()),
event_bus: Some(EventBus::new()),
..Default::default()
};
let kv_store = kv_provider
.key_value(&addr_str, &mut opts)
.await
.map_err(|e| {
GuardianError::Store(format!("Erro ao inicializar key-value store: {}", e))
})?;
debug!(target: "access_controller", address = %addr_str, "Key-value store inicializada");
let event_bus = EventBus::new();
let _write_access = params.get_access("write");
let controller = Self {
event_bus,
event_emitter: Arc::new(tokio::sync::Mutex::new(None)), guardian_db: kv_provider,
kv_store: RwLock::new(Some(Arc::new(tokio::sync::Mutex::new(kv_store)))), options: params,
span: tracing::info_span!("guardian_access_controller", address = %addr_str),
};
let write_access = controller.options.get_access("write");
if let Some(access_keys) = write_access {
for key in access_keys {
controller.grant("write", &key).await?;
}
}
Ok(controller)
}
}
#[async_trait::async_trait]
impl crate::access_control::traits::AccessController for GuardianDBAccessController {
fn get_type(&self) -> &str {
"guardian"
}
async fn get_authorized_by_role(&self, role: &str) -> Result<Vec<String>> {
self.get_authorized_by_role(role).await
}
async fn grant(&self, capability: &str, key_id: &str) -> Result<()> {
self.grant(capability, key_id).await
}
async fn revoke(&self, capability: &str, key_id: &str) -> Result<()> {
self.revoke(capability, key_id).await
}
async fn load(&self, address: &str) -> Result<()> {
self.load(address).await
}
async fn save(&self) -> Result<Box<dyn crate::access_control::manifest::ManifestParams>> {
self.save().await
}
async fn close(&self) -> Result<()> {
self.close().await
}
async fn can_append(
&self,
entry: &dyn crate::log::access_control::LogEntry,
identity_provider: &dyn crate::log::identity_provider::IdentityProvider,
additional_context: &dyn crate::log::access_control::CanAppendAdditionalContext,
) -> Result<()> {
self.can_append(entry, identity_provider, additional_context)
.await
}
}