sqlinsight-pro/src/SQLInsight.Licensing/LicenseVerifier.cs
Claude 1ba274d699
Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
Fase 3: offline-verifiable signed licenses and license tool
- SQLInsight.Licensing: ECDSA P-256 signed documents (SQLI1.payload.sig),
  licenses with plan, expiry, instance count and key id for rotation;
  30-day grace; invalid or missing licenses are states, never errors,
  and never stop monitoring (a license only adds hub sync and support)
- Trusted public keys are embedded; the source ships with none, so a
  published key can never mint licenses. The vendor adds its own.
- tools/SQLInsight.LicenseTool: keygen, issue, verify
- *.private.pem ignored by git

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 08:30:28 +00:00

34 lines
1.6 KiB
C#

namespace SQLInsight.Licensing;
/// <summary>
/// Checks a license text offline. Never throws on bad input: an invalid license is a state, not an error, and never
/// stops monitoring.
/// </summary>
public sealed class LicenseVerifier(TrustedKeys keys, TimeProvider time)
{
public static readonly TimeSpan GracePeriod = TimeSpan.FromDays(30);
public LicenseStatus Check(string? licenseText)
{
if (string.IsNullOrWhiteSpace(licenseText))
{
return new LicenseStatus(LicenseState.None, null, "Ingen licens: Community. Al overvågning er gratis; Hub-synkronisering og prioriteret support kræver et abonnement.");
}
var license = SignedDocument.Verify<License>(licenseText, keys, l => l.KeyId);
if (license is null)
{
return new LicenseStatus(LicenseState.Invalid, null, "Licensen er ugyldig: signaturen passer ikke, eller teksten er beskadiget. Overvågningen fortsætter.");
}
var now = time.GetUtcNow();
if (now <= license.ExpiresAt)
{
return new LicenseStatus(LicenseState.Valid, license, $"{license.Plan} til {license.Customer}, gyldig til {license.ExpiresAt:yyyy-MM-dd}.");
}
return now <= license.ExpiresAt + GracePeriod
? new LicenseStatus(LicenseState.ExpiredInGrace, license, $"Licensen udløb {license.ExpiresAt:yyyy-MM-dd}. Hub-synkronisering fortsætter til {(license.ExpiresAt + GracePeriod):yyyy-MM-dd}; forny venligst.")
: new LicenseStatus(LicenseState.Expired, license, $"Licensen udløb {license.ExpiresAt:yyyy-MM-dd}. Overvågningen fortsætter; Hub-synkronisering er stoppet.");
}
}