tenant fixes
This commit is contained in:
@@ -10,6 +10,7 @@ using StellaOps.AdvisoryAI.Attestation;
|
||||
using StellaOps.AdvisoryAI.Attestation.Models;
|
||||
using StellaOps.AdvisoryAI.Attestation.Storage;
|
||||
using StellaOps.AdvisoryAI.WebService.Security;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
|
||||
namespace StellaOps.AdvisoryAI.WebService.Endpoints;
|
||||
|
||||
@@ -34,7 +35,8 @@ public static class AttestationEndpoints
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
|
||||
// GET /v1/advisory-ai/runs/{runId}/claims
|
||||
app.MapGet("/v1/advisory-ai/runs/{runId}/claims", HandleGetRunClaims)
|
||||
@@ -46,7 +48,8 @@ public static class AttestationEndpoints
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
|
||||
// GET /v1/advisory-ai/attestations/recent
|
||||
app.MapGet("/v1/advisory-ai/attestations/recent", HandleListRecentAttestations)
|
||||
@@ -57,7 +60,8 @@ public static class AttestationEndpoints
|
||||
.Produces<RecentAttestationsResponse>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
|
||||
// POST /v1/advisory-ai/attestations/verify
|
||||
app.MapPost("/v1/advisory-ai/attestations/verify", HandleVerifyAttestation)
|
||||
@@ -69,7 +73,8 @@ public static class AttestationEndpoints
|
||||
.Produces<AttestationVerificationResponse>(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
}
|
||||
|
||||
private static async Task<IResult> HandleGetRunAttestation(
|
||||
|
||||
@@ -18,6 +18,7 @@ using StellaOps.AdvisoryAI.Chat.Services;
|
||||
using StellaOps.AdvisoryAI.Chat.Settings;
|
||||
using StellaOps.AdvisoryAI.WebService.Contracts;
|
||||
using StellaOps.AdvisoryAI.WebService.Security;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using System.Collections.Immutable;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
@@ -45,7 +46,8 @@ public static class ChatEndpoints
|
||||
{
|
||||
var group = builder.MapGroup("/api/v1/chat")
|
||||
.WithTags("Advisory Chat")
|
||||
.RequireAuthorization(AdvisoryAIPolicies.OperatePolicy);
|
||||
.RequireAuthorization(AdvisoryAIPolicies.OperatePolicy)
|
||||
.RequireTenant();
|
||||
|
||||
// Single query endpoint (non-streaming)
|
||||
group.MapPost("/query", ProcessQueryAsync)
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.AdvisoryAI.WebService.Security;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Evidence.Pack;
|
||||
using StellaOps.Evidence.Pack.Models;
|
||||
@@ -35,7 +36,8 @@ public static class EvidencePackEndpoints
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.OperatePolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
|
||||
// GET /v1/evidence-packs/{packId} - Get Evidence Pack
|
||||
app.MapGet("/v1/evidence-packs/{packId}", HandleGetEvidencePack)
|
||||
@@ -47,7 +49,8 @@ public static class EvidencePackEndpoints
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
|
||||
// POST /v1/evidence-packs/{packId}/sign - Sign Evidence Pack
|
||||
app.MapPost("/v1/evidence-packs/{packId}/sign", HandleSignEvidencePack)
|
||||
@@ -59,7 +62,8 @@ public static class EvidencePackEndpoints
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.OperatePolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
|
||||
// POST /v1/evidence-packs/{packId}/verify - Verify Evidence Pack
|
||||
app.MapPost("/v1/evidence-packs/{packId}/verify", HandleVerifyEvidencePack)
|
||||
@@ -71,7 +75,8 @@ public static class EvidencePackEndpoints
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
|
||||
// GET /v1/evidence-packs/{packId}/export - Export Evidence Pack
|
||||
app.MapGet("/v1/evidence-packs/{packId}/export", HandleExportEvidencePack)
|
||||
@@ -83,7 +88,8 @@ public static class EvidencePackEndpoints
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
|
||||
// GET /v1/runs/{runId}/evidence-packs - List Evidence Packs for Run
|
||||
app.MapGet("/v1/runs/{runId}/evidence-packs", HandleListRunEvidencePacks)
|
||||
@@ -94,7 +100,8 @@ public static class EvidencePackEndpoints
|
||||
.Produces<EvidencePackListResponse>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
|
||||
// GET /v1/evidence-packs - List Evidence Packs
|
||||
app.MapGet("/v1/evidence-packs", HandleListEvidencePacks)
|
||||
@@ -105,7 +112,8 @@ public static class EvidencePackEndpoints
|
||||
.Produces<EvidencePackListResponse>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireRateLimiting("advisory-ai");
|
||||
.RequireRateLimiting("advisory-ai")
|
||||
.RequireTenant();
|
||||
}
|
||||
|
||||
private static async Task<IResult> HandleCreateEvidencePack(
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.AdvisoryAI.KnowledgeSearch;
|
||||
using StellaOps.AdvisoryAI.WebService.Security;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
|
||||
namespace StellaOps.AdvisoryAI.WebService.Endpoints;
|
||||
|
||||
@@ -19,7 +20,8 @@ public static class KnowledgeSearchEndpoints
|
||||
{
|
||||
var group = builder.MapGroup("/v1/advisory-ai")
|
||||
.WithTags("Advisory AI - Knowledge Search")
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy);
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapPost("/search", SearchAsync)
|
||||
.WithName("AdvisoryAiKnowledgeSearch")
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.AdvisoryAI.Inference.LlmProviders;
|
||||
using StellaOps.AdvisoryAI.Plugin.Unified;
|
||||
using StellaOps.AdvisoryAI.WebService.Security;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Plugin.Abstractions.Capabilities;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
@@ -29,7 +30,8 @@ public static class LlmAdapterEndpoints
|
||||
{
|
||||
var group = builder.MapGroup("/v1/advisory-ai/adapters")
|
||||
.WithTags("Advisory AI - LLM Adapters")
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy);
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet("/llm/providers", ListProvidersAsync)
|
||||
.WithName("ListLlmProviders")
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.AdvisoryAI.Runs;
|
||||
using StellaOps.AdvisoryAI.WebService.Security;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Determinism;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
@@ -29,7 +30,8 @@ public static class RunEndpoints
|
||||
{
|
||||
var group = builder.MapGroup("/api/v1/runs")
|
||||
.WithTags("Runs")
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy);
|
||||
.RequireAuthorization(AdvisoryAIPolicies.ViewPolicy)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapPost("/", CreateRunAsync)
|
||||
.WithName("CreateRun")
|
||||
|
||||
@@ -29,6 +29,7 @@ using StellaOps.AdvisoryAI.WebService.Services;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Evidence.Pack;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Router.AspNet;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
@@ -103,6 +104,7 @@ var routerEnabled = builder.Services.AddRouterMicroservice(
|
||||
version: System.Reflection.CustomAttributeExtensions.GetCustomAttribute<System.Reflection.AssemblyInformationalVersionAttribute>(System.Reflection.Assembly.GetExecutingAssembly())?.InformationalVersion ?? "1.0.0",
|
||||
routerOptionsSection: "Router");
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
builder.Services.AddRateLimiter(options =>
|
||||
@@ -145,6 +147,7 @@ if (app.Environment.IsDevelopment())
|
||||
|
||||
app.UseStellaOpsCors();
|
||||
app.UseAuthorization();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
app.UseRateLimiter();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ public sealed class CompanionExplainEndpointTests
|
||||
using var client = factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Client", "companion-tests");
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Scopes", "advisory:companion");
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
|
||||
var request = new CompanionExplainRequest
|
||||
{
|
||||
@@ -84,6 +85,7 @@ public sealed class CompanionExplainEndpointTests
|
||||
using var client = factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Client", "companion-tests");
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Scopes", "advisory:companion");
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
|
||||
var request = new CompanionExplainRequest
|
||||
{
|
||||
|
||||
@@ -49,6 +49,7 @@ public sealed class KnowledgeSearchEndpointsIntegrationTests : IDisposable
|
||||
{
|
||||
using var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Scopes", "advisory:search");
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
|
||||
var response = await client.PostAsJsonAsync("/v1/advisory-ai/search", new AdvisoryKnowledgeSearchRequest
|
||||
{
|
||||
@@ -75,6 +76,7 @@ public sealed class KnowledgeSearchEndpointsIntegrationTests : IDisposable
|
||||
{
|
||||
using var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Scopes", "advisory:index:write");
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
|
||||
var response = await client.PostAsync("/v1/advisory-ai/index/rebuild", content: null);
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
@@ -40,6 +40,7 @@ public sealed class LlmAdapterEndpointsIntegrationTests
|
||||
{
|
||||
using var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Scopes", "advisory:adapter:read");
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
|
||||
var response = await client.GetAsync("/v1/advisory-ai/adapters/llm/providers");
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
@@ -57,6 +58,7 @@ public sealed class LlmAdapterEndpointsIntegrationTests
|
||||
{
|
||||
using var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Scopes", "advisory:adapter:invoke");
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
|
||||
var request = new OpenAiChatCompletionRequest
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using StellaOps.AirGap.Time.Models;
|
||||
using StellaOps.AirGap.Time.Services;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using System.Security.Claims;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
|
||||
namespace StellaOps.AirGap.Controller.Endpoints;
|
||||
|
||||
@@ -14,7 +15,8 @@ internal static class AirGapEndpoints
|
||||
public static RouteGroupBuilder MapAirGapEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/system/airgap")
|
||||
.RequireAuthorization(AirGapPolicies.StatusRead);
|
||||
.RequireAuthorization(AirGapPolicies.StatusRead)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet("/status", HandleStatus)
|
||||
.RequireAuthorization(AirGapPolicies.StatusRead)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.AirGap.Controller.Auth;
|
||||
using StellaOps.AirGap.Controller.DependencyInjection;
|
||||
using StellaOps.AirGap.Controller.Endpoints;
|
||||
@@ -29,6 +30,7 @@ builder.Services.AddSingleton<TimeProvider>(TimeProvider.System);
|
||||
|
||||
builder.Services.AddAirGapController(builder.Configuration);
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
// Stella Router integration
|
||||
@@ -44,6 +46,7 @@ app.LogStellaOpsLocalHostname("airgap-controller");
|
||||
app.UseStellaOpsCors();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
app.MapAirGapEndpoints();
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -12,6 +12,7 @@ using StellaOps.BinaryIndex.VexBridge;
|
||||
using StellaOps.BinaryIndex.WebService.Middleware;
|
||||
using StellaOps.BinaryIndex.WebService.Services;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.BinaryIndex.WebService.Telemetry;
|
||||
|
||||
using StellaOps.Router.AspNet;
|
||||
@@ -65,6 +66,7 @@ builder.Services.AddResolutionRateLimiting(options =>
|
||||
builder.Services.AddHealthChecks()
|
||||
.AddRedis(redisConnectionString, name: "redis");
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
// Stella Router integration
|
||||
@@ -88,6 +90,7 @@ app.UseStellaOpsCors();
|
||||
// HTTPS redirection removed — the gateway handles TLS termination.
|
||||
app.UseResolutionRateLimiting();
|
||||
app.UseAuthorization();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
app.MapControllers();
|
||||
app.MapHealthChecks("/health");
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Cartographer.Options;
|
||||
|
||||
using StellaOps.Router.AspNet;
|
||||
@@ -71,6 +72,7 @@ if (authorityOptions.Enabled)
|
||||
builder.Services.AddHealthChecks()
|
||||
.AddCheck("cartographer_ready", () => HealthCheckResult.Healthy(), tags: new[] { "ready" });
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
// Stella Router integration
|
||||
@@ -99,6 +101,7 @@ if (authorityOptions.Enabled)
|
||||
app.UseAuthorization();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
}
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
|
||||
app.MapHealthChecks("/healthz").AllowAnonymous();
|
||||
app.MapHealthChecks("/readyz", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
|
||||
|
||||
@@ -100,6 +100,7 @@ public sealed class EvidenceAuditEndpointsTests : IDisposable
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(EvidenceLockerTestAuthHandler.SchemeName);
|
||||
client.DefaultRequestHeaders.Add("X-Test-Tenant", tenantId);
|
||||
client.DefaultRequestHeaders.Add("X-Test-Scopes", scopes);
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", tenantId);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -412,6 +412,7 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "test-token");
|
||||
client.DefaultRequestHeaders.Add("X-Tenant-Id", tenantId);
|
||||
client.DefaultRequestHeaders.Add("X-Scopes", scopes);
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", tenantId);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -467,6 +467,7 @@ public sealed class EvidenceLockerWebServiceContractTests : IDisposable
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "test-token");
|
||||
client.DefaultRequestHeaders.Add("X-Tenant-Id", tenantId);
|
||||
client.DefaultRequestHeaders.Add("X-Scopes", scopes);
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", tenantId);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -308,6 +308,7 @@ public sealed class EvidenceReindexIntegrationTests : IDisposable
|
||||
client.DefaultRequestHeaders.Add("X-Tenant-Id", tenantId);
|
||||
client.DefaultRequestHeaders.Add("X-Test-Subject", "test-user@example.com");
|
||||
client.DefaultRequestHeaders.Add("X-Scopes", scopes);
|
||||
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", tenantId);
|
||||
}
|
||||
|
||||
private static string ComputeSha256(string input)
|
||||
|
||||
@@ -32,6 +32,8 @@ public sealed class ExportEndpointsTests : IClassFixture<ExportEndpointsTests.Ex
|
||||
{
|
||||
_fixture = fixture;
|
||||
_client = fixture.DerivedFactory.CreateClient();
|
||||
_client.DefaultRequestHeaders.Add("Authorization", "Bearer test-token");
|
||||
_client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
|
||||
namespace StellaOps.ExportCenter.Core.Persistence;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for managing export distributions.
|
||||
/// </summary>
|
||||
public interface IExportDistributionRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a distribution by ID.
|
||||
/// </summary>
|
||||
Task<ExportDistribution?> GetByIdAsync(
|
||||
Guid tenantId,
|
||||
Guid distributionId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a distribution by idempotency key.
|
||||
/// </summary>
|
||||
Task<ExportDistribution?> GetByIdempotencyKeyAsync(
|
||||
Guid tenantId,
|
||||
string idempotencyKey,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists distributions for a run.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportDistribution>> ListByRunAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists distributions by status.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportDistribution>> ListByStatusAsync(
|
||||
Guid tenantId,
|
||||
ExportDistributionStatus status,
|
||||
int limit = 100,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists distributions due for retention deletion.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportDistribution>> ListExpiredAsync(
|
||||
DateTimeOffset asOf,
|
||||
int limit = 100,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new distribution record.
|
||||
/// </summary>
|
||||
Task<ExportDistribution> CreateAsync(
|
||||
ExportDistribution distribution,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Updates a distribution record.
|
||||
/// Returns the updated record, or null if not found.
|
||||
/// </summary>
|
||||
Task<ExportDistribution?> UpdateAsync(
|
||||
ExportDistribution distribution,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Performs an idempotent upsert based on idempotency key.
|
||||
/// Returns existing distribution if key matches, otherwise creates new.
|
||||
/// </summary>
|
||||
Task<(ExportDistribution Distribution, bool WasCreated)> UpsertByIdempotencyKeyAsync(
|
||||
ExportDistribution distribution,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Marks a distribution for deletion.
|
||||
/// </summary>
|
||||
Task<bool> MarkForDeletionAsync(
|
||||
Guid tenantId,
|
||||
Guid distributionId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a distribution record and returns whether it existed.
|
||||
/// </summary>
|
||||
Task<bool> DeleteAsync(
|
||||
Guid tenantId,
|
||||
Guid distributionId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets distribution statistics for a run.
|
||||
/// </summary>
|
||||
Task<ExportDistributionStats> GetStatsAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Statistics for distributions of a run.
|
||||
/// </summary>
|
||||
public sealed record ExportDistributionStats
|
||||
{
|
||||
public int Total { get; init; }
|
||||
public int Pending { get; init; }
|
||||
public int Distributing { get; init; }
|
||||
public int Distributed { get; init; }
|
||||
public int Verified { get; init; }
|
||||
public int Failed { get; init; }
|
||||
public int Cancelled { get; init; }
|
||||
public long TotalSizeBytes { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
|
||||
namespace StellaOps.ExportCenter.Core.Persistence;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for managing export profiles.
|
||||
/// </summary>
|
||||
public interface IExportProfileRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a profile by ID for a tenant.
|
||||
/// </summary>
|
||||
Task<ExportProfile?> GetByIdAsync(
|
||||
Guid tenantId,
|
||||
Guid profileId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists profiles for a tenant with optional filtering.
|
||||
/// </summary>
|
||||
Task<(IReadOnlyList<ExportProfile> Items, int TotalCount)> ListAsync(
|
||||
Guid tenantId,
|
||||
ExportProfileStatus? status = null,
|
||||
ExportProfileKind? kind = null,
|
||||
string? search = null,
|
||||
int offset = 0,
|
||||
int limit = 50,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new profile.
|
||||
/// </summary>
|
||||
Task<ExportProfile> CreateAsync(
|
||||
ExportProfile profile,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing profile.
|
||||
/// </summary>
|
||||
Task<ExportProfile?> UpdateAsync(
|
||||
ExportProfile profile,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Archives a profile (soft delete).
|
||||
/// </summary>
|
||||
Task<bool> ArchiveAsync(
|
||||
Guid tenantId,
|
||||
Guid profileId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a profile name is unique within a tenant.
|
||||
/// </summary>
|
||||
Task<bool> IsNameUniqueAsync(
|
||||
Guid tenantId,
|
||||
string name,
|
||||
Guid? excludeProfileId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets active scheduled profiles for processing.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportProfile>> GetScheduledProfilesAsync(
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
|
||||
namespace StellaOps.ExportCenter.Core.Persistence;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for managing export runs.
|
||||
/// </summary>
|
||||
public interface IExportRunRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a run by ID for a tenant.
|
||||
/// </summary>
|
||||
Task<ExportRun?> GetByIdAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists runs for a tenant with optional filtering.
|
||||
/// </summary>
|
||||
Task<(IReadOnlyList<ExportRun> Items, int TotalCount)> ListAsync(
|
||||
Guid tenantId,
|
||||
Guid? profileId = null,
|
||||
ExportRunStatus? status = null,
|
||||
ExportRunTrigger? trigger = null,
|
||||
DateTimeOffset? createdAfter = null,
|
||||
DateTimeOffset? createdBefore = null,
|
||||
string? correlationId = null,
|
||||
int offset = 0,
|
||||
int limit = 50,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new run.
|
||||
/// </summary>
|
||||
Task<ExportRun> CreateAsync(
|
||||
ExportRun run,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Updates run status and progress.
|
||||
/// </summary>
|
||||
Task<ExportRun?> UpdateAsync(
|
||||
ExportRun run,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Cancels a run if it's in a cancellable state.
|
||||
/// </summary>
|
||||
Task<bool> CancelAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets active runs count for concurrency checks.
|
||||
/// </summary>
|
||||
Task<int> GetActiveRunsCountAsync(
|
||||
Guid tenantId,
|
||||
Guid? profileId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets queued runs count.
|
||||
/// </summary>
|
||||
Task<int> GetQueuedRunsCountAsync(
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next queued run to execute.
|
||||
/// </summary>
|
||||
Task<ExportRun?> DequeueNextRunAsync(
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repository for managing export artifacts.
|
||||
/// </summary>
|
||||
public interface IExportArtifactRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an artifact by ID.
|
||||
/// </summary>
|
||||
Task<ExportArtifact?> GetByIdAsync(
|
||||
Guid tenantId,
|
||||
Guid artifactId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists artifacts for a run.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportArtifact>> ListByRunAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new artifact record.
|
||||
/// </summary>
|
||||
Task<ExportArtifact> CreateAsync(
|
||||
ExportArtifact artifact,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes artifacts for a run.
|
||||
/// </summary>
|
||||
Task<int> DeleteByRunAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an export artifact.
|
||||
/// </summary>
|
||||
public sealed record ExportArtifact
|
||||
{
|
||||
public required Guid ArtifactId { get; init; }
|
||||
public required Guid RunId { get; init; }
|
||||
public required Guid TenantId { get; init; }
|
||||
public required string Name { get; init; }
|
||||
public required string Kind { get; init; }
|
||||
public required string Path { get; init; }
|
||||
public long SizeBytes { get; init; }
|
||||
public string? ContentType { get; init; }
|
||||
public required string Checksum { get; init; }
|
||||
public string ChecksumAlgorithm { get; init; } = "SHA-256";
|
||||
public IReadOnlyDictionary<string, string>? Metadata { get; init; }
|
||||
public DateTimeOffset CreatedAt { get; init; }
|
||||
public DateTimeOffset? ExpiresAt { get; init; }
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Npgsql;
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
using StellaOps.ExportCenter.Infrastructure.Db;
|
||||
using StellaOps.ExportCenter.Infrastructure.EfCore.Models;
|
||||
using StellaOps.ExportCenter.WebService.Distribution;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
|
||||
namespace StellaOps.ExportCenter.Infrastructure.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using Npgsql;
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
using StellaOps.ExportCenter.Infrastructure.Db;
|
||||
using StellaOps.ExportCenter.Infrastructure.EfCore.Models;
|
||||
using StellaOps.ExportCenter.WebService.Api;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
|
||||
namespace StellaOps.ExportCenter.Infrastructure.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using Npgsql;
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
using StellaOps.ExportCenter.Infrastructure.Db;
|
||||
using StellaOps.ExportCenter.Infrastructure.EfCore.Models;
|
||||
using StellaOps.ExportCenter.WebService.Api;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
|
||||
namespace StellaOps.ExportCenter.Infrastructure.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
using StellaOps.ExportCenter.WebService.Api;
|
||||
using StellaOps.TestKit;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
using StellaOps.ExportCenter.WebService.Api;
|
||||
using Xunit;
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
using StellaOps.ExportCenter.Core.Planner;
|
||||
using IExportProfileRepository = StellaOps.ExportCenter.Core.Persistence.IExportProfileRepository;
|
||||
using StellaOps.ExportCenter.WebService.Telemetry;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Claims;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Api;
|
||||
|
||||
|
||||
@@ -1,66 +1,3 @@
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Api;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for managing export profiles.
|
||||
/// </summary>
|
||||
public interface IExportProfileRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a profile by ID for a tenant.
|
||||
/// </summary>
|
||||
Task<ExportProfile?> GetByIdAsync(
|
||||
Guid tenantId,
|
||||
Guid profileId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists profiles for a tenant with optional filtering.
|
||||
/// </summary>
|
||||
Task<(IReadOnlyList<ExportProfile> Items, int TotalCount)> ListAsync(
|
||||
Guid tenantId,
|
||||
ExportProfileStatus? status = null,
|
||||
ExportProfileKind? kind = null,
|
||||
string? search = null,
|
||||
int offset = 0,
|
||||
int limit = 50,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new profile.
|
||||
/// </summary>
|
||||
Task<ExportProfile> CreateAsync(
|
||||
ExportProfile profile,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing profile.
|
||||
/// </summary>
|
||||
Task<ExportProfile?> UpdateAsync(
|
||||
ExportProfile profile,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Archives a profile (soft delete).
|
||||
/// </summary>
|
||||
Task<bool> ArchiveAsync(
|
||||
Guid tenantId,
|
||||
Guid profileId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a profile name is unique within a tenant.
|
||||
/// </summary>
|
||||
Task<bool> IsNameUniqueAsync(
|
||||
Guid tenantId,
|
||||
string name,
|
||||
Guid? excludeProfileId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets active scheduled profiles for processing.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportProfile>> GetScheduledProfilesAsync(
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
// This interface has been moved to StellaOps.ExportCenter.Core.Persistence.
|
||||
// Import that namespace instead of StellaOps.ExportCenter.WebService.Api for IExportProfileRepository.
|
||||
// This file is kept for reference only.
|
||||
|
||||
@@ -1,133 +1,4 @@
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Api;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for managing export runs.
|
||||
/// </summary>
|
||||
public interface IExportRunRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a run by ID for a tenant.
|
||||
/// </summary>
|
||||
Task<ExportRun?> GetByIdAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists runs for a tenant with optional filtering.
|
||||
/// </summary>
|
||||
Task<(IReadOnlyList<ExportRun> Items, int TotalCount)> ListAsync(
|
||||
Guid tenantId,
|
||||
Guid? profileId = null,
|
||||
ExportRunStatus? status = null,
|
||||
ExportRunTrigger? trigger = null,
|
||||
DateTimeOffset? createdAfter = null,
|
||||
DateTimeOffset? createdBefore = null,
|
||||
string? correlationId = null,
|
||||
int offset = 0,
|
||||
int limit = 50,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new run.
|
||||
/// </summary>
|
||||
Task<ExportRun> CreateAsync(
|
||||
ExportRun run,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Updates run status and progress.
|
||||
/// </summary>
|
||||
Task<ExportRun?> UpdateAsync(
|
||||
ExportRun run,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Cancels a run if it's in a cancellable state.
|
||||
/// </summary>
|
||||
Task<bool> CancelAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets active runs count for concurrency checks.
|
||||
/// </summary>
|
||||
Task<int> GetActiveRunsCountAsync(
|
||||
Guid tenantId,
|
||||
Guid? profileId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets queued runs count.
|
||||
/// </summary>
|
||||
Task<int> GetQueuedRunsCountAsync(
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next queued run to execute.
|
||||
/// </summary>
|
||||
Task<ExportRun?> DequeueNextRunAsync(
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repository for managing export artifacts.
|
||||
/// </summary>
|
||||
public interface IExportArtifactRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an artifact by ID.
|
||||
/// </summary>
|
||||
Task<ExportArtifact?> GetByIdAsync(
|
||||
Guid tenantId,
|
||||
Guid artifactId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists artifacts for a run.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportArtifact>> ListByRunAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new artifact record.
|
||||
/// </summary>
|
||||
Task<ExportArtifact> CreateAsync(
|
||||
ExportArtifact artifact,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes artifacts for a run.
|
||||
/// </summary>
|
||||
Task<int> DeleteByRunAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an export artifact.
|
||||
/// </summary>
|
||||
public sealed record ExportArtifact
|
||||
{
|
||||
public required Guid ArtifactId { get; init; }
|
||||
public required Guid RunId { get; init; }
|
||||
public required Guid TenantId { get; init; }
|
||||
public required string Name { get; init; }
|
||||
public required string Kind { get; init; }
|
||||
public required string Path { get; init; }
|
||||
public long SizeBytes { get; init; }
|
||||
public string? ContentType { get; init; }
|
||||
public required string Checksum { get; init; }
|
||||
public string ChecksumAlgorithm { get; init; } = "SHA-256";
|
||||
public IReadOnlyDictionary<string, string>? Metadata { get; init; }
|
||||
public DateTimeOffset CreatedAt { get; init; }
|
||||
public DateTimeOffset? ExpiresAt { get; init; }
|
||||
}
|
||||
// The IExportRunRepository, IExportArtifactRepository interfaces and ExportArtifact record
|
||||
// have been moved to StellaOps.ExportCenter.Core.Persistence.
|
||||
// Import that namespace instead of StellaOps.ExportCenter.WebService.Api.
|
||||
// This file is kept for reference only.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Api;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
using StellaOps.ExportCenter.WebService.Distribution.Oci;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Distribution;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Distribution;
|
||||
|
||||
|
||||
@@ -1,112 +1,4 @@
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Distribution;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for managing export distributions.
|
||||
/// </summary>
|
||||
public interface IExportDistributionRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a distribution by ID.
|
||||
/// </summary>
|
||||
Task<ExportDistribution?> GetByIdAsync(
|
||||
Guid tenantId,
|
||||
Guid distributionId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a distribution by idempotency key.
|
||||
/// </summary>
|
||||
Task<ExportDistribution?> GetByIdempotencyKeyAsync(
|
||||
Guid tenantId,
|
||||
string idempotencyKey,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists distributions for a run.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportDistribution>> ListByRunAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists distributions by status.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportDistribution>> ListByStatusAsync(
|
||||
Guid tenantId,
|
||||
ExportDistributionStatus status,
|
||||
int limit = 100,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists distributions due for retention deletion.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ExportDistribution>> ListExpiredAsync(
|
||||
DateTimeOffset asOf,
|
||||
int limit = 100,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new distribution record.
|
||||
/// </summary>
|
||||
Task<ExportDistribution> CreateAsync(
|
||||
ExportDistribution distribution,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Updates a distribution record.
|
||||
/// Returns the updated record, or null if not found.
|
||||
/// </summary>
|
||||
Task<ExportDistribution?> UpdateAsync(
|
||||
ExportDistribution distribution,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Performs an idempotent upsert based on idempotency key.
|
||||
/// Returns existing distribution if key matches, otherwise creates new.
|
||||
/// </summary>
|
||||
Task<(ExportDistribution Distribution, bool WasCreated)> UpsertByIdempotencyKeyAsync(
|
||||
ExportDistribution distribution,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Marks a distribution for deletion.
|
||||
/// </summary>
|
||||
Task<bool> MarkForDeletionAsync(
|
||||
Guid tenantId,
|
||||
Guid distributionId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a distribution record and returns whether it existed.
|
||||
/// </summary>
|
||||
Task<bool> DeleteAsync(
|
||||
Guid tenantId,
|
||||
Guid distributionId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets distribution statistics for a run.
|
||||
/// </summary>
|
||||
Task<ExportDistributionStats> GetStatsAsync(
|
||||
Guid tenantId,
|
||||
Guid runId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Statistics for distributions of a run.
|
||||
/// </summary>
|
||||
public sealed record ExportDistributionStats
|
||||
{
|
||||
public int Total { get; init; }
|
||||
public int Pending { get; init; }
|
||||
public int Distributing { get; init; }
|
||||
public int Distributed { get; init; }
|
||||
public int Verified { get; init; }
|
||||
public int Failed { get; init; }
|
||||
public int Cancelled { get; init; }
|
||||
public long TotalSizeBytes { get; init; }
|
||||
}
|
||||
// The IExportDistributionRepository interface and ExportDistributionStats record
|
||||
// have been moved to StellaOps.ExportCenter.Core.Persistence.
|
||||
// Import that namespace instead of StellaOps.ExportCenter.WebService.Distribution.
|
||||
// This file is kept for reference only.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.ExportCenter.Core.Domain;
|
||||
using StellaOps.ExportCenter.Core.Persistence;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Distribution;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using StellaOps.AirGap.Policy;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.ExportCenter.WebService;
|
||||
using StellaOps.ExportCenter.WebService.Api;
|
||||
using StellaOps.ExportCenter.WebService.Attestation;
|
||||
@@ -104,6 +105,7 @@ builder.Services.AddExportApiServices(options =>
|
||||
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
// Stella Router integration
|
||||
@@ -125,6 +127,7 @@ if (app.Environment.IsDevelopment())
|
||||
app.UseStellaOpsCors();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
|
||||
// OpenAPI discovery endpoints (anonymous)
|
||||
@@ -162,19 +165,22 @@ app.MapGet("/exports", () => Results.Ok(Array.Empty<object>()))
|
||||
.RequireAuthorization(StellaOpsResourceServerPolicies.ExportViewer)
|
||||
.WithDeprecation(DeprecatedEndpointsRegistry.ListExports)
|
||||
.WithSummary("List exports (DEPRECATED)")
|
||||
.WithDescription("This endpoint is deprecated. Use GET /v1/exports/profiles instead.");
|
||||
.WithDescription("This endpoint is deprecated. Use GET /v1/exports/profiles instead.")
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/exports", () => Results.Accepted("/exports", new { status = "scheduled" }))
|
||||
.RequireAuthorization(StellaOpsResourceServerPolicies.ExportOperator)
|
||||
.WithDeprecation(DeprecatedEndpointsRegistry.CreateExport)
|
||||
.WithSummary("Create export (DEPRECATED)")
|
||||
.WithDescription("This endpoint is deprecated. Use POST /v1/exports/evidence or /v1/exports/attestations instead.");
|
||||
.WithDescription("This endpoint is deprecated. Use POST /v1/exports/evidence or /v1/exports/attestations instead.")
|
||||
.RequireTenant();
|
||||
|
||||
app.MapDelete("/exports/{id}", (string id) => Results.NoContent())
|
||||
.RequireAuthorization(StellaOpsResourceServerPolicies.ExportAdmin)
|
||||
.WithDeprecation(DeprecatedEndpointsRegistry.DeleteExport)
|
||||
.WithSummary("Delete export (DEPRECATED)")
|
||||
.WithDescription("This endpoint is deprecated. Use POST /v1/exports/runs/{id}/cancel instead.");
|
||||
.WithDescription("This endpoint is deprecated. Use POST /v1/exports/runs/{id}/cancel instead.")
|
||||
.RequireTenant();
|
||||
|
||||
// Refresh Router endpoint cache
|
||||
app.TryRefreshStellaRouterEndpoints(routerEnabled);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// =============================================================================
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
@@ -27,6 +28,8 @@ public sealed class EvidenceDecisionApiIntegrationTests : IClassFixture<Findings
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
});
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "test-token");
|
||||
_client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "GET /v1/alerts returns paginated list")]
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// =============================================================================
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
@@ -28,6 +29,8 @@ public sealed class ScoringEndpointsIntegrationTests : IClassFixture<FindingsLed
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
});
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "test-token");
|
||||
_client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
}
|
||||
|
||||
#region Task 8 - Single Score Endpoint Tests
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
@@ -30,6 +31,8 @@ public sealed class ScoringObservabilityTests : IClassFixture<FindingsLedgerWebA
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
});
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "test-token");
|
||||
_client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
}
|
||||
|
||||
#region Trace Context Tests
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// =============================================================================
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
@@ -28,6 +29,8 @@ public sealed class WebhookEndpointsIntegrationTests : IClassFixture<FindingsLed
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
});
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "test-token");
|
||||
_client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "test-tenant");
|
||||
}
|
||||
|
||||
#region Registration Tests
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Graph.Api.Contracts;
|
||||
using StellaOps.Graph.Api.Security;
|
||||
using StellaOps.Graph.Api.Services;
|
||||
@@ -55,6 +56,7 @@ builder.Services.AddAuthorization(options =>
|
||||
GraphScopeClaimReader.HasAnyScope(context.User, GraphPolicies.ExportScopes));
|
||||
});
|
||||
});
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
// Stella Router integration
|
||||
@@ -73,6 +75,7 @@ app.UseRouting();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
|
||||
app.MapPost("/graph/search", async (HttpContext context, GraphSearchRequest request, IGraphSearchService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -109,7 +112,8 @@ app.MapPost("/graph/search", async (HttpContext context, GraphSearchRequest requ
|
||||
LogAudit(context, "/graph/search", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
|
||||
return Results.Empty;
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/graph/query", async (HttpContext context, GraphQueryRequest request, IGraphQueryService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -146,7 +150,8 @@ app.MapPost("/graph/query", async (HttpContext context, GraphQueryRequest reques
|
||||
LogAudit(context, "/graph/query", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
|
||||
return Results.Empty;
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/graph/paths", async (HttpContext context, GraphPathRequest request, IGraphPathService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -183,7 +188,8 @@ app.MapPost("/graph/paths", async (HttpContext context, GraphPathRequest request
|
||||
LogAudit(context, "/graph/paths", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
|
||||
return Results.Empty;
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/graph/diff", async (HttpContext context, GraphDiffRequest request, IGraphDiffService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -220,7 +226,8 @@ app.MapPost("/graph/diff", async (HttpContext context, GraphDiffRequest request,
|
||||
LogAudit(context, "/graph/diff", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
|
||||
return Results.Empty;
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/graph/lineage", async (HttpContext context, GraphLineageRequest request, IGraphLineageService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -249,7 +256,8 @@ app.MapPost("/graph/lineage", async (HttpContext context, GraphLineageRequest re
|
||||
var response = await service.GetLineageAsync(tenantId, request, ct);
|
||||
LogAudit(context, "/graph/lineage", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
return Results.Ok(response);
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/graph/export", async (HttpContext context, GraphExportRequest request, IGraphExportService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -288,7 +296,8 @@ app.MapPost("/graph/export", async (HttpContext context, GraphExportRequest requ
|
||||
};
|
||||
LogAudit(context, "/graph/export", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
return Results.Ok(manifest);
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/graph/export/{jobId}", async (string jobId, HttpContext context, IGraphExportService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -316,11 +325,12 @@ app.MapGet("/graph/export/{jobId}", async (string jobId, HttpContext context, IG
|
||||
context.Response.Headers["X-Content-SHA256"] = job.Sha256;
|
||||
LogAudit(context, "/graph/export/download", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
return Results.File(job.Payload, job.ContentType, $"graph-export-{job.JobId}.{job.Format}");
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────────
|
||||
// â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€
|
||||
// Edge Metadata API
|
||||
// ────────────────────────────────────────────────────────────────────────────────
|
||||
// â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€â"€
|
||||
|
||||
app.MapPost("/graph/edges/metadata", async (EdgeMetadataRequest request, HttpContext context, IEdgeMetadataService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -340,7 +350,8 @@ app.MapPost("/graph/edges/metadata", async (EdgeMetadataRequest request, HttpCon
|
||||
var response = await service.GetEdgeMetadataAsync(auth.TenantId!, request, ct);
|
||||
LogAudit(context, "/graph/edges/metadata", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
return Results.Ok(response);
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/graph/edges/{edgeId}/metadata", async (string edgeId, HttpContext context, IEdgeMetadataService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -366,7 +377,8 @@ app.MapGet("/graph/edges/{edgeId}/metadata", async (string edgeId, HttpContext c
|
||||
|
||||
LogAudit(context, "/graph/edges/metadata", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
return Results.Ok(result);
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/graph/edges/path/{sourceNodeId}/{targetNodeId}", async (string sourceNodeId, string targetNodeId, HttpContext context, IEdgeMetadataService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -386,7 +398,8 @@ app.MapGet("/graph/edges/path/{sourceNodeId}/{targetNodeId}", async (string sour
|
||||
var edges = await service.GetPathEdgesWithMetadataAsync(auth.TenantId!, sourceNodeId, targetNodeId, ct);
|
||||
LogAudit(context, "/graph/edges/path", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
return Results.Ok(new { sourceNodeId, targetNodeId, edges = edges.ToList() });
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/graph/edges/by-reason/{reason}", async (string reason, int? limit, string? cursor, HttpContext context, IEdgeMetadataService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -412,7 +425,8 @@ app.MapGet("/graph/edges/by-reason/{reason}", async (string reason, int? limit,
|
||||
var response = await service.QueryByReasonAsync(auth.TenantId!, edgeReason, limit ?? 100, cursor, ct);
|
||||
LogAudit(context, "/graph/edges/by-reason", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
return Results.Ok(response);
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/graph/edges/by-evidence", async (string evidenceType, string evidenceRef, HttpContext context, IEdgeMetadataService service, CancellationToken ct) =>
|
||||
{
|
||||
@@ -432,7 +446,8 @@ app.MapGet("/graph/edges/by-evidence", async (string evidenceType, string eviden
|
||||
var edges = await service.QueryByEvidenceAsync(auth.TenantId!, evidenceType, evidenceRef, ct);
|
||||
LogAudit(context, "/graph/edges/by-evidence", StatusCodes.Status200OK, sw.ElapsedMilliseconds);
|
||||
return Results.Ok(edges);
|
||||
});
|
||||
})
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/healthz", () => Results.Ok(new { status = "ok" }));
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using StellaOps.IssuerDirectory.WebService.Contracts;
|
||||
using StellaOps.IssuerDirectory.WebService.Options;
|
||||
using StellaOps.IssuerDirectory.WebService.Security;
|
||||
using StellaOps.IssuerDirectory.WebService.Services;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
|
||||
namespace StellaOps.IssuerDirectory.WebService.Endpoints;
|
||||
|
||||
@@ -14,7 +15,8 @@ public static class IssuerEndpoints
|
||||
public static RouteGroupBuilder MapIssuerEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/issuer-directory/issuers")
|
||||
.WithTags("Issuer Directory");
|
||||
.WithTags("Issuer Directory")
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet(string.Empty, ListIssuers)
|
||||
.RequireAuthorization(IssuerDirectoryPolicies.Reader)
|
||||
|
||||
@@ -5,6 +5,7 @@ using StellaOps.IssuerDirectory.WebService.Constants;
|
||||
using StellaOps.IssuerDirectory.WebService.Contracts;
|
||||
using StellaOps.IssuerDirectory.WebService.Security;
|
||||
using StellaOps.IssuerDirectory.WebService.Services;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
|
||||
namespace StellaOps.IssuerDirectory.WebService.Endpoints;
|
||||
|
||||
@@ -12,7 +13,8 @@ internal static class IssuerKeyEndpoints
|
||||
{
|
||||
public static void MapIssuerKeyEndpoints(this RouteGroupBuilder group)
|
||||
{
|
||||
var keysGroup = group.MapGroup("{issuerId}/keys");
|
||||
var keysGroup = group.MapGroup("{issuerId}/keys")
|
||||
.RequireTenant();
|
||||
|
||||
keysGroup.MapGet(string.Empty, ListKeys)
|
||||
.RequireAuthorization(IssuerDirectoryPolicies.Reader)
|
||||
|
||||
@@ -4,6 +4,7 @@ using StellaOps.IssuerDirectory.WebService.Constants;
|
||||
using StellaOps.IssuerDirectory.WebService.Contracts;
|
||||
using StellaOps.IssuerDirectory.WebService.Security;
|
||||
using StellaOps.IssuerDirectory.WebService.Services;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
|
||||
namespace StellaOps.IssuerDirectory.WebService.Endpoints;
|
||||
|
||||
@@ -11,7 +12,8 @@ internal static class IssuerTrustEndpoints
|
||||
{
|
||||
public static void MapIssuerTrustEndpoints(this RouteGroupBuilder group)
|
||||
{
|
||||
var trustGroup = group.MapGroup("{issuerId}/trust");
|
||||
var trustGroup = group.MapGroup("{issuerId}/trust")
|
||||
.RequireTenant();
|
||||
|
||||
trustGroup.MapGet(string.Empty, GetTrust)
|
||||
.RequireAuthorization(IssuerDirectoryPolicies.Reader)
|
||||
|
||||
@@ -11,6 +11,7 @@ using Serilog;
|
||||
using Serilog.Events;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Configuration;
|
||||
using StellaOps.Infrastructure.Postgres.Options;
|
||||
using StellaOps.IssuerDirectory.Core.Services;
|
||||
@@ -100,6 +101,7 @@ builder.Services.AddOpenTelemetry()
|
||||
.AddRuntimeInstrumentation())
|
||||
.WithTracing(tracing => tracing.AddAspNetCoreInstrumentation().AddHttpClientInstrumentation());
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
// Stella Router integration
|
||||
@@ -117,6 +119,7 @@ app.UseSerilogRequestLogging();
|
||||
app.UseStellaOpsCors();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
|
||||
var issuerGroup = app.MapIssuerEndpoints();
|
||||
|
||||
@@ -398,8 +398,8 @@ static void ConfigureEndpoints(WebApplication app)
|
||||
var options = app.Services.GetRequiredService<IOptions<NotifyWebServiceOptions>>().Value;
|
||||
var tenantHeader = options.Api.TenantHeader;
|
||||
var apiBasePath = options.Api.BasePath.TrimEnd('/');
|
||||
var apiGroup = app.MapGroup(options.Api.BasePath);
|
||||
var internalGroup = app.MapGroup(options.Api.InternalBasePath);
|
||||
var apiGroup = app.MapGroup(options.Api.BasePath).RequireTenant();
|
||||
var internalGroup = app.MapGroup(options.Api.InternalBasePath).RequireTenant();
|
||||
|
||||
internalGroup.MapPost("/rules/normalize", (JsonNode? body, NotifySchemaMigrationService service) => Normalize(body, service.UpgradeRule))
|
||||
.WithName("notify.rules.normalize")
|
||||
|
||||
@@ -8,6 +8,7 @@ using StellaOps.OpsMemory.Models;
|
||||
using StellaOps.OpsMemory.Playbook;
|
||||
using StellaOps.OpsMemory.Storage;
|
||||
using StellaOps.OpsMemory.WebService.Security;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace StellaOps.OpsMemory.WebService.Endpoints;
|
||||
@@ -25,7 +26,8 @@ public static class OpsMemoryEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/opsmemory")
|
||||
.WithTags("OpsMemory")
|
||||
.RequireAuthorization(OpsMemoryPolicies.Read);
|
||||
.RequireAuthorization(OpsMemoryPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapPost("/decisions", RecordDecisionAsync)
|
||||
.WithName("RecordDecision")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using Npgsql;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.OpsMemory.Playbook;
|
||||
@@ -47,6 +48,7 @@ builder.Services.AddAuthorization(options =>
|
||||
options.AddStellaOpsScopePolicy(OpsMemoryPolicies.Write, StellaOpsScopes.OpsMemoryWrite);
|
||||
});
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
// Stella Router integration
|
||||
@@ -69,6 +71,7 @@ if (app.Environment.IsDevelopment())
|
||||
app.UseStellaOpsCors();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
|
||||
// Map endpoints
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
|
||||
@@ -25,7 +26,8 @@ public static class ApprovalEndpoints
|
||||
{
|
||||
var group = app.MapGroup(prefix)
|
||||
.WithTags("Approvals")
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead)
|
||||
.RequireTenant();
|
||||
|
||||
var list = group.MapGet(string.Empty, ListApprovals)
|
||||
.WithDescription("Return a list of release approval requests for the calling tenant, optionally filtered by status (Pending, Approved, Rejected), urgency level, and target environment. Each record includes the associated release, requester identity, SLA deadline, and policy gate context.");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
@@ -18,7 +19,8 @@ public static class AuditEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/audit")
|
||||
.WithTags("Orchestrator Audit")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
// List and get operations
|
||||
group.MapGet(string.Empty, ListAuditEntries)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Core.Services;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
@@ -18,7 +19,8 @@ public static class CircuitBreakerEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/circuit-breakers")
|
||||
.WithTags("Orchestrator Circuit Breakers")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
// List circuit breakers
|
||||
group.MapGet(string.Empty, ListCircuitBreakers)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Scheduling;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
@@ -18,7 +19,8 @@ public static class DagEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/dag")
|
||||
.WithTags("Orchestrator DAG")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet("run/{runId:guid}", GetRunDag)
|
||||
.WithName("Orchestrator_GetRunDag")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Npgsql;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.DeadLetter;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
@@ -22,7 +23,8 @@ public static class DeadLetterEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/deadletter")
|
||||
.WithTags("Orchestrator Dead-Letter")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
// Entry management
|
||||
group.MapGet(string.Empty, ListEntries)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Core.Domain.Export;
|
||||
using StellaOps.Orchestrator.Core.Services;
|
||||
@@ -18,7 +19,8 @@ public static class ExportJobEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/export")
|
||||
.WithTags("Export Jobs")
|
||||
.RequireAuthorization(OrchestratorPolicies.ExportViewer);
|
||||
.RequireAuthorization(OrchestratorPolicies.ExportViewer)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapPost("jobs", CreateExportJob)
|
||||
.WithName("Orchestrator_CreateExportJob")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Services;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
@@ -14,7 +15,8 @@ public static class FirstSignalEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/runs")
|
||||
.WithTags("Orchestrator Runs")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet("{runId:guid}/first-signal", GetFirstSignal)
|
||||
.WithName("Orchestrator_GetFirstSignal")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
@@ -17,7 +18,8 @@ public static class JobEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/jobs")
|
||||
.WithTags("Orchestrator Jobs")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet(string.Empty, ListJobs)
|
||||
.WithName("Orchestrator_ListJobs")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Metrics.Kpi;
|
||||
|
||||
namespace StellaOps.Orchestrator.WebService.Endpoints;
|
||||
@@ -16,7 +17,8 @@ public static class KpiEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/metrics/kpis")
|
||||
.WithTags("Quality KPIs")
|
||||
.RequireAuthorization(OrchestratorPolicies.ObservabilityRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.ObservabilityRead)
|
||||
.RequireTenant();
|
||||
|
||||
// GET /api/v1/metrics/kpis
|
||||
group.MapGet("/", GetQualityKpis)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
@@ -18,7 +19,8 @@ public static class LedgerEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/ledger")
|
||||
.WithTags("Orchestrator Ledger")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
// Ledger entry operations
|
||||
group.MapGet(string.Empty, ListLedgerEntries)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
@@ -22,7 +23,8 @@ public static class PackRegistryEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/registry/packs")
|
||||
.WithTags("Orchestrator Pack Registry")
|
||||
.RequireAuthorization(OrchestratorPolicies.PacksRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.PacksRead)
|
||||
.RequireTenant();
|
||||
|
||||
// Pack CRUD endpoints
|
||||
group.MapPost("", CreatePack)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using PackLogLevel = StellaOps.Orchestrator.Core.Domain.LogLevel;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Core.Domain.Events;
|
||||
@@ -38,7 +39,8 @@ public static class PackRunEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/pack-runs")
|
||||
.WithTags("Orchestrator Pack Runs")
|
||||
.RequireAuthorization(OrchestratorPolicies.PacksRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.PacksRead)
|
||||
.RequireTenant();
|
||||
|
||||
// Scheduling endpoints
|
||||
group.MapPost("", SchedulePackRun)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Infrastructure.Postgres;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
@@ -19,7 +20,8 @@ public static class QuotaEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/quotas")
|
||||
.WithTags("Orchestrator Quotas")
|
||||
.RequireAuthorization(OrchestratorPolicies.Quota);
|
||||
.RequireAuthorization(OrchestratorPolicies.Quota)
|
||||
.RequireTenant();
|
||||
|
||||
// Quota CRUD operations
|
||||
group.MapGet(string.Empty, ListQuotas)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Core.Services;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
@@ -18,7 +19,8 @@ public static class QuotaGovernanceEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/quota-governance")
|
||||
.WithTags("Orchestrator Quota Governance")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
// Policy management
|
||||
group.MapGet("policies", ListPolicies)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
|
||||
@@ -21,7 +22,8 @@ public static class ReleaseControlV2Endpoints
|
||||
{
|
||||
var approvals = app.MapGroup("/api/v1/approvals")
|
||||
.WithTags("Approvals v2")
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead)
|
||||
.RequireTenant();
|
||||
|
||||
approvals.MapGet(string.Empty, ListApprovals)
|
||||
.WithName("ApprovalsV2_List")
|
||||
@@ -73,13 +75,15 @@ public static class ReleaseControlV2Endpoints
|
||||
|
||||
var apiRuns = app.MapGroup("/api/v1/runs")
|
||||
.WithTags("Runs v2")
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead)
|
||||
.RequireTenant();
|
||||
MapRunGroup(apiRuns);
|
||||
apiRuns.WithGroupName("runs-v2");
|
||||
|
||||
var legacyV1Runs = app.MapGroup("/v1/runs")
|
||||
.WithTags("Runs v2")
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead)
|
||||
.RequireTenant();
|
||||
MapRunGroup(legacyV1Runs);
|
||||
legacyV1Runs.WithGroupName("runs-v1-compat");
|
||||
}
|
||||
@@ -88,7 +92,8 @@ public static class ReleaseControlV2Endpoints
|
||||
{
|
||||
var environments = app.MapGroup("/api/v1/environments")
|
||||
.WithTags("Environments v2")
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead)
|
||||
.RequireTenant();
|
||||
|
||||
environments.MapGet("/{id}", GetEnvironmentDetail)
|
||||
.WithName("EnvironmentsV2_Get")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
|
||||
namespace StellaOps.Orchestrator.WebService.Endpoints;
|
||||
@@ -19,7 +20,8 @@ public static class ReleaseDashboardEndpoints
|
||||
{
|
||||
var group = app.MapGroup(prefix)
|
||||
.WithTags("ReleaseDashboard")
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead)
|
||||
.RequireTenant();
|
||||
|
||||
var dashboard = group.MapGet("/dashboard", GetDashboard)
|
||||
.WithDescription("Return a consolidated release dashboard snapshot for the Console control plane, including pending approvals, active promotions, recent deployments, and environment health indicators. Used by the UI to populate the main release management view.");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
|
||||
namespace StellaOps.Orchestrator.WebService.Endpoints;
|
||||
@@ -27,7 +28,8 @@ public static class ReleaseEndpoints
|
||||
{
|
||||
var group = app.MapGroup(prefix)
|
||||
.WithTags("Releases")
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead);
|
||||
.RequireAuthorization(OrchestratorPolicies.ReleaseRead)
|
||||
.RequireTenant();
|
||||
|
||||
var list = group.MapGet(string.Empty, ListReleases)
|
||||
.WithDescription("Return a paginated list of releases for the calling tenant, optionally filtered by status, environment, project, and creation time window. Each release record includes its name, version, current status, component count, and lifecycle timestamps.");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
@@ -18,7 +19,8 @@ public static class RunEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/runs")
|
||||
.WithTags("Orchestrator Runs")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet(string.Empty, ListRuns)
|
||||
.WithName("Orchestrator_ListRuns")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Core.SloManagement;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
@@ -18,7 +19,8 @@ public static class SloEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/slos")
|
||||
.WithTags("Orchestrator SLOs")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
// SLO CRUD operations
|
||||
group.MapGet(string.Empty, ListSlos)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
using StellaOps.Orchestrator.WebService.Contracts;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
@@ -17,7 +18,8 @@ public static class SourceEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/sources")
|
||||
.WithTags("Orchestrator Sources")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet(string.Empty, ListSources)
|
||||
.WithName("Orchestrator_ListSources")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
using StellaOps.Orchestrator.WebService.Streaming;
|
||||
@@ -18,7 +19,8 @@ public static class StreamEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/stream")
|
||||
.WithTags("Orchestrator Streams")
|
||||
.RequireAuthorization(OrchestratorPolicies.Read);
|
||||
.RequireAuthorization(OrchestratorPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet("jobs/{jobId:guid}", StreamJob)
|
||||
.WithName("Orchestrator_StreamJob")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Orchestrator.Core.Domain;
|
||||
using StellaOps.Orchestrator.Infrastructure;
|
||||
using StellaOps.Orchestrator.Infrastructure.Repositories;
|
||||
@@ -24,7 +25,8 @@ public static class WorkerEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/orchestrator/worker")
|
||||
.WithTags("Orchestrator Workers")
|
||||
.RequireAuthorization(OrchestratorPolicies.Operate);
|
||||
.RequireAuthorization(OrchestratorPolicies.Operate)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapPost("claim", ClaimJob)
|
||||
.WithName("Orchestrator_ClaimJob")
|
||||
|
||||
@@ -9,11 +9,13 @@ using StellaOps.Orchestrator.WebService.Endpoints;
|
||||
using StellaOps.Orchestrator.WebService.Services;
|
||||
using StellaOps.Orchestrator.WebService.Streaming;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Router.AspNet;
|
||||
using StellaOps.Telemetry.Core;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
@@ -139,6 +141,7 @@ if (app.Environment.IsDevelopment())
|
||||
}
|
||||
|
||||
app.UseStellaOpsCors();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
|
||||
// Enable telemetry context propagation (extracts tenant/actor/correlation from headers)
|
||||
// Per ORCH-OBS-50-001
|
||||
|
||||
@@ -9,6 +9,7 @@ using StellaOps.PacksRegistry.WebService;
|
||||
using StellaOps.PacksRegistry.WebService.Contracts;
|
||||
using StellaOps.PacksRegistry.WebService.Options;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Router.AspNet;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
@@ -57,6 +58,7 @@ builder.Services.AddSingleton(TimeProvider.System);
|
||||
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
// Stella Router integration
|
||||
@@ -76,6 +78,7 @@ if (app.Environment.IsDevelopment())
|
||||
}
|
||||
|
||||
app.UseStellaOpsCors();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
app.MapHealthChecks("/healthz");
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
|
||||
@@ -160,7 +163,8 @@ app.MapPost("/api/v1/packs", async (PackUploadRequest request, PackService servi
|
||||
.WithDescription("Uploads a new policy pack as base64-encoded content with optional signature and provenance attachment. Returns 201 Created with the registered pack record and assigned pack ID. Requires the X-StellaOps-Tenant header or a tenantId body field.")
|
||||
.Produces<PackResponse>(StatusCodes.Status201Created)
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/packs", async (string? tenant, bool? includeDeprecated, PackService service, LifecycleService lifecycleService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -197,7 +201,8 @@ app.MapGet("/api/v1/packs", async (string? tenant, bool? includeDeprecated, Pack
|
||||
.WithName("ListPacks")
|
||||
.WithDescription("Returns the list of policy packs for the specified tenant, optionally excluding deprecated packs. When tenant allowlists are configured, a tenant query parameter or X-StellaOps-Tenant header is required.")
|
||||
.Produces<IEnumerable<PackResponse>>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/packs/{packId}", async (string packId, PackService service, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -226,7 +231,8 @@ app.MapGet("/api/v1/packs/{packId}", async (string packId, PackService service,
|
||||
.Produces<PackResponse>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/packs/{packId}/content", async (string packId, PackService service, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -262,7 +268,8 @@ app.MapGet("/api/v1/packs/{packId}/content", async (string packId, PackService s
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/packs/{packId}/provenance", async (string packId, PackService service, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -302,7 +309,8 @@ app.MapGet("/api/v1/packs/{packId}/provenance", async (string packId, PackServic
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/packs/{packId}/manifest", async (string packId, PackService service, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -344,7 +352,8 @@ app.MapGet("/api/v1/packs/{packId}/manifest", async (string packId, PackService
|
||||
.Produces<PackManifestResponse>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/api/v1/packs/{packId}/signature", async (string packId, RotateSignatureRequest request, PackService service, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -391,7 +400,8 @@ app.MapPost("/api/v1/packs/{packId}/signature", async (string packId, RotateSign
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/api/v1/packs/{packId}/attestations", async (string packId, AttestationUploadRequest request, AttestationService attestationService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -433,7 +443,8 @@ app.MapPost("/api/v1/packs/{packId}/attestations", async (string packId, Attesta
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/packs/{packId}/attestations", async (string packId, AttestationService attestationService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -461,7 +472,8 @@ app.MapGet("/api/v1/packs/{packId}/attestations", async (string packId, Attestat
|
||||
.Produces<IEnumerable<AttestationResponse>>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/packs/{packId}/attestations/{type}", async (string packId, string type, AttestationService attestationService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -496,7 +508,8 @@ app.MapGet("/api/v1/packs/{packId}/attestations/{type}", async (string packId, s
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/packs/{packId}/parity", async (string packId, ParityService parityService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -525,7 +538,8 @@ app.MapGet("/api/v1/packs/{packId}/parity", async (string packId, ParityService
|
||||
.Produces<ParityResponse>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/packs/{packId}/lifecycle", async (string packId, LifecycleService lifecycleService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -554,7 +568,8 @@ app.MapGet("/api/v1/packs/{packId}/lifecycle", async (string packId, LifecycleSe
|
||||
.Produces<LifecycleResponse>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/api/v1/packs/{packId}/lifecycle", async (string packId, LifecycleRequest request, LifecycleService lifecycleService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -596,7 +611,8 @@ app.MapPost("/api/v1/packs/{packId}/lifecycle", async (string packId, LifecycleR
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/api/v1/packs/{packId}/parity", async (string packId, ParityRequest request, ParityService parityService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -638,7 +654,8 @@ app.MapPost("/api/v1/packs/{packId}/parity", async (string packId, ParityRequest
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/api/v1/export/offline-seed", async (OfflineSeedRequest request, ExportService exportService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -667,7 +684,8 @@ app.MapPost("/api/v1/export/offline-seed", async (OfflineSeedRequest request, Ex
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden);
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/api/v1/mirrors", async (MirrorRequest request, MirrorService mirrorService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -700,7 +718,8 @@ app.MapPost("/api/v1/mirrors", async (MirrorRequest request, MirrorService mirro
|
||||
.Produces<MirrorResponse>(StatusCodes.Status201Created)
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden);
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/mirrors", async (string? tenant, MirrorService mirrorService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -723,7 +742,8 @@ app.MapGet("/api/v1/mirrors", async (string? tenant, MirrorService mirrorService
|
||||
.WithDescription("Returns all mirror registrations for the specified tenant, or all mirrors if no tenant filter is applied. Returns 403 if the caller's tenant allowlist excludes the requested tenant.")
|
||||
.Produces<IEnumerable<MirrorResponse>>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden);
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapPost("/api/v1/mirrors/{id}/sync", async (string id, MirrorSyncRequest request, MirrorService mirrorService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -752,7 +772,8 @@ app.MapPost("/api/v1/mirrors/{id}/sync", async (string id, MirrorSyncRequest req
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireTenant();
|
||||
|
||||
app.MapGet("/api/v1/compliance/summary", async (string? tenant, ComplianceService complianceService, HttpContext context, AuthOptions auth, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -775,7 +796,8 @@ app.MapGet("/api/v1/compliance/summary", async (string? tenant, ComplianceServic
|
||||
.WithDescription("Returns a compliance summary for the specified tenant's pack collection including signed pack count, unsigned count, packs with attestations, deprecated packs, and mirror sync status breakdown. Returns 403 if the tenant is not allowed.")
|
||||
.Produces<ComplianceSummary>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden);
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.RequireTenant();
|
||||
|
||||
// Refresh Router endpoint cache
|
||||
app.TryRefreshStellaRouterEndpoints(routerEnabled);
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -20,7 +21,8 @@ public static class AdministrationTrustSigningMutationEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/administration/trust-signing")
|
||||
.WithTags("Administration")
|
||||
.RequireAuthorization(PlatformPolicies.TrustRead);
|
||||
.RequireAuthorization(PlatformPolicies.TrustRead)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet("/keys", async Task<IResult>(
|
||||
HttpContext context,
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -18,7 +19,8 @@ public static class AnalyticsEndpoints
|
||||
{
|
||||
var analytics = app.MapGroup("/api/analytics")
|
||||
.WithTags("Analytics")
|
||||
.RequireAuthorization(PlatformPolicies.AnalyticsRead);
|
||||
.RequireAuthorization(PlatformPolicies.AnalyticsRead)
|
||||
.RequireTenant();
|
||||
|
||||
analytics.MapGet("/suppliers", async Task<IResult> (
|
||||
HttpContext context,
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -16,7 +17,8 @@ public static class ContextEndpoints
|
||||
{
|
||||
var context = app.MapGroup("/api/v2/context")
|
||||
.WithTags("Platform Context")
|
||||
.RequireAuthorization(PlatformPolicies.ContextRead);
|
||||
.RequireAuthorization(PlatformPolicies.ContextRead)
|
||||
.RequireTenant();
|
||||
|
||||
context.MapGet("/regions", async Task<IResult>(
|
||||
HttpContext httpContext,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
|
||||
@@ -19,7 +20,8 @@ public static class EnvironmentSettingsAdminEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/platform/envsettings/db")
|
||||
.WithTags("Environment Settings Admin")
|
||||
.RequireAuthorization(PlatformPolicies.SetupRead);
|
||||
.RequireAuthorization(PlatformPolicies.SetupRead)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet("/", async (IEnvironmentSettingsStore store, CancellationToken ct) =>
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Options;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
using StellaOps.ReleaseOrchestrator.EvidenceThread.Export;
|
||||
@@ -33,7 +34,8 @@ public static class EvidenceThreadEndpoints
|
||||
{
|
||||
var evidence = app.MapGroup("/api/v1/evidence")
|
||||
.WithTags("Evidence Thread")
|
||||
.RequireAuthorization(PlatformPolicies.ContextRead);
|
||||
.RequireAuthorization(PlatformPolicies.ContextRead)
|
||||
.RequireTenant();
|
||||
|
||||
// GET /api/v1/evidence/{artifactDigest} - Get evidence thread for artifact
|
||||
evidence.MapGet("/{artifactDigest}", GetEvidenceThread)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -21,7 +22,8 @@ public static class FederationTelemetryEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/v1/telemetry/federation")
|
||||
.WithTags("Federated Telemetry")
|
||||
.RequireAuthorization(PlatformPolicies.FederationRead);
|
||||
.RequireAuthorization(PlatformPolicies.FederationRead)
|
||||
.RequireTenant();
|
||||
|
||||
// GET /consent — get consent state
|
||||
group.MapGet("/consent", async Task<IResult>(
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -25,7 +26,8 @@ public static class FunctionMapEndpoints
|
||||
{
|
||||
var maps = app.MapGroup("/api/v1/function-maps")
|
||||
.WithTags("Function Maps")
|
||||
.RequireAuthorization(PlatformPolicies.FunctionMapRead);
|
||||
.RequireAuthorization(PlatformPolicies.FunctionMapRead)
|
||||
.RequireTenant();
|
||||
|
||||
MapCrudEndpoints(maps);
|
||||
MapVerifyEndpoints(maps);
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -16,7 +17,8 @@ public static class IntegrationReadModelEndpoints
|
||||
{
|
||||
var integrations = app.MapGroup("/api/v2/integrations")
|
||||
.WithTags("Integrations V2")
|
||||
.RequireAuthorization(PlatformPolicies.IntegrationsRead);
|
||||
.RequireAuthorization(PlatformPolicies.IntegrationsRead)
|
||||
.RequireTenant();
|
||||
|
||||
integrations.MapGet("/feeds", async Task<IResult>(
|
||||
HttpContext context,
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -17,7 +18,8 @@ public static class LegacyAliasEndpoints
|
||||
{
|
||||
var legacy = app.MapGroup("/api/v1")
|
||||
.WithTags("Pack22 Legacy Aliases")
|
||||
.RequireAuthorization(PlatformPolicies.ContextRead);
|
||||
.RequireAuthorization(PlatformPolicies.ContextRead)
|
||||
.RequireTenant();
|
||||
|
||||
legacy.MapGet("/context/regions", async Task<IResult>(
|
||||
HttpContext context,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -44,7 +45,8 @@ public static class PackAdapterEndpoints
|
||||
|
||||
var platform = app.MapGroup("/api/v1/platform")
|
||||
.WithTags("Platform Ops")
|
||||
.RequireAuthorization(PlatformPolicies.HealthRead);
|
||||
.RequireAuthorization(PlatformPolicies.HealthRead)
|
||||
.RequireTenant();
|
||||
|
||||
platform.MapGet("/data-integrity/summary", (
|
||||
HttpContext context,
|
||||
@@ -158,7 +160,8 @@ public static class PackAdapterEndpoints
|
||||
.RequireAuthorization(PlatformPolicies.HealthRead);
|
||||
|
||||
var administration = app.MapGroup("/api/v1/administration")
|
||||
.WithTags("Administration");
|
||||
.WithTags("Administration")
|
||||
.RequireTenant();
|
||||
|
||||
administration.MapGet("/summary", (
|
||||
HttpContext context,
|
||||
|
||||
@@ -8,6 +8,7 @@ using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -19,7 +20,8 @@ public static class PlatformEndpoints
|
||||
{
|
||||
var platform = app.MapGroup("/api/v1/platform")
|
||||
.WithTags("Platform")
|
||||
.RequireAuthorization(PlatformPolicies.HealthRead);
|
||||
.RequireAuthorization(PlatformPolicies.HealthRead)
|
||||
.RequireTenant();
|
||||
|
||||
MapHealthEndpoints(platform);
|
||||
MapQuotaEndpoints(platform);
|
||||
@@ -478,7 +480,8 @@ public static class PlatformEndpoints
|
||||
{
|
||||
var quotas = app.MapGroup("/api/v1/authority/quotas")
|
||||
.WithTags("Platform Quotas Compatibility")
|
||||
.RequireAuthorization(PlatformPolicies.QuotaRead);
|
||||
.RequireAuthorization(PlatformPolicies.QuotaRead)
|
||||
.RequireTenant();
|
||||
|
||||
quotas.MapGet(string.Empty, async Task<IResult> (
|
||||
HttpContext context,
|
||||
@@ -715,7 +718,8 @@ public static class PlatformEndpoints
|
||||
|
||||
var rateLimits = app.MapGroup("/api/v1/gateway/rate-limits")
|
||||
.WithTags("Platform Gateway Compatibility")
|
||||
.RequireAuthorization(PlatformPolicies.QuotaRead);
|
||||
.RequireAuthorization(PlatformPolicies.QuotaRead)
|
||||
.RequireTenant();
|
||||
|
||||
rateLimits.MapGet(string.Empty, (HttpContext context, PlatformRequestContextResolver resolver) =>
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -26,7 +27,8 @@ public static class PolicyInteropEndpoints
|
||||
{
|
||||
var interop = app.MapGroup("/api/v1/policy/interop")
|
||||
.WithTags("PolicyInterop")
|
||||
.RequireAuthorization(PlatformPolicies.PolicyRead);
|
||||
.RequireAuthorization(PlatformPolicies.PolicyRead)
|
||||
.RequireTenant();
|
||||
|
||||
MapExportEndpoint(interop);
|
||||
MapImportEndpoint(interop);
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -20,7 +21,8 @@ public static class ReleaseControlEndpoints
|
||||
{
|
||||
var bundles = app.MapGroup("/api/v1/release-control/bundles")
|
||||
.WithTags("Release Control")
|
||||
.RequireAuthorization(PlatformPolicies.ReleaseControlRead);
|
||||
.RequireAuthorization(PlatformPolicies.ReleaseControlRead)
|
||||
.RequireTenant();
|
||||
|
||||
bundles.MapGet(string.Empty, async Task<IResult>(
|
||||
HttpContext context,
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -17,7 +18,8 @@ public static class ReleaseReadModelEndpoints
|
||||
{
|
||||
var releases = app.MapGroup("/api/v2/releases")
|
||||
.WithTags("Releases V2")
|
||||
.RequireAuthorization(PlatformPolicies.ReleaseControlRead);
|
||||
.RequireAuthorization(PlatformPolicies.ReleaseControlRead)
|
||||
.RequireTenant();
|
||||
|
||||
releases.MapGet(string.Empty, async Task<IResult>(
|
||||
HttpContext context,
|
||||
|
||||
@@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Platform.WebService.Constants;
|
||||
using StellaOps.Platform.WebService.Contracts;
|
||||
using StellaOps.Platform.WebService.Services;
|
||||
@@ -26,7 +27,8 @@ public static class ScoreEndpoints
|
||||
{
|
||||
var score = app.MapGroup("/api/v1/score")
|
||||
.WithTags("Score")
|
||||
.RequireAuthorization(PlatformPolicies.ScoreRead);
|
||||
.RequireAuthorization(PlatformPolicies.ScoreRead)
|
||||
.RequireTenant();
|
||||
|
||||
MapEvaluateEndpoints(score);
|
||||
MapHistoryEndpoints(score);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user