use super::helpers::TrustTaskOutcome;
use serde_json::Value;
use trust_tasks_rs::TrustTask;
use trust_tasks_rs::specs::device::disable::v0_1 as disable_spec;
use trust_tasks_rs::specs::device::heartbeat::v0_1 as heartbeat_spec;
use trust_tasks_rs::specs::device::list::v0_1 as list_spec;
use trust_tasks_rs::specs::device::register::v0_1 as register_spec;
use trust_tasks_rs::specs::device::set_wake::v0_1 as set_wake_spec;
use trust_tasks_rs::specs::device::wipe::v0_1 as wipe_spec;
use crate::auth::AuthClaims;
use crate::operations;
use crate::server::AppState;
use super::helpers::{TRANSPORT_TRUST_TASK, app_error_to_reject, parse_payload, success_response};
pub(super) async fn handle_register(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let payload: register_spec::Payload = match parse_payload(&doc) {
Ok(p) => p,
Err(resp) => return resp,
};
let consumer_kind = operations::device::wire_kind_to_internal(&payload.consumer_kind);
let display_name = payload.display_name.to_string();
let hpke_public_key = payload.hpke_public_key.as_ref().map(|k| k.to_string());
match operations::device::register_device(
&state.acl_ks,
&state.audit_ks,
auth,
consumer_kind,
display_name,
payload.platform,
hpke_public_key,
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_heartbeat(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let payload: heartbeat_spec::Payload = match parse_payload(&doc) {
Ok(p) => p,
Err(resp) => return resp,
};
match operations::device::heartbeat_device(&state.acl_ks, auth, payload.platform).await {
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_list(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let payload: list_spec::Payload = match parse_payload(&doc) {
Ok(p) => p,
Err(resp) => return resp,
};
match operations::device::list_devices(&state.acl_ks, auth, &payload).await {
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_disable(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let payload: disable_spec::Payload = match parse_payload(&doc) {
Ok(p) => p,
Err(resp) => return resp,
};
let device_id = payload.device_id.to_string();
match operations::device::disable_device(&state.acl_ks, &state.audit_ks, auth, &device_id).await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_wipe(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let payload: wipe_spec::Payload = match parse_payload(&doc) {
Ok(p) => p,
Err(resp) => return resp,
};
let device_id = payload.device_id.to_string();
let reason = payload.reason.to_string();
let scope = payload.scope.to_string();
match operations::device::wipe_device(
&state.acl_ks,
&state.audit_ks,
auth,
&device_id,
&reason,
&scope,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_set_wake(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let payload: set_wake_spec::Payload = match parse_payload(&doc) {
Ok(p) => p,
Err(resp) => return resp,
};
let wake = payload
.wake_handle
.map(|h| (h.gateway.to_string(), h.handle.to_string()));
let suggested = payload
.suggested_triggers
.unwrap_or_default()
.iter()
.map(|t| t.to_string())
.collect::<Vec<_>>();
let vta_did = state.config.read().await.vta_did.clone();
#[cfg(feature = "didcomm")]
let provision_inputs = (wake.clone(), vta_did.clone());
match operations::device::set_wake_device(
&state.acl_ks,
&state.audit_ks,
auth,
wake,
suggested,
vta_did,
)
.await
{
Ok(body) => {
#[cfg(feature = "didcomm")]
provision_gateway(state, &provision_inputs.0, &provision_inputs.1, &body);
success_response(&doc, body)
}
Err(e) => app_error_to_reject(&doc, e),
}
}
#[cfg(feature = "didcomm")]
fn provision_gateway(
state: &AppState,
wake: &Option<(String, String)>,
vta_did: &Option<String>,
body: &Value,
) {
const TRUST_TASK_ENVELOPE_TYPE: &str = "https://trusttasks.org/binding/didcomm/0.1/envelope";
let Some((gateway, handle)) = wake.clone() else {
return;
};
if !gateway.starts_with("did:") {
return; }
let Some(triggers) = body
.get("triggerPolicy")
.and_then(|p| p.get("allowedTriggers"))
.cloned()
else {
return;
};
let provision = serde_json::json!({
"id": format!("urn:uuid:{}", uuid::Uuid::new_v4()),
"type": "https://trusttasks.org/spec/push/provision/0.1",
"issuer": vta_did,
"recipient": gateway,
"payload": { "handle": handle, "policy": { "allowedTriggers": triggers } },
});
let bridge = state.didcomm_bridge.clone();
tokio::spawn(async move {
match bridge
.send_and_wait(
&gateway,
TRUST_TASK_ENVELOPE_TYPE,
provision,
TRUST_TASK_ENVELOPE_TYPE,
vta_sdk::protocols::PROBLEM_REPORT_TYPE,
15,
)
.await
{
Ok(_) => tracing::info!(gateway = %gateway, "push gateway allowlist provisioned"),
Err(e) => {
tracing::warn!(error = %e, gateway = %gateway, "gateway provision failed (best-effort)")
}
}
});
}