#![cfg(feature = "webvh")]
use super::helpers::TrustTaskOutcome;
use didwebvh_rs::witness::Witnesses;
use serde_json::Value;
use trust_tasks_rs::{RejectReason, TrustTask};
use vta_sdk::protocols::did_management::{
create::CreateDidWebvhBody,
delete::DeleteDidWebvhBody,
get::GetDidWebvhBody,
lifecycle::GetDidWebvhLogBody,
list::ListDidsWebvhBody,
servers::{
AddWebvhServerBody, ListWebvhServersBody, RegisterDidWithServerBody,
RegisterDidWithServerResultBody, RemoveWebvhServerBody, UpdateWebvhServerBody,
},
update::{RotateDidWebvhKeysBody, UpdateDidWebvhBody},
};
use crate::auth::AuthClaims;
use crate::error::AppError;
use crate::operations;
use crate::operations::did_webvh::{
RegisterDidWithServerError, RegisterDidWithServerParams, RotateDidWebvhKeysOptions,
UpdateDidWebvhOptions, register_did_with_server,
};
use crate::server::AppState;
use super::helpers::{
TRANSPORT_TRUST_TASK, app_error_to_reject, parse_payload, reject_with, success_response,
};
pub(super) async fn handle_servers_list(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let _: ListWebvhServersBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
match operations::did_webvh::list_webvh_servers(&state.webvh_ks, auth, TRANSPORT_TRUST_TASK)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_servers_add(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
if let Err(e) = auth.require_super_admin() {
return app_error_to_reject(&doc, e);
}
let req: AddWebvhServerBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
let did_resolver = match state.did_resolver.as_ref() {
Some(r) => r,
None => {
return app_error_to_reject(
&doc,
AppError::Internal("DID resolver not available".into()),
);
}
};
match operations::did_webvh::add_webvh_server(
&state.webvh_ks,
auth,
&req.id,
&req.did,
req.label,
did_resolver,
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_servers_update(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
if let Err(e) = auth.require_super_admin() {
return app_error_to_reject(&doc, e);
}
let req: UpdateWebvhServerBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
match operations::did_webvh::update_webvh_server(
&state.webvh_ks,
auth,
&req.id,
req.label,
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_servers_remove(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
if let Err(e) = auth.require_super_admin() {
return app_error_to_reject(&doc, e);
}
let req: RemoveWebvhServerBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
match operations::did_webvh::remove_webvh_server(
&state.webvh_ks,
auth,
&req.id,
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_dids_list(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let req: ListDidsWebvhBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
match operations::did_webvh::list_dids_webvh(
&state.webvh_ks,
auth,
req.context_id.as_deref(),
req.server_id.as_deref(),
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_dids_create(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let body: CreateDidWebvhBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
let config = state.config.read().await;
let did_resolver = match state.did_resolver.as_ref() {
Some(r) => r,
None => {
return app_error_to_reject(
&doc,
AppError::Internal("DID resolver not available".into()),
);
}
};
let params = body.into();
let deps =
operations::did_webvh::CreateDidWebvhDeps::from_app_state(state, &config, did_resolver);
match operations::did_webvh::create_did_webvh(&deps, auth, params, TRANSPORT_TRUST_TASK).await {
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_dids_get(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let req: GetDidWebvhBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
match operations::did_webvh::get_did_webvh(
&state.webvh_ks,
auth,
&req.did,
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_dids_get_log(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let req: GetDidWebvhLogBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
match operations::did_webvh::get_did_webvh_log(
&state.webvh_ks,
auth,
&req.did,
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_dids_delete(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let req: DeleteDidWebvhBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
let did_resolver = match state.did_resolver.as_ref() {
Some(r) => r,
None => {
return app_error_to_reject(
&doc,
AppError::Internal("DID resolver not available".into()),
);
}
};
let vta_did = state.config.read().await.vta_did.clone();
let deps = operations::did_webvh::WebvhDeps::from_app_state(state, did_resolver);
match operations::did_webvh::delete_did_webvh(
&deps,
auth,
&req.did,
vta_did.as_deref(),
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_dids_update(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let req: UpdateDidWithDid = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
let UpdateDidWithDid { did, body } = req;
let options = match update_body_to_options(body) {
Ok(o) => o,
Err(resp) => return reject_with(&doc, resp),
};
let did_resolver = match state.did_resolver.as_ref() {
Some(r) => r,
None => {
return app_error_to_reject(
&doc,
AppError::Internal("DID resolver not available".into()),
);
}
};
let vta_did = state.config.read().await.vta_did.clone();
let deps = operations::did_webvh::WebvhDeps::from_app_state(state, did_resolver);
match operations::did_webvh::update_did_webvh(
&deps,
auth,
&did,
options,
vta_did.as_deref(),
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, AppError::from(e)),
}
}
pub(super) async fn handle_dids_rotate_keys(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let req: RotateKeysWithDid = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
let did_resolver = match state.did_resolver.as_ref() {
Some(r) => r,
None => {
return app_error_to_reject(
&doc,
AppError::Internal("DID resolver not available".into()),
);
}
};
let vta_did = state.config.read().await.vta_did.clone();
let options = RotateDidWebvhKeysOptions {
pre_rotation_count: req.body.pre_rotation_count,
label: req.body.label,
};
let deps = operations::did_webvh::WebvhDeps::from_app_state(state, did_resolver);
match operations::did_webvh::rotate_did_webvh_keys(
&deps,
auth,
&req.did,
options,
vta_did.as_deref(),
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, AppError::from(e)),
}
}
pub(super) async fn handle_dids_register_with_server(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
if let Err(e) = auth.require_super_admin() {
return app_error_to_reject(&doc, e);
}
let req: RegisterDidWithServerBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
let did_resolver = match state.did_resolver.as_ref() {
Some(r) => r,
None => {
return app_error_to_reject(
&doc,
AppError::Internal("DID resolver not available".into()),
);
}
};
let vta_did = state.config.read().await.vta_did.clone();
let deps = operations::did_webvh::WebvhDeps::from_app_state(state, did_resolver);
match register_did_with_server(
&deps,
auth,
RegisterDidWithServerParams {
did: req.did,
server_id: req.server_id,
force: req.force,
domain: req.domain.clone(),
},
vta_did.as_deref(),
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(result) => success_response(
&doc,
RegisterDidWithServerResultBody {
did: result.did,
server_id: result.server_id,
log_entry_count: result.log_entry_count,
},
),
Err(e) => app_error_to_reject(&doc, map_register_err(e)),
}
}
fn map_register_err(e: RegisterDidWithServerError) -> AppError {
use RegisterDidWithServerError as E;
match e {
E::Auth(msg) => AppError::Forbidden(msg),
E::DidNotFound(msg) | E::ServerNotFound(msg) | E::LogMissing(msg) => {
AppError::NotFound(msg)
}
E::AlreadyServerManaged { .. } | E::Conflict(_) => AppError::Conflict(e.to_string()),
E::Transport(msg) | E::Publish(msg) => AppError::Internal(format!("publish: {msg}")),
E::DidUrlParse { .. } => AppError::Validation(e.to_string()),
E::Storage(msg) => AppError::Internal(msg),
}
}
fn update_body_to_options(body: UpdateDidWebvhBody) -> Result<UpdateDidWebvhOptions, RejectReason> {
let witnesses = match body.witnesses {
Some(v) => match serde_json::from_value::<Witnesses>(v) {
Ok(w) => Some(w),
Err(e) => {
return Err(RejectReason::MalformedRequest {
reason: format!("witnesses: {e}"),
});
}
},
None => None,
};
Ok(UpdateDidWebvhOptions {
document: body.document,
pre_rotation_count: body.pre_rotation_count,
witnesses,
watchers: body.watchers,
ttl: body.ttl,
label: body.label,
expected_version_id: body.expected_version_id,
})
}
#[derive(Debug, serde::Deserialize)]
struct RotateKeysWithDid {
did: String,
#[serde(flatten)]
body: RotateDidWebvhKeysBody,
}
#[derive(Debug, serde::Deserialize)]
struct UpdateDidWithDid {
did: String,
#[serde(flatten)]
body: UpdateDidWebvhBody,
}