mirror of https://github.com/coder/coder.git
refactor: deduplicate / type license feature code (#5734)
This commit is contained in:
parent
ea1b03f7c9
commit
501cfa9e8d
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Entitlement string
|
||||
|
@ -14,19 +15,24 @@ const (
|
|||
EntitlementNotEntitled Entitlement = "not_entitled"
|
||||
)
|
||||
|
||||
// To add a new feature, modify this set of enums as well as the FeatureNames
|
||||
// array below.
|
||||
type FeatureName string
|
||||
|
||||
const (
|
||||
FeatureUserLimit = "user_limit"
|
||||
FeatureAuditLog = "audit_log"
|
||||
FeatureBrowserOnly = "browser_only"
|
||||
FeatureSCIM = "scim"
|
||||
FeatureTemplateRBAC = "template_rbac"
|
||||
FeatureHighAvailability = "high_availability"
|
||||
FeatureMultipleGitAuth = "multiple_git_auth"
|
||||
FeatureExternalProvisionerDaemons = "external_provisioner_daemons"
|
||||
FeatureAppearance = "appearance"
|
||||
FeatureUserLimit FeatureName = "user_limit"
|
||||
FeatureAuditLog FeatureName = "audit_log"
|
||||
FeatureBrowserOnly FeatureName = "browser_only"
|
||||
FeatureSCIM FeatureName = "scim"
|
||||
FeatureTemplateRBAC FeatureName = "template_rbac"
|
||||
FeatureHighAvailability FeatureName = "high_availability"
|
||||
FeatureMultipleGitAuth FeatureName = "multiple_git_auth"
|
||||
FeatureExternalProvisionerDaemons FeatureName = "external_provisioner_daemons"
|
||||
FeatureAppearance FeatureName = "appearance"
|
||||
)
|
||||
|
||||
var FeatureNames = []string{
|
||||
// FeatureNames must be kept in-sync with the Feature enum above.
|
||||
var FeatureNames = []FeatureName{
|
||||
FeatureUserLimit,
|
||||
FeatureAuditLog,
|
||||
FeatureBrowserOnly,
|
||||
|
@ -38,6 +44,29 @@ var FeatureNames = []string{
|
|||
FeatureAppearance,
|
||||
}
|
||||
|
||||
// Humanize returns the feature name in a human-readable format.
|
||||
func (n FeatureName) Humanize() string {
|
||||
switch n {
|
||||
case FeatureTemplateRBAC:
|
||||
return "Template RBAC"
|
||||
case FeatureSCIM:
|
||||
return "SCIM"
|
||||
default:
|
||||
return strings.Title(strings.ReplaceAll(string(n), "_", " "))
|
||||
}
|
||||
}
|
||||
|
||||
// AlwaysEnable returns if the feature is always enabled if entitled.
|
||||
// Warning: We don't know if we need this functionality.
|
||||
// This method may disappear at any time.
|
||||
func (n FeatureName) AlwaysEnable() bool {
|
||||
return map[FeatureName]bool{
|
||||
FeatureMultipleGitAuth: true,
|
||||
FeatureExternalProvisionerDaemons: true,
|
||||
FeatureAppearance: true,
|
||||
}[n]
|
||||
}
|
||||
|
||||
type Feature struct {
|
||||
Entitlement Entitlement `json:"entitlement"`
|
||||
Enabled bool `json:"enabled"`
|
||||
|
@ -46,12 +75,12 @@ type Feature struct {
|
|||
}
|
||||
|
||||
type Entitlements struct {
|
||||
Features map[string]Feature `json:"features"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Errors []string `json:"errors"`
|
||||
HasLicense bool `json:"has_license"`
|
||||
Experimental bool `json:"experimental"`
|
||||
Trial bool `json:"trial"`
|
||||
Features map[FeatureName]Feature `json:"features"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Errors []string `json:"errors"`
|
||||
HasLicense bool `json:"has_license"`
|
||||
Experimental bool `json:"experimental"`
|
||||
Trial bool `json:"trial"`
|
||||
}
|
||||
|
||||
func (c *Client) Entitlements(ctx context.Context) (Entitlements, error) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type AddLicenseRequest struct {
|
||||
|
@ -25,6 +26,30 @@ type License struct {
|
|||
Claims map[string]interface{} `json:"claims"`
|
||||
}
|
||||
|
||||
// Features provides the feature claims in license.
|
||||
func (l *License) Features() (map[FeatureName]int64, error) {
|
||||
strMap, ok := l.Claims["features"].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, xerrors.New("features key is unexpected type")
|
||||
}
|
||||
fMap := make(map[FeatureName]int64)
|
||||
for k, v := range strMap {
|
||||
jn, ok := v.(json.Number)
|
||||
if !ok {
|
||||
return nil, xerrors.Errorf("feature %q has unexpected type", k)
|
||||
}
|
||||
|
||||
n, err := jn.Int64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fMap[FeatureName(k)] = n
|
||||
}
|
||||
|
||||
return fMap, nil
|
||||
}
|
||||
|
||||
func (c *Client) AddLicense(ctx context.Context, r AddLicenseRequest) (License, error) {
|
||||
res, err := c.Request(ctx, http.MethodPost, "/api/v2/licenses", r)
|
||||
if err != nil {
|
||||
|
|
|
@ -88,17 +88,17 @@ func featuresList() *cobra.Command {
|
|||
}
|
||||
|
||||
type featureRow struct {
|
||||
Name string `table:"name"`
|
||||
Entitlement string `table:"entitlement"`
|
||||
Enabled bool `table:"enabled"`
|
||||
Limit *int64 `table:"limit"`
|
||||
Actual *int64 `table:"actual"`
|
||||
Name codersdk.FeatureName `table:"name"`
|
||||
Entitlement string `table:"entitlement"`
|
||||
Enabled bool `table:"enabled"`
|
||||
Limit *int64 `table:"limit"`
|
||||
Actual *int64 `table:"actual"`
|
||||
}
|
||||
|
||||
// displayFeatures will return a table displaying all features passed in.
|
||||
// filterColumns must be a subset of the feature fields and will determine which
|
||||
// columns to display
|
||||
func displayFeatures(filterColumns []string, features map[string]codersdk.Feature) (string, error) {
|
||||
func displayFeatures(filterColumns []string, features map[codersdk.FeatureName]codersdk.Feature) (string, error) {
|
||||
rows := make([]featureRow, 0, len(features))
|
||||
for name, feat := range features {
|
||||
rows = append(rows, featureRow{
|
||||
|
|
|
@ -9,8 +9,10 @@ import (
|
|||
"github.com/coder/coder/cli/clitest"
|
||||
"github.com/coder/coder/cli/cliui"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/cli"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/pty/ptytest"
|
||||
)
|
||||
|
||||
|
@ -23,7 +25,9 @@ func TestCreateGroup(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
var (
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/cli"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/pty/ptytest"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
@ -26,7 +27,9 @@ func TestGroupDelete(t *testing.T) {
|
|||
admin := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
ctx, _ := testutil.Context(t)
|
||||
|
@ -57,7 +60,9 @@ func TestGroupDelete(t *testing.T) {
|
|||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
cmd, root := clitest.NewWithSubcommands(t, cli.EnterpriseSubcommands(),
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/cli"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/pty/ptytest"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
@ -26,7 +27,9 @@ func TestGroupEdit(t *testing.T) {
|
|||
admin := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
ctx, _ := testutil.Context(t)
|
||||
|
@ -77,7 +80,9 @@ func TestGroupEdit(t *testing.T) {
|
|||
admin := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
ctx, _ := testutil.Context(t)
|
||||
|
@ -106,7 +111,9 @@ func TestGroupEdit(t *testing.T) {
|
|||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
cmd, root := clitest.NewWithSubcommands(t, cli.EnterpriseSubcommands(), "groups", "edit")
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/cli"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/pty/ptytest"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
@ -24,7 +25,9 @@ func TestGroupList(t *testing.T) {
|
|||
admin := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
ctx, _ := testutil.Context(t)
|
||||
|
@ -81,7 +84,9 @@ func TestGroupList(t *testing.T) {
|
|||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
cmd, root := clitest.NewWithSubcommands(t, cli.EnterpriseSubcommands(), "groups", "list")
|
||||
|
|
|
@ -338,7 +338,7 @@ func (s *fakeLicenseAPI) deleteLicense(rw http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
func (*fakeLicenseAPI) entitlements(rw http.ResponseWriter, r *http.Request) {
|
||||
features := make(map[string]codersdk.Feature)
|
||||
features := make(map[codersdk.FeatureName]codersdk.Feature)
|
||||
for _, f := range codersdk.FeatureNames {
|
||||
features[f] = codersdk.Feature{
|
||||
Entitlement: codersdk.EntitlementEntitled,
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
|
@ -30,7 +31,9 @@ func TestServiceBanners(t *testing.T) {
|
|||
require.False(t, sb.ServiceBanner.Enabled)
|
||||
|
||||
coderdenttest.AddLicense(t, adminClient, coderdenttest.LicenseOptions{
|
||||
ServiceBanners: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAppearance: 1,
|
||||
},
|
||||
})
|
||||
|
||||
// Default state
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/coder/coder/coderd/rbac"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
|
@ -28,7 +29,9 @@ func TestCheckACLPermissions(t *testing.T) {
|
|||
// Create adminClient, member, and org adminClient
|
||||
adminUser := coderdtest.CreateFirstUser(t, adminClient)
|
||||
_ = coderdenttest.AddLicense(t, adminClient, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
memberClient := coderdtest.CreateAnotherUser(t, adminClient, adminUser.OrganizationID)
|
||||
|
|
|
@ -238,7 +238,7 @@ func (api *API) updateEntitlements(ctx context.Context) error {
|
|||
api.entitlementsMu.Lock()
|
||||
defer api.entitlementsMu.Unlock()
|
||||
|
||||
entitlements, err := license.Entitlements(ctx, api.Database, api.Logger, len(api.replicaManager.All()), len(api.GitAuthConfigs), api.Keys, map[string]bool{
|
||||
entitlements, err := license.Entitlements(ctx, api.Database, api.Logger, len(api.replicaManager.All()), len(api.GitAuthConfigs), api.Keys, map[codersdk.FeatureName]bool{
|
||||
codersdk.FeatureAuditLog: api.AuditLogging,
|
||||
codersdk.FeatureBrowserOnly: api.BrowserOnly,
|
||||
codersdk.FeatureSCIM: len(api.SCIMAPIKey) != 0,
|
||||
|
@ -252,7 +252,7 @@ func (api *API) updateEntitlements(ctx context.Context) error {
|
|||
}
|
||||
entitlements.Experimental = api.DeploymentConfig.Experimental.Value
|
||||
|
||||
featureChanged := func(featureName string) (changed bool, enabled bool) {
|
||||
featureChanged := func(featureName codersdk.FeatureName) (changed bool, enabled bool) {
|
||||
if api.entitlements.Features == nil {
|
||||
return true, entitlements.Features[featureName].Enabled
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/coder/coder/enterprise/audit"
|
||||
"github.com/coder/coder/enterprise/coderd"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
|
@ -41,10 +42,12 @@ func TestEntitlements(t *testing.T) {
|
|||
})
|
||||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
UserLimit: 100,
|
||||
AuditLog: true,
|
||||
TemplateRBAC: true,
|
||||
ExternalProvisionerDaemons: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureUserLimit: 100,
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
codersdk.FeatureExternalProvisionerDaemons: 1,
|
||||
},
|
||||
})
|
||||
res, err := client.Entitlements(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
@ -68,8 +71,10 @@ func TestEntitlements(t *testing.T) {
|
|||
})
|
||||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
license := coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
UserLimit: 100,
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureUserLimit: 100,
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
res, err := client.Entitlements(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
@ -99,7 +104,9 @@ func TestEntitlements(t *testing.T) {
|
|||
UploadedAt: database.Now(),
|
||||
Exp: database.Now().AddDate(1, 0, 0),
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
}),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
@ -125,7 +132,9 @@ func TestEntitlements(t *testing.T) {
|
|||
UploadedAt: database.Now(),
|
||||
Exp: database.Now().AddDate(1, 0, 0),
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
}),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
@ -165,7 +174,9 @@ func TestAuditLogging(t *testing.T) {
|
|||
})
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
auditor := *api.AGPL.Auditor.Load()
|
||||
ea := audit.NewAuditor(audit.DefaultFilter)
|
||||
|
|
|
@ -99,21 +99,13 @@ func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, io.Closer, *c
|
|||
}
|
||||
|
||||
type LicenseOptions struct {
|
||||
AccountType string
|
||||
AccountID string
|
||||
Trial bool
|
||||
AllFeatures bool
|
||||
GraceAt time.Time
|
||||
ExpiresAt time.Time
|
||||
UserLimit int64
|
||||
AuditLog bool
|
||||
BrowserOnly bool
|
||||
SCIM bool
|
||||
TemplateRBAC bool
|
||||
HighAvailability bool
|
||||
MultipleGitAuth bool
|
||||
ExternalProvisionerDaemons bool
|
||||
ServiceBanners bool
|
||||
AccountType string
|
||||
AccountID string
|
||||
Trial bool
|
||||
AllFeatures bool
|
||||
GraceAt time.Time
|
||||
ExpiresAt time.Time
|
||||
Features license.Features
|
||||
}
|
||||
|
||||
// AddLicense generates a new license with the options provided and inserts it.
|
||||
|
@ -133,42 +125,6 @@ func GenerateLicense(t *testing.T, options LicenseOptions) string {
|
|||
if options.GraceAt.IsZero() {
|
||||
options.GraceAt = time.Now().Add(time.Hour)
|
||||
}
|
||||
var auditLog int64
|
||||
if options.AuditLog {
|
||||
auditLog = 1
|
||||
}
|
||||
var browserOnly int64
|
||||
if options.BrowserOnly {
|
||||
browserOnly = 1
|
||||
}
|
||||
var scim int64
|
||||
if options.SCIM {
|
||||
scim = 1
|
||||
}
|
||||
highAvailability := int64(0)
|
||||
if options.HighAvailability {
|
||||
highAvailability = 1
|
||||
}
|
||||
|
||||
rbacEnabled := int64(0)
|
||||
if options.TemplateRBAC {
|
||||
rbacEnabled = 1
|
||||
}
|
||||
|
||||
multipleGitAuth := int64(0)
|
||||
if options.MultipleGitAuth {
|
||||
multipleGitAuth = 1
|
||||
}
|
||||
|
||||
externalProvisionerDaemons := int64(0)
|
||||
if options.ExternalProvisionerDaemons {
|
||||
externalProvisionerDaemons = 1
|
||||
}
|
||||
|
||||
serviceBanners := int64(0)
|
||||
if options.ServiceBanners {
|
||||
serviceBanners = 1
|
||||
}
|
||||
|
||||
c := &license.Claims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
|
@ -183,17 +139,7 @@ func GenerateLicense(t *testing.T, options LicenseOptions) string {
|
|||
Trial: options.Trial,
|
||||
Version: license.CurrentVersion,
|
||||
AllFeatures: options.AllFeatures,
|
||||
Features: license.Features{
|
||||
UserLimit: options.UserLimit,
|
||||
AuditLog: auditLog,
|
||||
BrowserOnly: browserOnly,
|
||||
SCIM: scim,
|
||||
HighAvailability: highAvailability,
|
||||
TemplateRBAC: rbacEnabled,
|
||||
MultipleGitAuth: multipleGitAuth,
|
||||
ExternalProvisionerDaemons: externalProvisionerDaemons,
|
||||
Appearance: serviceBanners,
|
||||
},
|
||||
Features: options.Features,
|
||||
}
|
||||
tok := jwt.NewWithClaims(jwt.SigningMethodEdDSA, c)
|
||||
tok.Header[license.HeaderKeyID] = testKeyID
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/coder/coder/coderd/rbac"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
|
@ -32,9 +33,11 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
|
|||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
admin := coderdtest.CreateFirstUser(t, client)
|
||||
license := coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
ExternalProvisionerDaemons: true,
|
||||
lic := coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
codersdk.FeatureExternalProvisionerDaemons: 1,
|
||||
},
|
||||
})
|
||||
group, err := client.CreateGroup(ctx, admin.OrganizationID, codersdk.CreateGroupRequest{
|
||||
Name: "testgroup",
|
||||
|
@ -43,7 +46,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
|
|||
|
||||
groupObj := rbac.ResourceGroup.InOrg(admin.OrganizationID)
|
||||
a := coderdtest.NewAuthTester(ctx, t, client, api.AGPL, admin)
|
||||
a.URLParams["licenses/{id}"] = fmt.Sprintf("licenses/%d", license.ID)
|
||||
a.URLParams["licenses/{id}"] = fmt.Sprintf("licenses/%d", lic.ID)
|
||||
a.URLParams["groups/{group}"] = fmt.Sprintf("groups/%s", group.ID.String())
|
||||
a.URLParams["{groupName}"] = group.Name
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
|
@ -26,7 +27,9 @@ func TestCreateGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -54,8 +57,10 @@ func TestCreateGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
|
||||
ctx, _ := testutil.Context(t)
|
||||
|
@ -78,7 +83,9 @@ func TestCreateGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
_, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -102,7 +109,9 @@ func TestCreateGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
_, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -125,7 +134,9 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -157,7 +168,9 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -179,7 +192,9 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
_, user3 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -205,7 +220,9 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
_, user3 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -248,8 +265,10 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
|
||||
ctx, _ := testutil.Context(t)
|
||||
|
@ -277,7 +296,9 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group1, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -308,7 +329,9 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -332,7 +355,9 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -356,7 +381,9 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
ctx, _ := testutil.Context(t)
|
||||
|
@ -382,7 +409,9 @@ func TestPatchGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -411,7 +440,9 @@ func TestGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -431,7 +462,9 @@ func TestGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -451,7 +484,9 @@ func TestGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
_, user3 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -481,7 +516,9 @@ func TestGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
client1, _ := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
||||
|
@ -502,7 +539,9 @@ func TestGroup(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
_, user1 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -535,7 +574,9 @@ func TestGroup(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
_, user1 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -576,7 +617,9 @@ func TestGroups(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
_, user3 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -622,7 +665,9 @@ func TestDeleteGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
group1, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
|
||||
|
@ -654,8 +699,10 @@ func TestDeleteGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
|
||||
|
@ -681,7 +728,9 @@ func TestDeleteGroup(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
ctx, _ := testutil.Context(t)
|
||||
err := client.DeleteGroup(ctx, user.OrganizationID)
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"crypto/ed25519"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
|
@ -24,12 +23,12 @@ func Entitlements(
|
|||
replicaCount int,
|
||||
gitAuthCount int,
|
||||
keys map[string]ed25519.PublicKey,
|
||||
enablements map[string]bool,
|
||||
enablements map[codersdk.FeatureName]bool,
|
||||
) (codersdk.Entitlements, error) {
|
||||
now := time.Now()
|
||||
// Default all entitlements to be disabled.
|
||||
entitlements := codersdk.Entitlements{
|
||||
Features: map[string]codersdk.Feature{},
|
||||
Features: map[codersdk.FeatureName]codersdk.Feature{},
|
||||
Warnings: []string{},
|
||||
Errors: []string{},
|
||||
}
|
||||
|
@ -68,67 +67,34 @@ func Entitlements(
|
|||
// LicenseExpires we must be in grace period.
|
||||
entitlement = codersdk.EntitlementGracePeriod
|
||||
}
|
||||
if claims.Features.UserLimit > 0 {
|
||||
limit := claims.Features.UserLimit
|
||||
priorLimit := entitlements.Features[codersdk.FeatureUserLimit]
|
||||
if priorLimit.Limit != nil && *priorLimit.Limit > limit {
|
||||
limit = *priorLimit.Limit
|
||||
for featureName, featureValue := range claims.Features {
|
||||
// Can this be negative?
|
||||
if featureValue <= 0 {
|
||||
continue
|
||||
}
|
||||
entitlements.Features[codersdk.FeatureUserLimit] = codersdk.Feature{
|
||||
Enabled: true,
|
||||
Entitlement: entitlement,
|
||||
Limit: &limit,
|
||||
Actual: &activeUserCount,
|
||||
}
|
||||
}
|
||||
if claims.Features.AuditLog > 0 {
|
||||
entitlements.Features[codersdk.FeatureAuditLog] = codersdk.Feature{
|
||||
Entitlement: entitlement,
|
||||
Enabled: enablements[codersdk.FeatureAuditLog],
|
||||
}
|
||||
}
|
||||
if claims.Features.BrowserOnly > 0 {
|
||||
entitlements.Features[codersdk.FeatureBrowserOnly] = codersdk.Feature{
|
||||
Entitlement: entitlement,
|
||||
Enabled: enablements[codersdk.FeatureBrowserOnly],
|
||||
}
|
||||
}
|
||||
if claims.Features.SCIM > 0 {
|
||||
entitlements.Features[codersdk.FeatureSCIM] = codersdk.Feature{
|
||||
Entitlement: entitlement,
|
||||
Enabled: enablements[codersdk.FeatureSCIM],
|
||||
}
|
||||
}
|
||||
if claims.Features.HighAvailability > 0 {
|
||||
entitlements.Features[codersdk.FeatureHighAvailability] = codersdk.Feature{
|
||||
Entitlement: entitlement,
|
||||
Enabled: enablements[codersdk.FeatureHighAvailability],
|
||||
}
|
||||
}
|
||||
if claims.Features.TemplateRBAC > 0 {
|
||||
entitlements.Features[codersdk.FeatureTemplateRBAC] = codersdk.Feature{
|
||||
Entitlement: entitlement,
|
||||
Enabled: enablements[codersdk.FeatureTemplateRBAC],
|
||||
}
|
||||
}
|
||||
if claims.Features.MultipleGitAuth > 0 {
|
||||
entitlements.Features[codersdk.FeatureMultipleGitAuth] = codersdk.Feature{
|
||||
Entitlement: entitlement,
|
||||
Enabled: true,
|
||||
}
|
||||
}
|
||||
if claims.Features.ExternalProvisionerDaemons > 0 {
|
||||
entitlements.Features[codersdk.FeatureExternalProvisionerDaemons] = codersdk.Feature{
|
||||
Entitlement: entitlement,
|
||||
Enabled: true,
|
||||
}
|
||||
}
|
||||
if claims.Features.Appearance > 0 {
|
||||
entitlements.Features[codersdk.FeatureAppearance] = codersdk.Feature{
|
||||
Entitlement: entitlement,
|
||||
Enabled: true,
|
||||
|
||||
switch featureName {
|
||||
// User limit has special treatment as our only non-boolean feature.
|
||||
case codersdk.FeatureUserLimit:
|
||||
limit := featureValue
|
||||
priorLimit := entitlements.Features[codersdk.FeatureUserLimit]
|
||||
if priorLimit.Limit != nil && *priorLimit.Limit > limit {
|
||||
limit = *priorLimit.Limit
|
||||
}
|
||||
entitlements.Features[codersdk.FeatureUserLimit] = codersdk.Feature{
|
||||
Enabled: true,
|
||||
Entitlement: entitlement,
|
||||
Limit: &limit,
|
||||
Actual: &activeUserCount,
|
||||
}
|
||||
default:
|
||||
entitlements.Features[featureName] = codersdk.Feature{
|
||||
Entitlement: entitlement,
|
||||
Enabled: enablements[featureName] || featureName.AlwaysEnable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if claims.AllFeatures {
|
||||
allFeatures = true
|
||||
}
|
||||
|
@ -171,7 +137,7 @@ func Entitlements(
|
|||
if !feature.Enabled {
|
||||
continue
|
||||
}
|
||||
niceName := strings.Title(strings.ReplaceAll(featureName, "_", " "))
|
||||
niceName := featureName.Humanize()
|
||||
switch feature.Entitlement {
|
||||
case codersdk.EntitlementNotEntitled:
|
||||
entitlements.Warnings = append(entitlements.Warnings,
|
||||
|
@ -249,17 +215,7 @@ var (
|
|||
ErrMissingLicenseExpires = xerrors.New("license missing license_expires")
|
||||
)
|
||||
|
||||
type Features struct {
|
||||
UserLimit int64 `json:"user_limit"`
|
||||
AuditLog int64 `json:"audit_log"`
|
||||
BrowserOnly int64 `json:"browser_only"`
|
||||
SCIM int64 `json:"scim"`
|
||||
TemplateRBAC int64 `json:"template_rbac"`
|
||||
HighAvailability int64 `json:"high_availability"`
|
||||
MultipleGitAuth int64 `json:"multiple_git_auth"`
|
||||
ExternalProvisionerDaemons int64 `json:"external_provisioner_daemons"`
|
||||
Appearance int64 `json:"appearance"`
|
||||
}
|
||||
type Features map[codersdk.FeatureName]int64
|
||||
|
||||
type Claims struct {
|
||||
jwt.RegisteredClaims
|
||||
|
|
|
@ -3,7 +3,6 @@ package license_test
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -19,17 +18,13 @@ import (
|
|||
|
||||
func TestEntitlements(t *testing.T) {
|
||||
t.Parallel()
|
||||
all := map[string]bool{
|
||||
codersdk.FeatureAuditLog: true,
|
||||
codersdk.FeatureBrowserOnly: true,
|
||||
codersdk.FeatureSCIM: true,
|
||||
codersdk.FeatureHighAvailability: true,
|
||||
codersdk.FeatureTemplateRBAC: true,
|
||||
codersdk.FeatureMultipleGitAuth: true,
|
||||
codersdk.FeatureExternalProvisionerDaemons: true,
|
||||
codersdk.FeatureAppearance: true,
|
||||
all := make(map[codersdk.FeatureName]bool)
|
||||
for _, n := range codersdk.FeatureNames {
|
||||
all[n] = true
|
||||
}
|
||||
|
||||
empty := map[codersdk.FeatureName]bool{}
|
||||
|
||||
t.Run("Defaults", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
db := databasefake.New()
|
||||
|
@ -49,7 +44,7 @@ func TestEntitlements(t *testing.T) {
|
|||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{}),
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, map[string]bool{})
|
||||
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)
|
||||
|
@ -63,19 +58,17 @@ func TestEntitlements(t *testing.T) {
|
|||
db := databasefake.New()
|
||||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
UserLimit: 100,
|
||||
AuditLog: true,
|
||||
BrowserOnly: true,
|
||||
SCIM: true,
|
||||
HighAvailability: true,
|
||||
TemplateRBAC: true,
|
||||
MultipleGitAuth: true,
|
||||
ExternalProvisionerDaemons: true,
|
||||
ServiceBanners: true,
|
||||
Features: func() license.Features {
|
||||
f := make(license.Features)
|
||||
for _, name := range codersdk.FeatureNames {
|
||||
f[name] = 1
|
||||
}
|
||||
return f
|
||||
}(),
|
||||
}),
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, map[string]bool{})
|
||||
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)
|
||||
|
@ -88,16 +81,13 @@ func TestEntitlements(t *testing.T) {
|
|||
db := databasefake.New()
|
||||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
UserLimit: 100,
|
||||
AuditLog: true,
|
||||
BrowserOnly: true,
|
||||
SCIM: true,
|
||||
HighAvailability: true,
|
||||
TemplateRBAC: true,
|
||||
ExternalProvisionerDaemons: true,
|
||||
ServiceBanners: true,
|
||||
GraceAt: time.Now().Add(-time.Hour),
|
||||
ExpiresAt: time.Now().Add(time.Hour),
|
||||
Features: license.Features{
|
||||
codersdk.FeatureUserLimit: 100,
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
|
||||
GraceAt: time.Now().Add(-time.Hour),
|
||||
ExpiresAt: time.Now().Add(time.Hour),
|
||||
}),
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
})
|
||||
|
@ -105,20 +95,12 @@ func TestEntitlements(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.True(t, entitlements.HasLicense)
|
||||
require.False(t, entitlements.Trial)
|
||||
for _, featureName := range codersdk.FeatureNames {
|
||||
if featureName == codersdk.FeatureUserLimit {
|
||||
continue
|
||||
}
|
||||
if featureName == codersdk.FeatureHighAvailability {
|
||||
continue
|
||||
}
|
||||
if featureName == codersdk.FeatureMultipleGitAuth {
|
||||
continue
|
||||
}
|
||||
niceName := strings.Title(strings.ReplaceAll(featureName, "_", " "))
|
||||
require.Equal(t, codersdk.EntitlementGracePeriod, entitlements.Features[featureName].Entitlement)
|
||||
require.Contains(t, entitlements.Warnings, fmt.Sprintf("%s is enabled but your license for this feature is expired.", niceName))
|
||||
}
|
||||
|
||||
require.Equal(t, codersdk.EntitlementGracePeriod, entitlements.Features[codersdk.FeatureAuditLog].Entitlement)
|
||||
require.Contains(
|
||||
t, entitlements.Warnings,
|
||||
fmt.Sprintf("%s is enabled but your license for this feature is expired.", codersdk.FeatureAuditLog.Humanize()),
|
||||
)
|
||||
})
|
||||
t.Run("SingleLicenseNotEntitled", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -141,7 +123,7 @@ func TestEntitlements(t *testing.T) {
|
|||
if featureName == codersdk.FeatureMultipleGitAuth {
|
||||
continue
|
||||
}
|
||||
niceName := strings.Title(strings.ReplaceAll(featureName, "_", " "))
|
||||
niceName := featureName.Humanize()
|
||||
// Ensures features that are not entitled are properly disabled.
|
||||
require.False(t, entitlements.Features[featureName].Enabled)
|
||||
require.Equal(t, codersdk.EntitlementNotEntitled, entitlements.Features[featureName].Entitlement)
|
||||
|
@ -159,11 +141,13 @@ func TestEntitlements(t *testing.T) {
|
|||
})
|
||||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
UserLimit: 1,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureUserLimit: 1,
|
||||
},
|
||||
}),
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, map[string]bool{})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, empty)
|
||||
require.NoError(t, err)
|
||||
require.True(t, entitlements.HasLicense)
|
||||
require.Contains(t, entitlements.Warnings, "Your deployment has 2 active users but is only licensed for 1.")
|
||||
|
@ -175,17 +159,21 @@ func TestEntitlements(t *testing.T) {
|
|||
db.InsertUser(context.Background(), database.InsertUserParams{})
|
||||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
UserLimit: 10,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureUserLimit: 10,
|
||||
},
|
||||
}),
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
})
|
||||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
UserLimit: 1,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureUserLimit: 1,
|
||||
},
|
||||
}),
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, map[string]bool{})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, empty)
|
||||
require.NoError(t, err)
|
||||
require.True(t, entitlements.HasLicense)
|
||||
require.Empty(t, entitlements.Warnings)
|
||||
|
@ -208,7 +196,7 @@ func TestEntitlements(t *testing.T) {
|
|||
}),
|
||||
})
|
||||
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, map[string]bool{})
|
||||
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)
|
||||
|
@ -252,10 +240,12 @@ func TestEntitlements(t *testing.T) {
|
|||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
}),
|
||||
})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 2, 1, coderdenttest.Keys, map[string]bool{
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 2, 1, coderdenttest.Keys, map[codersdk.FeatureName]bool{
|
||||
codersdk.FeatureHighAvailability: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
@ -269,13 +259,15 @@ func TestEntitlements(t *testing.T) {
|
|||
db := databasefake.New()
|
||||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
HighAvailability: true,
|
||||
GraceAt: time.Now().Add(-time.Hour),
|
||||
ExpiresAt: time.Now().Add(time.Hour),
|
||||
Features: license.Features{
|
||||
codersdk.FeatureHighAvailability: 1,
|
||||
},
|
||||
GraceAt: time.Now().Add(-time.Hour),
|
||||
ExpiresAt: time.Now().Add(time.Hour),
|
||||
}),
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 2, 1, coderdenttest.Keys, map[string]bool{
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 2, 1, coderdenttest.Keys, map[codersdk.FeatureName]bool{
|
||||
codersdk.FeatureHighAvailability: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
@ -300,10 +292,12 @@ func TestEntitlements(t *testing.T) {
|
|||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
}),
|
||||
})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 2, coderdenttest.Keys, map[string]bool{
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 2, coderdenttest.Keys, map[codersdk.FeatureName]bool{
|
||||
codersdk.FeatureMultipleGitAuth: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
@ -317,13 +311,15 @@ func TestEntitlements(t *testing.T) {
|
|||
db := databasefake.New()
|
||||
db.InsertLicense(context.Background(), database.InsertLicenseParams{
|
||||
JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
MultipleGitAuth: true,
|
||||
GraceAt: time.Now().Add(-time.Hour),
|
||||
ExpiresAt: time.Now().Add(time.Hour),
|
||||
GraceAt: time.Now().Add(-time.Hour),
|
||||
ExpiresAt: time.Now().Add(time.Hour),
|
||||
Features: license.Features{
|
||||
codersdk.FeatureMultipleGitAuth: 1,
|
||||
},
|
||||
}),
|
||||
Exp: time.Now().Add(time.Hour),
|
||||
})
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 2, coderdenttest.Keys, map[string]bool{
|
||||
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 2, coderdenttest.Keys, map[codersdk.FeatureName]bool{
|
||||
codersdk.FeatureMultipleGitAuth: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -2,7 +2,6 @@ package coderd_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
|
@ -27,14 +26,16 @@ func TestPostLicense(t *testing.T) {
|
|||
respLic := coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountType: license.AccountTypeSalesforce,
|
||||
AccountID: "testing",
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
assert.GreaterOrEqual(t, respLic.ID, int32(0))
|
||||
// just a couple spot checks for sanity
|
||||
assert.Equal(t, "testing", respLic.Claims["account_id"])
|
||||
features, ok := respLic.Claims["features"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, json.Number("1"), features[codersdk.FeatureAuditLog])
|
||||
features, err := respLic.Features()
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 1, features[codersdk.FeatureAuditLog])
|
||||
})
|
||||
|
||||
t.Run("Unauthorized", func(t *testing.T) {
|
||||
|
@ -78,21 +79,24 @@ func TestGetLicense(t *testing.T) {
|
|||
defer cancel()
|
||||
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "testing",
|
||||
AuditLog: true,
|
||||
SCIM: true,
|
||||
BrowserOnly: true,
|
||||
TemplateRBAC: true,
|
||||
AccountID: "testing",
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
codersdk.FeatureSCIM: 1,
|
||||
codersdk.FeatureBrowserOnly: 1,
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "testing2",
|
||||
AuditLog: true,
|
||||
SCIM: true,
|
||||
BrowserOnly: true,
|
||||
Trial: true,
|
||||
UserLimit: 200,
|
||||
TemplateRBAC: false,
|
||||
AccountID: "testing2",
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
codersdk.FeatureSCIM: 1,
|
||||
codersdk.FeatureBrowserOnly: 1,
|
||||
codersdk.FeatureUserLimit: 200,
|
||||
},
|
||||
Trial: true,
|
||||
})
|
||||
|
||||
licenses, err := client.Licenses(ctx)
|
||||
|
@ -100,31 +104,27 @@ func TestGetLicense(t *testing.T) {
|
|||
require.Len(t, licenses, 2)
|
||||
assert.Equal(t, int32(1), licenses[0].ID)
|
||||
assert.Equal(t, "testing", licenses[0].Claims["account_id"])
|
||||
assert.Equal(t, map[string]interface{}{
|
||||
codersdk.FeatureUserLimit: json.Number("0"),
|
||||
codersdk.FeatureAuditLog: json.Number("1"),
|
||||
codersdk.FeatureSCIM: json.Number("1"),
|
||||
codersdk.FeatureBrowserOnly: json.Number("1"),
|
||||
codersdk.FeatureHighAvailability: json.Number("0"),
|
||||
codersdk.FeatureTemplateRBAC: json.Number("1"),
|
||||
codersdk.FeatureMultipleGitAuth: json.Number("0"),
|
||||
codersdk.FeatureExternalProvisionerDaemons: json.Number("0"),
|
||||
codersdk.FeatureAppearance: json.Number("0"),
|
||||
}, licenses[0].Claims["features"])
|
||||
|
||||
features, err := licenses[0].Features()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, map[codersdk.FeatureName]int64{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
codersdk.FeatureSCIM: 1,
|
||||
codersdk.FeatureBrowserOnly: 1,
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
}, features)
|
||||
assert.Equal(t, int32(2), licenses[1].ID)
|
||||
assert.Equal(t, "testing2", licenses[1].Claims["account_id"])
|
||||
assert.Equal(t, true, licenses[1].Claims["trial"])
|
||||
assert.Equal(t, map[string]interface{}{
|
||||
codersdk.FeatureUserLimit: json.Number("200"),
|
||||
codersdk.FeatureAuditLog: json.Number("1"),
|
||||
codersdk.FeatureSCIM: json.Number("1"),
|
||||
codersdk.FeatureBrowserOnly: json.Number("1"),
|
||||
codersdk.FeatureHighAvailability: json.Number("0"),
|
||||
codersdk.FeatureTemplateRBAC: json.Number("0"),
|
||||
codersdk.FeatureMultipleGitAuth: json.Number("0"),
|
||||
codersdk.FeatureExternalProvisionerDaemons: json.Number("0"),
|
||||
codersdk.FeatureAppearance: json.Number("0"),
|
||||
}, licenses[1].Claims["features"])
|
||||
|
||||
features, err = licenses[1].Features()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, map[codersdk.FeatureName]int64{
|
||||
codersdk.FeatureUserLimit: 200,
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
codersdk.FeatureSCIM: 1,
|
||||
codersdk.FeatureBrowserOnly: 1,
|
||||
}, features)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -168,12 +168,16 @@ func TestDeleteLicense(t *testing.T) {
|
|||
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "testing",
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "testing2",
|
||||
AuditLog: true,
|
||||
UserLimit: 200,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
codersdk.FeatureUserLimit: 200,
|
||||
},
|
||||
})
|
||||
|
||||
licenses, err := client.Licenses(ctx)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/coder/coder/coderd/provisionerdserver"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
@ -36,7 +37,9 @@ func TestProvisionerDaemonServe(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
ExternalProvisionerDaemons: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureExternalProvisionerDaemons: 1,
|
||||
},
|
||||
})
|
||||
srv, err := client.ServeProvisionerDaemon(context.Background(), user.OrganizationID, []codersdk.ProvisionerType{
|
||||
codersdk.ProvisionerTypeEcho,
|
||||
|
@ -50,7 +53,9 @@ func TestProvisionerDaemonServe(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
ExternalProvisionerDaemons: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureExternalProvisionerDaemons: 1,
|
||||
},
|
||||
})
|
||||
another := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
|
||||
_, err := another.ServeProvisionerDaemon(context.Background(), user.OrganizationID, []codersdk.ProvisionerType{
|
||||
|
@ -69,7 +74,9 @@ func TestProvisionerDaemonServe(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
ExternalProvisionerDaemons: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureExternalProvisionerDaemons: 1,
|
||||
},
|
||||
})
|
||||
closer := coderdtest.NewExternalProvisionerDaemon(t, client, user.OrganizationID, map[string]string{
|
||||
provisionerdserver.TagScope: provisionerdserver.ScopeUser,
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/coder/coder/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
|
@ -58,7 +59,9 @@ func TestReplicas(t *testing.T) {
|
|||
})
|
||||
firstUser := coderdtest.CreateFirstUser(t, firstClient)
|
||||
coderdenttest.AddLicense(t, firstClient, coderdenttest.LicenseOptions{
|
||||
HighAvailability: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureHighAvailability: 1,
|
||||
},
|
||||
})
|
||||
|
||||
secondClient := coderdenttest.New(t, &coderdenttest.Options{
|
||||
|
@ -100,7 +103,9 @@ func TestReplicas(t *testing.T) {
|
|||
})
|
||||
firstUser := coderdtest.CreateFirstUser(t, firstClient)
|
||||
coderdenttest.AddLicense(t, firstClient, coderdenttest.LicenseOptions{
|
||||
HighAvailability: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureHighAvailability: 1,
|
||||
},
|
||||
})
|
||||
|
||||
secondClient := coderdenttest.New(t, &coderdenttest.Options{
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/coder/coder/cryptorand"
|
||||
"github.com/coder/coder/enterprise/coderd"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
|
@ -66,7 +67,9 @@ func TestScim(t *testing.T) {
|
|||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "coolin",
|
||||
SCIM: false,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureSCIM: 0,
|
||||
},
|
||||
})
|
||||
|
||||
res, err := client.Request(ctx, "POST", "/scim/v2/Users", struct{}{})
|
||||
|
@ -85,7 +88,9 @@ func TestScim(t *testing.T) {
|
|||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "coolin",
|
||||
SCIM: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureSCIM: 1,
|
||||
},
|
||||
})
|
||||
|
||||
res, err := client.Request(ctx, "POST", "/scim/v2/Users", struct{}{})
|
||||
|
@ -105,7 +110,9 @@ func TestScim(t *testing.T) {
|
|||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "coolin",
|
||||
SCIM: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureSCIM: 1,
|
||||
},
|
||||
})
|
||||
|
||||
sUser := makeScimUser(t)
|
||||
|
@ -136,7 +143,9 @@ func TestScim(t *testing.T) {
|
|||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "coolin",
|
||||
SCIM: false,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureSCIM: 0,
|
||||
},
|
||||
})
|
||||
|
||||
res, err := client.Request(ctx, "PATCH", "/scim/v2/Users/bob", struct{}{})
|
||||
|
@ -155,7 +164,9 @@ func TestScim(t *testing.T) {
|
|||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "coolin",
|
||||
SCIM: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureSCIM: 1,
|
||||
},
|
||||
})
|
||||
|
||||
res, err := client.Request(ctx, "PATCH", "/scim/v2/Users/bob", struct{}{})
|
||||
|
@ -175,7 +186,9 @@ func TestScim(t *testing.T) {
|
|||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
AccountID: "coolin",
|
||||
SCIM: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureSCIM: 1,
|
||||
},
|
||||
})
|
||||
|
||||
sUser := makeScimUser(t)
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/cryptorand"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
@ -27,7 +28,9 @@ func TestTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -68,7 +71,9 @@ func TestTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
_, user1 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -92,7 +97,9 @@ func TestTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
client1, _ := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -142,7 +149,9 @@ func TestTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
_, user1 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -180,7 +189,9 @@ func TestTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
_, user1 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -218,7 +229,9 @@ func TestTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
|
@ -266,7 +279,9 @@ func TestTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
client1, user1 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -322,7 +337,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -374,8 +391,10 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
AuditLog: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
|
@ -405,7 +424,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -466,7 +487,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
|
@ -491,7 +514,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
|
@ -516,7 +541,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -542,7 +569,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
client2, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -577,7 +606,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
client2, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -619,7 +650,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
|
@ -641,7 +674,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
client1, user1 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -706,7 +741,9 @@ func TestUpdateTemplateACL(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
client1, _ := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
|
||||
|
@ -764,7 +801,9 @@ func TestTemplateAccess(t *testing.T) {
|
|||
ownerClient := coderdenttest.New(t, nil)
|
||||
owner := coderdtest.CreateFirstUser(t, ownerClient)
|
||||
_ = coderdenttest.AddLicense(t, ownerClient, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
type coderUser struct {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
"github.com/coder/coder/testutil"
|
||||
|
@ -39,7 +40,9 @@ func TestBlockNonBrowser(t *testing.T) {
|
|||
})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
BrowserOnly: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureBrowserOnly: 1,
|
||||
},
|
||||
})
|
||||
_, agent := setupWorkspaceAgent(t, client, user, 0)
|
||||
_, err := client.DialWorkspaceAgent(context.Background(), agent.ID, nil)
|
||||
|
@ -56,7 +59,9 @@ func TestBlockNonBrowser(t *testing.T) {
|
|||
})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
BrowserOnly: false,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureBrowserOnly: 0,
|
||||
},
|
||||
})
|
||||
_, agent := setupWorkspaceAgent(t, client, user, 0)
|
||||
conn, err := client.DialWorkspaceAgent(context.Background(), agent.ID, nil)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
"github.com/coder/coder/testutil"
|
||||
|
@ -45,7 +46,9 @@ func TestWorkspaceQuota(t *testing.T) {
|
|||
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
verifyQuota(ctx, t, client, 0, 0)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/coder/coder/coderd/util/ptr"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/enterprise/coderd/license"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
|
@ -26,7 +27,9 @@ func TestCreateWorkspace(t *testing.T) {
|
|||
client := coderdenttest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
|
||||
TemplateRBAC: true,
|
||||
Features: license.Features{
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
})
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
|
|
|
@ -63,7 +63,11 @@ type TypescriptTypes struct {
|
|||
// String just combines all the codeblocks.
|
||||
func (t TypescriptTypes) String() string {
|
||||
var s strings.Builder
|
||||
_, _ = s.WriteString("// Code generated by 'make site/src/api/typesGenerated.ts'. DO NOT EDIT.\n\n")
|
||||
const prelude = `
|
||||
// Code generated by 'make site/src/api/typesGenerated.ts'. DO NOT EDIT.
|
||||
|
||||
`
|
||||
_, _ = s.WriteString(prelude)
|
||||
|
||||
sortedTypes := make([]string, 0, len(t.Types))
|
||||
sortedEnums := make([]string, 0, len(t.Enums))
|
||||
|
@ -223,6 +227,18 @@ func (g *Generator) generateAll() (*TypescriptTypes, error) {
|
|||
name, strings.Join(values, " | "),
|
||||
))
|
||||
|
||||
var pluralName string
|
||||
if strings.HasSuffix(name, "s") {
|
||||
pluralName = name + "es"
|
||||
} else {
|
||||
pluralName = name + "s"
|
||||
}
|
||||
|
||||
// Generate array used for enumerating all possible values.
|
||||
_, _ = s.WriteString(fmt.Sprintf("export const %s: %s[] = [%s]\n",
|
||||
pluralName, name, strings.Join(values, ", "),
|
||||
))
|
||||
|
||||
enumCodeBlocks[name] = s.String()
|
||||
}
|
||||
|
||||
|
@ -644,6 +660,7 @@ func (g *Generator) typescriptType(ty types.Type) (TypescriptType, error) {
|
|||
aboveTypeLine = aboveTypeLine + "\n"
|
||||
}
|
||||
aboveTypeLine = aboveTypeLine + valueType.AboveTypeLine
|
||||
|
||||
return TypescriptType{
|
||||
ValueType: fmt.Sprintf("Record<%s, %s>", keyType.ValueType, valueType.ValueType),
|
||||
AboveTypeLine: aboveTypeLine,
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
|
||||
// From codersdk/enums.go
|
||||
export type Enum = "bar" | "baz" | "foo" | "qux"
|
||||
export const Enums: Enum[] = ["bar", "baz", "foo", "qux"]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { useSelector } from "@xstate/react"
|
||||
import { FeatureNames } from "api/types"
|
||||
import { FullScreenLoader } from "components/Loader/FullScreenLoader"
|
||||
import { RequirePermission } from "components/RequirePermission/RequirePermission"
|
||||
import { TemplateLayout } from "components/TemplateLayout/TemplateLayout"
|
||||
|
@ -195,7 +194,7 @@ export const AppRouter: FC = () => {
|
|||
element={
|
||||
<RequirePermission
|
||||
isFeatureVisible={
|
||||
featureVisibility[FeatureNames.AuditLog] &&
|
||||
featureVisibility["audit_log"] &&
|
||||
Boolean(permissions?.viewAuditLog)
|
||||
}
|
||||
>
|
||||
|
|
|
@ -16,17 +16,28 @@ export const hardCodedCSRFCookie = (): string => {
|
|||
return csrfToken
|
||||
}
|
||||
|
||||
// defaultEntitlements has a default set of disabled functionality.
|
||||
export const defaultEntitlements = (): TypesGen.Entitlements => {
|
||||
const features: TypesGen.Entitlements["features"] = {}
|
||||
for (const feature in Types.FeatureNames) {
|
||||
features[feature] = {
|
||||
// withDefaultFeatures sets all unspecified features to not_entitled and disabled.
|
||||
export const withDefaultFeatures = (
|
||||
fs: Partial<TypesGen.Entitlements["features"]>,
|
||||
): TypesGen.Entitlements["features"] => {
|
||||
for (const k in TypesGen.FeatureNames) {
|
||||
const feature = k as TypesGen.FeatureName
|
||||
// Skip fields that are already filled.
|
||||
if (fs[feature] !== undefined) {
|
||||
continue
|
||||
}
|
||||
fs[feature] = {
|
||||
enabled: false,
|
||||
entitlement: "not_entitled",
|
||||
}
|
||||
}
|
||||
return fs as TypesGen.Entitlements["features"]
|
||||
}
|
||||
|
||||
// defaultEntitlements has a default set of disabled functionality.
|
||||
export const defaultEntitlements = (): TypesGen.Entitlements => {
|
||||
return {
|
||||
features: features,
|
||||
features: withDefaultFeatures({}),
|
||||
has_license: false,
|
||||
errors: [],
|
||||
warnings: [],
|
||||
|
|
|
@ -14,14 +14,3 @@ export interface ReconnectingPTYRequest {
|
|||
export type WorkspaceBuildTransition = "start" | "stop" | "delete"
|
||||
|
||||
export type Message = { message: string }
|
||||
|
||||
// Keep up to date with coder/codersdk/features.go
|
||||
export enum FeatureNames {
|
||||
AuditLog = "audit_log",
|
||||
UserLimit = "user_limit",
|
||||
BrowserOnly = "browser_only",
|
||||
SCIM = "scim",
|
||||
TemplateRBAC = "template_rbac",
|
||||
HighAvailability = "high_availability",
|
||||
Appearance = "appearance",
|
||||
}
|
||||
|
|
|
@ -333,7 +333,7 @@ export interface DeploymentConfigField<T extends Flaggable> {
|
|||
|
||||
// From codersdk/features.go
|
||||
export interface Entitlements {
|
||||
readonly features: Record<string, Feature>
|
||||
readonly features: Record<FeatureName, Feature>
|
||||
readonly warnings: string[]
|
||||
readonly errors: string[]
|
||||
readonly has_license: boolean
|
||||
|
@ -1051,42 +1051,99 @@ export interface WorkspacesResponse {
|
|||
|
||||
// From codersdk/apikey.go
|
||||
export type APIKeyScope = "all" | "application_connect"
|
||||
export const APIKeyScopes: APIKeyScope[] = ["all", "application_connect"]
|
||||
|
||||
// From codersdk/audit.go
|
||||
export type AuditAction = "create" | "delete" | "start" | "stop" | "write"
|
||||
export const AuditActions: AuditAction[] = [
|
||||
"create",
|
||||
"delete",
|
||||
"start",
|
||||
"stop",
|
||||
"write",
|
||||
]
|
||||
|
||||
// From codersdk/workspacebuilds.go
|
||||
export type BuildReason = "autostart" | "autostop" | "initiator"
|
||||
export const BuildReasons: BuildReason[] = [
|
||||
"autostart",
|
||||
"autostop",
|
||||
"initiator",
|
||||
]
|
||||
|
||||
// From codersdk/features.go
|
||||
export type Entitlement = "entitled" | "grace_period" | "not_entitled"
|
||||
export const Entitlements: Entitlement[] = [
|
||||
"entitled",
|
||||
"grace_period",
|
||||
"not_entitled",
|
||||
]
|
||||
|
||||
// From codersdk/features.go
|
||||
export type FeatureName =
|
||||
| "appearance"
|
||||
| "audit_log"
|
||||
| "browser_only"
|
||||
| "external_provisioner_daemons"
|
||||
| "high_availability"
|
||||
| "multiple_git_auth"
|
||||
| "scim"
|
||||
| "template_rbac"
|
||||
| "user_limit"
|
||||
export const FeatureNames: FeatureName[] = [
|
||||
"appearance",
|
||||
"audit_log",
|
||||
"browser_only",
|
||||
"external_provisioner_daemons",
|
||||
"high_availability",
|
||||
"multiple_git_auth",
|
||||
"scim",
|
||||
"template_rbac",
|
||||
"user_limit",
|
||||
]
|
||||
|
||||
// From codersdk/agentconn.go
|
||||
export type ListeningPortNetwork = "tcp"
|
||||
export const ListeningPortNetworks: ListeningPortNetwork[] = ["tcp"]
|
||||
|
||||
// From codersdk/provisionerdaemons.go
|
||||
export type LogLevel = "debug" | "error" | "info" | "trace" | "warn"
|
||||
export const LogLevels: LogLevel[] = ["debug", "error", "info", "trace", "warn"]
|
||||
|
||||
// From codersdk/provisionerdaemons.go
|
||||
export type LogSource = "provisioner" | "provisioner_daemon"
|
||||
export const LogSources: LogSource[] = ["provisioner", "provisioner_daemon"]
|
||||
|
||||
// From codersdk/apikey.go
|
||||
export type LoginType = "github" | "oidc" | "password" | "token"
|
||||
export const LoginTypes: LoginType[] = ["github", "oidc", "password", "token"]
|
||||
|
||||
// From codersdk/parameters.go
|
||||
export type ParameterDestinationScheme =
|
||||
| "environment_variable"
|
||||
| "none"
|
||||
| "provisioner_variable"
|
||||
export const ParameterDestinationSchemes: ParameterDestinationScheme[] = [
|
||||
"environment_variable",
|
||||
"none",
|
||||
"provisioner_variable",
|
||||
]
|
||||
|
||||
// From codersdk/parameters.go
|
||||
export type ParameterScope = "import_job" | "template" | "workspace"
|
||||
export const ParameterScopes: ParameterScope[] = [
|
||||
"import_job",
|
||||
"template",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
// From codersdk/parameters.go
|
||||
export type ParameterSourceScheme = "data" | "none"
|
||||
export const ParameterSourceSchemes: ParameterSourceScheme[] = ["data", "none"]
|
||||
|
||||
// From codersdk/parameters.go
|
||||
export type ParameterTypeSystem = "hcl" | "none"
|
||||
export const ParameterTypeSystems: ParameterTypeSystem[] = ["hcl", "none"]
|
||||
|
||||
// From codersdk/provisionerdaemons.go
|
||||
export type ProvisionerJobStatus =
|
||||
|
@ -1096,12 +1153,22 @@ export type ProvisionerJobStatus =
|
|||
| "pending"
|
||||
| "running"
|
||||
| "succeeded"
|
||||
export const ProvisionerJobStatuses: ProvisionerJobStatus[] = [
|
||||
"canceled",
|
||||
"canceling",
|
||||
"failed",
|
||||
"pending",
|
||||
"running",
|
||||
"succeeded",
|
||||
]
|
||||
|
||||
// From codersdk/organizations.go
|
||||
export type ProvisionerStorageMethod = "file"
|
||||
export const ProvisionerStorageMethods: ProvisionerStorageMethod[] = ["file"]
|
||||
|
||||
// From codersdk/organizations.go
|
||||
export type ProvisionerType = "echo" | "terraform"
|
||||
export const ProvisionerTypes: ProvisionerType[] = ["echo", "terraform"]
|
||||
|
||||
// From codersdk/audit.go
|
||||
export type ResourceType =
|
||||
|
@ -1114,15 +1181,33 @@ export type ResourceType =
|
|||
| "user"
|
||||
| "workspace"
|
||||
| "workspace_build"
|
||||
export const ResourceTypes: ResourceType[] = [
|
||||
"api_key",
|
||||
"git_ssh_key",
|
||||
"group",
|
||||
"organization",
|
||||
"template",
|
||||
"template_version",
|
||||
"user",
|
||||
"workspace",
|
||||
"workspace_build",
|
||||
]
|
||||
|
||||
// From codersdk/sse.go
|
||||
export type ServerSentEventType = "data" | "error" | "ping"
|
||||
export const ServerSentEventTypes: ServerSentEventType[] = [
|
||||
"data",
|
||||
"error",
|
||||
"ping",
|
||||
]
|
||||
|
||||
// From codersdk/templates.go
|
||||
export type TemplateRole = "" | "admin" | "use"
|
||||
export const TemplateRoles: TemplateRole[] = ["", "admin", "use"]
|
||||
|
||||
// From codersdk/users.go
|
||||
export type UserStatus = "active" | "suspended"
|
||||
export const UserStatuses: UserStatus[] = ["active", "suspended"]
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export type WorkspaceAgentStatus =
|
||||
|
@ -1130,6 +1215,12 @@ export type WorkspaceAgentStatus =
|
|||
| "connecting"
|
||||
| "disconnected"
|
||||
| "timeout"
|
||||
export const WorkspaceAgentStatuses: WorkspaceAgentStatus[] = [
|
||||
"connected",
|
||||
"connecting",
|
||||
"disconnected",
|
||||
"timeout",
|
||||
]
|
||||
|
||||
// From codersdk/workspaceapps.go
|
||||
export type WorkspaceAppHealth =
|
||||
|
@ -1137,9 +1228,20 @@ export type WorkspaceAppHealth =
|
|||
| "healthy"
|
||||
| "initializing"
|
||||
| "unhealthy"
|
||||
export const WorkspaceAppHealths: WorkspaceAppHealth[] = [
|
||||
"disabled",
|
||||
"healthy",
|
||||
"initializing",
|
||||
"unhealthy",
|
||||
]
|
||||
|
||||
// From codersdk/workspaceapps.go
|
||||
export type WorkspaceAppSharingLevel = "authenticated" | "owner" | "public"
|
||||
export const WorkspaceAppSharingLevels: WorkspaceAppSharingLevel[] = [
|
||||
"authenticated",
|
||||
"owner",
|
||||
"public",
|
||||
]
|
||||
|
||||
// From codersdk/workspacebuilds.go
|
||||
export type WorkspaceStatus =
|
||||
|
@ -1153,9 +1255,26 @@ export type WorkspaceStatus =
|
|||
| "starting"
|
||||
| "stopped"
|
||||
| "stopping"
|
||||
export const WorkspaceStatuses: WorkspaceStatus[] = [
|
||||
"canceled",
|
||||
"canceling",
|
||||
"deleted",
|
||||
"deleting",
|
||||
"failed",
|
||||
"pending",
|
||||
"running",
|
||||
"starting",
|
||||
"stopped",
|
||||
"stopping",
|
||||
]
|
||||
|
||||
// From codersdk/workspacebuilds.go
|
||||
export type WorkspaceTransition = "delete" | "start" | "stop"
|
||||
export const WorkspaceTransitions: WorkspaceTransition[] = [
|
||||
"delete",
|
||||
"start",
|
||||
"stop",
|
||||
]
|
||||
|
||||
// From codersdk/deploymentconfig.go
|
||||
export type Flaggable = string | number | boolean | string[] | GitAuthConfig[]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { shallowEqual, useActor, useSelector } from "@xstate/react"
|
||||
import { FeatureNames } from "api/types"
|
||||
import { useContext, FC } from "react"
|
||||
import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors"
|
||||
import { XServiceContext } from "../../xServices/StateContext"
|
||||
|
@ -17,8 +16,7 @@ export const Navbar: FC = () => {
|
|||
shallowEqual,
|
||||
)
|
||||
const canViewAuditLog =
|
||||
featureVisibility[FeatureNames.AuditLog] &&
|
||||
Boolean(permissions?.viewAuditLog)
|
||||
featureVisibility["audit_log"] && Boolean(permissions?.viewAuditLog)
|
||||
const canViewDeployment = Boolean(permissions?.viewDeploymentConfig)
|
||||
const onSignOut = () => authSend("SIGN_OUT")
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useSelector } from "@xstate/react"
|
||||
import { FeatureNames } from "api/types"
|
||||
import { FeatureName } from "api/typesGenerated"
|
||||
import { useContext } from "react"
|
||||
import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors"
|
||||
import { XServiceContext } from "xServices/StateContext"
|
||||
|
||||
export const useFeatureVisibility = (): Record<FeatureNames, boolean> => {
|
||||
export const useFeatureVisibility = (): Record<FeatureName, boolean> => {
|
||||
const xServices = useContext(XServiceContext)
|
||||
return useSelector(xServices.entitlementsXService, selectFeatureVisibility)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { useActor } from "@xstate/react"
|
||||
import { FeatureNames } from "api/types"
|
||||
import { AppearanceConfig } from "api/typesGenerated"
|
||||
import { useContext, FC } from "react"
|
||||
import { Helmet } from "react-helmet-async"
|
||||
|
@ -20,7 +19,7 @@ const AppearanceSettingsPage: FC = () => {
|
|||
const appearance = appearanceXService.context.appearance
|
||||
|
||||
const isEntitled =
|
||||
entitlementsState.context.entitlements.features[FeatureNames.Appearance]
|
||||
entitlementsState.context.entitlements.features["appearance"]
|
||||
.entitlement !== "not_entitled"
|
||||
|
||||
const updateAppearance = (
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { useActor } from "@xstate/react"
|
||||
import { FeatureNames } from "api/types"
|
||||
import { useDeploySettings } from "components/DeploySettingsLayout/DeploySettingsLayout"
|
||||
import { useContext, FC } from "react"
|
||||
import { Helmet } from "react-helmet-async"
|
||||
|
@ -21,13 +20,11 @@ const SecuritySettingsPage: FC = () => {
|
|||
<SecuritySettingsPageView
|
||||
deploymentConfig={deploymentConfig}
|
||||
featureAuditLogEnabled={
|
||||
entitlementsState.context.entitlements.features[FeatureNames.AuditLog]
|
||||
.enabled
|
||||
entitlementsState.context.entitlements.features["audit_log"].enabled
|
||||
}
|
||||
featureBrowserOnlyEnabled={
|
||||
entitlementsState.context.entitlements.features[
|
||||
FeatureNames.BrowserOnly
|
||||
].enabled
|
||||
entitlementsState.context.entitlements.features["browser_only"]
|
||||
.enabled
|
||||
}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { useActor, useSelector } from "@xstate/react"
|
||||
import { FeatureNames } from "api/types"
|
||||
import dayjs from "dayjs"
|
||||
import { useContext, useEffect } from "react"
|
||||
import { Helmet } from "react-helmet-async"
|
||||
|
@ -123,9 +122,9 @@ export const WorkspaceReadyPage = ({
|
|||
resources={workspace.latest_build.resources}
|
||||
builds={builds}
|
||||
canUpdateWorkspace={canUpdateWorkspace}
|
||||
hideSSHButton={featureVisibility[FeatureNames.BrowserOnly]}
|
||||
hideSSHButton={featureVisibility["browser_only"]}
|
||||
hideVSCodeDesktopButton={
|
||||
!experimental || featureVisibility[FeatureNames.BrowserOnly]
|
||||
!experimental || featureVisibility["browser_only"]
|
||||
}
|
||||
workspaceErrors={{
|
||||
[WorkspaceErrors.GET_RESOURCES_ERROR]: refreshWorkspaceWarning,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { withDefaultFeatures } from "./../api/api"
|
||||
import { FieldError } from "api/errors"
|
||||
import { everyOneGroup } from "util/groups"
|
||||
import * as Types from "../api/types"
|
||||
|
@ -938,7 +939,7 @@ export const MockEntitlements: TypesGen.Entitlements = {
|
|||
errors: [],
|
||||
warnings: [],
|
||||
has_license: false,
|
||||
features: {},
|
||||
features: withDefaultFeatures({}),
|
||||
experimental: false,
|
||||
trial: false,
|
||||
}
|
||||
|
@ -949,7 +950,7 @@ export const MockEntitlementsWithWarnings: TypesGen.Entitlements = {
|
|||
has_license: true,
|
||||
experimental: false,
|
||||
trial: false,
|
||||
features: {
|
||||
features: withDefaultFeatures({
|
||||
user_limit: {
|
||||
enabled: true,
|
||||
entitlement: "grace_period",
|
||||
|
@ -964,7 +965,7 @@ export const MockEntitlementsWithWarnings: TypesGen.Entitlements = {
|
|||
enabled: true,
|
||||
entitlement: "entitled",
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
export const MockEntitlementsWithAuditLog: TypesGen.Entitlements = {
|
||||
|
@ -973,12 +974,12 @@ export const MockEntitlementsWithAuditLog: TypesGen.Entitlements = {
|
|||
has_license: true,
|
||||
experimental: false,
|
||||
trial: false,
|
||||
features: {
|
||||
features: withDefaultFeatures({
|
||||
audit_log: {
|
||||
enabled: true,
|
||||
entitlement: "entitled",
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
export const MockAuditLog: TypesGen.AuditLog = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Feature } from "api/typesGenerated"
|
||||
import { Feature, FeatureName } from "api/typesGenerated"
|
||||
import { State } from "xstate"
|
||||
import { EntitlementsContext, EntitlementsEvent } from "./entitlementsXService"
|
||||
|
||||
|
@ -28,7 +28,7 @@ export const getFeatureVisibility = (
|
|||
|
||||
export const selectFeatureVisibility = (
|
||||
state: EntitlementState,
|
||||
): Record<string, boolean> => {
|
||||
): Record<FeatureName, boolean> => {
|
||||
return getFeatureVisibility(
|
||||
state.context.entitlements.has_license,
|
||||
state.context.entitlements.features,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { withDefaultFeatures } from "./../../api/api"
|
||||
import { MockEntitlementsWithWarnings } from "testHelpers/entities"
|
||||
import { assign, createMachine } from "xstate"
|
||||
import * as API from "../../api/api"
|
||||
|
@ -18,7 +19,7 @@ export type EntitlementsEvent =
|
|||
const emptyEntitlements = {
|
||||
errors: [],
|
||||
warnings: [],
|
||||
features: {},
|
||||
features: withDefaultFeatures({}),
|
||||
has_license: false,
|
||||
experimental: false,
|
||||
trial: false,
|
||||
|
|
Loading…
Reference in New Issue