-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Adding perpetual licensing #4608
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ae0486f
682e83b
0d027c2
03f4eb3
f7b15fb
7722027
0f8f8fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| using System.Reflection; | ||
|
|
||
| namespace AutoMapper.Licensing; | ||
|
|
||
| internal static class BuildInfo | ||
| { | ||
| public static DateTimeOffset? BuildDate { get; } = GetBuildDate(); | ||
|
|
||
| private static DateTimeOffset? GetBuildDate() | ||
| { | ||
| var assembly = typeof(BuildInfo).Assembly; | ||
|
|
||
| var buildDateAttribute = assembly | ||
| .GetCustomAttributes<AssemblyMetadataAttribute>() | ||
| .FirstOrDefault(a => a.Key == "BuildDateUtc"); | ||
|
|
||
| if (buildDateAttribute?.Value != null && | ||
| DateTimeOffset.TryParse(buildDateAttribute.Value, out var buildDate)) | ||
jbogard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| return buildDate; | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -110,4 +110,119 @@ public void Should_return_invalid_when_expired() | |
| logMessages | ||
| .ShouldContain(log => log.Level == LogLevel.Error); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Should_allow_perpetual_license_when_build_date_before_expiration() | ||
| { | ||
| var factory = new LoggerFactory(); | ||
| var provider = new FakeLoggerProvider(); | ||
| factory.AddProvider(provider); | ||
|
|
||
| var buildDate = DateTimeOffset.UtcNow.AddDays(-30); | ||
| var licenseValidator = new LicenseValidator(factory, buildDate); | ||
| var license = new License( | ||
| new Claim("account_id", Guid.NewGuid().ToString()), | ||
| new Claim("customer_id", Guid.NewGuid().ToString()), | ||
| new Claim("sub_id", Guid.NewGuid().ToString()), | ||
| new Claim("iat", DateTimeOffset.UtcNow.AddYears(-1).ToUnixTimeSeconds().ToString()), | ||
| new Claim("exp", DateTimeOffset.UtcNow.AddDays(-1).ToUnixTimeSeconds().ToString()), | ||
| new Claim("edition", nameof(Edition.Professional)), | ||
| new Claim("type", nameof(AutoMapper.Licensing.ProductType.AutoMapper)), | ||
| new Claim("perpetual", "true")); | ||
|
|
||
| license.IsConfigured.ShouldBeTrue(); | ||
| license.IsPerpetual.ShouldBeTrue(); | ||
|
|
||
| licenseValidator.Validate(license); | ||
|
|
||
| var logMessages = provider.Collector.GetSnapshot(); | ||
| logMessages.ShouldNotContain(log => log.Level == LogLevel.Error); | ||
| logMessages.ShouldContain(log => log.Level == LogLevel.Information && | ||
| log.Message.Contains("perpetual")); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Should_reject_perpetual_license_when_build_date_after_expiration() | ||
| { | ||
| var factory = new LoggerFactory(); | ||
| var provider = new FakeLoggerProvider(); | ||
| factory.AddProvider(provider); | ||
|
|
||
| var buildDate = DateTimeOffset.UtcNow.AddDays(-1); // Build date in past, after expiration | ||
jbogard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var licenseValidator = new LicenseValidator(factory, buildDate); | ||
| var license = new License( | ||
| new Claim("account_id", Guid.NewGuid().ToString()), | ||
| new Claim("customer_id", Guid.NewGuid().ToString()), | ||
| new Claim("sub_id", Guid.NewGuid().ToString()), | ||
| new Claim("iat", DateTimeOffset.UtcNow.AddYears(-1).ToUnixTimeSeconds().ToString()), | ||
| new Claim("exp", DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeSeconds().ToString()), | ||
| new Claim("edition", nameof(Edition.Professional)), | ||
| new Claim("type", nameof(AutoMapper.Licensing.ProductType.AutoMapper)), | ||
| new Claim("perpetual", "true")); | ||
|
|
||
| license.IsConfigured.ShouldBeTrue(); | ||
| license.IsPerpetual.ShouldBeTrue(); | ||
|
|
||
| licenseValidator.Validate(license); | ||
|
|
||
| var logMessages = provider.Collector.GetSnapshot(); | ||
| logMessages.ShouldContain(log => log.Level == LogLevel.Error && | ||
| log.Message.Contains("expired")); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Should_warn_and_error_when_perpetual_but_build_date_is_null() | ||
| { | ||
| var factory = new LoggerFactory(); | ||
| var provider = new FakeLoggerProvider(); | ||
| factory.AddProvider(provider); | ||
|
|
||
| var licenseValidator = new LicenseValidator(factory, buildDate: null); | ||
| var license = new License( | ||
| new Claim("account_id", Guid.NewGuid().ToString()), | ||
| new Claim("customer_id", Guid.NewGuid().ToString()), | ||
| new Claim("sub_id", Guid.NewGuid().ToString()), | ||
| new Claim("iat", DateTimeOffset.UtcNow.AddYears(-1).ToUnixTimeSeconds().ToString()), | ||
| new Claim("exp", DateTimeOffset.UtcNow.AddDays(-10).ToUnixTimeSeconds().ToString()), | ||
| new Claim("edition", nameof(Edition.Professional)), | ||
| new Claim("type", nameof(AutoMapper.Licensing.ProductType.AutoMapper)), | ||
| new Claim("perpetual", "true")); | ||
|
|
||
| license.IsConfigured.ShouldBeTrue(); | ||
| license.IsPerpetual.ShouldBeTrue(); | ||
|
|
||
| licenseValidator.Validate(license); | ||
|
|
||
| var logMessages = provider.Collector.GetSnapshot(); | ||
| logMessages.ShouldContain(log => log.Level == LogLevel.Warning && log.Message.Contains("perpetual")); | ||
| logMessages.ShouldContain(log => log.Level == LogLevel.Error && log.Message.Contains("expired")); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Should_handle_missing_perpetual_claim() | ||
| { | ||
| var factory = new LoggerFactory(); | ||
| var provider = new FakeLoggerProvider(); | ||
| factory.AddProvider(provider); | ||
|
|
||
| var licenseValidator = new LicenseValidator(factory); | ||
| var license = new License( | ||
| new Claim("account_id", Guid.NewGuid().ToString()), | ||
| new Claim("customer_id", Guid.NewGuid().ToString()), | ||
| new Claim("sub_id", Guid.NewGuid().ToString()), | ||
| new Claim("iat", DateTimeOffset.UtcNow.AddDays(-1).ToUnixTimeSeconds().ToString()), | ||
| new Claim("exp", DateTimeOffset.UtcNow.AddDays(1).ToUnixTimeSeconds().ToString()), | ||
| new Claim("edition", nameof(Edition.Community)), | ||
| new Claim("type", nameof(AutoMapper.Licensing.ProductType.Bundle))); | ||
|
|
||
| license.IsConfigured.ShouldBeTrue(); | ||
| license.IsPerpetual.ShouldBeFalse(); | ||
|
|
||
| licenseValidator.Validate(license); | ||
|
|
||
| var logMessages = provider.Collector.GetSnapshot(); | ||
| logMessages.ShouldNotContain(log => log.Level == LogLevel.Error | ||
| || log.Level == LogLevel.Warning | ||
| || log.Level == LogLevel.Critical); | ||
| } | ||
|
Comment on lines
+201
to
+227
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The EmbedBuildDate target uses
DateTime.UtcNowwhich makes every build produce a different assembly, conflicting with theDeterministicbuild setting on line 26. This will break deterministic builds and may cause issues with build caching, reproducible builds, and package signing.Consider using a source control-based approach (like git commit timestamp) or an environment variable that can be set to a fixed value in CI/CD pipelines while allowing dynamic values for local development builds. For example, you could check for an environment variable first:
BuildDateUtc>$([System.Environment]::GetEnvironmentVariable('BUILD_DATE_UTC', 'EnvironmentVariableTarget.Process'))</BuildDateUtc>and only fall back to UtcNow if not set, or use SOURCE_DATE_EPOCH for reproducible builds.