use std::ops::ControlFlow;
use auths_core::storage::keychain::{KeyAlias, extract_public_key_bytes};
use auths_id::keri::delegation::{mark_org_policy, read_org_policy_hash};
use auths_id::keri::types::Prefix;
use auths_id::keri::{Event, Said};
use auths_id::policy::{CompiledPolicy, Decision, EvalContext, compile_from_json, evaluate_strict};
use crate::context::AuthsContext;
use crate::domains::org::delegation::ensure_single_sig_org;
use crate::domains::org::error::OrgError;
pub use auths_id::policy::Expr;
#[derive(Debug, Clone)]
pub struct OrgPolicySet {
pub org_did: String,
pub policy_hash: String,
pub description: String,
}
#[derive(Debug, Clone)]
pub struct LoadedOrgPolicy {
pub compiled: CompiledPolicy,
pub policy_hash: String,
pub source_json: String,
}
fn collect_org_kel(ctx: &AuthsContext, org_prefix: &Prefix) -> Vec<Event> {
let mut events = Vec::new();
let _ = ctx.registry.visit_events(org_prefix, 0, &mut |e| {
events.push(e.clone());
ControlFlow::Continue(())
});
events
}
fn policy_blob_key(source_hash_hex: &str) -> Said {
Said::new_unchecked(format!("policy-{source_hash_hex}"))
}
fn compile_err(errs: Vec<auths_id::policy::CompileError>) -> OrgError {
OrgError::PolicyCompile {
reason: errs
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join("; "),
}
}
pub fn set_org_policy(
ctx: &AuthsContext,
org_prefix: &Prefix,
org_alias: &KeyAlias,
policy_json: &[u8],
) -> Result<OrgPolicySet, OrgError> {
ensure_single_sig_org(ctx, org_prefix)?;
let compiled = compile_from_json(policy_json).map_err(compile_err)?;
let hash_hex = hex::encode(compiled.source_hash());
ctx.registry
.store_credential(org_prefix, &policy_blob_key(&hash_hex), policy_json)
.map_err(OrgError::Storage)?;
let (_pk, org_curve) = extract_public_key_bytes(
ctx.key_storage.as_ref(),
org_alias,
ctx.passphrase_provider.as_ref(),
)
.map_err(OrgError::CryptoError)?;
mark_org_policy(
ctx.registry.as_ref(),
org_prefix,
org_alias,
org_curve,
&hash_hex,
ctx.passphrase_provider.as_ref(),
ctx.key_storage.as_ref(),
)
.map_err(OrgError::Delegation)?;
Ok(OrgPolicySet {
org_did: format!("did:keri:{}", org_prefix.as_str()),
policy_hash: hash_hex,
description: compiled.describe(),
})
}
pub fn load_org_policy(
ctx: &AuthsContext,
org_prefix: &Prefix,
) -> Result<Option<LoadedOrgPolicy>, OrgError> {
let kel = collect_org_kel(ctx, org_prefix);
let Some(hash_hex) = read_org_policy_hash(&kel) else {
return Ok(None);
};
let bytes = ctx
.registry
.load_credential(org_prefix, &policy_blob_key(&hash_hex))
.map_err(OrgError::Storage)?
.ok_or_else(|| OrgError::PolicyBlobMissing {
hash: hash_hex.clone(),
})?;
let compiled = compile_from_json(&bytes).map_err(compile_err)?;
let actual = hex::encode(compiled.source_hash());
if actual != hash_hex {
return Err(OrgError::PolicyIntegrity {
expected: hash_hex,
actual,
});
}
Ok(Some(LoadedOrgPolicy {
compiled,
policy_hash: hash_hex,
source_json: String::from_utf8_lossy(&bytes).into_owned(),
}))
}
pub fn evaluate_with_org_policy(policy: &LoadedOrgPolicy, eval_ctx: &EvalContext) -> Decision {
evaluate_strict(&policy.compiled, eval_ctx).with_policy_hash(*policy.compiled.source_hash())
}