Backfill live auth scope and evidence route metadata

This commit is contained in:
master
2026-03-08 22:56:55 +02:00
parent 5d5f4de2e1
commit 622f015421
8 changed files with 346 additions and 7 deletions

View File

@@ -5,6 +5,8 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Auth.Abstractions;
using StellaOps.Authority.Persistence.InMemory.Stores;
using StellaOps.Authority.Persistence.Postgres.Models;
using StellaOps.Authority.Persistence.Postgres.Repositories;
using StellaOps.Authority.Plugins.Abstractions;
@@ -21,6 +23,57 @@ namespace StellaOps.Authority.Plugin.Standard.Tests;
public class StandardPluginBootstrapperTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task StartAsync_EnsuresBootstrapClientsWithoutBootstrapUser()
{
var services = new ServiceCollection();
services.AddOptions<StandardPluginOptions>("standard")
.Configure(options =>
{
options.TenantId = "demo-prod";
options.BootstrapClients = new[]
{
new BootstrapClientOptions
{
ClientId = "stella-ops-ui",
DisplayName = "Stella Ops Console",
AllowedGrantTypes = "authorization_code refresh_token",
AllowedScopes = $"openid profile {StellaOpsScopes.UiRead} {StellaOpsScopes.RegistryAdmin}",
RedirectUris = "https://stella-ops.local/auth/callback https://stella-ops.local/auth/silent-refresh",
PostLogoutRedirectUris = "https://stella-ops.local/",
RequirePkce = true
}
};
});
var clientStore = new InMemoryClientStore();
services.AddSingleton<IAuthorityClientStore>(clientStore);
services.AddSingleton<IAuthorityRevocationStore>(new StubRevocationStore());
services.AddSingleton<TimeProvider>(new FakeTimeProvider(DateTimeOffset.Parse("2025-12-29T13:00:00Z")));
services.AddSingleton(sp =>
new StandardClientProvisioningStore(
"standard",
sp.GetRequiredService<IAuthorityClientStore>(),
sp.GetRequiredService<IAuthorityRevocationStore>(),
sp.GetRequiredService<TimeProvider>()));
services.AddSingleton<StandardPluginBootstrapper>(sp =>
new StandardPluginBootstrapper("standard", sp.GetRequiredService<IServiceScopeFactory>(), NullLogger<StandardPluginBootstrapper>.Instance));
using var provider = services.BuildServiceProvider();
var bootstrapper = provider.GetRequiredService<StandardPluginBootstrapper>();
await bootstrapper.StartAsync(TestContext.Current.CancellationToken);
var client = await clientStore.FindByClientIdAsync("stella-ops-ui", TestContext.Current.CancellationToken);
Assert.NotNull(client);
Assert.Contains(StellaOpsScopes.RegistryAdmin, client!.AllowedScopes);
Assert.Contains("authorization_code", client.AllowedGrantTypes);
Assert.True(client.RequirePkce);
Assert.Equal("demo-prod", client.Properties[AuthorityClientMetadataKeys.Tenant]);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task StartAsync_DoesNotThrow_WhenBootstrapFails()

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using StellaOps.Auth.Abstractions;
using StellaOps.Authority.Plugin.Standard;
using StellaOps.Cryptography;
@@ -25,6 +26,30 @@ public class StandardPluginOptionsTests
options.Validate("standard");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_AllowsBootstrapClientWhenConfigured()
{
var options = new StandardPluginOptions
{
BootstrapClients = new[]
{
new BootstrapClientOptions
{
ClientId = "stella-ops-ui",
DisplayName = "Stella Ops Console",
AllowedGrantTypes = "authorization_code refresh_token",
AllowedScopes = $"openid profile {StellaOpsScopes.UiRead} {StellaOpsScopes.RegistryAdmin}",
RedirectUris = "https://stella-ops.local/auth/callback",
PostLogoutRedirectUris = "https://stella-ops.local/"
}
}
};
options.Normalize(Path.Combine(Path.GetTempPath(), "config", "standard.yaml"));
options.Validate("standard");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_Throws_WhenBootstrapUserIncomplete()
@@ -42,6 +67,30 @@ public class StandardPluginOptionsTests
Assert.Contains("bootstrapUser", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_Throws_WhenConfidentialBootstrapClientMissingSecret()
{
var options = new StandardPluginOptions
{
BootstrapClients = new[]
{
new BootstrapClientOptions
{
ClientId = "registry-admin",
Confidential = true,
AllowedGrantTypes = "client_credentials",
AllowedScopes = StellaOpsScopes.RegistryAdmin
}
}
};
options.Normalize(Path.Combine(Path.GetTempPath(), "config", "standard.yaml"));
var ex = Assert.Throws<InvalidOperationException>(() => options.Validate("standard"));
Assert.Contains("clientsecret", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_Throws_WhenLockoutWindowMinutesInvalid()
@@ -115,6 +164,16 @@ public class StandardPluginOptionsTests
{
Username = " admin ",
Password = " "
},
BootstrapClients = new[]
{
new BootstrapClientOptions
{
ClientId = " Stella-Ops-Ui ",
AllowedGrantTypes = " refresh_token authorization_code ",
AllowedScopes = $" {StellaOpsScopes.UiRead} {StellaOpsScopes.RegistryAdmin.ToUpperInvariant()} ",
RedirectUris = " https://stella-ops.local/auth/callback "
}
}
};
@@ -123,6 +182,9 @@ public class StandardPluginOptionsTests
Assert.Equal("tenant-a", options.TenantId);
Assert.Equal("admin", options.BootstrapUser?.Username);
Assert.Null(options.BootstrapUser?.Password);
Assert.Equal("Stella-Ops-Ui", options.BootstrapClients[0].ClientId);
Assert.Equal("authorization_code refresh_token", options.BootstrapClients[0].AllowedGrantTypes);
Assert.Equal($"{StellaOpsScopes.RegistryAdmin} {StellaOpsScopes.UiRead}", options.BootstrapClients[0].AllowedScopes);
}
[Trait("Category", TestCategories.Unit)]