archy/core/archipelago/src/api/rpc/credentials.rs

151 lines
5.2 KiB
Rust
Raw Normal View History

use super::RpcHandler;
use crate::credentials;
use crate::identity_manager::IdentityManager;
use anyhow::Result;
impl RpcHandler {
/// Issue a Verifiable Credential from one of the user's identities.
pub(super) async fn handle_identity_issue_credential(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
let issuer_id = params
.get("issuer_id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing issuer_id"))?;
let subject_did = params
.get("subject_did")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing subject_did"))?;
let credential_type = params
.get("type")
.and_then(|v| v.as_str())
.unwrap_or("VerifiableCredential");
let claims = params
.get("claims")
.cloned()
.unwrap_or(serde_json::json!({}));
let expires_at = params.get("expires_at").and_then(|v| v.as_str());
let manager = IdentityManager::new(&self.config.data_dir).await?;
let issuer_record = manager.get(issuer_id).await?;
let issuer_did = issuer_record.did.clone();
// Capture identity_id for the signing closure
let data_dir = self.config.data_dir.clone();
let sign_id = issuer_id.to_string();
let vc = credentials::issue_credential(
&self.config.data_dir,
&issuer_did,
subject_did,
credential_type,
claims,
expires_at,
|bytes| {
// Use block_in_place to avoid deadlocking the tokio runtime
let hex_msg = hex::encode(bytes);
tokio::task::block_in_place(|| {
let rt = tokio::runtime::Handle::current();
rt.block_on(async {
let mgr = IdentityManager::new(&data_dir).await?;
mgr.sign(&sign_id, hex_msg.as_bytes()).await
})
})
},
)
.await?;
Ok(serde_json::json!({
"id": vc.id,
"issuer": vc.issuer,
"subject": vc.subject,
"type": vc.credential_type,
"issued_at": vc.issued_at,
"status": vc.status,
}))
}
/// Verify a credential by its ID.
pub(super) async fn handle_identity_verify_credential(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
let credential_id = params
.get("id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing id"))?;
let store = credentials::load_credentials(&self.config.data_dir).await?;
let vc = store
.credentials
.iter()
.find(|c| c.id == credential_id)
.ok_or_else(|| anyhow::anyhow!("Credential not found"))?;
let data_dir = self.config.data_dir.clone();
let valid = credentials::verify_credential(vc, |did, bytes, signature| {
let hex_msg = hex::encode(bytes);
tokio::task::block_in_place(|| {
let rt = tokio::runtime::Handle::current();
rt.block_on(async {
let mgr = IdentityManager::new(&data_dir).await?;
mgr.verify(did, hex_msg.as_bytes(), signature).await
})
})
})?;
Ok(serde_json::json!({
"id": vc.id,
"valid": valid,
"status": vc.status,
}))
}
/// List all credentials, optionally filtered by DID.
pub(super) async fn handle_identity_list_credentials(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let filter_did = params
.as_ref()
.and_then(|p| p.get("did"))
.and_then(|v| v.as_str());
let creds = credentials::list_credentials(&self.config.data_dir, filter_did).await?;
let items: Vec<serde_json::Value> = creds
.into_iter()
.map(|c| {
serde_json::json!({
"id": c.id,
"issuer": c.issuer,
"subject": c.subject,
"type": c.credential_type,
"claims": c.claims,
"issued_at": c.issued_at,
"expires_at": c.expires_at,
"status": c.status,
})
})
.collect();
Ok(serde_json::json!({ "credentials": items }))
}
/// Revoke a credential.
pub(super) async fn handle_identity_revoke_credential(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
let id = params
.get("id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing id"))?;
credentials::revoke_credential(&self.config.data_dir, id).await?;
Ok(serde_json::json!({ "ok": true }))
}
}