use affinidi_data_integrity::{DataIntegrityProof, SignOptions};
use affinidi_tdk::secrets_resolver::secrets::Secret;
use serde_json::Value;
use trust_tasks_rs::TrustTask;
use crate::errors::OpenVTCError;
pub use trust_tasks_capability_client::{
CAPABILITY_DISABLE_TYPE, CAPABILITY_ENABLE_TYPE, CAPABILITY_LIST_TYPE, CapabilityReply,
CapabilitySummary, TRUST_TASK_ENVELOPE_TYPE, build_list_document, build_toggle_document,
correlation_thread, parse_capability_reply, parse_envelope_document, parse_envelope_reply,
};
pub type RequestDocument = TrustTask<Value>;
pub async fn sign_document(
doc: &mut TrustTask<Value>,
signing_secret: &Secret,
) -> Result<(), OpenVTCError> {
let mut doc_value = serde_json::to_value(&*doc)
.map_err(|e| OpenVTCError::Config(format!("serialise capability document: {e}")))?;
if let Some(obj) = doc_value.as_object_mut() {
obj.remove("proof");
}
let proof = DataIntegrityProof::sign(
&doc_value,
signing_secret,
SignOptions::new().with_proof_purpose("authentication"),
)
.await
.map_err(|e| OpenVTCError::Config(format!("sign capability document: {e}")))?;
let proof_value = serde_json::to_value(&proof)
.map_err(|e| OpenVTCError::Config(format!("serialise proof: {e}")))?;
doc.proof = Some(
serde_json::from_value(proof_value)
.map_err(|e| OpenVTCError::Config(format!("convert proof: {e}")))?,
);
Ok(())
}
pub async fn send_capability_document(
route: &crate::community_send::Delivery<'_>,
doc: &TrustTask<Value>,
) -> Result<String, OpenVTCError> {
let body = serde_json::to_value(doc)
.map_err(|e| OpenVTCError::Config(format!("serialise capability document: {e}")))?;
let thid = correlation_thread(doc).to_string();
crate::community_send::send_document(route, doc.id.clone(), body).await?;
Ok(thid)
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
#[test]
fn re_exported_builders_are_addressed() {
let list = build_list_document("did:example:me", "did:example:vtc");
assert_eq!(list.recipient.as_deref(), Some("did:example:vtc"));
assert_eq!(list.type_uri.slug(), "governance/capability/list");
let enable = build_toggle_document(
"did:example:me",
"did:example:vtc",
"git-trust",
"0.1",
true,
);
assert_eq!(enable.payload["config"]["authority"], "did:example:vtc");
}
#[tokio::test]
async fn a_list_request_is_signed_for_authentication() {
use affinidi_tdk::dids::{DID, KeyType};
let (me, signer) = DID::generate_did_key(KeyType::Ed25519).unwrap();
let mut a = build_list_document(&me, "did:example:vtc");
let b = build_list_document(&me, "did:example:vtc");
assert_ne!(a.id, b.id, "every request has its own id");
sign_document(&mut a, &signer).await.unwrap();
let v = serde_json::to_value(&a).unwrap();
assert_eq!(v["issuer"], me.as_str());
assert_eq!(v["recipient"], "did:example:vtc");
assert!(v.get("issuedAt").is_some(), "{v}");
assert_eq!(v["proof"]["proofPurpose"], "authentication");
}
#[tokio::test]
async fn a_toggle_request_is_signed_for_authentication() {
use affinidi_tdk::dids::{DID, KeyType};
let (issuer_did, signer) =
DID::generate_did_key(KeyType::Ed25519).expect("did:key generates");
let mut doc =
build_toggle_document(&issuer_did, "did:example:vtc", "git-trust", "0.1", true);
sign_document(&mut doc, &signer).await.expect("signs");
let proof = doc.proof.as_ref().expect("a proof is attached");
assert_eq!(proof.proof_purpose, "authentication");
}
}