vta-service 0.3.0

Service for Verifiable Trust Agents operating in Verifiable Trust Communities
Documentation
use std::sync::Arc;

use tokio::sync::RwLock;
use tracing::info;

use vta_sdk::credentials::CredentialBundle;
use vta_sdk::protocols::credential_management::generate::GenerateCredentialsResultBody;

use crate::acl::{AclEntry, Role, store_acl_entry, validate_acl_modification};
use crate::auth::AuthClaims;
use crate::auth::credentials::generate_did_key;
use crate::auth::session::now_epoch;
use crate::config::AppConfig;
use crate::error::AppError;
use crate::store::KeyspaceHandle;

pub async fn generate_credentials(
    acl_ks: &KeyspaceHandle,
    config: &Arc<RwLock<AppConfig>>,
    auth: &AuthClaims,
    role: Role,
    label: Option<String>,
    allowed_contexts: Vec<String>,
    channel: &str,
) -> Result<GenerateCredentialsResultBody, AppError> {
    auth.require_manage()?;
    validate_acl_modification(auth, &allowed_contexts)?;

    let cfg = config.read().await;
    let vta_did = cfg
        .vta_did
        .as_ref()
        .ok_or_else(|| AppError::Internal("VTA DID not configured".into()))?
        .clone();
    let vta_url = cfg.public_url.clone();
    drop(cfg);

    let (did, private_key_multibase) = generate_did_key();

    let entry = AclEntry {
        did: did.clone(),
        role: role.clone(),
        label,
        allowed_contexts,
        created_at: now_epoch(),
        created_by: auth.did.clone(),
    };
    store_acl_entry(acl_ks, &entry).await?;

    let bundle = CredentialBundle {
        did: did.clone(),
        private_key_multibase,
        vta_did,
        vta_url,
    };
    let credential = bundle
        .encode()
        .map_err(|e| AppError::Internal(e.to_string()))?;

    info!(channel, did = %did, role = %role, caller = %auth.did, "credentials generated");

    Ok(GenerateCredentialsResultBody {
        did,
        credential,
        role: role.to_string(),
    })
}