#![allow(clippy::result_large_err)]
use super::helpers::TrustTaskOutcome;
use serde_json::Value;
use trust_tasks_rs::TrustTask;
use vta_sdk::protocols::credential_exchange::{
PendingApproveBody, PendingDenyBody, PendingDenyResponse, PendingListResponse,
PendingPresentationSummary, RequestedCredentialSummary,
};
use crate::audit::audit;
use crate::auth::AuthClaims;
use crate::operations::credential_exchange::{
RequestedCredential, approve_pending_presentation, deny_pending_presentation, pending,
};
use crate::server::AppState;
use super::helpers::{app_error_to_reject, parse_payload, success_response};
fn summarize(record: pending::PendingPresentation) -> PendingPresentationSummary {
PendingPresentationSummary {
id: record.id,
verifier_did: record.verifier_did,
requested: record
.requested
.into_iter()
.map(summarize_requested)
.collect(),
purpose: record.purpose,
created_at: record.created_at,
expires_at: record.expires_at,
}
}
fn summarize_requested(r: RequestedCredential) -> RequestedCredentialSummary {
RequestedCredentialSummary {
credential_query_id: r.credential_query_id,
credential_id: r.credential_id,
claims: r.claims,
}
}
pub(super) async fn handle_pending_list(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
if let Err(e) = auth.require_super_admin() {
return app_error_to_reject(&doc, e);
}
let records = match pending::list(&state.vault_ks).await {
Ok(r) => r,
Err(e) => return app_error_to_reject(&doc, e),
};
let now = chrono::Utc::now();
let pending_out: Vec<PendingPresentationSummary> = records
.into_iter()
.filter(|r| r.status == pending::PendingStatus::Pending && r.expires_at > now)
.map(summarize)
.collect();
audit!(
"credential-exchange.pending-list",
actor = &auth.did,
resource = "pending-present",
outcome = "success"
);
success_response(
&doc,
PendingListResponse {
pending: pending_out,
},
)
}
pub(super) async fn handle_pending_approve(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
if let Err(e) = auth.require_super_admin() {
return app_error_to_reject(&doc, e);
}
let body: PendingApproveBody = match parse_payload(&doc) {
Ok(b) => b,
Err(resp) => return resp,
};
let present = match approve_pending_presentation(
&state.vault_ks,
&state.keys_ks,
&state.seed_store,
auth,
&body.id,
state.status_list_resolver.as_deref(),
chrono::Utc::now(),
)
.await
{
Ok(p) => p,
Err(e) => return app_error_to_reject(&doc, e),
};
audit!(
"credential-exchange.pending-approve",
actor = &auth.did,
resource = &body.id,
outcome = "success"
);
success_response(&doc, present)
}
pub(super) async fn handle_pending_deny(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
if let Err(e) = auth.require_super_admin() {
return app_error_to_reject(&doc, e);
}
let body: PendingDenyBody = match parse_payload(&doc) {
Ok(b) => b,
Err(resp) => return resp,
};
if let Err(e) = deny_pending_presentation(&state.vault_ks, &body.id).await {
return app_error_to_reject(&doc, e);
}
audit!(
"credential-exchange.pending-deny",
actor = &auth.did,
resource = &body.id,
outcome = "success"
);
success_response(
&doc,
PendingDenyResponse {
id: body.id,
status: "denied".to_string(),
},
)
}