auths_sdk/workflows/
approval.rs1use chrono::{DateTime, Duration, Utc};
9
10use auths_policy::approval::ApprovalAttestation;
11use auths_policy::types::{CanonicalCapability, CanonicalDid};
12
13use crate::error::ApprovalError;
14
15pub struct GrantApprovalConfig {
17 pub request_hash: String,
19 pub approver_did: String,
21 pub note: Option<String>,
23}
24
25pub struct ListApprovalsConfig {
27 pub repo_path: std::path::PathBuf,
29}
30
31pub struct GrantApprovalResult {
33 pub request_hash: String,
35 pub approver_did: String,
37 pub jti: String,
39 pub expires_at: DateTime<Utc>,
41 pub context_summary: String,
43}
44
45pub fn build_approval_attestation(
59 request_hash_hex: &str,
60 approver_did: CanonicalDid,
61 capabilities: Vec<CanonicalCapability>,
62 now: DateTime<Utc>,
63 expires_at: DateTime<Utc>,
64) -> Result<ApprovalAttestation, ApprovalError> {
65 if now >= expires_at {
66 return Err(ApprovalError::RequestExpired { expires_at });
67 }
68
69 let request_hash = auths_verifier::Hash256::new(hex_to_hash(request_hash_hex)?);
70 let jti = uuid_v4(now);
71
72 let attestation_expires = std::cmp::min(expires_at, now + Duration::minutes(5));
74
75 Ok(ApprovalAttestation {
76 jti,
77 approver_did,
78 request_hash,
79 expires_at: attestation_expires,
80 approved_capabilities: capabilities,
81 })
82}
83
84fn hex_to_hash(hex: &str) -> Result<[u8; 32], ApprovalError> {
85 let bytes = hex::decode(hex).map_err(|_| ApprovalError::RequestNotFound {
86 hash: hex.to_string(),
87 })?;
88 if bytes.len() != 32 {
89 return Err(ApprovalError::RequestNotFound {
90 hash: hex.to_string(),
91 });
92 }
93 let mut arr = [0u8; 32];
94 arr.copy_from_slice(&bytes);
95 Ok(arr)
96}
97
98fn uuid_v4(now: DateTime<Utc>) -> String {
99 let ts = now.timestamp_nanos_opt().unwrap_or_default() as u64;
100 format!(
101 "{:08x}-{:04x}-4{:03x}-{:04x}-{:012x}",
102 (ts >> 32) as u32,
103 (ts >> 16) & 0xffff,
104 ts & 0x0fff,
105 0x8000 | ((ts >> 20) & 0x3fff),
106 ts & 0xffffffffffff,
107 )
108}