- 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.
122 lines
4.0 KiB
C#
122 lines
4.0 KiB
C#
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
|
|
});
|
|
}
|
|
}
|