use std::ops::ControlFlow;
use auths_core::storage::keychain::{KeyAlias, extract_public_key_bytes};
use auths_id::keri::delegation::{mark_org_oidc_policy, read_org_oidc_policy_digest};
use auths_id::keri::types::Prefix;
use auths_id::keri::{Event, Said};
use auths_id::ports::registry::RegistryBackend;
use auths_verifier::oidc_policy::OidcSubjectPolicy;
use sha2::{Digest, Sha256};
use crate::context::AuthsContext;
use crate::domains::org::delegation::ensure_single_sig_org;
use crate::domains::org::error::OrgError;
#[derive(Debug, Clone)]
pub struct OrgOidcPolicySet {
pub org_did: String,
pub policy_digest: String,
pub policy: OidcSubjectPolicy,
}
#[derive(Debug, Clone)]
pub struct LoadedOrgOidcPolicy {
pub policy: OidcSubjectPolicy,
pub policy_digest: String,
pub source_json: String,
}
fn oidc_policy_digest(source: &[u8]) -> String {
hex::encode(Sha256::digest(source))
}
fn oidc_policy_blob_key(digest_hex: &str) -> Said {
Said::new_unchecked(format!("oidcpolicy-{digest_hex}"))
}
pub fn set_org_oidc_policy(
ctx: &AuthsContext,
org_prefix: &Prefix,
org_alias: &KeyAlias,
policy_json: &[u8],
) -> Result<OrgOidcPolicySet, OrgError> {
ensure_single_sig_org(ctx, org_prefix)?;
let source = std::str::from_utf8(policy_json).map_err(|e| OrgError::OidcPolicyInvalid {
reason: e.to_string(),
})?;
let policy = OidcSubjectPolicy::parse(source).map_err(|e| OrgError::OidcPolicyInvalid {
reason: e.to_string(),
})?;
let digest_hex = oidc_policy_digest(policy_json);
ctx.registry
.store_credential(org_prefix, &oidc_policy_blob_key(&digest_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_oidc_policy(
ctx.registry.as_ref(),
org_prefix,
org_alias,
org_curve,
&digest_hex,
ctx.passphrase_provider.as_ref(),
ctx.key_storage.as_ref(),
)
.map_err(OrgError::Delegation)?;
Ok(OrgOidcPolicySet {
org_did: format!("did:keri:{}", org_prefix.as_str()),
policy_digest: digest_hex,
policy,
})
}
pub fn load_org_oidc_policy(
registry: &(dyn RegistryBackend + Send + Sync),
org_prefix: &Prefix,
) -> Result<Option<LoadedOrgOidcPolicy>, OrgError> {
let mut events: Vec<Event> = Vec::new();
let _ = registry.visit_events(org_prefix, 0, &mut |e| {
events.push(e.clone());
ControlFlow::Continue(())
});
let Some(digest_hex) = read_org_oidc_policy_digest(&events) else {
return Ok(None);
};
let bytes = registry
.load_credential(org_prefix, &oidc_policy_blob_key(&digest_hex))
.map_err(OrgError::Storage)?
.ok_or_else(|| OrgError::PolicyBlobMissing {
hash: digest_hex.clone(),
})?;
let actual = oidc_policy_digest(&bytes);
if actual != digest_hex {
return Err(OrgError::PolicyIntegrity {
expected: digest_hex,
actual,
});
}
let source = String::from_utf8_lossy(&bytes).into_owned();
let policy = OidcSubjectPolicy::parse(&source).map_err(|e| OrgError::OidcPolicyInvalid {
reason: e.to_string(),
})?;
Ok(Some(LoadedOrgOidcPolicy {
policy,
policy_digest: digest_hex,
source_json: source,
}))
}