audit work, fixed StellaOps.sln warnings/errors, fixed tests, sprints work, new advisories
This commit is contained in:
@@ -16,9 +16,5 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" >
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -22,6 +22,7 @@ internal sealed class LdapIdentityProviderPlugin : IIdentityProviderPlugin
|
||||
private readonly IOptionsMonitor<LdapPluginOptions> optionsMonitor;
|
||||
private readonly LdapClientProvisioningStore clientProvisioningStore;
|
||||
private readonly ILogger<LdapIdentityProviderPlugin> logger;
|
||||
private readonly TimeProvider timeProvider;
|
||||
private readonly LdapCapabilityProbe capabilityProbe;
|
||||
private readonly AuthorityIdentityProviderCapabilities manifestCapabilities;
|
||||
private readonly SemaphoreSlim capabilityGate = new(1, 1);
|
||||
@@ -38,7 +39,8 @@ internal sealed class LdapIdentityProviderPlugin : IIdentityProviderPlugin
|
||||
ILdapConnectionFactory connectionFactory,
|
||||
IOptionsMonitor<LdapPluginOptions> optionsMonitor,
|
||||
LdapClientProvisioningStore clientProvisioningStore,
|
||||
ILogger<LdapIdentityProviderPlugin> logger)
|
||||
ILogger<LdapIdentityProviderPlugin> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
this.pluginContext = pluginContext ?? throw new ArgumentNullException(nameof(pluginContext));
|
||||
this.credentialStore = credentialStore ?? throw new ArgumentNullException(nameof(credentialStore));
|
||||
@@ -47,6 +49,7 @@ internal sealed class LdapIdentityProviderPlugin : IIdentityProviderPlugin
|
||||
this.optionsMonitor = optionsMonitor ?? throw new ArgumentNullException(nameof(optionsMonitor));
|
||||
this.clientProvisioningStore = clientProvisioningStore ?? throw new ArgumentNullException(nameof(clientProvisioningStore));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.timeProvider = timeProvider ?? TimeProvider.System;
|
||||
|
||||
capabilityProbe = new LdapCapabilityProbe(pluginContext.Manifest.Name, connectionFactory, logger);
|
||||
|
||||
@@ -142,7 +145,7 @@ internal sealed class LdapIdentityProviderPlugin : IIdentityProviderPlugin
|
||||
var checkBootstrap = manifestCapabilities.SupportsBootstrap && options.Bootstrap.Enabled;
|
||||
var fingerprint = LdapCapabilitySnapshotCache.ComputeFingerprint(options, checkProvisioning, checkBootstrap);
|
||||
|
||||
if (LdapCapabilitySnapshotCache.TryGet(Name, fingerprint, DateTimeOffset.UtcNow, out var snapshot))
|
||||
if (LdapCapabilitySnapshotCache.TryGet(Name, fingerprint, timeProvider.GetUtcNow(), out var snapshot))
|
||||
{
|
||||
UpdateCapabilities(snapshot, checkProvisioning, checkBootstrap, logDegrade: true);
|
||||
}
|
||||
@@ -158,7 +161,7 @@ internal sealed class LdapIdentityProviderPlugin : IIdentityProviderPlugin
|
||||
var checkProvisioning = manifestCapabilities.SupportsClientProvisioning && options.ClientProvisioning.Enabled;
|
||||
var checkBootstrap = manifestCapabilities.SupportsBootstrap && options.Bootstrap.Enabled;
|
||||
var fingerprint = LdapCapabilitySnapshotCache.ComputeFingerprint(options, checkProvisioning, checkBootstrap);
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var now = timeProvider.GetUtcNow();
|
||||
|
||||
if (LdapCapabilitySnapshotCache.TryGet(Name, fingerprint, now, out var cached))
|
||||
{
|
||||
@@ -169,7 +172,7 @@ internal sealed class LdapIdentityProviderPlugin : IIdentityProviderPlugin
|
||||
await capabilityGate.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (LdapCapabilitySnapshotCache.TryGet(Name, fingerprint, DateTimeOffset.UtcNow, out cached))
|
||||
if (LdapCapabilitySnapshotCache.TryGet(Name, fingerprint, timeProvider.GetUtcNow(), out cached))
|
||||
{
|
||||
UpdateCapabilities(cached, checkProvisioning, checkBootstrap, logDegrade: true);
|
||||
return;
|
||||
@@ -183,7 +186,7 @@ internal sealed class LdapIdentityProviderPlugin : IIdentityProviderPlugin
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
LdapCapabilitySnapshotCache.Set(Name, fingerprint, DateTimeOffset.UtcNow, options.CapabilityProbe.CacheTtl, snapshot);
|
||||
LdapCapabilitySnapshotCache.Set(Name, fingerprint, timeProvider.GetUtcNow(), options.CapabilityProbe.CacheTtl, snapshot);
|
||||
UpdateCapabilities(snapshot, checkProvisioning, checkBootstrap, logDegrade: true);
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Credential store for validating OIDC tokens.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Globalization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
@@ -161,7 +162,7 @@ internal sealed class OidcCredentialStore : IUserCredentialStore
|
||||
new[]
|
||||
{
|
||||
new AuthEventProperty { Name = "oidc_issuer", Value = ClassifiedString.Public(jwtToken.Issuer) },
|
||||
new AuthEventProperty { Name = "token_valid_until", Value = ClassifiedString.Public(jwtToken.ValidTo.ToString("O")) }
|
||||
new AuthEventProperty { Name = "token_valid_until", Value = ClassifiedString.Public(jwtToken.ValidTo.ToString("O", CultureInfo.InvariantCulture)) }
|
||||
});
|
||||
}
|
||||
catch (SecurityTokenExpiredException ex)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Credential store for validating SAML assertions.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Globalization;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
@@ -31,6 +32,7 @@ internal sealed class SamlCredentialStore : IUserCredentialStore
|
||||
private readonly IMemoryCache sessionCache;
|
||||
private readonly ILogger<SamlCredentialStore> logger;
|
||||
private readonly IHttpClientFactory httpClientFactory;
|
||||
private readonly TimeProvider timeProvider;
|
||||
private readonly Saml2SecurityTokenHandler tokenHandler;
|
||||
private X509Certificate2? idpSigningCertificate;
|
||||
private string? certificateCacheKey;
|
||||
@@ -42,13 +44,15 @@ internal sealed class SamlCredentialStore : IUserCredentialStore
|
||||
IOptionsMonitor<SamlPluginOptions> optionsMonitor,
|
||||
IMemoryCache sessionCache,
|
||||
ILogger<SamlCredentialStore> logger,
|
||||
IHttpClientFactory httpClientFactory)
|
||||
IHttpClientFactory httpClientFactory,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
this.pluginName = pluginName ?? throw new ArgumentNullException(nameof(pluginName));
|
||||
this.optionsMonitor = optionsMonitor ?? throw new ArgumentNullException(nameof(optionsMonitor));
|
||||
this.sessionCache = sessionCache ?? throw new ArgumentNullException(nameof(sessionCache));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
|
||||
this.timeProvider = timeProvider ?? TimeProvider.System;
|
||||
|
||||
tokenHandler = new Saml2SecurityTokenHandler();
|
||||
|
||||
@@ -162,7 +166,7 @@ internal sealed class SamlCredentialStore : IUserCredentialStore
|
||||
["email"] = email,
|
||||
["issuer"] = token.Assertion.Issuer?.Value,
|
||||
["session_index"] = token.Assertion.Id?.Value,
|
||||
["auth_instant"] = token.Assertion.IssueInstant.ToString("O")
|
||||
["auth_instant"] = token.Assertion.IssueInstant.ToString("O", CultureInfo.InvariantCulture)
|
||||
};
|
||||
|
||||
var user = new AuthorityUserDescriptor(
|
||||
@@ -398,7 +402,7 @@ internal sealed class SamlCredentialStore : IUserCredentialStore
|
||||
{
|
||||
idpSigningCertificate = certificate;
|
||||
certificateCacheKey = key;
|
||||
lastMetadataRefresh = DateTimeOffset.UtcNow;
|
||||
lastMetadataRefresh = timeProvider.GetUtcNow();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -427,7 +431,7 @@ internal sealed class SamlCredentialStore : IUserCredentialStore
|
||||
return true;
|
||||
}
|
||||
|
||||
return DateTimeOffset.UtcNow - lastMetadataRefresh.Value >= options.MetadataRefreshInterval;
|
||||
return timeProvider.GetUtcNow() - lastMetadataRefresh.Value >= options.MetadataRefreshInterval;
|
||||
}
|
||||
|
||||
private static string BuildCertificateCacheKey(SamlPluginOptions options)
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" >
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Authority\StellaOps.Authority.csproj" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -98,8 +99,8 @@ internal sealed class AckTokenPayload
|
||||
writer.WriteString("channel", Channel);
|
||||
writer.WriteString("webhook", Webhook);
|
||||
writer.WriteString("nonce", Nonce);
|
||||
writer.WriteString("issuedAt", IssuedAt.UtcDateTime.ToString("O"));
|
||||
writer.WriteString("expiresAt", ExpiresAt.UtcDateTime.ToString("O"));
|
||||
writer.WriteString("issuedAt", IssuedAt.UtcDateTime.ToString("O", CultureInfo.InvariantCulture));
|
||||
writer.WriteString("expiresAt", ExpiresAt.UtcDateTime.ToString("O", CultureInfo.InvariantCulture));
|
||||
|
||||
writer.WritePropertyName("actions");
|
||||
writer.WriteStartArray();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using StellaOps.Authority.Persistence.Documents;
|
||||
using StellaOps.Authority.Persistence.Sessions;
|
||||
@@ -453,7 +454,7 @@ internal sealed class PostgresTokenStore : IAuthorityTokenStore, IAuthorityRefre
|
||||
|
||||
if (document.RevokedAt is not null)
|
||||
{
|
||||
properties["revoked_at"] = document.RevokedAt.Value.ToUniversalTime().ToString("O");
|
||||
properties["revoked_at"] = document.RevokedAt.Value.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(document.RevokedReason))
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Authority ConfigDiff Tests Charter
|
||||
|
||||
## Mission
|
||||
- Maintain deterministic tests for Authority configuration diffing.
|
||||
|
||||
## Responsibilities
|
||||
- Validate config diff output stability and edge cases.
|
||||
- Keep fixtures deterministic and offline-safe.
|
||||
|
||||
## Required Reading
|
||||
- docs/README.md
|
||||
- docs/07_HIGH_LEVEL_ARCHITECTURE.md
|
||||
- docs/modules/platform/architecture-overview.md
|
||||
- docs/modules/authority/architecture.md
|
||||
|
||||
## Definition of Done
|
||||
- Tests are deterministic and offline-safe.
|
||||
- Coverage includes mismatch detection and ordering behavior.
|
||||
|
||||
## Working Agreement
|
||||
- Use fixed time and ids in fixtures.
|
||||
- Avoid non-deterministic ordering; assert sorted output.
|
||||
Reference in New Issue
Block a user