stabilizaiton work - projects rework for maintenanceability and ui livening

This commit is contained in:
master
2026-02-03 23:40:04 +02:00
parent 074ce117ba
commit 557feefdc3
3305 changed files with 186813 additions and 107843 deletions

View File

@@ -3,7 +3,7 @@ namespace StellaOps.Audit.ReplayToken;
/// <summary>
/// A deterministic, content-addressable replay token with optional expiration.
/// </summary>
public sealed class ReplayToken : IEquatable<ReplayToken>
public sealed partial class ReplayToken : IEquatable<ReplayToken>
{
public const string Scheme = "replay";
public const string DefaultAlgorithm = "SHA-256";
@@ -75,7 +75,6 @@ public sealed class ReplayToken : IEquatable<ReplayToken>
ExpiresAt = expiresAt;
Algorithm = string.IsNullOrWhiteSpace(algorithm) ? DefaultAlgorithm : algorithm.Trim();
// Default to v2.0 if expiration is set, otherwise use provided or default
if (expiresAt.HasValue && string.IsNullOrWhiteSpace(version))
{
Version = VersionWithExpiration;
@@ -85,115 +84,4 @@ public sealed class ReplayToken : IEquatable<ReplayToken>
Version = string.IsNullOrWhiteSpace(version) ? DefaultVersion : version.Trim();
}
}
/// <summary>
/// Checks if the token has expired.
/// </summary>
/// <param name="currentTime">The current time to check against. If null, uses DateTimeOffset.UtcNow.</param>
/// <returns>True if the token has expired; false if not expired or has no expiration.</returns>
public bool IsExpired(DateTimeOffset? currentTime = null)
{
if (!ExpiresAt.HasValue)
{
return false; // v1.0 tokens without expiration never expire
}
var now = currentTime ?? DateTimeOffset.UtcNow;
return now >= ExpiresAt.Value;
}
/// <summary>
/// Gets the remaining time until expiration.
/// </summary>
/// <param name="currentTime">The current time to check against. If null, uses DateTimeOffset.UtcNow.</param>
/// <returns>The remaining time, or null if no expiration or already expired.</returns>
public TimeSpan? GetTimeToExpiration(DateTimeOffset? currentTime = null)
{
if (!ExpiresAt.HasValue)
{
return null;
}
var now = currentTime ?? DateTimeOffset.UtcNow;
var remaining = ExpiresAt.Value - now;
return remaining > TimeSpan.Zero ? remaining : null;
}
/// <summary>
/// Parse a canonical token string.
/// Supports both v1.0 format (4 parts) and v2.0 format with expiration (5 parts).
/// GeneratedAt is set to UnixEpoch because the canonical format does not include it.
/// </summary>
public static ReplayToken Parse(string canonical)
{
if (string.IsNullOrWhiteSpace(canonical))
{
throw new ArgumentException("Token cannot be empty.", nameof(canonical));
}
var parts = canonical.Split(':', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
// v1.0 format: replay:v1.0:SHA-256:hash (4 parts)
// v2.0 format: replay:v2.0:SHA-256:hash:expiry_unix_seconds (5 parts)
if (parts.Length < 4 || parts.Length > 5 || !string.Equals(parts[0], Scheme, StringComparison.Ordinal))
{
throw new FormatException($"Invalid replay token format: {canonical}");
}
var versionPart = parts[1];
if (!versionPart.StartsWith("v", StringComparison.Ordinal) || versionPart.Length <= 1)
{
throw new FormatException($"Invalid replay token version: {canonical}");
}
var version = versionPart[1..];
var algorithm = parts[2];
var value = parts[3];
DateTimeOffset? expiresAt = null;
if (parts.Length == 5)
{
if (!long.TryParse(parts[4], out var unixSeconds))
{
throw new FormatException($"Invalid expiration timestamp in replay token: {canonical}");
}
expiresAt = DateTimeOffset.FromUnixTimeSeconds(unixSeconds);
}
return new ReplayToken(value, DateTimeOffset.UnixEpoch, expiresAt, algorithm, version);
}
/// <summary>
/// Try to parse a canonical token string.
/// </summary>
/// <returns>True if parsing succeeded; false otherwise.</returns>
public static bool TryParse(string canonical, out ReplayToken? token)
{
try
{
token = Parse(canonical);
return true;
}
catch
{
token = null;
return false;
}
}
public override string ToString() => Canonical;
public bool Equals(ReplayToken? other)
{
if (other is null)
{
return false;
}
return string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase);
}
public override bool Equals(object? obj) => obj is ReplayToken other && Equals(other);
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value);
}