wip: doctor/cli/docs/api to vector db consolidation; api hardening for descriptions, tenant, and scopes; migrations and conversions of all DALs to EF v10
This commit is contained in:
@@ -124,7 +124,7 @@ public sealed class IdentityHeaderPolicyMiddlewareTests
|
||||
#region Header Overwriting (Not Set-If-Missing)
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAsync_OverwritesSpoofedTenantWithClaimValue()
|
||||
public async Task InvokeAsync_RejectsSpoofedTenantHeaderWhenOverrideDisabled()
|
||||
{
|
||||
var middleware = CreateMiddleware();
|
||||
var claims = new[]
|
||||
@@ -133,15 +133,15 @@ public sealed class IdentityHeaderPolicyMiddlewareTests
|
||||
new Claim(StellaOpsClaimTypes.Subject, "real-subject")
|
||||
};
|
||||
var context = CreateHttpContext("/api/scan", claims);
|
||||
context.Response.Body = new MemoryStream();
|
||||
|
||||
// Client attempts to spoof tenant
|
||||
context.Request.Headers["X-StellaOps-Tenant"] = "spoofed-tenant";
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
Assert.True(_nextCalled);
|
||||
// Header should contain claim value, not spoofed value
|
||||
Assert.Equal("real-tenant", context.Request.Headers["X-StellaOps-Tenant"].ToString());
|
||||
Assert.False(_nextCalled);
|
||||
Assert.Equal(StatusCodes.Status403Forbidden, context.Response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -225,6 +225,7 @@ public sealed class IdentityHeaderPolicyMiddlewareTests
|
||||
|
||||
Assert.True(_nextCalled);
|
||||
Assert.Equal("tenant-abc", context.Request.Headers["X-StellaOps-Tenant"].ToString());
|
||||
Assert.Equal("tenant-abc", context.Request.Headers["X-Tenant-Id"].ToString());
|
||||
Assert.Equal("tenant-abc", context.Items[GatewayContextKeys.TenantId]);
|
||||
}
|
||||
|
||||
@@ -243,6 +244,25 @@ public sealed class IdentityHeaderPolicyMiddlewareTests
|
||||
|
||||
Assert.True(_nextCalled);
|
||||
Assert.Equal("legacy-tenant-456", context.Request.Headers["X-StellaOps-Tenant"].ToString());
|
||||
Assert.Equal("legacy-tenant-456", context.Request.Headers["X-Tenant-Id"].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAsync_AuthenticatedRequestWithoutTenantClaim_DoesNotWriteTenantHeaders()
|
||||
{
|
||||
var middleware = CreateMiddleware();
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(StellaOpsClaimTypes.Subject, "user")
|
||||
};
|
||||
var context = CreateHttpContext("/api/scan", claims);
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
Assert.True(_nextCalled);
|
||||
Assert.DoesNotContain("X-StellaOps-Tenant", context.Request.Headers.Keys);
|
||||
Assert.DoesNotContain("X-Stella-Tenant", context.Request.Headers.Keys);
|
||||
Assert.DoesNotContain("X-Tenant-Id", context.Request.Headers.Keys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -308,6 +328,109 @@ public sealed class IdentityHeaderPolicyMiddlewareTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tenant Override
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAsync_OverrideEnabledAndAllowed_UsesRequestedTenant()
|
||||
{
|
||||
_options.EnableTenantOverride = true;
|
||||
var middleware = CreateMiddleware();
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(StellaOpsClaimTypes.Subject, "user"),
|
||||
new Claim(StellaOpsClaimTypes.Tenant, "tenant-a"),
|
||||
new Claim(StellaOpsClaimTypes.AllowedTenants, "tenant-a tenant-b")
|
||||
};
|
||||
var context = CreateHttpContext("/api/platform", claims);
|
||||
|
||||
context.Request.Headers["X-StellaOps-Tenant"] = "TENANT-B";
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
Assert.True(_nextCalled);
|
||||
Assert.Equal("tenant-b", context.Request.Headers["X-StellaOps-Tenant"].ToString());
|
||||
Assert.Equal("tenant-b", context.Request.Headers["X-Tenant-Id"].ToString());
|
||||
Assert.Equal("tenant-b", context.Items[GatewayContextKeys.TenantId]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAsync_OverrideEnabledButNotAllowed_ReturnsForbidden()
|
||||
{
|
||||
_options.EnableTenantOverride = true;
|
||||
var middleware = CreateMiddleware();
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(StellaOpsClaimTypes.Subject, "user"),
|
||||
new Claim(StellaOpsClaimTypes.Tenant, "tenant-a"),
|
||||
new Claim(StellaOpsClaimTypes.AllowedTenants, "tenant-a tenant-c")
|
||||
};
|
||||
var context = CreateHttpContext("/api/platform", claims);
|
||||
context.Response.Body = new MemoryStream();
|
||||
context.Request.Headers["X-StellaOps-Tenant"] = "tenant-b";
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
Assert.False(_nextCalled);
|
||||
Assert.Equal(StatusCodes.Status403Forbidden, context.Response.StatusCode);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Auth Header Passthrough
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAsync_PreservesAuthorizationHeadersForApprovedConfiguredPrefix()
|
||||
{
|
||||
_options.JwtPassthroughPrefixes = ["/connect"];
|
||||
_options.ApprovedAuthPassthroughPrefixes = ["/connect", "/console"];
|
||||
var middleware = CreateMiddleware();
|
||||
var context = CreateHttpContext("/connect/token");
|
||||
context.Request.Headers.Authorization = "Bearer token-value";
|
||||
context.Request.Headers["DPoP"] = "proof-value";
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
Assert.True(_nextCalled);
|
||||
Assert.Equal("Bearer token-value", context.Request.Headers.Authorization.ToString());
|
||||
Assert.Equal("proof-value", context.Request.Headers["DPoP"].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAsync_StripsAuthorizationHeadersWhenConfiguredPrefixIsNotApproved()
|
||||
{
|
||||
_options.JwtPassthroughPrefixes = ["/api/v1/authority"];
|
||||
_options.ApprovedAuthPassthroughPrefixes = ["/connect"];
|
||||
var middleware = CreateMiddleware();
|
||||
var context = CreateHttpContext("/api/v1/authority/clients");
|
||||
context.Request.Headers.Authorization = "Bearer token-value";
|
||||
context.Request.Headers["DPoP"] = "proof-value";
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
Assert.True(_nextCalled);
|
||||
Assert.False(context.Request.Headers.ContainsKey("Authorization"));
|
||||
Assert.False(context.Request.Headers.ContainsKey("DPoP"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAsync_StripsAuthorizationHeadersWhenPrefixIsNotConfigured()
|
||||
{
|
||||
_options.JwtPassthroughPrefixes = [];
|
||||
_options.ApprovedAuthPassthroughPrefixes = ["/connect"];
|
||||
var middleware = CreateMiddleware();
|
||||
var context = CreateHttpContext("/connect/token");
|
||||
context.Request.Headers.Authorization = "Bearer token-value";
|
||||
context.Request.Headers["DPoP"] = "proof-value";
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
Assert.True(_nextCalled);
|
||||
Assert.False(context.Request.Headers.ContainsKey("Authorization"));
|
||||
Assert.False(context.Request.Headers.ContainsKey("DPoP"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Legacy Header Compatibility
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user