more audit work

This commit is contained in:
master
2026-01-08 10:21:51 +02:00
parent 43c02081ef
commit 51cf4bc16c
546 changed files with 36721 additions and 4003 deletions

View File

@@ -10,8 +10,6 @@ using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
using LedgerProgram = StellaOps.Findings.Ledger.WebService.Program;
namespace StellaOps.Findings.Ledger.Tests.Integration;
/// <summary>
@@ -19,11 +17,11 @@ namespace StellaOps.Findings.Ledger.Tests.Integration;
/// </summary>
[Trait("Category", "Integration")]
[Trait("Sprint", "3602")]
public sealed class EvidenceDecisionApiIntegrationTests : IClassFixture<WebApplicationFactory<LedgerProgram>>
public sealed class EvidenceDecisionApiIntegrationTests : IClassFixture<FindingsLedgerWebApplicationFactory>
{
private readonly HttpClient _client;
public EvidenceDecisionApiIntegrationTests(WebApplicationFactory<LedgerProgram> factory)
public EvidenceDecisionApiIntegrationTests(FindingsLedgerWebApplicationFactory factory)
{
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{

View File

@@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Auth.Abstractions;
using StellaOps.Auth.ServerIntegration;
using LedgerProgram = StellaOps.Findings.Ledger.WebService.Program;
namespace StellaOps.Findings.Ledger.Tests.Integration;
public sealed class FindingsLedgerWebApplicationFactory : WebApplicationFactory<LedgerProgram>
{
private static readonly IReadOnlyDictionary<string, string?> DefaultEnvironment =
new Dictionary<string, string?>(StringComparer.Ordinal)
{
["FINDINGS_LEDGER_FINDINGS__LEDGER__DATABASE__CONNECTIONSTRING"] = "Host=localhost;Database=stellaops_test;Username=stella;Password=stella",
["FINDINGS_LEDGER_FINDINGS__LEDGER__ATTACHMENTS__ENCRYPTIONKEY"] = "test-encryption-key",
["FINDINGS_LEDGER_FINDINGS__LEDGER__ATTACHMENTS__SIGNEDURLSECRET"] = "test-signed-url-secret",
["FINDINGS_LEDGER_FINDINGS__LEDGER__ATTACHMENTS__CSRFSHAREDSECRET"] = "test-csrf-secret",
["FINDINGS_LEDGER_FINDINGS__LEDGER__AUTHORITY__ISSUER"] = "https://authority.local",
["FINDINGS_LEDGER_FINDINGS__LEDGER__AUTHORITY__REQUIREHTTPSMETADATA"] = "false",
["FINDINGS_LEDGER_FINDINGS__LEDGER__AUTHORITY__REQUIREDSCOPES__0"] = StellaOpsScopes.FindingsRead,
["FINDINGS_LEDGER_FINDINGS__LEDGER__AUTHORITY__REQUIREDSCOPES__1"] = "findings:write"
};
private readonly Dictionary<string, string?> originalEnvironment = new(StringComparer.Ordinal);
public FindingsLedgerWebApplicationFactory()
{
foreach (var pair in DefaultEnvironment)
{
originalEnvironment[pair.Key] = Environment.GetEnvironmentVariable(pair.Key);
Environment.SetEnvironmentVariable(pair.Key, pair.Value);
}
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Production");
builder.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = false;
options.ValidateOnBuild = false;
});
builder.ConfigureAppConfiguration((context, configBuilder) =>
{
configBuilder.AddInMemoryCollection(new Dictionary<string, string?>
{
["findings:ledger:database:connectionString"] = "Host=localhost;Database=stellaops_test;Username=stella;Password=stella",
["findings:ledger:attachments:encryptionKey"] = "test-encryption-key",
["findings:ledger:attachments:signedUrlSecret"] = "test-signed-url-secret",
["findings:ledger:attachments:csrfSharedSecret"] = "test-csrf-secret",
["findings:ledger:authority:issuer"] = "https://authority.local",
["findings:ledger:authority:requireHttpsMetadata"] = "false",
["findings:ledger:authority:requiredScopes:0"] = StellaOpsScopes.FindingsRead,
["findings:ledger:authority:requiredScopes:1"] = "findings:write"
});
});
builder.ConfigureTestServices(services =>
{
services.RemoveAll<IHostedService>();
services.RemoveAll<IConfigureOptions<AuthenticationOptions>>();
services.RemoveAll<IPostConfigureOptions<AuthenticationOptions>>();
services.RemoveAll<IConfigureOptions<JwtBearerOptions>>();
services.RemoveAll<IPostConfigureOptions<JwtBearerOptions>>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = FindingsLedgerTestAuthHandler.SchemeName;
options.DefaultChallengeScheme = FindingsLedgerTestAuthHandler.SchemeName;
})
.AddScheme<AuthenticationSchemeOptions, FindingsLedgerTestAuthHandler>(
FindingsLedgerTestAuthHandler.SchemeName,
_ => { })
.AddScheme<AuthenticationSchemeOptions, FindingsLedgerTestAuthHandler>(
StellaOpsAuthenticationDefaults.AuthenticationScheme,
_ => { });
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
{
return;
}
foreach (var pair in originalEnvironment)
{
Environment.SetEnvironmentVariable(pair.Key, pair.Value);
}
}
}
internal sealed class FindingsLedgerTestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
internal const string SchemeName = "FindingsLedgerTest";
public FindingsLedgerTestAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder)
: base(options, logger, encoder)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue("Authorization", out var rawHeader) ||
!AuthenticationHeaderValue.TryParse(rawHeader, out var header))
{
return Task.FromResult(AuthenticateResult.NoResult());
}
if (!string.Equals(header.Scheme, "Bearer", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(header.Scheme, SchemeName, StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(AuthenticateResult.NoResult());
}
if (!string.Equals(header.Parameter, "test-token", StringComparison.Ordinal))
{
return Task.FromResult(AuthenticateResult.Fail("Invalid test token."));
}
var claims = new List<Claim>
{
new(StellaOpsClaimTypes.Subject, "test-user")
};
if (Request.Headers.TryGetValue("X-Tenant-Id", out var tenantValue) &&
Guid.TryParse(tenantValue.ToString(), out var tenantId))
{
claims.Add(new Claim(StellaOpsClaimTypes.Tenant, tenantId.ToString("D")));
}
if (Request.Headers.TryGetValue("X-Scopes", out var scopesValue))
{
var scopes = scopesValue
.ToString()
.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (scopes.Length > 0)
{
claims.Add(new Claim(StellaOpsClaimTypes.Scope, string.Join(' ', scopes)));
}
}
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.Headers["WWW-Authenticate"] = "Bearer";
return base.HandleChallengeAsync(properties);
}
}

View File

@@ -11,8 +11,6 @@ using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
using LedgerProgram = StellaOps.Findings.Ledger.WebService.Program;
namespace StellaOps.Findings.Ledger.Tests.Integration;
/// <summary>
@@ -20,11 +18,11 @@ namespace StellaOps.Findings.Ledger.Tests.Integration;
/// </summary>
[Trait("Category", "Integration")]
[Trait("Sprint", "8200.0012.0004")]
public sealed class ScoringAuthorizationTests : IClassFixture<WebApplicationFactory<LedgerProgram>>
public sealed class ScoringAuthorizationTests : IClassFixture<FindingsLedgerWebApplicationFactory>
{
private readonly HttpClient _client;
public ScoringAuthorizationTests(WebApplicationFactory<LedgerProgram> factory)
public ScoringAuthorizationTests(FindingsLedgerWebApplicationFactory factory)
{
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{

View File

@@ -11,8 +11,6 @@ using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
using LedgerProgram = StellaOps.Findings.Ledger.WebService.Program;
namespace StellaOps.Findings.Ledger.Tests.Integration;
/// <summary>
@@ -20,11 +18,11 @@ namespace StellaOps.Findings.Ledger.Tests.Integration;
/// </summary>
[Trait("Category", "Integration")]
[Trait("Sprint", "8200.0012.0004")]
public sealed class ScoringEndpointsIntegrationTests : IClassFixture<WebApplicationFactory<LedgerProgram>>
public sealed class ScoringEndpointsIntegrationTests : IClassFixture<FindingsLedgerWebApplicationFactory>
{
private readonly HttpClient _client;
public ScoringEndpointsIntegrationTests(WebApplicationFactory<LedgerProgram> factory)
public ScoringEndpointsIntegrationTests(FindingsLedgerWebApplicationFactory factory)
{
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{

View File

@@ -12,8 +12,6 @@ using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
using LedgerProgram = StellaOps.Findings.Ledger.WebService.Program;
namespace StellaOps.Findings.Ledger.Tests.Integration;
/// <summary>
@@ -22,11 +20,11 @@ namespace StellaOps.Findings.Ledger.Tests.Integration;
/// </summary>
[Trait("Category", "Integration")]
[Trait("Sprint", "8200.0012.0004")]
public sealed class ScoringObservabilityTests : IClassFixture<WebApplicationFactory<LedgerProgram>>
public sealed class ScoringObservabilityTests : IClassFixture<FindingsLedgerWebApplicationFactory>
{
private readonly HttpClient _client;
public ScoringObservabilityTests(WebApplicationFactory<LedgerProgram> factory)
public ScoringObservabilityTests(FindingsLedgerWebApplicationFactory factory)
{
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{

View File

@@ -11,8 +11,6 @@ using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
using LedgerProgram = StellaOps.Findings.Ledger.WebService.Program;
namespace StellaOps.Findings.Ledger.Tests.Integration;
/// <summary>
@@ -20,11 +18,11 @@ namespace StellaOps.Findings.Ledger.Tests.Integration;
/// </summary>
[Trait("Category", "Integration")]
[Trait("Sprint", "8200.0012.0004")]
public sealed class WebhookEndpointsIntegrationTests : IClassFixture<WebApplicationFactory<LedgerProgram>>
public sealed class WebhookEndpointsIntegrationTests : IClassFixture<FindingsLedgerWebApplicationFactory>
{
private readonly HttpClient _client;
public WebhookEndpointsIntegrationTests(WebApplicationFactory<LedgerProgram> factory)
public WebhookEndpointsIntegrationTests(FindingsLedgerWebApplicationFactory factory)
{
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{

View File

@@ -8,3 +8,4 @@ Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.
| AUDIT-0343-M | DONE | Revalidated 2026-01-07; maintainability audit for Findings.Ledger.Tests. |
| AUDIT-0343-T | DONE | Revalidated 2026-01-07; test coverage audit for Findings.Ledger.Tests. |
| AUDIT-0343-A | DONE | Waived (test project; revalidated 2026-01-07). |
| LEDGER-TESTS-0001 | DOING | Stabilize Findings Ledger WebService test harness (config/auth + stubs). |