up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
This commit is contained in:
@@ -1,43 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class ServiceCollectionExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void AddStellaOpsResourceServerAuthentication_ConfiguresJwtBearer()
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["Authority:ResourceServer:Authority"] = "https://authority.example",
|
||||
["Authority:ResourceServer:Audiences:0"] = "api://concelier",
|
||||
["Authority:ResourceServer:RequiredScopes:0"] = "concelier.jobs.trigger",
|
||||
["Authority:ResourceServer:BypassNetworks:0"] = "127.0.0.1/32"
|
||||
})
|
||||
.Build();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddStellaOpsResourceServerAuthentication(configuration);
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
|
||||
var resourceOptions = provider.GetRequiredService<IOptionsMonitor<StellaOpsResourceServerOptions>>().CurrentValue;
|
||||
var jwtOptions = provider.GetRequiredService<IOptionsMonitor<JwtBearerOptions>>().Get(StellaOpsAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
Assert.NotNull(jwtOptions.Authority);
|
||||
Assert.Equal(new Uri("https://authority.example/"), new Uri(jwtOptions.Authority!));
|
||||
Assert.True(jwtOptions.TokenValidationParameters.ValidateAudience);
|
||||
Assert.Contains("api://concelier", jwtOptions.TokenValidationParameters.ValidAudiences);
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class ServiceCollectionExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void AddStellaOpsResourceServerAuthentication_ConfiguresJwtBearer()
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["Authority:ResourceServer:Authority"] = "https://authority.example",
|
||||
["Authority:ResourceServer:Audiences:0"] = "api://concelier",
|
||||
["Authority:ResourceServer:RequiredScopes:0"] = "concelier.jobs.trigger",
|
||||
["Authority:ResourceServer:BypassNetworks:0"] = "127.0.0.1/32"
|
||||
})
|
||||
.Build();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddStellaOpsResourceServerAuthentication(configuration);
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
|
||||
var resourceOptions = provider.GetRequiredService<IOptionsMonitor<StellaOpsResourceServerOptions>>().CurrentValue;
|
||||
var jwtOptions = provider.GetRequiredService<IOptionsMonitor<JwtBearerOptions>>().Get(StellaOpsAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
Assert.NotNull(jwtOptions.Authority);
|
||||
Assert.Equal(new Uri("https://authority.example/"), new Uri(jwtOptions.Authority!));
|
||||
Assert.True(jwtOptions.TokenValidationParameters.ValidateAudience);
|
||||
Assert.Contains("api://concelier", jwtOptions.TokenValidationParameters.ValidAudiences);
|
||||
Assert.Equal(TimeSpan.FromSeconds(60), jwtOptions.TokenValidationParameters.ClockSkew);
|
||||
Assert.Equal(new[] { "concelier.jobs.trigger" }, resourceOptions.NormalizedScopes);
|
||||
Assert.IsType<StellaOpsAuthorityConfigurationManager>(jwtOptions.ConfigurationManager);
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class StellaOpsResourceServerOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Validate_NormalisesCollections()
|
||||
{
|
||||
var options = new StellaOpsResourceServerOptions
|
||||
{
|
||||
Authority = "https://authority.stella-ops.test",
|
||||
BackchannelTimeout = TimeSpan.FromSeconds(10),
|
||||
TokenClockSkew = TimeSpan.FromSeconds(30)
|
||||
};
|
||||
|
||||
options.Audiences.Add(" api://concelier ");
|
||||
options.Audiences.Add("api://concelier");
|
||||
options.Audiences.Add("api://concelier-admin");
|
||||
|
||||
options.RequiredScopes.Add(" Concelier.Jobs.Trigger ");
|
||||
options.RequiredScopes.Add("concelier.jobs.trigger");
|
||||
options.RequiredScopes.Add("AUTHORITY.USERS.MANAGE");
|
||||
|
||||
options.RequiredTenants.Add(" Tenant-Alpha ");
|
||||
options.RequiredTenants.Add("tenant-alpha");
|
||||
options.RequiredTenants.Add("Tenant-Beta");
|
||||
|
||||
options.BypassNetworks.Add("127.0.0.1/32");
|
||||
options.BypassNetworks.Add(" 127.0.0.1/32 ");
|
||||
options.BypassNetworks.Add("::1/128");
|
||||
|
||||
options.Validate();
|
||||
|
||||
Assert.Equal(new Uri("https://authority.stella-ops.test"), options.AuthorityUri);
|
||||
Assert.Equal(new[] { "api://concelier", "api://concelier-admin" }, options.Audiences);
|
||||
Assert.Equal(new[] { "authority.users.manage", "concelier.jobs.trigger" }, options.NormalizedScopes);
|
||||
Assert.Equal(new[] { "tenant-alpha", "tenant-beta" }, options.NormalizedTenants);
|
||||
Assert.True(options.BypassMatcher.IsAllowed(IPAddress.Parse("127.0.0.1")));
|
||||
Assert.True(options.BypassMatcher.IsAllowed(IPAddress.IPv6Loopback));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Throws_When_AuthorityMissing()
|
||||
{
|
||||
var options = new StellaOpsResourceServerOptions();
|
||||
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => options.Validate());
|
||||
|
||||
Assert.Contains("Authority", exception.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Net;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class StellaOpsResourceServerOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Validate_NormalisesCollections()
|
||||
{
|
||||
var options = new StellaOpsResourceServerOptions
|
||||
{
|
||||
Authority = "https://authority.stella-ops.test",
|
||||
BackchannelTimeout = TimeSpan.FromSeconds(10),
|
||||
TokenClockSkew = TimeSpan.FromSeconds(30)
|
||||
};
|
||||
|
||||
options.Audiences.Add(" api://concelier ");
|
||||
options.Audiences.Add("api://concelier");
|
||||
options.Audiences.Add("api://concelier-admin");
|
||||
|
||||
options.RequiredScopes.Add(" Concelier.Jobs.Trigger ");
|
||||
options.RequiredScopes.Add("concelier.jobs.trigger");
|
||||
options.RequiredScopes.Add("AUTHORITY.USERS.MANAGE");
|
||||
|
||||
options.RequiredTenants.Add(" Tenant-Alpha ");
|
||||
options.RequiredTenants.Add("tenant-alpha");
|
||||
options.RequiredTenants.Add("Tenant-Beta");
|
||||
|
||||
options.BypassNetworks.Add("127.0.0.1/32");
|
||||
options.BypassNetworks.Add(" 127.0.0.1/32 ");
|
||||
options.BypassNetworks.Add("::1/128");
|
||||
|
||||
options.Validate();
|
||||
|
||||
Assert.Equal(new Uri("https://authority.stella-ops.test"), options.AuthorityUri);
|
||||
Assert.Equal(new[] { "api://concelier", "api://concelier-admin" }, options.Audiences);
|
||||
Assert.Equal(new[] { "authority.users.manage", "concelier.jobs.trigger" }, options.NormalizedScopes);
|
||||
Assert.Equal(new[] { "tenant-alpha", "tenant-beta" }, options.NormalizedTenants);
|
||||
Assert.True(options.BypassMatcher.IsAllowed(IPAddress.Parse("127.0.0.1")));
|
||||
Assert.True(options.BypassMatcher.IsAllowed(IPAddress.IPv6Loopback));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Throws_When_AuthorityMissing()
|
||||
{
|
||||
var options = new StellaOpsResourceServerOptions();
|
||||
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => options.Validate());
|
||||
|
||||
Assert.Contains("Authority", exception.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,21 +15,21 @@ using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
using OpenIddict.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class StellaOpsScopeAuthorizationHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Succeeds_WhenScopePresent()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
{
|
||||
options.Authority = "https://authority.example";
|
||||
options.RequiredTenants.Add("tenant-alpha");
|
||||
options.Validate();
|
||||
});
|
||||
|
||||
|
||||
namespace StellaOps.Auth.ServerIntegration.Tests;
|
||||
|
||||
public class StellaOpsScopeAuthorizationHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Succeeds_WhenScopePresent()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
{
|
||||
options.Authority = "https://authority.example";
|
||||
options.RequiredTenants.Add("tenant-alpha");
|
||||
options.Validate();
|
||||
});
|
||||
|
||||
var (handler, accessor, sink) = CreateHandler(optionsMonitor, remoteAddress: IPAddress.Parse("10.0.0.1"));
|
||||
var requirement = new StellaOpsScopeRequirement(new[] { StellaOpsScopes.ConcelierJobsTrigger });
|
||||
var principal = new StellaOpsPrincipalBuilder()
|
||||
@@ -108,9 +108,9 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenScopeMissingAndNoBypass()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
public async Task HandleRequirement_Fails_WhenScopeMissingAndNoBypass()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
{
|
||||
options.Authority = "https://authority.example";
|
||||
options.Validate();
|
||||
@@ -133,9 +133,9 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Fails_WhenDefaultScopeMissing()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
{
|
||||
options.Authority = "https://authority.example";
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
{
|
||||
options.Authority = "https://authority.example";
|
||||
options.RequiredScopes.Add(StellaOpsScopes.PolicyRun);
|
||||
options.Validate();
|
||||
});
|
||||
@@ -162,9 +162,9 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
[Fact]
|
||||
public async Task HandleRequirement_Succeeds_WhenDefaultScopePresent()
|
||||
{
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
{
|
||||
options.Authority = "https://authority.example";
|
||||
var optionsMonitor = CreateOptionsMonitor(options =>
|
||||
{
|
||||
options.Authority = "https://authority.example";
|
||||
options.RequiredScopes.Add(StellaOpsScopes.PolicyRun);
|
||||
options.Validate();
|
||||
});
|
||||
@@ -514,24 +514,24 @@ public class StellaOpsScopeAuthorizationHandlerTests
|
||||
{
|
||||
private readonly TOptions value;
|
||||
|
||||
public TestOptionsMonitor(Action<TOptions> configure)
|
||||
{
|
||||
value = new TOptions();
|
||||
configure(value);
|
||||
}
|
||||
|
||||
public TOptions CurrentValue => value;
|
||||
|
||||
public TOptions Get(string? name) => value;
|
||||
|
||||
public IDisposable OnChange(Action<TOptions, string> listener) => NullDisposable.Instance;
|
||||
|
||||
private sealed class NullDisposable : IDisposable
|
||||
{
|
||||
public static NullDisposable Instance { get; } = new();
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public TestOptionsMonitor(Action<TOptions> configure)
|
||||
{
|
||||
value = new TOptions();
|
||||
configure(value);
|
||||
}
|
||||
|
||||
public TOptions CurrentValue => value;
|
||||
|
||||
public TOptions Get(string? name) => value;
|
||||
|
||||
public IDisposable OnChange(Action<TOptions, string> listener) => NullDisposable.Instance;
|
||||
|
||||
private sealed class NullDisposable : IDisposable
|
||||
{
|
||||
public static NullDisposable Instance { get; } = new();
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user