more audit work
This commit is contained in:
@@ -25,8 +25,8 @@ internal static class AirGapEndpointExtensions
|
||||
// GET /api/v1/concelier/airgap/catalog - Aggregated bundle catalog
|
||||
group.MapGet("/catalog", async (
|
||||
HttpContext context,
|
||||
IBundleCatalogService catalogService,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleCatalogService catalogService,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromQuery] string? cursor,
|
||||
[FromQuery] int? limit,
|
||||
CancellationToken cancellationToken) =>
|
||||
@@ -46,8 +46,8 @@ internal static class AirGapEndpointExtensions
|
||||
// GET /api/v1/concelier/airgap/sources - List registered sources
|
||||
group.MapGet("/sources", (
|
||||
HttpContext context,
|
||||
IBundleSourceRegistry sourceRegistry,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor) =>
|
||||
[FromServices] IBundleSourceRegistry sourceRegistry,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor) =>
|
||||
{
|
||||
var airGapOptions = optionsMonitor.CurrentValue.AirGap;
|
||||
if (!airGapOptions.Enabled)
|
||||
@@ -62,8 +62,8 @@ internal static class AirGapEndpointExtensions
|
||||
// POST /api/v1/concelier/airgap/sources - Register new source
|
||||
group.MapPost("/sources", async (
|
||||
HttpContext context,
|
||||
IBundleSourceRegistry sourceRegistry,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleSourceRegistry sourceRegistry,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromBody] BundleSourceRegistration registration,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -87,8 +87,8 @@ internal static class AirGapEndpointExtensions
|
||||
// GET /api/v1/concelier/airgap/sources/{sourceId} - Get specific source
|
||||
group.MapGet("/sources/{sourceId}", (
|
||||
HttpContext context,
|
||||
IBundleSourceRegistry sourceRegistry,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleSourceRegistry sourceRegistry,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
string sourceId) =>
|
||||
{
|
||||
var airGapOptions = optionsMonitor.CurrentValue.AirGap;
|
||||
@@ -109,8 +109,8 @@ internal static class AirGapEndpointExtensions
|
||||
// DELETE /api/v1/concelier/airgap/sources/{sourceId} - Unregister source
|
||||
group.MapDelete("/sources/{sourceId}", async (
|
||||
HttpContext context,
|
||||
IBundleSourceRegistry sourceRegistry,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleSourceRegistry sourceRegistry,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
string sourceId,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -131,8 +131,8 @@ internal static class AirGapEndpointExtensions
|
||||
// POST /api/v1/concelier/airgap/sources/{sourceId}/validate - Validate source
|
||||
group.MapPost("/sources/{sourceId}/validate", async (
|
||||
HttpContext context,
|
||||
IBundleSourceRegistry sourceRegistry,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleSourceRegistry sourceRegistry,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
string sourceId,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -151,8 +151,8 @@ internal static class AirGapEndpointExtensions
|
||||
// GET /api/v1/concelier/airgap/status - Sealed-mode status
|
||||
group.MapGet("/status", (
|
||||
HttpContext context,
|
||||
ISealedModeEnforcer sealedModeEnforcer,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor) =>
|
||||
[FromServices] ISealedModeEnforcer sealedModeEnforcer,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor) =>
|
||||
{
|
||||
var airGapOptions = optionsMonitor.CurrentValue.AirGap;
|
||||
if (!airGapOptions.Enabled)
|
||||
@@ -168,9 +168,9 @@ internal static class AirGapEndpointExtensions
|
||||
// Per CONCELIER-WEB-AIRGAP-58-001
|
||||
group.MapPost("/bundles/{bundleId}/import", async (
|
||||
HttpContext context,
|
||||
IBundleCatalogService catalogService,
|
||||
IBundleTimelineEmitter timelineEmitter,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleCatalogService catalogService,
|
||||
[FromServices] IBundleTimelineEmitter timelineEmitter,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
string bundleId,
|
||||
[FromBody] BundleImportRequestDto requestDto,
|
||||
CancellationToken cancellationToken) =>
|
||||
|
||||
@@ -30,8 +30,8 @@ internal static class CanonicalAdvisoryEndpointExtensions
|
||||
// GET /api/v1/canonical/{id} - Get canonical advisory by ID
|
||||
group.MapGet("/{id:guid}", async (
|
||||
Guid id,
|
||||
ICanonicalAdvisoryService service,
|
||||
IInterestScoringService? scoringService,
|
||||
[FromServices] ICanonicalAdvisoryService service,
|
||||
[FromServices] IInterestScoringService? scoringService,
|
||||
HttpContext context,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
@@ -63,7 +63,7 @@ internal static class CanonicalAdvisoryEndpointExtensions
|
||||
[FromQuery] string? mergeHash,
|
||||
[FromQuery] int? offset,
|
||||
[FromQuery] int? limit,
|
||||
ICanonicalAdvisoryService service,
|
||||
[FromServices] ICanonicalAdvisoryService service,
|
||||
HttpContext context,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
@@ -126,7 +126,7 @@ internal static class CanonicalAdvisoryEndpointExtensions
|
||||
group.MapPost("/ingest/{source}", async (
|
||||
string source,
|
||||
[FromBody] RawAdvisoryRequest request,
|
||||
ICanonicalAdvisoryService service,
|
||||
[FromServices] ICanonicalAdvisoryService service,
|
||||
HttpContext context,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
@@ -187,7 +187,7 @@ internal static class CanonicalAdvisoryEndpointExtensions
|
||||
group.MapPost("/ingest/{source}/batch", async (
|
||||
string source,
|
||||
[FromBody] IEnumerable<RawAdvisoryRequest> requests,
|
||||
ICanonicalAdvisoryService service,
|
||||
[FromServices] ICanonicalAdvisoryService service,
|
||||
HttpContext context,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
@@ -246,7 +246,7 @@ internal static class CanonicalAdvisoryEndpointExtensions
|
||||
group.MapPatch("/{id:guid}/status", async (
|
||||
Guid id,
|
||||
[FromBody] UpdateStatusRequest request,
|
||||
ICanonicalAdvisoryService service,
|
||||
[FromServices] ICanonicalAdvisoryService service,
|
||||
HttpContext context,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
@@ -267,8 +267,8 @@ internal static class CanonicalAdvisoryEndpointExtensions
|
||||
// GET /api/v1/canonical/{id}/provenance - Get provenance scopes for canonical
|
||||
group.MapGet("/{id:guid}/provenance", async (
|
||||
Guid id,
|
||||
IProvenanceScopeService? provenanceService,
|
||||
ICanonicalAdvisoryService canonicalService,
|
||||
[FromServices] IProvenanceScopeService? provenanceService,
|
||||
[FromServices] ICanonicalAdvisoryService canonicalService,
|
||||
HttpContext context,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
|
||||
@@ -23,8 +23,8 @@ internal static class FederationEndpointExtensions
|
||||
// GET /api/v1/federation/export - Export delta bundle
|
||||
group.MapGet("/export", async (
|
||||
HttpContext context,
|
||||
IBundleExportService exportService,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleExportService exportService,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
CancellationToken cancellationToken,
|
||||
[FromQuery(Name = "since_cursor")] string? sinceCursor = null,
|
||||
[FromQuery] bool sign = true,
|
||||
@@ -83,8 +83,8 @@ internal static class FederationEndpointExtensions
|
||||
// GET /api/v1/federation/export/preview - Preview export statistics
|
||||
group.MapGet("/export/preview", async (
|
||||
HttpContext context,
|
||||
IBundleExportService exportService,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleExportService exportService,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
CancellationToken cancellationToken,
|
||||
[FromQuery(Name = "since_cursor")] string? sinceCursor = null) =>
|
||||
{
|
||||
@@ -114,7 +114,7 @@ internal static class FederationEndpointExtensions
|
||||
// GET /api/v1/federation/status - Federation status
|
||||
group.MapGet("/status", (
|
||||
HttpContext context,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor) =>
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor) =>
|
||||
{
|
||||
var options = optionsMonitor.CurrentValue;
|
||||
|
||||
@@ -134,8 +134,8 @@ internal static class FederationEndpointExtensions
|
||||
// Per SPRINT_8200_0014_0003_CONCEL_bundle_import_merge Task 25-26.
|
||||
group.MapPost("/import", async (
|
||||
HttpContext context,
|
||||
IBundleImportService importService,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleImportService importService,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
CancellationToken cancellationToken,
|
||||
[FromQuery(Name = "dry_run")] bool dryRun = false,
|
||||
[FromQuery(Name = "skip_signature")] bool skipSignature = false,
|
||||
@@ -230,8 +230,8 @@ internal static class FederationEndpointExtensions
|
||||
// POST /api/v1/federation/import/validate - Validate bundle without importing
|
||||
group.MapPost("/import/validate", async (
|
||||
HttpContext context,
|
||||
IBundleImportService importService,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleImportService importService,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var options = optionsMonitor.CurrentValue;
|
||||
@@ -264,8 +264,8 @@ internal static class FederationEndpointExtensions
|
||||
// POST /api/v1/federation/import/preview - Preview import
|
||||
group.MapPost("/import/preview", async (
|
||||
HttpContext context,
|
||||
IBundleImportService importService,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IBundleImportService importService,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var options = optionsMonitor.CurrentValue;
|
||||
@@ -313,8 +313,8 @@ internal static class FederationEndpointExtensions
|
||||
// Per SPRINT_8200_0014_0003_CONCEL_bundle_import_merge Task 30.
|
||||
group.MapGet("/sites", async (
|
||||
HttpContext context,
|
||||
ISyncLedgerRepository ledgerRepository,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] ISyncLedgerRepository ledgerRepository,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
CancellationToken cancellationToken,
|
||||
[FromQuery(Name = "enabled_only")] bool enabledOnly = false) =>
|
||||
{
|
||||
@@ -350,8 +350,8 @@ internal static class FederationEndpointExtensions
|
||||
// GET /api/v1/federation/sites/{siteId} - Get site details
|
||||
group.MapGet("/sites/{siteId}", async (
|
||||
HttpContext context,
|
||||
ISyncLedgerRepository ledgerRepository,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] ISyncLedgerRepository ledgerRepository,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
string siteId,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -404,8 +404,8 @@ internal static class FederationEndpointExtensions
|
||||
// Per SPRINT_8200_0014_0003_CONCEL_bundle_import_merge Task 31.
|
||||
group.MapPut("/sites/{siteId}/policy", async (
|
||||
HttpContext context,
|
||||
ISyncLedgerRepository ledgerRepository,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] ISyncLedgerRepository ledgerRepository,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
string siteId,
|
||||
[FromBody] SitePolicyUpdateRequest request,
|
||||
CancellationToken cancellationToken) =>
|
||||
|
||||
@@ -79,8 +79,8 @@ internal static class FeedSnapshotEndpointExtensions
|
||||
|
||||
private static async Task<IResult> CreateSnapshotAsync(
|
||||
HttpContext context,
|
||||
IFeedSnapshotCoordinator coordinator,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IFeedSnapshotCoordinator coordinator,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromBody] CreateSnapshotRequest? request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -129,8 +129,8 @@ internal static class FeedSnapshotEndpointExtensions
|
||||
|
||||
private static async Task<IResult> ListSnapshotsAsync(
|
||||
HttpContext context,
|
||||
IFeedSnapshotCoordinator coordinator,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IFeedSnapshotCoordinator coordinator,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromQuery] int? limit,
|
||||
[FromQuery] string? cursor,
|
||||
CancellationToken cancellationToken)
|
||||
@@ -165,8 +165,8 @@ internal static class FeedSnapshotEndpointExtensions
|
||||
|
||||
private static async Task<IResult> GetSnapshotAsync(
|
||||
HttpContext context,
|
||||
IFeedSnapshotCoordinator coordinator,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IFeedSnapshotCoordinator coordinator,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
string snapshotId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -201,8 +201,8 @@ internal static class FeedSnapshotEndpointExtensions
|
||||
|
||||
private static async Task<IResult> ExportSnapshotAsync(
|
||||
HttpContext context,
|
||||
IFeedSnapshotCoordinator coordinator,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IFeedSnapshotCoordinator coordinator,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
string snapshotId,
|
||||
[FromQuery] string? format,
|
||||
CancellationToken cancellationToken)
|
||||
@@ -242,8 +242,8 @@ internal static class FeedSnapshotEndpointExtensions
|
||||
|
||||
private static async Task<IResult> ImportSnapshotAsync(
|
||||
HttpContext context,
|
||||
IFeedSnapshotCoordinator coordinator,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IFeedSnapshotCoordinator coordinator,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
IFormFile file,
|
||||
[FromQuery] bool? validate,
|
||||
CancellationToken cancellationToken)
|
||||
@@ -293,8 +293,8 @@ internal static class FeedSnapshotEndpointExtensions
|
||||
|
||||
private static async Task<IResult> ValidateSnapshotAsync(
|
||||
HttpContext context,
|
||||
IFeedSnapshotCoordinator coordinator,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] IFeedSnapshotCoordinator coordinator,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
string snapshotId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -330,8 +330,8 @@ internal static class FeedSnapshotEndpointExtensions
|
||||
|
||||
private static IResult ListSourcesAsync(
|
||||
HttpContext context,
|
||||
IFeedSnapshotCoordinator coordinator,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor)
|
||||
[FromServices] IFeedSnapshotCoordinator coordinator,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor)
|
||||
{
|
||||
var options = optionsMonitor.CurrentValue;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ internal static class InterestScoreEndpointExtensions
|
||||
// GET /api/v1/canonical/{id}/score - Get interest score for a canonical advisory
|
||||
group.MapGet("/canonical/{id:guid}/score", async (
|
||||
Guid id,
|
||||
IInterestScoringService scoringService,
|
||||
[FromServices] IInterestScoringService scoringService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var score = await scoringService.GetScoreAsync(id, ct).ConfigureAwait(false);
|
||||
@@ -48,7 +48,7 @@ internal static class InterestScoreEndpointExtensions
|
||||
[FromQuery] double? maxScore,
|
||||
[FromQuery] int? offset,
|
||||
[FromQuery] int? limit,
|
||||
IInterestScoreRepository repository,
|
||||
[FromServices] IInterestScoreRepository repository,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var scores = await repository.GetAllAsync(offset ?? 0, limit ?? 50, ct).ConfigureAwait(false);
|
||||
@@ -80,7 +80,7 @@ internal static class InterestScoreEndpointExtensions
|
||||
|
||||
// GET /api/v1/scores/distribution - Get score distribution statistics
|
||||
group.MapGet("/scores/distribution", async (
|
||||
IInterestScoreRepository repository,
|
||||
[FromServices] IInterestScoreRepository repository,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var distribution = await repository.GetScoreDistributionAsync(ct).ConfigureAwait(false);
|
||||
@@ -103,7 +103,7 @@ internal static class InterestScoreEndpointExtensions
|
||||
// POST /api/v1/canonical/{id}/score/compute - Compute score for a canonical
|
||||
group.MapPost("/canonical/{id:guid}/score/compute", async (
|
||||
Guid id,
|
||||
IInterestScoringService scoringService,
|
||||
[FromServices] IInterestScoringService scoringService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var score = await scoringService.ComputeScoreAsync(id, ct).ConfigureAwait(false);
|
||||
@@ -118,7 +118,7 @@ internal static class InterestScoreEndpointExtensions
|
||||
// POST /api/v1/scores/recalculate - Admin endpoint to trigger full recalculation
|
||||
group.MapPost("/scores/recalculate", async (
|
||||
[FromBody] RecalculateRequest? request,
|
||||
IInterestScoringService scoringService,
|
||||
[FromServices] IInterestScoringService scoringService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
int updated;
|
||||
@@ -147,8 +147,8 @@ internal static class InterestScoreEndpointExtensions
|
||||
// POST /api/v1/scores/degrade - Admin endpoint to run stub degradation
|
||||
group.MapPost("/scores/degrade", async (
|
||||
[FromBody] DegradeRequest? request,
|
||||
IInterestScoringService scoringService,
|
||||
Microsoft.Extensions.Options.IOptions<InterestScoreOptions> options,
|
||||
[FromServices] IInterestScoringService scoringService,
|
||||
[FromServices] Microsoft.Extensions.Options.IOptions<InterestScoreOptions> options,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var threshold = request?.Threshold ?? options.Value.DegradationPolicy.DegradationThreshold;
|
||||
@@ -169,8 +169,8 @@ internal static class InterestScoreEndpointExtensions
|
||||
// POST /api/v1/scores/restore - Admin endpoint to restore stubs
|
||||
group.MapPost("/scores/restore", async (
|
||||
[FromBody] RestoreRequest? request,
|
||||
IInterestScoringService scoringService,
|
||||
Microsoft.Extensions.Options.IOptions<InterestScoreOptions> options,
|
||||
[FromServices] IInterestScoringService scoringService,
|
||||
[FromServices] Microsoft.Extensions.Options.IOptions<InterestScoreOptions> options,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var threshold = request?.Threshold ?? options.Value.DegradationPolicy.RestorationThreshold;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Concelier.WebService.Diagnostics;
|
||||
using StellaOps.Concelier.WebService.Options;
|
||||
@@ -18,9 +19,9 @@ internal static class MirrorEndpointExtensions
|
||||
public static void MapConcelierMirrorEndpoints(this WebApplication app, bool authorityConfigured, bool enforceAuthority)
|
||||
{
|
||||
app.MapGet("/concelier/exports/index.json", async (
|
||||
MirrorFileLocator locator,
|
||||
MirrorRateLimiter limiter,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] MirrorFileLocator locator,
|
||||
[FromServices] MirrorRateLimiter limiter,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
HttpContext context,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -51,9 +52,9 @@ internal static class MirrorEndpointExtensions
|
||||
|
||||
app.MapGet("/concelier/exports/{**relativePath}", async (
|
||||
string? relativePath,
|
||||
MirrorFileLocator locator,
|
||||
MirrorRateLimiter limiter,
|
||||
IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
[FromServices] MirrorFileLocator locator,
|
||||
[FromServices] MirrorRateLimiter limiter,
|
||||
[FromServices] IOptionsMonitor<ConcelierOptions> optionsMonitor,
|
||||
HttpContext context,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ internal static class SbomEndpointExtensions
|
||||
// POST /api/v1/learn/sbom - Register and learn from an SBOM
|
||||
group.MapPost("/learn/sbom", async (
|
||||
[FromBody] LearnSbomRequest request,
|
||||
ISbomRegistryService registryService,
|
||||
[FromServices] ISbomRegistryService registryService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var input = new SbomRegistrationInput
|
||||
@@ -62,7 +62,7 @@ internal static class SbomEndpointExtensions
|
||||
// GET /api/v1/sboms/{digest}/affected - Get advisories affecting an SBOM
|
||||
group.MapGet("/sboms/{digest}/affected", async (
|
||||
string digest,
|
||||
ISbomRegistryService registryService,
|
||||
[FromServices] ISbomRegistryService registryService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var registration = await registryService.GetByDigestAsync(digest, ct).ConfigureAwait(false);
|
||||
@@ -103,7 +103,7 @@ internal static class SbomEndpointExtensions
|
||||
[FromQuery] int? offset,
|
||||
[FromQuery] int? limit,
|
||||
[FromQuery] string? tenantId,
|
||||
ISbomRegistryService registryService,
|
||||
[FromServices] ISbomRegistryService registryService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var registrations = await registryService.ListAsync(
|
||||
@@ -140,7 +140,7 @@ internal static class SbomEndpointExtensions
|
||||
// GET /api/v1/sboms/{digest} - Get SBOM registration details
|
||||
group.MapGet("/sboms/{digest}", async (
|
||||
string digest,
|
||||
ISbomRegistryService registryService,
|
||||
[FromServices] ISbomRegistryService registryService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var registration = await registryService.GetByDigestAsync(digest, ct).ConfigureAwait(false);
|
||||
@@ -174,7 +174,7 @@ internal static class SbomEndpointExtensions
|
||||
// DELETE /api/v1/sboms/{digest} - Unregister an SBOM
|
||||
group.MapDelete("/sboms/{digest}", async (
|
||||
string digest,
|
||||
ISbomRegistryService registryService,
|
||||
[FromServices] ISbomRegistryService registryService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
await registryService.UnregisterAsync(digest, ct).ConfigureAwait(false);
|
||||
@@ -187,7 +187,7 @@ internal static class SbomEndpointExtensions
|
||||
// POST /api/v1/sboms/{digest}/rematch - Rematch SBOM against current advisories
|
||||
group.MapPost("/sboms/{digest}/rematch", async (
|
||||
string digest,
|
||||
ISbomRegistryService registryService,
|
||||
[FromServices] ISbomRegistryService registryService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
try
|
||||
@@ -216,7 +216,7 @@ internal static class SbomEndpointExtensions
|
||||
group.MapPatch("/sboms/{digest}", async (
|
||||
string digest,
|
||||
[FromBody] SbomDeltaRequest request,
|
||||
ISbomRegistryService registryService,
|
||||
[FromServices] ISbomRegistryService registryService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
try
|
||||
@@ -258,7 +258,7 @@ internal static class SbomEndpointExtensions
|
||||
// GET /api/v1/sboms/stats - Get SBOM registry statistics
|
||||
group.MapGet("/sboms/stats", async (
|
||||
[FromQuery] string? tenantId,
|
||||
ISbomRegistryService registryService,
|
||||
[FromServices] ISbomRegistryService registryService,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var stats = await registryService.GetStatsAsync(tenantId, ct).ConfigureAwait(false);
|
||||
|
||||
@@ -104,6 +104,10 @@ builder.Host.ConfigureAppConfiguration((context, cfg) =>
|
||||
#pragma warning restore ASP0013
|
||||
|
||||
var JsonOptions = CreateJsonOptions();
|
||||
builder.Services.ConfigureHttpJsonOptions(options =>
|
||||
{
|
||||
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
|
||||
});
|
||||
|
||||
builder.Configuration.AddStellaOpsDefaults(options =>
|
||||
{
|
||||
@@ -155,6 +159,26 @@ if (builder.Environment.IsEnvironment("Testing"))
|
||||
}
|
||||
|
||||
ConcelierOptionsPostConfigure.Apply(concelierOptions, contentRootPath);
|
||||
concelierOptions.Authority ??= new ConcelierOptions.AuthorityOptions();
|
||||
concelierOptions.Authority.RequiredScopes ??= new List<string>();
|
||||
concelierOptions.Authority.ClientScopes ??= new List<string>();
|
||||
if (concelierOptions.Authority.RequiredScopes.Count == 0)
|
||||
{
|
||||
concelierOptions.Authority.RequiredScopes.Add(StellaOpsScopes.ConcelierJobsTrigger);
|
||||
}
|
||||
|
||||
if (concelierOptions.Authority.ClientScopes.Count == 0)
|
||||
{
|
||||
foreach (var scope in concelierOptions.Authority.RequiredScopes)
|
||||
{
|
||||
concelierOptions.Authority.ClientScopes.Add(scope);
|
||||
}
|
||||
}
|
||||
|
||||
if (concelierOptions.Authority.ClientScopes.Count == 0)
|
||||
{
|
||||
concelierOptions.Authority.ClientScopes.Add(StellaOpsScopes.ConcelierJobsTrigger);
|
||||
}
|
||||
// Skip validation in Testing to allow factory-provided wiring.
|
||||
}
|
||||
else
|
||||
@@ -473,6 +497,7 @@ builder.Services.RegisterPluginRoutines(builder.Configuration, pluginHostOptions
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
|
||||
var app = builder.Build();
|
||||
var swaggerEnabled = app.Configuration.GetValue<bool>("Swagger:Enabled");
|
||||
|
||||
app.Logger.LogWarning("Authority enabled: {AuthorityEnabled}, test signing secret configured: {HasTestSecret}", authorityConfigured, !string.IsNullOrWhiteSpace(concelierOptions.Authority?.TestSigningSecret));
|
||||
|
||||
@@ -514,6 +539,7 @@ app.MapConcelierMirrorEndpoints(authorityConfigured, enforceAuthority);
|
||||
|
||||
// Canonical advisory endpoints (Sprint 8200.0012.0003)
|
||||
app.MapCanonicalAdvisoryEndpoints();
|
||||
app.MapInterestScoreEndpoints();
|
||||
|
||||
app.MapGet("/.well-known/openapi", ([FromServices] OpenApiDiscoveryDocumentProvider provider, HttpContext context) =>
|
||||
{
|
||||
@@ -559,6 +585,53 @@ app.MapGet("/.well-known/openapi", ([FromServices] OpenApiDiscoveryDocumentProvi
|
||||
}
|
||||
}).WithName("GetConcelierOpenApiDocument");
|
||||
|
||||
if (swaggerEnabled)
|
||||
{
|
||||
app.MapGet("/swagger/v1/swagger.json", ([FromServices] OpenApiDiscoveryDocumentProvider provider, HttpContext context) =>
|
||||
{
|
||||
var (payload, etag) = provider.GetDocument();
|
||||
|
||||
if (context.Request.Headers.IfNoneMatch.Count > 0)
|
||||
{
|
||||
foreach (var candidate in context.Request.Headers.IfNoneMatch)
|
||||
{
|
||||
if (Matches(candidate, etag))
|
||||
{
|
||||
context.Response.Headers.ETag = etag;
|
||||
context.Response.Headers.CacheControl = "public, max-age=300, immutable";
|
||||
return HttpResults.StatusCode(StatusCodes.Status304NotModified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.Headers.ETag = etag;
|
||||
context.Response.Headers.CacheControl = "public, max-age=300, immutable";
|
||||
return HttpResults.Text(payload, "application/json");
|
||||
|
||||
static bool Matches(string? candidate, string expected)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(candidate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var trimmed = candidate.Trim();
|
||||
if (string.Equals(trimmed, expected, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (trimmed.StartsWith("W/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var weakValue = trimmed[2..].TrimStart();
|
||||
return string.Equals(weakValue, expected, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}).WithName("GetConcelierSwaggerDocument");
|
||||
}
|
||||
|
||||
var orchestratorGroup = app.MapGroup("/internal/orch");
|
||||
if (authorityConfigured)
|
||||
{
|
||||
|
||||
@@ -272,7 +272,10 @@ internal sealed class AdvisoryChunkBuilder
|
||||
AdvisoryStructuredFieldContent content,
|
||||
AdvisoryProvenance provenance)
|
||||
{
|
||||
var fingerprint = string.Concat(documentId, '|', fieldPath);
|
||||
var normalizedMask = NormalizeFieldMask(provenance.FieldMask);
|
||||
var observationPath = normalizedMask.Count > 0 ? normalizedMask[0] : fieldPath;
|
||||
var resolvedMask = normalizedMask.Count > 0 ? normalizedMask : new[] { fieldPath };
|
||||
var fingerprint = string.Concat(documentId, '|', observationPath);
|
||||
var chunkId = CreateChunkId(fingerprint);
|
||||
|
||||
return new AdvisoryStructuredFieldEntry(
|
||||
@@ -281,16 +284,27 @@ internal sealed class AdvisoryChunkBuilder
|
||||
content,
|
||||
new AdvisoryStructuredFieldProvenance(
|
||||
documentId,
|
||||
fieldPath,
|
||||
observationPath,
|
||||
provenance.Source,
|
||||
provenance.Kind,
|
||||
provenance.Value,
|
||||
provenance.RecordedAt,
|
||||
NormalizeFieldMask(provenance.FieldMask)));
|
||||
resolvedMask));
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> NormalizeFieldMask(ImmutableArray<string> mask)
|
||||
=> mask.IsDefaultOrEmpty ? Array.Empty<string>() : mask;
|
||||
{
|
||||
if (mask.IsDefaultOrEmpty)
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
return mask
|
||||
.Select(static entry => entry?.Trim())
|
||||
.Where(static entry => !string.IsNullOrWhiteSpace(entry))
|
||||
.Select(static entry => entry!)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private string CreateChunkId(string input)
|
||||
{
|
||||
|
||||
@@ -104,13 +104,26 @@ internal sealed class OpenApiDiscoveryDocumentProvider
|
||||
pathsObject[path] = pathItem;
|
||||
}
|
||||
|
||||
var components = new JsonObject
|
||||
{
|
||||
["securitySchemes"] = new JsonObject
|
||||
{
|
||||
["Bearer"] = new JsonObject
|
||||
{
|
||||
["type"] = "http",
|
||||
["scheme"] = "bearer",
|
||||
["bearerFormat"] = "JWT"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new JsonObject
|
||||
{
|
||||
["openapi"] = "3.1.0",
|
||||
["info"] = info,
|
||||
["servers"] = servers,
|
||||
["paths"] = pathsObject,
|
||||
["components"] = new JsonObject() // ready for future schemas
|
||||
["components"] = components
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user