use chrono::{DateTime, Duration, Utc};
use auths_policy::approval::ApprovalAttestation;
use auths_policy::types::{CanonicalCapability, CanonicalDid};
use crate::error::ApprovalError;
pub struct GrantApprovalConfig {
pub request_hash: String,
pub approver_did: String,
pub note: Option<String>,
}
pub struct ListApprovalsConfig {
pub repo_path: std::path::PathBuf,
}
pub struct GrantApprovalResult {
pub request_hash: String,
pub approver_did: String,
pub jti: String,
pub expires_at: DateTime<Utc>,
pub context_summary: String,
}
pub fn build_approval_attestation(
request_hash_hex: &str,
approver_did: CanonicalDid,
capabilities: Vec<CanonicalCapability>,
now: DateTime<Utc>,
expires_at: DateTime<Utc>,
) -> Result<ApprovalAttestation, ApprovalError> {
if now >= expires_at {
return Err(ApprovalError::RequestExpired { expires_at });
}
let request_hash = auths_verifier::Hash256::new(hex_to_hash(request_hash_hex)?);
let jti = uuid_v4(now);
let attestation_expires = std::cmp::min(expires_at, now + Duration::minutes(5));
Ok(ApprovalAttestation {
jti,
approver_did,
request_hash,
expires_at: attestation_expires,
approved_capabilities: capabilities,
})
}
fn hex_to_hash(hex: &str) -> Result<[u8; 32], ApprovalError> {
let bytes = hex::decode(hex).map_err(|_| ApprovalError::RequestNotFound {
hash: hex.to_string(),
})?;
if bytes.len() != 32 {
return Err(ApprovalError::RequestNotFound {
hash: hex.to_string(),
});
}
let mut arr = [0u8; 32];
arr.copy_from_slice(&bytes);
Ok(arr)
}
fn uuid_v4(now: DateTime<Utc>) -> String {
let ts = now.timestamp_nanos_opt().unwrap_or_default() as u64;
format!(
"{:08x}-{:04x}-4{:03x}-{:04x}-{:012x}",
(ts >> 32) as u32,
(ts >> 16) & 0xffff,
ts & 0x0fff,
0x8000 | ((ts >> 20) & 0x3fff),
ts & 0xffffffffffff,
)
}