Implement VEX document verification system with issuer management and signature verification
- Added IIssuerDirectory interface for managing VEX document issuers, including methods for registration, revocation, and trust validation. - Created InMemoryIssuerDirectory class as an in-memory implementation of IIssuerDirectory for testing and single-instance deployments. - Introduced ISignatureVerifier interface for verifying signatures on VEX documents, with support for multiple signature formats. - Developed SignatureVerifier class as the default implementation of ISignatureVerifier, allowing extensibility for different signature formats. - Implemented handlers for DSSE and JWS signature formats, including methods for verification and signature extraction. - Defined various records and enums for issuer and signature metadata, enhancing the structure and clarity of the verification process.
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Policy.Engine.AirGap;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// Endpoints for staleness signaling and fallback status per CONTRACT-SEALED-MODE-004.
|
||||
/// </summary>
|
||||
public static class StalenessEndpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapStalenessSignaling(this IEndpointRouteBuilder routes)
|
||||
{
|
||||
var group = routes.MapGroup("/system/airgap/staleness");
|
||||
|
||||
group.MapGet("/status", GetStalenessStatusAsync)
|
||||
.WithName("AirGap.GetStalenessStatus")
|
||||
.WithDescription("Get staleness signal status for health monitoring");
|
||||
|
||||
group.MapGet("/fallback", GetFallbackStatusAsync)
|
||||
.WithName("AirGap.GetFallbackStatus")
|
||||
.WithDescription("Get fallback mode status and configuration");
|
||||
|
||||
group.MapPost("/evaluate", EvaluateStalenessAsync)
|
||||
.WithName("AirGap.EvaluateStaleness")
|
||||
.WithDescription("Trigger staleness evaluation and signaling")
|
||||
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:status:read"));
|
||||
|
||||
group.MapPost("/recover", SignalRecoveryAsync)
|
||||
.WithName("AirGap.SignalRecovery")
|
||||
.WithDescription("Signal staleness recovery after time anchor refresh")
|
||||
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:seal"));
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetStalenessStatusAsync(
|
||||
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
|
||||
IStalenessSignalingService service,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
tenantId = "default";
|
||||
}
|
||||
|
||||
var status = await service.GetSignalStatusAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Return different status codes based on health
|
||||
if (status.IsBreach)
|
||||
{
|
||||
return Results.Json(status, statusCode: StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
if (status.HasWarning)
|
||||
{
|
||||
// Return 200 but with warning headers
|
||||
return Results.Ok(status);
|
||||
}
|
||||
|
||||
return Results.Ok(status);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetFallbackStatusAsync(
|
||||
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
|
||||
IStalenessSignalingService service,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
tenantId = "default";
|
||||
}
|
||||
|
||||
var config = await service.GetFallbackConfigurationAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
var isActive = await service.IsFallbackActiveAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return Results.Ok(new
|
||||
{
|
||||
fallbackActive = isActive,
|
||||
configuration = config
|
||||
});
|
||||
}
|
||||
|
||||
private static async Task<IResult> EvaluateStalenessAsync(
|
||||
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
|
||||
IStalenessSignalingService service,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
tenantId = "default";
|
||||
}
|
||||
|
||||
await service.EvaluateAndSignalAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
var status = await service.GetSignalStatusAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return Results.Ok(new
|
||||
{
|
||||
evaluated = true,
|
||||
status
|
||||
});
|
||||
}
|
||||
|
||||
private static async Task<IResult> SignalRecoveryAsync(
|
||||
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
|
||||
IStalenessSignalingService service,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
tenantId = "default";
|
||||
}
|
||||
|
||||
await service.SignalRecoveryAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return Results.Ok(new
|
||||
{
|
||||
recovered = true,
|
||||
tenantId
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user