109 lines
3.5 KiB
C#
109 lines
3.5 KiB
C#
|
|
using Microsoft.Extensions.Logging;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
|
|
namespace StellaOps.Policy.Engine.Attestation;
|
|
|
|
/// <summary>
|
|
/// HTTP client for communicating with the Attestor service.
|
|
/// </summary>
|
|
public sealed class HttpAttestorClient : IAttestorClient
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
private readonly VerdictAttestationOptions _options;
|
|
private readonly ILogger<HttpAttestorClient> _logger;
|
|
|
|
public HttpAttestorClient(
|
|
HttpClient httpClient,
|
|
VerdictAttestationOptions options,
|
|
ILogger<HttpAttestorClient> logger)
|
|
{
|
|
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
|
|
// Configure HTTP client
|
|
_httpClient.BaseAddress = new Uri(_options.AttestorUrl);
|
|
_httpClient.Timeout = _options.Timeout;
|
|
}
|
|
|
|
public async Task<VerdictAttestationResult> CreateAttestationAsync(
|
|
VerdictAttestationRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (request is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(request));
|
|
}
|
|
|
|
_logger.LogDebug(
|
|
"Sending verdict attestation request to Attestor: {PredicateType} for {SubjectName}",
|
|
request.PredicateType,
|
|
request.Subject.Name);
|
|
|
|
try
|
|
{
|
|
// POST to internal attestation endpoint
|
|
var response = await _httpClient.PostAsJsonAsync(
|
|
"/internal/api/v1/attestations/verdict",
|
|
new
|
|
{
|
|
predicateType = request.PredicateType,
|
|
predicate = request.Predicate,
|
|
subject = new[]
|
|
{
|
|
new
|
|
{
|
|
name = request.Subject.Name,
|
|
digest = request.Subject.Digest
|
|
}
|
|
}
|
|
},
|
|
cancellationToken);
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<AttestationApiResponse>(
|
|
cancellationToken: cancellationToken);
|
|
|
|
if (result is null)
|
|
{
|
|
throw new InvalidOperationException("Attestor returned null response.");
|
|
}
|
|
|
|
_logger.LogDebug(
|
|
"Verdict attestation created: {VerdictId}, URI: {Uri}",
|
|
result.VerdictId,
|
|
result.AttestationUri);
|
|
|
|
return new VerdictAttestationResult(
|
|
verdictId: result.VerdictId,
|
|
attestationUri: result.AttestationUri,
|
|
rekorLogIndex: result.RekorLogIndex
|
|
);
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
_logger.LogError(
|
|
ex,
|
|
"HTTP error creating verdict attestation: {StatusCode}",
|
|
ex.StatusCode);
|
|
throw;
|
|
}
|
|
catch (JsonException ex)
|
|
{
|
|
_logger.LogError(
|
|
ex,
|
|
"Failed to deserialize Attestor response");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// API response model (internal, not part of public contract)
|
|
private sealed record AttestationApiResponse(
|
|
string VerdictId,
|
|
string AttestationUri,
|
|
long? RekorLogIndex);
|
|
}
|