From 1ec9886782e1c886245df0cb11c7ea5646f9c88a Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Thu, 21 Sep 2023 11:50:14 +0000 Subject: [PATCH 1/2] fix: use AlwaysEnable for licenses with all features Signed-off-by: Spike Curtis --- enterprise/coderd/license/license.go | 18 ++++++++- enterprise/coderd/license/license_test.go | 47 +++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/enterprise/coderd/license/license.go b/enterprise/coderd/license/license.go index 4abf8dfcd218e..539274ecdcbe3 100644 --- a/enterprise/coderd/license/license.go +++ b/enterprise/coderd/license/license.go @@ -61,6 +61,7 @@ func Entitlements( } allFeatures := false + allFeaturesEntitlement := codersdk.EntitlementNotEntitled // Here we loop through licenses to detect enabled features. for _, l := range licenses { @@ -117,7 +118,7 @@ func Entitlements( } default: entitlements.Features[featureName] = codersdk.Feature{ - Entitlement: entitlement, + Entitlement: maxEntitlement(entitlements.Features[featureName].Entitlement, entitlement), Enabled: enablements[featureName] || featureName.AlwaysEnable(), } } @@ -125,6 +126,7 @@ func Entitlements( if claims.AllFeatures { allFeatures = true + allFeaturesEntitlement = maxEntitlement(allFeaturesEntitlement, entitlement) } entitlements.RequireTelemetry = entitlements.RequireTelemetry || claims.RequireTelemetry } @@ -136,7 +138,8 @@ func Entitlements( continue } feature := entitlements.Features[featureName] - feature.Entitlement = codersdk.EntitlementEntitled + feature.Entitlement = maxEntitlement(feature.Entitlement, allFeaturesEntitlement) + feature.Enabled = enablements[featureName] || featureName.AlwaysEnable() entitlements.Features[featureName] = feature } } @@ -324,3 +327,14 @@ func keyFunc(keys map[string]ed25519.PublicKey) func(*jwt.Token) (interface{}, e return k, nil } } + +// maxEntitlement is the "greater" entitlement between the given values +func maxEntitlement(e1, e2 codersdk.Entitlement) codersdk.Entitlement { + if e1 == codersdk.EntitlementEntitled || e2 == codersdk.EntitlementEntitled { + return codersdk.EntitlementEntitled + } + if e1 == codersdk.EntitlementGracePeriod || e2 == codersdk.EntitlementGracePeriod { + return codersdk.EntitlementGracePeriod + } + return codersdk.EntitlementNotEntitled +} diff --git a/enterprise/coderd/license/license_test.go b/enterprise/coderd/license/license_test.go index 2f6ea9b396995..fa4d543fae48b 100644 --- a/enterprise/coderd/license/license_test.go +++ b/enterprise/coderd/license/license_test.go @@ -378,6 +378,53 @@ func TestEntitlements(t *testing.T) { } }) + t.Run("AllFeaturesAlwaysEnable", func(t *testing.T) { + t.Parallel() + db := dbfake.New() + db.InsertLicense(context.Background(), database.InsertLicenseParams{ + Exp: time.Now().Add(time.Hour), + JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + AllFeatures: true, + }), + }) + entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, empty) + require.NoError(t, err) + require.True(t, entitlements.HasLicense) + require.False(t, entitlements.Trial) + for _, featureName := range codersdk.FeatureNames { + if featureName == codersdk.FeatureUserLimit { + continue + } + feature := entitlements.Features[featureName] + require.Equal(t, featureName.AlwaysEnable(), feature.Enabled) + require.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) + } + }) + + t.Run("AllFeaturesGrace", func(t *testing.T) { + t.Parallel() + db := dbfake.New() + db.InsertLicense(context.Background(), database.InsertLicenseParams{ + Exp: time.Now().Add(time.Hour), + JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + AllFeatures: true, + GraceAt: time.Now().Add(-time.Hour), + ExpiresAt: time.Now().Add(time.Hour), + }), + }) + entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, all) + require.NoError(t, err) + require.True(t, entitlements.HasLicense) + require.False(t, entitlements.Trial) + for _, featureName := range codersdk.FeatureNames { + if featureName == codersdk.FeatureUserLimit { + continue + } + require.True(t, entitlements.Features[featureName].Enabled) + require.Equal(t, codersdk.EntitlementGracePeriod, entitlements.Features[featureName].Entitlement) + } + }) + t.Run("MultipleReplicasNoLicense", func(t *testing.T) { t.Parallel() db := dbfake.New() From fb452aa77806846afde79832d5459ed69fcc6526 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Fri, 22 Sep 2023 04:55:15 +0000 Subject: [PATCH 2/2] use dbtime.Now() intead of time.Now() Signed-off-by: Spike Curtis --- enterprise/coderd/license/license_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/enterprise/coderd/license/license_test.go b/enterprise/coderd/license/license_test.go index fa4d543fae48b..205987601616e 100644 --- a/enterprise/coderd/license/license_test.go +++ b/enterprise/coderd/license/license_test.go @@ -382,7 +382,7 @@ func TestEntitlements(t *testing.T) { t.Parallel() db := dbfake.New() db.InsertLicense(context.Background(), database.InsertLicenseParams{ - Exp: time.Now().Add(time.Hour), + Exp: dbtime.Now().Add(time.Hour), JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ AllFeatures: true, }), @@ -405,11 +405,11 @@ func TestEntitlements(t *testing.T) { t.Parallel() db := dbfake.New() db.InsertLicense(context.Background(), database.InsertLicenseParams{ - Exp: time.Now().Add(time.Hour), + Exp: dbtime.Now().Add(time.Hour), JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ AllFeatures: true, - GraceAt: time.Now().Add(-time.Hour), - ExpiresAt: time.Now().Add(time.Hour), + GraceAt: dbtime.Now().Add(-time.Hour), + ExpiresAt: dbtime.Now().Add(time.Hour), }), }) entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, all)