tenant fixes

This commit is contained in:
master
2026-02-23 23:44:50 +02:00
parent bdb1438654
commit 4f947a8b61
159 changed files with 1064 additions and 556 deletions

View File

@@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using StellaOps.Attestor.TileProxy.Services;
using System.Text.Json;
using StellaOps.Auth.ServerIntegration.Tenancy;
namespace StellaOps.Attestor.TileProxy.Endpoints;
@@ -48,7 +49,8 @@ public static class TileEndpoints
.Produces(StatusCodes.Status502BadGateway);
// Admin endpoints
var admin = endpoints.MapGroup("/_admin");
var admin = endpoints.MapGroup("/_admin")
.RequireTenant();
admin.MapGet("/cache/stats", GetCacheStats)
.WithName("GetCacheStats")

View File

@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using StellaOps.Attestor.Persistence.Entities;
using StellaOps.Attestor.Services;
using StellaOps.Auth.ServerIntegration.Tenancy;
namespace StellaOps.Attestor.WebService.Endpoints;
@@ -25,7 +26,8 @@ public static class VerdictEndpoints
{
var group = app.MapGroup("/api/v1/verdicts")
.WithTags("Verdicts")
.WithOpenApi();
.WithOpenApi()
.RequireTenant();
group.MapPost("/", CreateVerdict)
.WithName("CreateVerdict")

View File

@@ -26,6 +26,7 @@ public class ProofsApiContractTests : IClassFixture<AttestorTestWebApplicationFa
public ProofsApiContractTests(AttestorTestWebApplicationFactory factory)
{
_client = factory.CreateClient();
_client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
}
#region POST /proofs/{entry}/spine Contract Tests
@@ -302,6 +303,7 @@ public class AnchorsApiContractTests : IClassFixture<AttestorTestWebApplicationF
public AnchorsApiContractTests(AttestorTestWebApplicationFactory factory)
{
_client = factory.CreateClient();
_client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
}
[Fact]
@@ -346,6 +348,7 @@ public class VerifyApiContractTests : IClassFixture<AttestorTestWebApplicationFa
public VerifyApiContractTests(AttestorTestWebApplicationFactory factory)
{
_client = factory.CreateClient();
_client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
}
[Fact]

View File

@@ -177,6 +177,7 @@ public sealed class AttestationBundleEndpointsTests
private static void AttachAuth(HttpClient client)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "test-token");
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
}
}

View File

@@ -46,6 +46,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
// Act
var response = await client.GetAsync("/swagger/v1/swagger.json");
@@ -71,6 +72,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
// Act
var response = await client.GetAsync("/swagger/v1/swagger.json");
@@ -112,6 +114,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = new
{
@@ -143,6 +146,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var invalidEntryId = "invalid-entry-format";
var request = new
{
@@ -171,6 +175,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
// Act
@@ -197,6 +202,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var request = new
{
envelope = new
@@ -232,6 +238,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var request = new { }; // Missing envelope
var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/verify")
@@ -260,6 +267,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var digestId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e";
// Act
@@ -286,6 +294,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var digest = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e";
// Act
@@ -306,6 +315,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var endpoints = new[]
{
"/health",
@@ -348,6 +358,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/verify")
{
Content = new StringContent("<xml/>", Encoding.UTF8, "application/xml")
@@ -370,6 +381,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/verify")
{
Content = new StringContent("{}", Encoding.UTF8, "application/json")
@@ -399,6 +411,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/proofs/invalid-entry/spine")
{
Content = JsonContent.Create(new { })
@@ -437,6 +450,7 @@ public sealed class AttestorContractSnapshotTests : IClassFixture<AttestorTestWe
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
// Act
var response = await client.GetAsync("/health");

View File

@@ -50,6 +50,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"/proofs/{Uri.EscapeDataString(entryId)}/spine")
@@ -77,6 +78,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = new
@@ -117,6 +119,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"/proofs/{Uri.EscapeDataString(entryId)}/spine")
@@ -140,6 +143,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"/proofs/{Uri.EscapeDataString(entryId)}/spine")
@@ -161,6 +165,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
// Missing evidenceIds, reasoningId, vexVerdictId
@@ -189,6 +194,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = $"{invalidDigest}:pkg:npm/example@1.0.0";
var request = new
@@ -218,6 +224,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
// Array with null values
@@ -247,6 +254,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
// Create a very large array of evidence IDs (>10MB)
@@ -287,6 +295,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
// Actual implementation may use circuit breaker or graceful degradation
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
// Act
@@ -320,6 +329,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
// The system should either fail gracefully or continue without transparency logging
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = new
@@ -354,6 +364,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var request = new
{
@@ -383,6 +394,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"/proofs/{Uri.EscapeDataString(entryId)}/spine")
@@ -433,6 +445,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
// Request with multiple invalid fields
@@ -485,6 +498,7 @@ public sealed class AttestorNegativeTests : IClassFixture<AttestorTestWebApplica
{
// Arrange
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var invalidRequest = new { invalid = true };

View File

@@ -62,6 +62,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = CreateValidSpineRequest();
@@ -92,6 +93,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = CreateValidSpineRequest();
@@ -147,6 +149,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var request = CreateValidVerifyRequest();
// Act
@@ -188,6 +191,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = CreateValidSpineRequest();
@@ -224,6 +228,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = CreateValidSpineRequest();
@@ -264,6 +269,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
// Invalid request (missing required fields)
@@ -305,6 +311,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var nonExistentId = "sha256:0000000000000000000000000000000000000000000000000000000000000000:pkg:npm/nonexistent@1.0.0";
// Act
@@ -338,6 +345,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = CreateValidSpineRequest();
@@ -379,6 +387,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = CreateValidSpineRequest();
@@ -427,6 +436,7 @@ public sealed class AttestorOTelTraceTests : IClassFixture<AttestorTestWebApplic
ActivitySource.AddActivityListener(listener);
var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
var entryId = "sha256:4d5f6e7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e:pkg:npm/example@1.0.0";
var request = CreateValidSpineRequest();

View File

@@ -72,5 +72,6 @@ public sealed class WebServiceFeatureGateTests
private static void AttachAuth(HttpClient client)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "test-token");
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
}
}

View File

@@ -26,6 +26,7 @@ using StellaOps.Attestor.Watchlist;
using StellaOps.Attestor.WebService.Endpoints;
using StellaOps.Attestor.WebService.Options;
using StellaOps.Auth.ServerIntegration;
using StellaOps.Auth.ServerIntegration.Tenancy;
using StellaOps.Configuration;
using StellaOps.Cryptography.DependencyInjection;
using StellaOps.Determinism;
@@ -267,6 +268,7 @@ internal static class AttestorWebServiceComposition
configureOptions: options => { options.TimeProvider ??= TimeProvider.System; });
}
builder.Services.AddStellaOpsTenantServices();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("attestor:write", policy =>
@@ -411,6 +413,7 @@ internal static class AttestorWebServiceComposition
app.UseAuthentication();
app.UseAuthorization();
app.UseStellaOpsTenantMiddleware();
app.TryUseStellaRouter(routerEnabled);
app.MapHealthChecks("/health/ready");

View File

@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using StellaOps.Attestor.Persistence.Repositories;
using StellaOps.Auth.ServerIntegration.Tenancy;
namespace StellaOps.Attestor.WebService.Endpoints;
@@ -24,7 +25,8 @@ public static class PredicateRegistryEndpoints
public static void MapPredicateRegistryEndpoints(this IEndpointRouteBuilder app)
{
var group = app.MapGroup("/api/v1/attestor/predicates")
.WithTags("Predicate Registry");
.WithTags("Predicate Registry")
.RequireTenant();
group.MapGet("/", ListPredicateTypes)
.WithName("ListPredicateTypes")