use auths_core::ports::network::{NetworkError, RateLimitInfo, RegistryClient};
use auths_verifier::core::ResourceId;
use serde::Deserialize;
use thiserror::Error;
use crate::ports::artifact::{ArtifactDigest, ArtifactError, ArtifactSource};
pub struct ArtifactPublishConfig {
pub attestation: serde_json::Value,
pub package_name: Option<String>,
pub registry_url: String,
}
#[derive(Debug, Deserialize)]
pub struct ArtifactPublishResult {
pub attestation_rid: ResourceId,
pub package_name: Option<String>,
pub signer_did: String,
#[serde(skip)]
pub rate_limit: Option<RateLimitInfo>,
}
#[derive(Debug, Error)]
pub enum ArtifactPublishError {
#[error("artifact attestation already published (duplicate RID)")]
DuplicateAttestation,
#[error("signature verification failed at registry: {0}")]
VerificationFailed(String),
#[error("registry error ({status}): {body}")]
RegistryError {
status: u16,
body: String,
},
#[error("network error: {0}")]
Network(#[from] NetworkError),
#[error("failed to serialize publish request: {0}")]
Serialize(String),
#[error("failed to deserialize registry response: {0}")]
Deserialize(String),
}
pub async fn publish_artifact<R: RegistryClient>(
config: &ArtifactPublishConfig,
registry: &R,
) -> Result<ArtifactPublishResult, ArtifactPublishError> {
let mut body = serde_json::json!({ "attestation": config.attestation });
if let Some(ref name) = config.package_name {
body["package_name"] = serde_json::Value::String(name.clone());
}
let json_bytes =
serde_json::to_vec(&body).map_err(|e| ArtifactPublishError::Serialize(e.to_string()))?;
let response = registry
.post_json(&config.registry_url, "v1/artifacts", &json_bytes)
.await?;
match response.status {
201 => {
let mut result: ArtifactPublishResult = serde_json::from_slice(&response.body)
.map_err(|e| ArtifactPublishError::Deserialize(e.to_string()))?;
result.rate_limit = response.rate_limit;
Ok(result)
}
409 => Err(ArtifactPublishError::DuplicateAttestation),
422 => {
let body = String::from_utf8_lossy(&response.body).into_owned();
Err(ArtifactPublishError::VerificationFailed(body))
}
status => {
let body = String::from_utf8_lossy(&response.body).into_owned();
Err(ArtifactPublishError::RegistryError { status, body })
}
}
}
pub fn compute_digest(source: &dyn ArtifactSource) -> Result<ArtifactDigest, ArtifactError> {
source.digest()
}
pub async fn verify_artifact<R: RegistryClient>(
config: &ArtifactVerifyConfig,
registry: &R,
) -> Result<ArtifactVerifyResult, ArtifactPublishError> {
let body = serde_json::json!({
"attestation": config.attestation_json,
"issuer_key": config.signer_did,
});
let json_bytes =
serde_json::to_vec(&body).map_err(|e| ArtifactPublishError::Serialize(e.to_string()))?;
let response = registry
.post_json(&config.registry_url, "v1/verify", &json_bytes)
.await?;
match response.status {
200 => {
let result: ArtifactVerifyResult = serde_json::from_slice(&response.body)
.map_err(|e| ArtifactPublishError::Deserialize(e.to_string()))?;
Ok(result)
}
status => {
let body = String::from_utf8_lossy(&response.body).into_owned();
Err(ArtifactPublishError::RegistryError { status, body })
}
}
}
pub struct ArtifactVerifyConfig {
pub attestation_json: String,
pub signer_did: String,
pub registry_url: String,
}
#[derive(Debug, Deserialize)]
pub struct ArtifactVerifyResult {
pub valid: bool,
pub signer_did: Option<String>,
}