save progress

This commit is contained in:
StellaOps Bot
2026-01-02 21:06:27 +02:00
parent f46bde5575
commit 3f197814c5
441 changed files with 21545 additions and 4306 deletions

View File

@@ -29,8 +29,7 @@ public sealed class Sha256ReplayTokenGenerator : IReplayTokenGenerator
{
ArgumentNullException.ThrowIfNull(request);
var canonical = Canonicalize(request);
var hashHex = ComputeHash(canonical);
var hashHex = ComputeTokenValue(request, ReplayToken.DefaultVersion);
return new ReplayToken(hashHex, _timeProvider.GetUtcNow());
}
@@ -39,11 +38,16 @@ public sealed class Sha256ReplayTokenGenerator : IReplayTokenGenerator
{
ArgumentNullException.ThrowIfNull(request);
var canonical = Canonicalize(request);
var hashHex = ComputeHash(canonical);
var effectiveExpiration = expiration ?? ReplayToken.DefaultExpiration;
if (effectiveExpiration <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(expiration), "Expiration must be positive.");
}
var hashHex = ComputeTokenValue(request, ReplayToken.VersionWithExpiration);
var now = _timeProvider.GetUtcNow();
var expiresAt = now + (expiration ?? ReplayToken.DefaultExpiration);
var expiresAt = now + effectiveExpiration;
return new ReplayToken(hashHex, now, expiresAt, ReplayToken.DefaultAlgorithm, ReplayToken.VersionWithExpiration);
}
@@ -53,8 +57,8 @@ public sealed class Sha256ReplayTokenGenerator : IReplayTokenGenerator
ArgumentNullException.ThrowIfNull(token);
ArgumentNullException.ThrowIfNull(request);
var computed = Generate(request);
return string.Equals(token.Value, computed.Value, StringComparison.OrdinalIgnoreCase);
var computed = ComputeTokenValue(request, token.Version);
return string.Equals(token.Value, computed, StringComparison.OrdinalIgnoreCase);
}
public ReplayTokenVerificationResult VerifyWithExpiration(ReplayToken token, ReplayTokenRequest request)
@@ -63,8 +67,8 @@ public sealed class Sha256ReplayTokenGenerator : IReplayTokenGenerator
ArgumentNullException.ThrowIfNull(request);
// Check hash first
var computed = Generate(request);
if (!string.Equals(token.Value, computed.Value, StringComparison.OrdinalIgnoreCase))
var computed = ComputeTokenValue(request, token.Version);
if (!string.Equals(token.Value, computed, StringComparison.OrdinalIgnoreCase))
{
return ReplayTokenVerificationResult.Invalid;
}
@@ -84,6 +88,12 @@ public sealed class Sha256ReplayTokenGenerator : IReplayTokenGenerator
return _cryptoHash.ComputeHashHex(bytes, HashAlgorithms.Sha256);
}
private string ComputeTokenValue(ReplayTokenRequest request, string version)
{
var canonical = Canonicalize(request, version);
return ComputeHash(canonical);
}
private static string? NormalizeValue(string? value)
{
if (string.IsNullOrWhiteSpace(value))
@@ -117,23 +127,40 @@ public sealed class Sha256ReplayTokenGenerator : IReplayTokenGenerator
return new Dictionary<string, string>();
}
var normalized = values
.Where(static kvp => !string.IsNullOrWhiteSpace(kvp.Key))
.Select(static kvp => new KeyValuePair<string, string>(kvp.Key.Trim(), kvp.Value?.Trim() ?? string.Empty))
var normalized = new List<KeyValuePair<string, string>>(values.Count);
var seen = new HashSet<string>(StringComparer.Ordinal);
foreach (var kvp in values)
{
if (string.IsNullOrWhiteSpace(kvp.Key))
{
continue;
}
var key = kvp.Key.Trim();
if (!seen.Add(key))
{
throw new ArgumentException($"AdditionalContext contains duplicate key after normalization: '{key}'.", nameof(values));
}
normalized.Add(new KeyValuePair<string, string>(key, kvp.Value?.Trim() ?? string.Empty));
}
var ordered = normalized
.OrderBy(static kvp => kvp.Key, StringComparer.Ordinal)
.ToDictionary(static kvp => kvp.Key, static kvp => kvp.Value, StringComparer.Ordinal);
return normalized;
return ordered;
}
/// <summary>
/// Produces deterministic canonical representation of inputs.
/// </summary>
private static string Canonicalize(ReplayTokenRequest request)
private static string Canonicalize(ReplayTokenRequest request, string version)
{
var canonical = new CanonicalReplayInput
{
Version = ReplayToken.DefaultVersion,
Version = version,
FeedManifests = NormalizeSortedList(request.FeedManifests),
RulesVersion = NormalizeValue(request.RulesVersion),
RulesHash = NormalizeValue(request.RulesHash),