Skip to content

Commit f8f3d89

Browse files
authored
fix: label premium features in middleware error (coder#14360)
Previously, all features were called enterprise in the license check middleware.
1 parent 4446d61 commit f8f3d89

File tree

4 files changed

+59
-10
lines changed

4 files changed

+59
-10
lines changed

codersdk/deployment.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ func (n FeatureName) AlwaysEnable() bool {
128128
}[n]
129129
}
130130

131+
// Enterprise returns true if the feature is an enterprise feature.
132+
func (n FeatureName) Enterprise() bool {
133+
switch n {
134+
// Add all features that should be excluded in the Enterprise feature set.
135+
case FeatureMultipleOrganizations, FeatureCustomRoles:
136+
return false
137+
default:
138+
return true
139+
}
140+
}
141+
131142
// FeatureSet represents a grouping of features. Rather than manually
132143
// assigning features al-la-carte when making a license, a set can be specified.
133144
// Sets are dynamic in the sense a feature can be added to a set, granting the
@@ -152,13 +163,7 @@ func (set FeatureSet) Features() []FeatureName {
152163
copy(enterpriseFeatures, FeatureNames)
153164
// Remove the selection
154165
enterpriseFeatures = slices.DeleteFunc(enterpriseFeatures, func(f FeatureName) bool {
155-
switch f {
156-
// Add all features that should be excluded in the Enterprise feature set.
157-
case FeatureMultipleOrganizations, FeatureCustomRoles:
158-
return true
159-
default:
160-
return false
161-
}
166+
return !f.Enterprise()
162167
})
163168

164169
return enterpriseFeatures

enterprise/coderd/organizations_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,46 @@ func TestPatchOrganizationsByUser(t *testing.T) {
488488
require.Equal(t, displayName, o.DisplayName) // didn't change
489489
require.Equal(t, icon, o.Icon)
490490
})
491+
492+
t.Run("RevokedLicense", func(t *testing.T) {
493+
t.Parallel()
494+
dv := coderdtest.DeploymentValues(t)
495+
dv.Experiments = []string{string(codersdk.ExperimentMultiOrganization)}
496+
client, _ := coderdenttest.New(t, &coderdenttest.Options{
497+
Options: &coderdtest.Options{
498+
DeploymentValues: dv,
499+
},
500+
LicenseOptions: &coderdenttest.LicenseOptions{
501+
Features: license.Features{
502+
codersdk.FeatureMultipleOrganizations: 1,
503+
},
504+
},
505+
})
506+
ctx := testutil.Context(t, testutil.WaitMedium)
507+
508+
const displayName = "New Organization"
509+
o := coderdenttest.CreateOrganization(t, client, coderdenttest.CreateOrganizationOptions{}, func(request *codersdk.CreateOrganizationRequest) {
510+
request.DisplayName = displayName
511+
request.Icon = "/emojis/random.png"
512+
request.Name = "new-org"
513+
})
514+
515+
// Remove the license to block premium functionality.
516+
licenses, err := client.Licenses(ctx)
517+
require.NoError(t, err, "get licenses")
518+
for _, license := range licenses {
519+
// Should be only 1...
520+
err := client.DeleteLicense(ctx, license.ID)
521+
require.NoError(t, err, "delete license")
522+
}
523+
524+
// Verify functionality is lost.
525+
const icon = "/emojis/1f48f-1f3ff.png"
526+
o, err = client.UpdateOrganization(ctx, o.Name, codersdk.UpdateOrganizationRequest{
527+
Icon: ptr.Ref(icon),
528+
})
529+
require.ErrorContains(t, err, "Multiple Organizations is a Premium feature")
530+
})
491531
}
492532

493533
func TestPostOrganizationsByUser(t *testing.T) {

enterprise/coderd/roles_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func TestCustomOrganizationRole(t *testing.T) {
114114
role, err := owner.CreateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
115115
require.NoError(t, err, "upsert role")
116116

117-
// Remove the license to block enterprise functionality
117+
// Remove the license to block premium functionality
118118
licenses, err := owner.Licenses(ctx)
119119
require.NoError(t, err, "get licenses")
120120
for _, license := range licenses {
@@ -125,7 +125,7 @@ func TestCustomOrganizationRole(t *testing.T) {
125125

126126
// Verify functionality is lost
127127
_, err = owner.UpdateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
128-
require.ErrorContains(t, err, "Custom Roles is an Enterprise feature")
128+
require.ErrorContains(t, err, "Custom Roles is a Premium feature")
129129

130130
// Assign the custom template admin role
131131
tmplAdmin, _ := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.RoleIdentifier{Name: role.Name, OrganizationID: first.OrganizationID})

enterprise/coderd/templates.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,12 @@ func (api *API) RequireFeatureMW(feat codersdk.FeatureName) func(http.Handler) h
364364
enabled := api.entitlements.Features[feat].Enabled
365365
api.entitlementsMu.RUnlock()
366366
if !enabled {
367+
licenseType := "a Premium"
368+
if feat.Enterprise() {
369+
licenseType = "an Enterprise"
370+
}
367371
httpapi.Write(r.Context(), rw, http.StatusForbidden, codersdk.Response{
368-
Message: fmt.Sprintf("%s is an Enterprise feature. Contact sales!", feat.Humanize()),
372+
Message: fmt.Sprintf("%s is %s feature. Contact sales!", feat.Humanize(), licenseType),
369373
})
370374
return
371375
}

0 commit comments

Comments
 (0)