mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: label premium features in middleware error (#14360)
Previously, all features were called enterprise in the license check middleware.
This commit is contained in:
+12
-7
@@ -128,6 +128,17 @@ func (n FeatureName) AlwaysEnable() bool {
|
|||||||
}[n]
|
}[n]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enterprise returns true if the feature is an enterprise feature.
|
||||||
|
func (n FeatureName) Enterprise() bool {
|
||||||
|
switch n {
|
||||||
|
// Add all features that should be excluded in the Enterprise feature set.
|
||||||
|
case FeatureMultipleOrganizations, FeatureCustomRoles:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FeatureSet represents a grouping of features. Rather than manually
|
// FeatureSet represents a grouping of features. Rather than manually
|
||||||
// assigning features al-la-carte when making a license, a set can be specified.
|
// assigning features al-la-carte when making a license, a set can be specified.
|
||||||
// Sets are dynamic in the sense a feature can be added to a set, granting the
|
// 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 {
|
|||||||
copy(enterpriseFeatures, FeatureNames)
|
copy(enterpriseFeatures, FeatureNames)
|
||||||
// Remove the selection
|
// Remove the selection
|
||||||
enterpriseFeatures = slices.DeleteFunc(enterpriseFeatures, func(f FeatureName) bool {
|
enterpriseFeatures = slices.DeleteFunc(enterpriseFeatures, func(f FeatureName) bool {
|
||||||
switch f {
|
return !f.Enterprise()
|
||||||
// Add all features that should be excluded in the Enterprise feature set.
|
|
||||||
case FeatureMultipleOrganizations, FeatureCustomRoles:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return enterpriseFeatures
|
return enterpriseFeatures
|
||||||
|
|||||||
@@ -488,6 +488,46 @@ func TestPatchOrganizationsByUser(t *testing.T) {
|
|||||||
require.Equal(t, displayName, o.DisplayName) // didn't change
|
require.Equal(t, displayName, o.DisplayName) // didn't change
|
||||||
require.Equal(t, icon, o.Icon)
|
require.Equal(t, icon, o.Icon)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("RevokedLicense", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
dv := coderdtest.DeploymentValues(t)
|
||||||
|
dv.Experiments = []string{string(codersdk.ExperimentMultiOrganization)}
|
||||||
|
client, _ := coderdenttest.New(t, &coderdenttest.Options{
|
||||||
|
Options: &coderdtest.Options{
|
||||||
|
DeploymentValues: dv,
|
||||||
|
},
|
||||||
|
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||||
|
Features: license.Features{
|
||||||
|
codersdk.FeatureMultipleOrganizations: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
|
|
||||||
|
const displayName = "New Organization"
|
||||||
|
o := coderdenttest.CreateOrganization(t, client, coderdenttest.CreateOrganizationOptions{}, func(request *codersdk.CreateOrganizationRequest) {
|
||||||
|
request.DisplayName = displayName
|
||||||
|
request.Icon = "/emojis/random.png"
|
||||||
|
request.Name = "new-org"
|
||||||
|
})
|
||||||
|
|
||||||
|
// Remove the license to block premium functionality.
|
||||||
|
licenses, err := client.Licenses(ctx)
|
||||||
|
require.NoError(t, err, "get licenses")
|
||||||
|
for _, license := range licenses {
|
||||||
|
// Should be only 1...
|
||||||
|
err := client.DeleteLicense(ctx, license.ID)
|
||||||
|
require.NoError(t, err, "delete license")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify functionality is lost.
|
||||||
|
const icon = "/emojis/1f48f-1f3ff.png"
|
||||||
|
o, err = client.UpdateOrganization(ctx, o.Name, codersdk.UpdateOrganizationRequest{
|
||||||
|
Icon: ptr.Ref(icon),
|
||||||
|
})
|
||||||
|
require.ErrorContains(t, err, "Multiple Organizations is a Premium feature")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostOrganizationsByUser(t *testing.T) {
|
func TestPostOrganizationsByUser(t *testing.T) {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func TestCustomOrganizationRole(t *testing.T) {
|
|||||||
role, err := owner.CreateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
|
role, err := owner.CreateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
|
||||||
require.NoError(t, err, "upsert role")
|
require.NoError(t, err, "upsert role")
|
||||||
|
|
||||||
// Remove the license to block enterprise functionality
|
// Remove the license to block premium functionality
|
||||||
licenses, err := owner.Licenses(ctx)
|
licenses, err := owner.Licenses(ctx)
|
||||||
require.NoError(t, err, "get licenses")
|
require.NoError(t, err, "get licenses")
|
||||||
for _, license := range licenses {
|
for _, license := range licenses {
|
||||||
@@ -125,7 +125,7 @@ func TestCustomOrganizationRole(t *testing.T) {
|
|||||||
|
|
||||||
// Verify functionality is lost
|
// Verify functionality is lost
|
||||||
_, err = owner.UpdateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
|
_, err = owner.UpdateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
|
||||||
require.ErrorContains(t, err, "Custom Roles is an Enterprise feature")
|
require.ErrorContains(t, err, "Custom Roles is a Premium feature")
|
||||||
|
|
||||||
// Assign the custom template admin role
|
// Assign the custom template admin role
|
||||||
tmplAdmin, _ := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.RoleIdentifier{Name: role.Name, OrganizationID: first.OrganizationID})
|
tmplAdmin, _ := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.RoleIdentifier{Name: role.Name, OrganizationID: first.OrganizationID})
|
||||||
|
|||||||
@@ -364,8 +364,12 @@ func (api *API) RequireFeatureMW(feat codersdk.FeatureName) func(http.Handler) h
|
|||||||
enabled := api.entitlements.Features[feat].Enabled
|
enabled := api.entitlements.Features[feat].Enabled
|
||||||
api.entitlementsMu.RUnlock()
|
api.entitlementsMu.RUnlock()
|
||||||
if !enabled {
|
if !enabled {
|
||||||
|
licenseType := "a Premium"
|
||||||
|
if feat.Enterprise() {
|
||||||
|
licenseType = "an Enterprise"
|
||||||
|
}
|
||||||
httpapi.Write(r.Context(), rw, http.StatusForbidden, codersdk.Response{
|
httpapi.Write(r.Context(), rw, http.StatusForbidden, codersdk.Response{
|
||||||
Message: fmt.Sprintf("%s is an Enterprise feature. Contact sales!", feat.Humanize()),
|
Message: fmt.Sprintf("%s is %s feature. Contact sales!", feat.Humanize(), licenseType),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user