151 lines
5.2 KiB
Rust
151 lines
5.2 KiB
Rust
|
|
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 }))
|
||
|
|
}
|
||
|
|
}
|