diff --git a/enterprise/coderd/coderdenttest/coderdenttest.go b/enterprise/coderd/coderdenttest/coderdenttest.go index ce9050992e..a31d1d495b 100644 --- a/enterprise/coderd/coderdenttest/coderdenttest.go +++ b/enterprise/coderd/coderdenttest/coderdenttest.go @@ -186,6 +186,8 @@ type LicenseOptions struct { // past. IssuedAt time.Time Features license.Features + + AllowEmpty bool } func (opts *LicenseOptions) WithIssuedAt(now time.Time) *LicenseOptions { @@ -276,10 +278,10 @@ func GenerateLicense(t *testing.T, options LicenseOptions) string { issuedAt = time.Now().Add(-time.Minute) } - if options.AccountType == "" { + if !options.AllowEmpty && options.AccountType == "" { options.AccountType = license.AccountTypeSalesforce } - if options.AccountID == "" { + if !options.AllowEmpty && options.AccountID == "" { options.AccountID = "test-account-id" } diff --git a/enterprise/coderd/license/license.go b/enterprise/coderd/license/license.go index 40d14c294c..7fbac30fae 100644 --- a/enterprise/coderd/license/license.go +++ b/enterprise/coderd/license/license.go @@ -612,6 +612,8 @@ var ( ErrMissingLicenseExpires = xerrors.New("license has invalid or missing license_expires claim") ErrMissingExp = xerrors.New("license has invalid or missing exp (expires at) claim") ErrMultipleIssues = xerrors.New("license has multiple issues; contact support") + ErrMissingAccountType = xerrors.New("license must contain valid account type") + ErrMissingAccountID = xerrors.New("license must contain valid account ID") ) type Features map[codersdk.FeatureName]int64 @@ -696,12 +698,20 @@ func validateClaims(tok *jwt.Token) (*Claims, error) { if claims.NotBefore == nil { return nil, ErrMissingNotBefore } - if claims.LicenseExpires == nil { + + yearsHardLimit := time.Now().Add(5 /* years */ * 365 * 24 * time.Hour) + if claims.LicenseExpires == nil || claims.LicenseExpires.Time.After(yearsHardLimit) { return nil, ErrMissingLicenseExpires } if claims.ExpiresAt == nil { return nil, ErrMissingExp } + if claims.AccountType == "" { + return nil, ErrMissingAccountType + } + if claims.AccountID == "" { + return nil, ErrMissingAccountID + } return claims, nil } return nil, xerrors.New("unable to parse Claims") diff --git a/enterprise/coderd/licenses_test.go b/enterprise/coderd/licenses_test.go index bbd6ef717f..fbcbbf654e 100644 --- a/enterprise/coderd/licenses_test.go +++ b/enterprise/coderd/licenses_test.go @@ -54,6 +54,56 @@ func TestPostLicense(t *testing.T) { require.Contains(t, errResp.Message, "License cannot be used on this deployment!") }) + t.Run("InvalidAccountID", func(t *testing.T) { + t.Parallel() + // The generated deployment will start out with a different deployment ID. + client, _ := coderdenttest.New(t, &coderdenttest.Options{DontAddLicense: true}) + license := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + AllowEmpty: true, + AccountID: "", + }) + _, err := client.AddLicense(context.Background(), codersdk.AddLicenseRequest{ + License: license, + }) + errResp := &codersdk.Error{} + require.ErrorAs(t, err, &errResp) + require.Equal(t, http.StatusBadRequest, errResp.StatusCode()) + require.Contains(t, errResp.Message, "Invalid license") + }) + + t.Run("InvalidAccountType", func(t *testing.T) { + t.Parallel() + // The generated deployment will start out with a different deployment ID. + client, _ := coderdenttest.New(t, &coderdenttest.Options{DontAddLicense: true}) + license := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + AllowEmpty: true, + AccountType: "", + }) + _, err := client.AddLicense(context.Background(), codersdk.AddLicenseRequest{ + License: license, + }) + errResp := &codersdk.Error{} + require.ErrorAs(t, err, &errResp) + require.Equal(t, http.StatusBadRequest, errResp.StatusCode()) + require.Contains(t, errResp.Message, "Invalid license") + }) + + t.Run("InvalidLicenseExpires", func(t *testing.T) { + t.Parallel() + // The generated deployment will start out with a different deployment ID. + client, _ := coderdenttest.New(t, &coderdenttest.Options{DontAddLicense: true}) + license := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + GraceAt: time.Unix(99999999999, 0), + }) + _, err := client.AddLicense(context.Background(), codersdk.AddLicenseRequest{ + License: license, + }) + errResp := &codersdk.Error{} + require.ErrorAs(t, err, &errResp) + require.Equal(t, http.StatusBadRequest, errResp.StatusCode()) + require.Contains(t, errResp.Message, "Invalid license") + }) + t.Run("Unauthorized", func(t *testing.T) { t.Parallel() client, _ := coderdenttest.New(t, &coderdenttest.Options{DontAddLicense: true})