Files
git.stella-ops.org/src/Policy/StellaOps.Policy.Engine/Endpoints/SealedModeEndpoints.cs
StellaOps Bot 5e514532df 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.
2025-12-06 13:41:22 +02:00

160 lines
5.5 KiB
C#

using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.AirGap;
namespace StellaOps.Policy.Engine.Endpoints;
/// <summary>
/// Endpoints for sealed-mode operations per CONTRACT-SEALED-MODE-004.
/// </summary>
public static class SealedModeEndpoints
{
public static IEndpointRouteBuilder MapSealedMode(this IEndpointRouteBuilder routes)
{
var group = routes.MapGroup("/system/airgap");
group.MapPost("/seal", SealAsync)
.WithName("AirGap.Seal")
.WithDescription("Seal the environment")
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:seal"))
.ProducesProblem(StatusCodes.Status400BadRequest)
.ProducesProblem(StatusCodes.Status500InternalServerError);
group.MapPost("/unseal", UnsealAsync)
.WithName("AirGap.Unseal")
.WithDescription("Unseal the environment")
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:seal"))
.ProducesProblem(StatusCodes.Status500InternalServerError);
group.MapGet("/status", GetStatusAsync)
.WithName("AirGap.GetStatus")
.WithDescription("Get sealed-mode status")
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:status:read"));
group.MapPost("/verify", VerifyBundleAsync)
.WithName("AirGap.VerifyBundle")
.WithDescription("Verify a bundle against trust roots")
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:verify"))
.ProducesProblem(StatusCodes.Status400BadRequest)
.ProducesProblem(StatusCodes.Status422UnprocessableEntity);
return routes;
}
private static async Task<IResult> SealAsync(
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
[FromBody] SealRequest request,
ISealedModeService service,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(tenantId))
{
tenantId = "default";
}
try
{
var response = await service.SealAsync(tenantId, request, cancellationToken).ConfigureAwait(false);
return Results.Ok(response);
}
catch (SealedModeException ex)
{
return SealedModeResultHelper.ToProblem(ex);
}
catch (ArgumentException ex)
{
return SealedModeResultHelper.ToProblem(
SealedModeErrorCodes.SealFailed,
ex.Message,
"Ensure all required parameters are provided");
}
catch (Exception ex)
{
return SealedModeResultHelper.ToProblem(
SealedModeErrorCodes.SealFailed,
$"Seal operation failed: {ex.Message}");
}
}
private static async Task<IResult> UnsealAsync(
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
ISealedModeService service,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(tenantId))
{
tenantId = "default";
}
try
{
var response = await service.UnsealAsync(tenantId, cancellationToken).ConfigureAwait(false);
return Results.Ok(response);
}
catch (SealedModeException ex)
{
return SealedModeResultHelper.ToProblem(ex);
}
catch (Exception ex)
{
return SealedModeResultHelper.ToProblem(
SealedModeErrorCodes.UnsealFailed,
$"Unseal operation failed: {ex.Message}");
}
}
private static async Task<IResult> GetStatusAsync(
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
ISealedModeService service,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(tenantId))
{
tenantId = "default";
}
var status = await service.GetStatusAsync(tenantId, cancellationToken).ConfigureAwait(false);
return Results.Ok(status);
}
private static async Task<IResult> VerifyBundleAsync(
[FromBody] BundleVerifyRequest request,
ISealedModeService service,
CancellationToken cancellationToken)
{
try
{
var response = await service.VerifyBundleAsync(request, cancellationToken).ConfigureAwait(false);
// Return problem details if verification failed
if (!response.Valid && response.VerificationResult.Error is not null)
{
return SealedModeResultHelper.ToProblem(
SealedModeErrorCodes.SignatureInvalid,
response.VerificationResult.Error,
"Verify bundle integrity and trust root configuration",
422);
}
return Results.Ok(response);
}
catch (SealedModeException ex)
{
return SealedModeResultHelper.ToProblem(ex);
}
catch (ArgumentException ex)
{
return SealedModeResultHelper.ToProblem(
SealedModeErrorCodes.BundleInvalid,
ex.Message,
"Ensure bundle path is valid and accessible");
}
catch (FileNotFoundException ex)
{
return SealedModeResultHelper.ToProblem(
SealedModeErrorCodes.BundleInvalid,
$"Bundle file not found: {ex.FileName ?? ex.Message}",
"Verify the bundle path is correct");
}
}
}