From 9b36bcbf614ab9f3d65cd43f36813bdc3157254b Mon Sep 17 00:00:00 2001 From: Manuel Alejandro de Brito Fontes Date: Wed, 22 Mar 2023 19:48:57 +0000 Subject: [PATCH 1/2] Remove licensor component --- .github/CODEOWNERS | 1 - codecov.yml | 3 - components/dashboard/src/admin/License.tsx | 118 +---- .../gitpod-protocol/src/license-protocol.ts | 19 - components/licensor/.gitignore | 2 - components/licensor/BUILD.yaml | 16 - components/licensor/ee/cmd/genkey.go | 69 --- components/licensor/ee/cmd/root.go | 27 - components/licensor/ee/cmd/sign.go | 98 ---- components/licensor/ee/cmd/validate.go | 60 --- components/licensor/ee/pkg/licensor/gitpod.go | 70 --- components/licensor/ee/pkg/licensor/keys.go | 49 -- .../licensor/ee/pkg/licensor/licensor.go | 279 ---------- .../licensor/ee/pkg/licensor/licensor_test.go | 480 ------------------ .../licensor/ee/pkg/licensor/replicated.go | 145 ------ components/licensor/go.mod | 17 - components/licensor/go.sum | 30 -- components/licensor/main.go | 13 - components/licensor/typescript/.gitignore | 2 - components/licensor/typescript/BUILD.yaml | 22 - components/licensor/typescript/binding.gyp | 14 - components/licensor/typescript/ee/genapi.go | 104 ---- components/licensor/typescript/ee/main.go | 120 ----- components/licensor/typescript/ee/src/api.ts | 44 -- .../licensor/typescript/ee/src/index.ts | 71 --- .../licensor/typescript/ee/src/module.cc | 262 ---------- .../typescript/ee/src/nativemodule.d.ts | 15 - .../typescript/ee/src/nativemodule.js | 7 - components/licensor/typescript/package.json | 24 - components/licensor/typescript/tsconfig.json | 30 -- components/server/BUILD.yaml | 3 - .../billing/entitlement-service-license.ts | 11 - components/server/ee/src/container-module.ts | 4 - components/server/ee/src/license-source.ts | 30 -- components/server/ee/src/user/user-service.ts | 13 - .../ee/src/workspace/gitpod-server-impl.ts | 101 +--- .../ee/src/workspace/workspace-factory.ts | 23 - components/server/package.json | 1 - components/server/src/auth/rate-limiter.ts | 1 - components/server/src/container-module.ts | 3 - .../telemetry-data-provider.ts | 7 - .../src/workspace/gitpod-server-impl.ts | 37 -- gitpod-ws.code-workspace | 1 - .../pkg/components/server/configmap.go | 6 - .../pkg/components/server/deployment.go | 36 -- .../installer/pkg/components/server/types.go | 1 - install/installer/pkg/config/v1/config.go | 8 - install/installer/pkg/config/v1/validation.go | 28 - yarn.lock | 146 +----- 49 files changed, 18 insertions(+), 2653 deletions(-) delete mode 100644 components/licensor/.gitignore delete mode 100644 components/licensor/BUILD.yaml delete mode 100644 components/licensor/ee/cmd/genkey.go delete mode 100644 components/licensor/ee/cmd/root.go delete mode 100644 components/licensor/ee/cmd/sign.go delete mode 100644 components/licensor/ee/cmd/validate.go delete mode 100644 components/licensor/ee/pkg/licensor/gitpod.go delete mode 100644 components/licensor/ee/pkg/licensor/keys.go delete mode 100644 components/licensor/ee/pkg/licensor/licensor.go delete mode 100644 components/licensor/ee/pkg/licensor/licensor_test.go delete mode 100644 components/licensor/ee/pkg/licensor/replicated.go delete mode 100644 components/licensor/go.mod delete mode 100644 components/licensor/go.sum delete mode 100644 components/licensor/main.go delete mode 100644 components/licensor/typescript/.gitignore delete mode 100644 components/licensor/typescript/BUILD.yaml delete mode 100644 components/licensor/typescript/binding.gyp delete mode 100644 components/licensor/typescript/ee/genapi.go delete mode 100644 components/licensor/typescript/ee/main.go delete mode 100644 components/licensor/typescript/ee/src/api.ts delete mode 100644 components/licensor/typescript/ee/src/index.ts delete mode 100644 components/licensor/typescript/ee/src/module.cc delete mode 100644 components/licensor/typescript/ee/src/nativemodule.d.ts delete mode 100644 components/licensor/typescript/ee/src/nativemodule.js delete mode 100644 components/licensor/typescript/package.json delete mode 100644 components/licensor/typescript/tsconfig.json delete mode 100644 components/server/ee/src/license-source.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2b31fc898abf97..5776278b820fd5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -61,7 +61,6 @@ /install/installer/pkg/components/ws-manager-bridge @gitpod-io/engineering-webapp /install/installer/pkg/components/ws-proxy @gitpod-io/engineering-workspace /install/installer/pkg/config/versions @gitpod-io/engineering-ide -/components/licensor @gitpod-io/engineering-webapp /components/local-app-api @csweichel @akosyakov /components/local-app @gitpod-io/engineering-ide /components/openvsx-proxy @gitpod-io/engineering-ide diff --git a/codecov.yml b/codecov.yml index 82979f7f1b8332..5dd6d0e9688665 100644 --- a/codecov.yml +++ b/codecov.yml @@ -41,9 +41,6 @@ flags: components-image-builder-app: paths: - components/image-builder/ - components-licensor-app: - paths: - - components/licensor/ components-local-app-api-go-lib: paths: - components/local-app-api/go/ diff --git a/components/dashboard/src/admin/License.tsx b/components/dashboard/src/admin/License.tsx index 6be1225a446b0e..02406fe35cd7a2 100644 --- a/components/dashboard/src/admin/License.tsx +++ b/components/dashboard/src/admin/License.tsx @@ -33,11 +33,7 @@ export default function License() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const featureList = license?.enabledFeatures; - const features = license?.features; - - // if user seats is 0, it means that there is no user limit in the license - const userLimit = license?.seats === 0 ? "Unlimited" : license?.seats; + const userLimit = "Unlimited"; const [licenseLevel, paid, statusMessage] = license ? getSubscriptionLevel(license) : defaultMessage(); @@ -49,20 +45,6 @@ export default function License() { {licenseLevel} {paid} -
Available features:
-
- {features && - features.map((feat: string) => ( - - {featureList?.includes(feat) ? ( - - ) : ( - - )} - {capitalizeInitials(feat)} - - ))} -
@@ -71,18 +53,6 @@ export default function License() {

Registered Users

{license?.userCount || 0} / {userLimit} -

License Type

-

{capitalizeInitials(license?.type || "")}

- - Compare Plans -
- -
-
@@ -91,26 +61,8 @@ export default function License() { ); } -function capitalizeInitials(str: string): string { - return str - .split("-") - .map((item) => { - return item.charAt(0).toUpperCase() + item.slice(1); - }) - .join(" "); -} - function getSubscriptionLevel(license: LicenseInfo): ReactElement[] { - switch (license.plan) { - case "prod": - case "trial": - return professionalPlan(license.userCount || 0, license.seats, license.plan == "trial", license.validUntil); - case "community": - return communityPlan(license.userCount || 0, license.seats, license.fallbackAllowed); - default: { - return defaultMessage(); - } - } + return professionalPlan(license.userCount || 0); } function licenseLevel(level: string): ReactElement { @@ -136,16 +88,9 @@ function defaultMessage(): ReactElement[] { return [licenseLevel("Inactive"), additionalLicenseInfo("Free"), alertMessage()]; } -function professionalPlan(userCount: number, seats: number, trial: boolean, validUntil: string): ReactElement[] { - const alertMessage = (aboveLimit: boolean) => { - return aboveLimit ? ( - -
You have exceeded the usage limit.
-
- -
-
- ) : ( +function professionalPlan(userCount: number): ReactElement[] { + const alertMessage = () => { + return (
You have an active professional license.
@@ -155,56 +100,5 @@ function professionalPlan(userCount: number, seats: number, trial: boolean, vali ); }; - // seats === 0 means unlimited number of users - const aboveLimit: boolean = seats === 0 ? false : userCount > seats; - - const licenseTitle = () => { - const expDate = new Date(validUntil); - if (typeof expDate.getTime !== "function") { - return trial ? additionalLicenseInfo("Trial") : additionalLicenseInfo("Paid"); - } else { - return additionalLicenseInfo( - "Expires on " + - expDate.toLocaleDateString("en-DB", { year: "numeric", month: "short", day: "numeric" }), - ); - } - }; - - return [licenseLevel("Professional"), licenseTitle(), alertMessage(aboveLimit)]; -} - -function communityPlan(userCount: number, seats: number, fallbackAllowed: boolean): ReactElement[] { - const alertMessage = (aboveLimit: boolean) => { - if (aboveLimit) { - return fallbackAllowed ? ( -
-
No active license. You are using community edition.
-
- -
-
- ) : ( - -
No active license. You have exceeded the usage limit.
-
- -
-
- ); - } else { - return ( - -
You are using the free community edition.
-
- -
-
- ); - } - }; - - // seats === 0 means unlimited number of users - const aboveLimit: boolean = seats === 0 ? false : userCount > seats; - - return [licenseLevel("Community"), additionalLicenseInfo("Free"), alertMessage(aboveLimit)]; + return [licenseLevel("Professional"), additionalLicenseInfo("Paid"), alertMessage()]; } diff --git a/components/gitpod-protocol/src/license-protocol.ts b/components/gitpod-protocol/src/license-protocol.ts index 144b5cfe757ed5..3f3ddb4bf4e9d8 100644 --- a/components/gitpod-protocol/src/license-protocol.ts +++ b/components/gitpod-protocol/src/license-protocol.ts @@ -7,23 +7,10 @@ export interface LicenseValidationResult { valid: boolean; msg?: string; - issue?: LicenseIssue; } -export type LicenseIssue = "seats-exhausted"; - export interface LicenseInfo { - key: string; - seats: number; userCount?: number; - valid: boolean; - validUntil: string; - plan?: string; - features?: string[]; - enabledFeatures?: string[]; - type?: string; - errorMsg?: string; - fallbackAllowed: boolean; } export interface GetLicenseInfoResult { @@ -31,14 +18,8 @@ export interface GetLicenseInfoResult { licenseInfo: LicenseInfo; } -export enum LicenseFeature { - CreateSnapshot = "create-snapshot", - // room for more -} - export interface LicenseService { validateLicense(): Promise; getLicenseInfo(): Promise; adminGetLicense(): Promise; - licenseIncludesFeature(feature: LicenseFeature): Promise; } diff --git a/components/licensor/.gitignore b/components/licensor/.gitignore deleted file mode 100644 index 82178c016ed678..00000000000000 --- a/components/licensor/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -private_key.pem -public_key.pem diff --git a/components/licensor/BUILD.yaml b/components/licensor/BUILD.yaml deleted file mode 100644 index 11f74c5ba8d11b..00000000000000 --- a/components/licensor/BUILD.yaml +++ /dev/null @@ -1,16 +0,0 @@ -packages: - - name: lib - type: go - srcs: - - "go.mod" - - "go.sum" - - "ee/pkg/**/*.go" - config: - packaging: library - - name: app - type: go - srcs: - - "go.mod" - - "go.sum" - - "**/*.go" - - "*.go" diff --git a/components/licensor/ee/cmd/genkey.go b/components/licensor/ee/cmd/genkey.go deleted file mode 100644 index 4224866ab52b5b..00000000000000 --- a/components/licensor/ee/cmd/genkey.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package cmd - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "os" - - "github.com/spf13/cobra" -) - -// genkeyCmd represents the genkey command -var genkeyCmd = &cobra.Command{ - Use: "genkey", - Short: "Generates a public/private key for signing licenses", - RunE: func(cmd *cobra.Command, args []string) error { - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - return err - } - - privf, err := os.Create("private_key.pem") - if err != nil { - return err - } - defer privf.Close() - err = pem.Encode(privf, &pem.Block{ - Type: "PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(priv), - }) - if err != nil { - return err - } - - pubf, err := os.Create("public_key.pem") - if err != nil { - return err - } - defer pubf.Close() - err = pem.Encode(pubf, &pem.Block{ - Type: "PUBLIC KEY", - Bytes: x509.MarshalPKCS1PublicKey(&priv.PublicKey), - }) - if err != nil { - return err - } - - return nil - }, -} - -func init() { - rootCmd.AddCommand(genkeyCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // genkeyCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // genkeyCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} diff --git a/components/licensor/ee/cmd/root.go b/components/licensor/ee/cmd/root.go deleted file mode 100644 index ddb92884813334..00000000000000 --- a/components/licensor/ee/cmd/root.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" -) - -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ - Use: "licensor", - Short: "CLI for signing licenses", -} - -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) - } -} diff --git a/components/licensor/ee/cmd/sign.go b/components/licensor/ee/cmd/sign.go deleted file mode 100644 index 25e376ef0b338e..00000000000000 --- a/components/licensor/ee/cmd/sign.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package cmd - -import ( - "crypto/x509" - "encoding/pem" - "fmt" - "os" - "time" - - "github.com/spf13/cobra" - "golang.org/x/xerrors" - - "github.com/gitpod-io/gitpod/licensor/ee/pkg/licensor" -) - -// signCmd represents the sign command -var signCmd = &cobra.Command{ - Use: "sign", - Short: "Signs a license", - RunE: func(cmd *cobra.Command, args []string) error { - keyfn, _ := cmd.Flags().GetString("key") - - fc, err := os.ReadFile(keyfn) - if err != nil { - return err - } - block, _ := pem.Decode(fc) - if block == nil { - return xerrors.Errorf("no PEM encoded key found in %s", keyfn) - } - if block.Type != "PRIVATE KEY" { - return xerrors.Errorf("unknown PEM block type %s", block.Type) - } - priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return err - } - - var ( - domain, _ = cmd.Flags().GetString("domain") - id, _ = cmd.Flags().GetString("id") - level, _ = cmd.Flags().GetString("level") - seats, _ = cmd.Flags().GetInt("seats") - validFor, _ = cmd.Flags().GetDuration("valid-for") - ) - if domain == "" { - return xerrors.Errorf("--domain is mandatory") - } - if id == "" { - return xerrors.Errorf("--id is mandatory") - } - if level == "" { - return xerrors.Errorf("--level is mandatory") - } - if seats < 0 { - return xerrors.Errorf("--seats must be positive") - } - if validFor <= 0 { - return xerrors.Errorf("--valid-for must be positive") - } - - lvl, ok := licensor.NamedLevel[level] - if !ok { - return xerrors.Errorf("invalid license level: %s", level) - } - - l := licensor.LicensePayload{ - Domain: domain, - ID: id, - Seats: seats, - Level: lvl, - ValidUntil: time.Now().Add(validFor), - } - - res, err := licensor.Sign(l, priv) - if err != nil { - return err - } - - fmt.Println(string(res)) - return nil - }, -} - -func init() { - rootCmd.AddCommand(signCmd) - - signCmd.Flags().String("domain", "", "domain for which the license is valid") - signCmd.Flags().String("id", "", "ID of the license") - signCmd.Flags().String("level", "enterprise", "license level, must be one of team, enterprise") - signCmd.Flags().Int("seats", 5, "number of seats the license is valid for") - signCmd.Flags().StringP("key", "k", "private_key.pem", "path to the private key to sign the license with") - signCmd.Flags().Duration("valid-for", 365*24*time.Hour, "time the license is valid for") -} diff --git a/components/licensor/ee/cmd/validate.go b/components/licensor/ee/cmd/validate.go deleted file mode 100644 index bee8a0c92faba3..00000000000000 --- a/components/licensor/ee/cmd/validate.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package cmd - -import ( - "encoding/json" - "fmt" - "io" - "os" - - "github.com/spf13/cobra" - "golang.org/x/xerrors" - - "github.com/gitpod-io/gitpod/licensor/ee/pkg/licensor" -) - -// validateCmd represents the validate command -var validateCmd = &cobra.Command{ - Use: "validate [license]", - Short: "Validates a license - reads from stdin if no argument is provided", - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - domain, _ := cmd.Flags().GetString("domain") - licensorType, _ := cmd.Flags().GetString("licensor") - - var e *licensor.Evaluator - switch licensorType { - case string(licensor.LicenseTypeReplicated): - e = licensor.NewReplicatedEvaluator() - default: - var lic []byte - if len(args) == 0 { - lic, err = io.ReadAll(os.Stdin) - if err != nil { - return err - } - } else { - lic = []byte(args[0]) - } - - e = licensor.NewGitpodEvaluator(lic, domain) - } - - if msg, valid := e.Validate(); !valid { - return xerrors.Errorf(msg) - } - - b, _ := json.MarshalIndent(e.Inspect(), "", " ") - fmt.Println(string(b)) - return nil - }, -} - -func init() { - rootCmd.AddCommand(validateCmd) - validateCmd.Flags().String("domain", "", "domain to evaluate the license against") - validateCmd.Flags().String("licensor", "gitpod", "licensor to use") -} diff --git a/components/licensor/ee/pkg/licensor/gitpod.go b/components/licensor/ee/pkg/licensor/gitpod.go deleted file mode 100644 index 0b30524704c1a1..00000000000000 --- a/components/licensor/ee/pkg/licensor/gitpod.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package licensor - -import ( - "crypto" - "crypto/rsa" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "fmt" - "time" -) - -// NewGitpodEvaluator produces a new license evaluator from a license key -func NewGitpodEvaluator(key []byte, domain string) (res *Evaluator) { - if len(key) == 0 { - // fallback to the default license - return &Evaluator{ - lic: defaultLicense, - allowFallback: true, - plan: LicenseTypeCommunity, - } - } - - deckey := make([]byte, base64.StdEncoding.DecodedLen(len(key))) - n, err := base64.StdEncoding.Decode(deckey, key) - if err != nil { - return &Evaluator{invalid: fmt.Sprintf("cannot decode key: %q", err)} - } - deckey = deckey[:n] - - var lic licensePayload - err = json.Unmarshal(deckey, &lic) - if err != nil { - return &Evaluator{invalid: fmt.Sprintf("cannot unmarshal key: %q", err)} - } - - keyWoSig, err := json.Marshal(lic.LicensePayload) - if err != nil { - return &Evaluator{invalid: fmt.Sprintf("cannot remarshal key: %q", err)} - } - hashed := sha256.Sum256(keyWoSig) - - for _, k := range publicKeys { - err = rsa.VerifyPKCS1v15(k, crypto.SHA256, hashed[:], lic.Signature) - if err == nil { - break - } - } - if err != nil { - return &Evaluator{invalid: fmt.Sprintf("cannot verify key: %q", err)} - } - - if !matchesDomain(lic.Domain, domain) { - return &Evaluator{invalid: "wrong domain"} - } - - if lic.ValidUntil.Before(time.Now()) { - return &Evaluator{invalid: "not valid anymore"} - } - - return &Evaluator{ - lic: lic.LicensePayload, - allowFallback: false, // Gitpod licenses cannot fallback - assume these are always paid-for - plan: LicenseTypePaid, // This essentially means "paid" license - } -} diff --git a/components/licensor/ee/pkg/licensor/keys.go b/components/licensor/ee/pkg/licensor/keys.go deleted file mode 100644 index 9c4f7fe289db77..00000000000000 --- a/components/licensor/ee/pkg/licensor/keys.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package licensor - -import ( - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" -) - -var ( - keys = [][]byte{ - // Demo key - remove before publishing this code - []byte(`-----BEGIN PUBLIC KEY----- -MIIBCgKCAQEAtHhBNA9J7mh301CMP4Hfvv0OLMWDG3FjwR9nUAg3z5SFYnUz4tnP -NB7gDFNXUIUpetKUyyoAwAWwQsu4/zt9XDg6G25jiHZ/inEfI3xQV2tUhJm+zVLg -7RCUpVjbUZthaIhGyYm0Oa/Lqa8q/hInqP/Hlvgga+yfBurrYyhdaJFWpgF/m2ha -yFgEEE/427F/BP/qNfJN+v/ojtsJMM81/jGWH6Tm0bxoWa5nQPsGF7h0MjLc5pYp -NOrioO8lNSNu1Fz8cYwATxmdgA+0scS/pXyNcP1U9ELjpUAXaUdhthViQ4d5hXj2 -48DoltWJYg1Vgjj2eeYKr7JiJjrXlZoaFwIDAQAB ------END PUBLIC KEY-----`), - // TOOD: add trial license key here - // TODO: add actual production license key here - } -) - -var publicKeys []*rsa.PublicKey - -func init() { - publicKeys = make([]*rsa.PublicKey, len(keys)) - for i, pk := range keys { - block, _ := pem.Decode(pk) - if block == nil { - panic("invalid public licensor key") - } - if block.Type != "PUBLIC KEY" { - panic(fmt.Sprintf("unknown PEM block type %s", block.Type)) - } - - var err error - publicKeys[i], err = x509.ParsePKCS1PublicKey(block.Bytes) - if err != nil { - panic(err) - } - } -} diff --git a/components/licensor/ee/pkg/licensor/licensor.go b/components/licensor/ee/pkg/licensor/licensor.go deleted file mode 100644 index ed2bcb8e7ae5dd..00000000000000 --- a/components/licensor/ee/pkg/licensor/licensor.go +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package licensor - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "fmt" - "os" - "strings" - "time" -) - -type LicenseType string - -const ( - LicenseTypeGitpod LicenseType = "gitpod" - LicenseTypeReplicated LicenseType = "replicated" -) - -// LicenseSubscriptionLevel is initialized to have a standard license plan -// between replicated and gitpod licenses -type LicenseSubscriptionLevel string - -const ( - LicenseTypeCommunity LicenseSubscriptionLevel = "community" - LicenseTypePaid LicenseSubscriptionLevel = "prod" - LicenseTypeTrial LicenseSubscriptionLevel = "trial" - LicenseTypeDevelopment LicenseSubscriptionLevel = "dev" -) - -// LicenseData has type specific info about the license -type LicenseData struct { - Type LicenseType `json:"type"` - Payload LicensePayload `json:"payload"` - Plan LicenseSubscriptionLevel `json:"plan"` - FallbackAllowed bool `json:"fallbackAllowed"` -} - -// LicensePayload is the actual license content -type LicensePayload struct { - ID string `json:"id"` - Domain string `json:"domain"` - Level LicenseLevel `json:"level"` - ValidUntil time.Time `json:"validUntil"` - // Type LicenseType `json:"type"` - - // Seats == 0 means there's no seat limit - Seats int `json:"seats"` - - // CustomerID is used to identify installations in installation analytics - CustomerID string `json:"customerID,omitempty"` -} - -type licensePayload struct { - LicensePayload - Signature []byte `json:"signature"` -} - -// LicenseLevel determine feature availability - -type LicenseLevel int - -const ( - // This exists for historical reasons - it is now the same as LevelEnterprise - LevelTeam LicenseLevel = 0 - - // LevelEnterprise enables enterprise features, - // which applies after buying a license - LevelEnterprise LicenseLevel = 1 -) - -// NamedLevel maps level names to the actual level -var NamedLevel map[string]LicenseLevel = map[string]LicenseLevel{ - "team": LevelTeam, - "enterprise": LevelEnterprise, -} - -// Feature denotes a feature that can be enabled using a license key -type Feature string - -const ( - // FeatureAdminDashboard enables the admin dashboard API - FeatureAdminDashboard Feature = "admin-dashboard" - // FeaturePrebuild enables prebuilds - FeaturePrebuild Feature = "prebuild" - // FeatureSetTimeout enables custom timeouts for workspaces - FeatureSetTimeout Feature = "set-timeout" - // FeatureSnapshot enables snapshot support - FeatureSnapshot Feature = "snapshot" - // FeatureWorkspaceSharing enables live workspace sharing - FeatureWorkspaceSharing Feature = "workspace-sharing" -) - -type featureSet map[Feature]struct{} - -type allowance struct { - Features featureSet - - // Total prebuild time that can be used at a certain level. - // If zero the prebuild time is unlimited. - PrebuildTime time.Duration -} - -var allowanceMap = map[LicenseLevel]allowance{ - LevelTeam: { - Features: featureSet{ - FeatureAdminDashboard: struct{}{}, - }, - }, - LevelEnterprise: { - PrebuildTime: 0, - Features: featureSet{ - FeaturePrebuild: struct{}{}, - - FeatureAdminDashboard: struct{}{}, - FeatureSetTimeout: struct{}{}, - FeatureSnapshot: struct{}{}, - FeatureWorkspaceSharing: struct{}{}, - }, - }, -} - -func (lvl LicenseLevel) allowance() allowance { - a, ok := allowanceMap[lvl] - if !ok { - fmt.Fprintf(os.Stderr, "invalid license level %d - allowing nothing", lvl) - return allowance{} - } - - return a -} - -// Fallback license is used when the instance exceeds the number of licenses - it allows limited access -var fallbackLicense = LicensePayload{ - ID: "fallback-license", - Level: LevelEnterprise, - Seats: 0, - // Domain, ValidUntil are free for all -} - -// Default license is used when no valid license is given - it allows full access up to 10 users -var defaultLicense = LicensePayload{ - ID: "default-license", - Level: LevelEnterprise, - Seats: 0, - // Domain, ValidUntil are free for all -} - -// we match domains only for `gitpod` license and not with replicated license. -// In the case of replicated this ensures faster client onboarding -func matchesDomain(pattern, domain string) bool { - if pattern == "" { - return true - } - if domain == pattern { - return true - } - - if strings.HasPrefix(pattern, "*.") && len(pattern) > 2 { - domainSuffix := pattern[1:] - if strings.HasSuffix(domain, domainSuffix) { - return true - } - } - - return false -} - -// Evaluator determines what a license allows for -type Evaluator struct { - invalid string - allowFallback bool // Paid licenses cannot fallback and prevent additional signups - lic LicensePayload - plan LicenseSubscriptionLevel // Specifies if it is a community/free plan or paid plan -} - -// Validate returns false if the license isn't valid and a message explaining why that is. -func (e *Evaluator) Validate() (msg string, valid bool) { - if e.invalid == "" { - return "", true - } - - return e.invalid, false -} - -// Enabled determines if a feature is enabled by the license -func (e *Evaluator) Enabled(feature Feature, seats int) bool { - if e.invalid != "" { - return false - } - - var ok bool - if e.hasEnoughSeats(seats) { - // License has enough seats available - evaluate this license - _, ok = e.lic.Level.allowance().Features[feature] - } else if e.allowFallback { - // License has run out of seats - use the fallback license - _, ok = fallbackLicense.Level.allowance().Features[feature] - } - - return ok -} - -// hasEnoughSeats returns true if the license supports at least the give amount of seats -func (e *Evaluator) hasEnoughSeats(seats int) bool { - if e.invalid != "" { - return false - } - - return e.lic.Seats == 0 || seats <= e.lic.Seats -} - -// HasEnoughSeats is the public method to hasEnoughSeats. Will use fallback license if allowable -func (e *Evaluator) HasEnoughSeats(seats int) bool { - if e.invalid != "" { - return false - } - - if !e.allowFallback { - return e.hasEnoughSeats(seats) - } - // There is always more space if can use a fallback license - return true -} - -// Inspect returns the license information this evaluator holds. -// This function is intended for transparency/debugging purposes only and must -// never be used to determine feature eligibility under a license. All code making -// those kinds of decisions must be part of the Evaluator. -func (e *Evaluator) Inspect() LicensePayload { - return e.lic -} - -func (e *Evaluator) LicenseData() LicenseData { - data := LicenseData{ - Type: LicenseType(e.GetLicenseType()), - Payload: e.Inspect(), - FallbackAllowed: e.allowFallback, - Plan: e.plan, - } - - return data -} - -func (e *Evaluator) GetLicenseType() string { - return os.Getenv("GITPOD_LICENSE_TYPE") -} - -// Sign signs a license so that it can be used with the evaluator -func Sign(l LicensePayload, priv *rsa.PrivateKey) (res []byte, err error) { - rawl, err := json.Marshal(l) - if err != nil { - return nil, err - } - hashed := sha256.Sum256(rawl) - - sig, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, hashed[:]) - if err != nil { - return nil, err - } - - resl, err := json.Marshal(licensePayload{ - LicensePayload: l, - Signature: sig, - }) - if err != nil { - return nil, err - } - - res = make([]byte, base64.StdEncoding.EncodedLen(len(resl))) - base64.StdEncoding.Encode(res, resl) - return res, nil -} diff --git a/components/licensor/ee/pkg/licensor/licensor_test.go b/components/licensor/ee/pkg/licensor/licensor_test.go deleted file mode 100644 index c1a6e47e32f4f7..00000000000000 --- a/components/licensor/ee/pkg/licensor/licensor_test.go +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package licensor - -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "encoding/json" - "io/ioutil" - "net/http" - "testing" - "time" -) - -const ( - seats = 5 - domain = "foobar.com" - someID = "730d5134-768c-4a05-b7cd-ecf3757cada9" - replicatedLicenseUrl = "http://kotsadm:3000/license/v1/license" -) - -type licenseTest struct { - Name string - License *LicensePayload - Validate func(t *testing.T, eval *Evaluator) - Type LicenseType - NeverExpires bool - ReplicatedLicenseType *LicenseSubscriptionLevel -} - -// roundTripFunc . -type roundTripFunc func(req *http.Request) *http.Response - -// roundTrip . -func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { - return f(req), nil -} - -// newTestClient returns *http.Client with Transport replaced to avoid making real calls -func newTestClient(fn roundTripFunc) *http.Client { - return &http.Client{ - Transport: roundTripFunc(fn), - } -} - -func (test *licenseTest) Run(t *testing.T) { - t.Run(test.Name, func(t *testing.T) { - var eval *Evaluator - if test.Type == LicenseTypeGitpod { - if test.NeverExpires { - t.Fatal("gitpod licenses must have an expiry date") - } - - if test.License == nil { - eval = NewGitpodEvaluator(nil, "") - } else { - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatalf("cannot generate key: %q", err) - } - publicKeys = []*rsa.PublicKey{&priv.PublicKey} - lic, err := Sign(*test.License, priv) - if err != nil { - t.Fatalf("cannot sign license: %q", err) - } - - eval = NewGitpodEvaluator(lic, domain) - } - } else if test.Type == LicenseTypeReplicated { - client := newTestClient(func(req *http.Request) *http.Response { - act := req.URL.String() - if act != "http://kotsadm:3000/license/v1/license" { - t.Fatalf("invalid kotsadm url match: expected %s, got %v", replicatedLicenseUrl, act) - } - - payload, err := json.Marshal(replicatedLicensePayload{ - LicenseType: func() LicenseSubscriptionLevel { - if test.ReplicatedLicenseType == nil { - return LicenseTypePaid - } - return *test.ReplicatedLicenseType - }(), - ExpirationTime: func() *time.Time { - if test.License != nil { - return &test.License.ValidUntil - } - if !test.NeverExpires { - t := time.Now().Add(-6 * time.Hour) - return &t - } - return nil - }(), - Fields: []replicatedFields{ - { - Field: "domain", - Value: func() string { - if test.License != nil { - return test.License.Domain - } - return domain - }(), - }, - { - Field: "seats", - Value: func() int { - if test.License != nil { - return test.License.Seats - } - return seats - }(), - }, - }, - }) - if err != nil { - t.Fatalf("failed to convert payload: %v", err) - } - - return &http.Response{ - StatusCode: 200, - Body: ioutil.NopCloser(bytes.NewBuffer(payload)), - Header: make(http.Header), - } - }) - - if test.License == nil { - eval = newReplicatedEvaluator(client) - } else { - eval = newReplicatedEvaluator(client) - } - } else { - t.Fatalf("unknown license type: '%s'", test.Type) - } - - test.Validate(t, eval) - }) -} - -func TestSeats(t *testing.T) { - tests := []struct { - Name string - Licensed int - Probe int - WithinLimits bool - DefaultLicense bool - InvalidLicense bool - LicenseType LicenseType - NeverExpires bool - }{ - {"Gitpod: unlimited seats", 0, 1000, true, false, false, LicenseTypeGitpod, false}, - {"Gitpod: within limited seats", 50, 40, true, false, false, LicenseTypeGitpod, false}, - {"Gitpod: within limited seats (edge)", 50, 50, true, false, false, LicenseTypeGitpod, false}, - {"Gitpod: beyond limited seats", 50, 150, false, false, false, LicenseTypeGitpod, false}, - {"Gitpod: beyond limited seats (edge)", 50, 51, false, false, false, LicenseTypeGitpod, false}, - {"Gitpod: invalid license", 50, 50, false, false, true, LicenseTypeGitpod, false}, - {"Gitpod: within default license seats", 0, 7, true, true, false, LicenseTypeGitpod, false}, - {"Gitpod: within default license seats (edge)", 0, 10, true, true, false, LicenseTypeGitpod, false}, - {"Gitpod: beyond default license seats", 0, 11, true, true, false, LicenseTypeGitpod, false}, - - // correctly missing the default license tests as Replicated always has a license - {"Replicated: unlimited seats", 0, 1000, true, false, false, LicenseTypeReplicated, false}, - {"Replicated: within limited seats", 50, 40, true, false, false, LicenseTypeReplicated, false}, - {"Replicated: within limited seats (edge)", 50, 50, true, false, false, LicenseTypeReplicated, false}, - {"Replicated: beyond limited seats", 50, 150, false, false, false, LicenseTypeReplicated, false}, - {"Replicated: beyond limited seats (edge)", 50, 51, false, false, false, LicenseTypeReplicated, false}, - {"Replicated: invalid license", 50, 50, true, false, true, LicenseTypeReplicated, false}, - {"Replicated: beyond default license seats", 0, 11, true, true, false, LicenseTypeReplicated, false}, - - {"Replicated: unlimited seats", 0, 1000, true, false, false, LicenseTypeReplicated, true}, - {"Replicated: within limited seats", 50, 40, true, false, false, LicenseTypeReplicated, true}, - {"Replicated: within limited seats (edge)", 50, 50, true, false, false, LicenseTypeReplicated, true}, - {"Replicated: beyond limited seats", 50, 150, false, false, false, LicenseTypeReplicated, true}, - {"Replicated: beyond limited seats (edge)", 50, 51, false, false, false, LicenseTypeReplicated, true}, - {"Replicated: invalid license", 50, 50, true, false, true, LicenseTypeReplicated, true}, - {"Replicated: beyond default license seats", 0, 11, false, true, false, LicenseTypeReplicated, true}, - {"Replicated: invalid license within default seats", 50, 5, true, false, true, LicenseTypeReplicated, false}, - } - - for _, test := range tests { - validUntil := time.Now().Add(6 * time.Hour) - if test.InvalidLicense { - validUntil = time.Now().Add(-6 * time.Hour) - } - - lt := licenseTest{ - Name: test.Name, - License: &LicensePayload{ - ID: someID, - Domain: domain, - Level: LevelTeam, - Seats: test.Licensed, - ValidUntil: validUntil, - }, - Validate: func(t *testing.T, eval *Evaluator) { - withinLimits := eval.hasEnoughSeats(test.Probe) - if withinLimits != test.WithinLimits { - t.Errorf("hasEnoughSeats did not behave as expected: lic=%d probe=%d expected=%v actual=%v", test.Licensed, test.Probe, test.WithinLimits, withinLimits) - } - }, - Type: test.LicenseType, - NeverExpires: test.NeverExpires, - } - if test.DefaultLicense { - lt.License = nil - } - lt.Run(t) - } -} - -func TestFeatures(t *testing.T) { - replicatedCommunity := LicenseTypeCommunity - replicatedPaid := LicenseTypePaid - - tests := []struct { - Name string - DefaultLicense bool - Level LicenseLevel - Features []Feature - LicenseType LicenseType - UserCount int - ReplicatedLicenseType *LicenseSubscriptionLevel - }{ - {"Gitpod (in seats): no license", true, LicenseLevel(0), []Feature{ - FeatureAdminDashboard, - FeatureSetTimeout, - FeatureWorkspaceSharing, - FeatureSnapshot, - FeaturePrebuild, - }, LicenseTypeGitpod, 10, nil}, - {"Gitpod (in seats): invalid license level", false, LicenseLevel(666), []Feature{}, LicenseTypeGitpod, seats, nil}, - {"Gitpod (in seats): enterprise license", false, LevelEnterprise, []Feature{ - FeatureAdminDashboard, - FeatureSetTimeout, - FeatureWorkspaceSharing, - FeatureSnapshot, - FeaturePrebuild, - }, LicenseTypeGitpod, seats, nil}, - - {"Gitpod (over seats): no license", true, LicenseLevel(0), []Feature{ - FeatureAdminDashboard, - FeatureSetTimeout, - FeatureWorkspaceSharing, - FeatureSnapshot, - FeaturePrebuild, - }, LicenseTypeGitpod, 11, nil}, - {"Gitpod (over seats): invalid license level", false, LicenseLevel(666), []Feature{}, LicenseTypeGitpod, seats + 1, nil}, - {"Gitpod (over seats): enterprise license", false, LevelEnterprise, []Feature{}, LicenseTypeGitpod, seats + 1, nil}, - - {"Replicated (in seats): invalid license level", false, LicenseLevel(666), []Feature{ - FeatureAdminDashboard, - FeatureSetTimeout, - FeatureWorkspaceSharing, - FeatureSnapshot, - FeaturePrebuild, - }, LicenseTypeReplicated, seats, &replicatedPaid}, - {"Replicated (in seats): enterprise license", false, LevelEnterprise, []Feature{ - FeatureAdminDashboard, - FeatureSetTimeout, - FeatureWorkspaceSharing, - FeatureSnapshot, - FeaturePrebuild, - }, LicenseTypeReplicated, seats, &replicatedPaid}, - - {"Replicated (over seats - no fallback): invalid license level", true, LicenseLevel(666), []Feature{ - FeatureAdminDashboard, - FeatureSetTimeout, - FeatureWorkspaceSharing, - FeatureSnapshot, - FeaturePrebuild, - }, LicenseTypeReplicated, seats + 1, &replicatedPaid}, - {"Replicated (over seats - no fallback): enterprise license", true, LevelEnterprise, []Feature{ - FeatureAdminDashboard, - FeatureSetTimeout, - FeatureWorkspaceSharing, - FeatureSnapshot, - FeaturePrebuild, - }, LicenseTypeReplicated, seats + 1, &replicatedPaid}, - - {"Replicated (over seats - fallback): invalid license level", false, LicenseLevel(666), []Feature{ - FeatureAdminDashboard, - FeatureSetTimeout, - FeatureWorkspaceSharing, - FeatureSnapshot, - FeaturePrebuild, - }, LicenseTypeReplicated, seats + 1, &replicatedCommunity}, - {"Replicated (over seats - fallback): enterprise license", false, LevelEnterprise, []Feature{ - FeatureAdminDashboard, - FeatureSetTimeout, - FeatureWorkspaceSharing, - FeatureSnapshot, - FeaturePrebuild, - }, LicenseTypeReplicated, seats + 1, &replicatedCommunity}, - } - - for _, test := range tests { - lic := &LicensePayload{ - ID: someID, - Domain: domain, - Level: test.Level, - Seats: seats, - ValidUntil: time.Now().Add(6 * time.Hour), - } - if test.DefaultLicense { - lic = nil - } - lt := licenseTest{ - Name: test.Name, - License: lic, - ReplicatedLicenseType: test.ReplicatedLicenseType, - Validate: func(t *testing.T, eval *Evaluator) { - unavailableFeatures := featureSet{} - for f := range allowanceMap[LevelEnterprise].Features { - unavailableFeatures[f] = struct{}{} - } - for _, f := range test.Features { - delete(unavailableFeatures, f) - - if !eval.Enabled(f, test.UserCount) { - t.Errorf("license does not enable %s, but should", f) - } - } - - for f := range unavailableFeatures { - if eval.Enabled(f, test.UserCount) { - t.Errorf("license not enables %s, but shouldn't", f) - } - } - }, - Type: test.LicenseType, - } - lt.Run(t) - } -} - -func TestEvalutorKeys(t *testing.T) { - tests := []struct { - Name string - Keygen func() ([]*rsa.PublicKey, *rsa.PrivateKey, error) - EvalDomain string - Validation string - ValidFor time.Duration - }{ - { - Name: "single valid key", - Keygen: func() ([]*rsa.PublicKey, *rsa.PrivateKey, error) { - priv, err := rsa.GenerateKey(rand.Reader, 512) - if err != nil { - return nil, nil, err - } - return []*rsa.PublicKey{&priv.PublicKey}, priv, nil - }, - }, - { - Name: "single valid key but wrong domain", - Keygen: func() ([]*rsa.PublicKey, *rsa.PrivateKey, error) { - priv, err := rsa.GenerateKey(rand.Reader, 512) - if err != nil { - return nil, nil, err - } - return []*rsa.PublicKey{&priv.PublicKey}, priv, nil - }, - EvalDomain: "wrong-" + domain, - Validation: "wrong domain", - }, - { - Name: "single valid key but outdated", - ValidFor: -6 * time.Hour, - Keygen: func() ([]*rsa.PublicKey, *rsa.PrivateKey, error) { - priv, err := rsa.GenerateKey(rand.Reader, 512) - if err != nil { - return nil, nil, err - } - return []*rsa.PublicKey{&priv.PublicKey}, priv, nil - }, - Validation: "not valid anymore", - }, - { - Name: "multiple valid keys", - Keygen: func() (pks []*rsa.PublicKey, priv *rsa.PrivateKey, err error) { - pks = make([]*rsa.PublicKey, 3) - for i := 0; i < len(pks); i++ { - priv, err = rsa.GenerateKey(rand.Reader, 512) - if err != nil { - return nil, nil, err - } - pks[i] = &priv.PublicKey - } - return pks, priv, nil - }, - }, - { - Name: "multiple wrong keys", - Keygen: func() (pks []*rsa.PublicKey, priv *rsa.PrivateKey, err error) { - pks = make([]*rsa.PublicKey, 3) - for i := 0; i < len(pks); i++ { - priv, err = rsa.GenerateKey(rand.Reader, 512) - if err != nil { - return nil, nil, err - } - pks[i] = &priv.PublicKey - } - - priv, err = rsa.GenerateKey(rand.Reader, 512) - if err != nil { - return nil, nil, err - } - - return pks, priv, nil - }, - Validation: "cannot verify key: \"crypto/rsa: verification error\"", - }, - } - - for _, test := range tests { - t.Run(test.Name, func(t *testing.T) { - pks, priv, err := test.Keygen() - if err != nil { - t.Fatalf("cannot generate keys: %q", err) - } - if test.ValidFor == 0 { - test.ValidFor = 24 * time.Hour - } - - publicKeys = pks - lic, err := Sign(LicensePayload{ - ID: someID, - Domain: domain, - Level: LevelEnterprise, - Seats: 5, - ValidUntil: time.Now().Add(test.ValidFor), - }, priv) - if err != nil { - t.Fatalf("cannot sign test license: %q", err) - } - - dom := domain - if test.EvalDomain != "" { - dom = test.EvalDomain - } - - var errmsg string - e := NewGitpodEvaluator(lic, dom) - if msg, valid := e.Validate(); !valid { - errmsg = msg - } - if errmsg != test.Validation { - t.Errorf("unepxected validation result: expected \"%s\", got \"%s\"", test.Validation, errmsg) - } - }) - } -} - -func TestMatchesDomain(t *testing.T) { - tests := []struct { - Name string - Pattern string - Domain string - Matches bool - }{ - {Name: "no domain pattern", Pattern: "", Domain: "foobar.com", Matches: true}, - {Name: "exact match", Pattern: "foobar.com", Domain: "foobar.com", Matches: true}, - {Name: "exact mismatch", Pattern: "foobar.com", Domain: "does-not-match.com", Matches: false}, - {Name: "direct pattern match", Pattern: "*.foobar.com", Domain: "foobar.com", Matches: false}, - {Name: "pattern sub match", Pattern: "*.foobar.com", Domain: "foo.foobar.com", Matches: true}, - {Name: "direct pattern mismatch", Pattern: "*.foobar.com", Domain: "does-not-match.com", Matches: false}, - {Name: "pattern sub mismatch", Pattern: "*.foobar.com", Domain: "foo.does-not-match.com", Matches: false}, - {Name: "invalid pattern sub", Pattern: "foo.*.foobar.com", Domain: "foo.foobar.com", Matches: false}, - {Name: "invalid pattern empty", Pattern: "*.", Domain: "foobar.com", Matches: false}, - } - for _, test := range tests { - t.Run(test.Name, func(t *testing.T) { - act := matchesDomain(test.Pattern, test.Domain) - if act != test.Matches { - t.Errorf("unexpected domain match: expected %v, got %v", test.Matches, act) - } - }) - } -} diff --git a/components/licensor/ee/pkg/licensor/replicated.go b/components/licensor/ee/pkg/licensor/replicated.go deleted file mode 100644 index 0be7016d4a465f..00000000000000 --- a/components/licensor/ee/pkg/licensor/replicated.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package licensor - -import ( - "encoding/json" - "fmt" - "net/http" - "time" -) - -const ( - replicatedLicenseApiEndpoint = "http://kotsadm:3000/license/v1/license" - replicatedLicenseApiTimeout = 5 * time.Second -) - -type replicatedFields struct { - Field string `json:"field"` - Title string `json:"title"` - Type string `json:"type"` - Value interface{} `json:"value"` // This is of type "fieldType" -} - -// replicatedLicensePayload exists to convert the JSON structure to a LicensePayload -type replicatedLicensePayload struct { - LicenseID string `json:"license_id"` - InstallationID string `json:"installation_id"` - Assignee string `json:"assignee"` - ReleaseChannel string `json:"release_channel"` - LicenseType LicenseSubscriptionLevel `json:"license_type"` - ExpirationTime *time.Time `json:"expiration_time,omitempty"` // Not set if license never expires - Fields []replicatedFields `json:"fields"` -} - -type ReplicatedEvaluator struct { - invalid string - lic LicensePayload - plan LicenseSubscriptionLevel - allowFallback bool -} - -func (e *ReplicatedEvaluator) Enabled(feature Feature) bool { - if e.invalid != "" { - return false - } - - _, ok := e.lic.Level.allowance().Features[feature] - return ok -} - -func (e *ReplicatedEvaluator) HasEnoughSeats(seats int) bool { - if e.invalid != "" { - return false - } - - return e.lic.Seats == 0 || seats <= e.lic.Seats -} - -func (e *ReplicatedEvaluator) LicenseData() LicenseData { - data := LicenseData{ - Type: LicenseTypeReplicated, - Payload: e.Inspect(), - FallbackAllowed: e.allowFallback, - Plan: e.plan, - } - - return data -} - -func (e *ReplicatedEvaluator) Inspect() LicensePayload { - return e.lic -} - -func (e *ReplicatedEvaluator) Validate() (msg string, valid bool) { - if e.invalid == "" { - return "", true - } - - return e.invalid, false -} - -// defaultReplicatedLicense this is the default license if call fails -func defaultReplicatedLicense() *Evaluator { - - return &Evaluator{ - lic: defaultLicense, - allowFallback: true, - plan: LicenseTypeCommunity, - } -} - -// newReplicatedEvaluator exists to allow mocking of client -func newReplicatedEvaluator(client *http.Client) (res *Evaluator) { - resp, err := client.Get(replicatedLicenseApiEndpoint) - if err != nil { - return &Evaluator{invalid: fmt.Sprintf("cannot query kots admin, %q", err)} - } - defer resp.Body.Close() - - var replicatedPayload replicatedLicensePayload - err = json.NewDecoder(resp.Body).Decode(&replicatedPayload) - if err != nil { - return &Evaluator{invalid: fmt.Sprintf("cannot decode json data, %q", err)} - } - - lic := LicensePayload{ - ID: replicatedPayload.LicenseID, - Level: LevelEnterprise, - } - - // Search for the fields - for _, i := range replicatedPayload.Fields { - switch i.Field { - case "domain": - lic.Domain = i.Value.(string) - - case "seats": - lic.Seats = int(i.Value.(float64)) - - case "customerId": - lic.CustomerID = i.Value.(string) - } - } - - if replicatedPayload.ExpirationTime != nil { - lic.ValidUntil = *replicatedPayload.ExpirationTime - - if lic.ValidUntil.Before(time.Now()) { - return defaultReplicatedLicense() - } - } - - return &Evaluator{ - lic: lic, - allowFallback: replicatedPayload.LicenseType == LicenseTypeCommunity, // Only community licenses are allowed to fallback - plan: replicatedPayload.LicenseType, - } -} - -// NewReplicatedEvaluator gets the license data from the kots admin panel -func NewReplicatedEvaluator() (res *Evaluator) { - return newReplicatedEvaluator(&http.Client{Timeout: replicatedLicenseApiTimeout}) -} diff --git a/components/licensor/go.mod b/components/licensor/go.mod deleted file mode 100644 index bd34a5255b6c1c..00000000000000 --- a/components/licensor/go.mod +++ /dev/null @@ -1,17 +0,0 @@ -module github.com/gitpod-io/gitpod/licensor - -go 1.19 - -require ( - github.com/32leaves/bel v1.0.1 - github.com/sirupsen/logrus v1.8.1 - github.com/spf13/cobra v1.4.0 - golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 -) - -require ( - github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect -) diff --git a/components/licensor/go.sum b/components/licensor/go.sum deleted file mode 100644 index 400eed306b7c6c..00000000000000 --- a/components/licensor/go.sum +++ /dev/null @@ -1,30 +0,0 @@ -github.com/32leaves/bel v1.0.1 h1:AfZmt2haMfMQYM3eznUV40Ni3oc3tnrwtpI0KNx6K9g= -github.com/32leaves/bel v1.0.1/go.mod h1:Fx5kdWaCVbhMfvP4WWobqwyMUlUT4Z9BMBb54yA518U= -github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E= -github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= -github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7 h1:ux/56T2xqZO/3cP1I2F86qpeoYPCOzk+KF/UH/Ar+lk= -github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/components/licensor/main.go b/components/licensor/main.go deleted file mode 100644 index 3cdf5b32f8f556..00000000000000 --- a/components/licensor/main.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2020 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -// @deprecated - -package main - -import "github.com/gitpod-io/gitpod/licensor/ee/cmd" - -func main() { - cmd.Execute() -} diff --git a/components/licensor/typescript/.gitignore b/components/licensor/typescript/.gitignore deleted file mode 100644 index 79ba2909837fe0..00000000000000 --- a/components/licensor/typescript/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build -liblicensor.* diff --git a/components/licensor/typescript/BUILD.yaml b/components/licensor/typescript/BUILD.yaml deleted file mode 100644 index 738b32654108aa..00000000000000 --- a/components/licensor/typescript/BUILD.yaml +++ /dev/null @@ -1,22 +0,0 @@ -packages: - - name: lib - type: yarn - srcs: - - "ee/*.go" - - "ee/src/*" - - "*.gyp" - - package.json - deps: - - components/licensor:lib - prep: - - ["rm", "-f", "ee/lib/liblicensor.a"] - - ["go", "mod", "init", "licensor"] - - ["go", "mod", "edit", "-replace", "github.com/gitpod-io/gitpod/licensor=./components-licensor--lib"] - - ["yarn", "preinstall"] - config: - packaging: library - dontTest: true - commands: - build: ["yarn", "build"] - yarnLock: ${coreYarnLockBase}/../yarn.lock - tsconfig: tsconfig.json diff --git a/components/licensor/typescript/binding.gyp b/components/licensor/typescript/binding.gyp deleted file mode 100644 index 1eeafcda8d1c46..00000000000000 --- a/components/licensor/typescript/binding.gyp +++ /dev/null @@ -1,14 +0,0 @@ -{ - 'targets': [ - { - 'target_name': 'licensor', - # import all necessary source files - 'sources': [ - 'ee/lib/liblicensor.h', # this file was generated by go build - 'ee/src/module.cc' - ], - # libraries are relative to the 'build' directory - 'libraries': [ '../ee/lib/liblicensor.a' ] # this file was also generated by go build - } - ] -} \ No newline at end of file diff --git a/components/licensor/typescript/ee/genapi.go b/components/licensor/typescript/ee/genapi.go deleted file mode 100644 index 7523fcd65981cd..00000000000000 --- a/components/licensor/typescript/ee/genapi.go +++ /dev/null @@ -1,104 +0,0 @@ -//go:build genapi -// +build genapi - -package main - -import ( - "os" - "sort" - - "github.com/32leaves/bel" - - "github.com/gitpod-io/gitpod/licensor/ee/pkg/licensor" -) - -const ( - defaultSrcPath = "../../ee/pkg/licensor" - leewaySrcPath = "../components-licensor--lib/ee/pkg/licensor" - - preamble = `/** - * Copyright (c) 2022 Gitpod GmbH. All rights reserved. - * Licensed under the GNU Affero General Public License (AGPL). - * See License.AGPL.txt in the project root for license information. - */ - -// generated using github.com/32leaves/bel -// DO NOT MODIFY` -) - -func main() { - var pth = defaultSrcPath - if _, err := os.Stat(leewaySrcPath); err == nil { - pth = leewaySrcPath - } - - var res []bel.TypescriptType - handler, err := bel.NewParsedSourceEnumHandler(pth) - if err != nil { - panic(err) - } - ts, err := bel.Extract(struct { - Feature licensor.Feature - }{}, bel.WithEnumerations(handler)) - if err != nil { - panic(err) - } - for _, t := range ts { - if t.Name == "" { - continue - } - res = append(res, t) - } - - ts, err = bel.Extract(licensor.LicensePayload{}, bel.WithEnumerations(handler)) - if err != nil { - panic(err) - } - for _, t := range ts { - if t.Name == "" { - continue - } - - if t.Name == "LicensePayload" { - for i, m := range t.Members { - if m.Name == "validUntil" { - t.Members[i].Type.Name = "string" - } - } - } - - res = append(res, t) - } - - ts, err = bel.Extract(licensor.LicenseData{}, bel.WithEnumerations(handler)) - if err != nil { - panic(err) - } - for _, t := range ts { - if t.Name == "" { - continue - } - - if t.Name == "LicenseData" { - for i, m := range t.Members { - if m.Name == "payload" { - t.Members[i].Type.Name = "LicensePayload" - } - } - } - - res = append(res, t) - } - - sort.Slice(res, func(i, j int) bool { return res[i].Name < res[j].Name }) - - f, err := os.Create("src/api.ts") - if err != nil { - panic(err) - } - defer f.Close() - bel.Render(res, - bel.GenerateOutputTo(f), - bel.GeneratePreamble(preamble), - ) -} diff --git a/components/licensor/typescript/ee/main.go b/components/licensor/typescript/ee/main.go deleted file mode 100644 index 8ad053289c5c25..00000000000000 --- a/components/licensor/typescript/ee/main.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - -package main - -import ( - "C" - "encoding/json" - "os" - - log "github.com/sirupsen/logrus" - - "github.com/gitpod-io/gitpod/licensor/ee/pkg/licensor" -) - -var ( - instances map[int]*licensor.Evaluator = make(map[int]*licensor.Evaluator) - nextID int = 1 -) - -// Init initializes the global license evaluator from an environment variable -// -//export Init -func Init(key *C.char, domain *C.char) (id int) { - id = nextID - switch os.Getenv("GITPOD_LICENSE_TYPE") { - case string(licensor.LicenseTypeReplicated): - instances[id] = licensor.NewReplicatedEvaluator() - default: - instances[id] = licensor.NewGitpodEvaluator([]byte(C.GoString(key)), C.GoString(domain)) - } - nextID++ - - return id -} - -// GetLicenseData returns the info about license for the admin dashboard -// -//export GetLicenseData -func GetLicenseData(id int) (licData *C.char, ok bool) { - e, ok := instances[id] - if !ok { - return - } - - b, err := json.Marshal(e.LicenseData()) - if err != nil { - log.WithError(err).Warn("GetLicenseData(): cannot retrieve license data") - return nil, false - } - - return C.CString(string(b)), true - -} - -// Validate returns false if the license isn't valid and a message explaining why that is. -// -//export Validate -func Validate(id int) (msg *C.char, valid bool) { - e, ok := instances[id] - if !ok { - return C.CString("invalid instance ID"), false - } - - gmsg, valid := e.Validate() - return C.CString(gmsg), valid -} - -// Enabled returns true if a license enables a feature -// -//export Enabled -func Enabled(id int, feature *C.char, seats int) (enabled, ok bool) { - e, ok := instances[id] - if !ok { - return - } - - return e.Enabled(licensor.Feature(C.GoString(feature)), seats), true -} - -// HasEnoughSeats returns true if the license supports at least the given number of seats. -// -//export HasEnoughSeats -func HasEnoughSeats(id int, seats int) (permitted, ok bool) { - e, ok := instances[id] - if !ok { - return - } - - return e.HasEnoughSeats(seats), true -} - -// Inspect returns the license information this evaluator holds. -// -//export Inspect -func Inspect(id int) (lic *C.char, ok bool) { - e, ok := instances[id] - if !ok { - return - } - - b, err := json.Marshal(e.Inspect()) - if err != nil { - log.WithError(err).Warn("Inspect(): cannot marshal license payload") - return nil, false - } - - return C.CString(string(b)), true -} - -// Dispose removes/disposes an instance formerly created using Init. If the id does not exist, nothing happens. -// -//export Dispose -func Dispose(id int) { - delete(instances, id) -} - -// required to build -func main() {} diff --git a/components/licensor/typescript/ee/src/api.ts b/components/licensor/typescript/ee/src/api.ts deleted file mode 100644 index 6a06ca3a57dc64..00000000000000 --- a/components/licensor/typescript/ee/src/api.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2022 Gitpod GmbH. All rights reserved. - * Licensed under the GNU Affero General Public License (AGPL). - * See License.AGPL.txt in the project root for license information. - */ -// generated using github.com/32leaves/bel -// DO NOT MODIFY -export enum Feature { - FeatureAdminDashboard = "admin-dashboard", - FeaturePrebuild = "prebuild", - FeatureSetTimeout = "set-timeout", - FeatureSnapshot = "snapshot", - FeatureWorkspaceSharing = "workspace-sharing", -} -export interface LicenseData { - type: LicenseType - payload: LicensePayload - plan: LicenseSubscriptionLevel - fallbackAllowed: boolean -} - -export enum LicenseLevel { - LevelTeam = 0, - LevelEnterprise = 1, -} -export interface LicensePayload { - id: string - domain: string - level: LicenseLevel - validUntil: string - seats: number - customerID?: string -} - -export enum LicenseSubscriptionLevel { - LicenseTypeCommunity = "community", - LicenseTypePaid = "prod", - LicenseTypeTrial = "trial", - LicenseTypeDevelopment = "dev", -} -export enum LicenseType { - LicenseTypeGitpod = "gitpod", - LicenseTypeReplicated = "replicated", -} diff --git a/components/licensor/typescript/ee/src/index.ts b/components/licensor/typescript/ee/src/index.ts deleted file mode 100644 index 3c5f2696b66f6f..00000000000000 --- a/components/licensor/typescript/ee/src/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2022 Gitpod GmbH. All rights reserved. - * Licensed under the GNU Affero General Public License (AGPL). - * See License.AGPL.txt in the project root for license information. - */ - -import { injectable, inject, postConstruct } from "inversify"; -import { init, Instance, dispose, isEnabled, hasEnoughSeats, inspect, validate, getLicenseData } from "./nativemodule"; -import { Feature, LicensePayload, LicenseData } from "./api"; - -export const LicenseKeySource = Symbol("LicenseKeySource"); - -export interface LicenseKeySource { - // getKey returns a license key - getKey(): Promise<{ - key: string; - domain: string; - }>; -} - -@injectable() -export class LicenseEvaluator { - @inject(LicenseKeySource) protected keySource: LicenseKeySource; - protected instanceID: Instance; - - @postConstruct() - protected async init() { - const { key, domain } = await this.keySource.getKey(); - this.instanceID = init(key, domain); - - const { msg, valid } = validate(this.instanceID); - if (!valid) { - console.error(`invalid license: falling back to default`, { domain, msg }); - } else { - console.log("enterprise license accepted", this.inspect()); - } - } - - public async reloadLicense() { - this.dispose(); - await this.init(); - } - - public validate(): { msg?: string; valid: boolean } { - const v = validate(this.instanceID); - if (v.valid) { - return { valid: true }; - } - return { msg: v.msg, valid: false }; - } - - public isEnabled(feature: Feature, seats: number): boolean { - return isEnabled(this.instanceID, feature, seats); - } - - public hasEnoughSeats(seats: number): boolean { - return hasEnoughSeats(this.instanceID, seats); - } - - public inspect(): LicensePayload { - return JSON.parse(inspect(this.instanceID)); - } - - public getLicenseData(): LicenseData { - return JSON.parse(getLicenseData(this.instanceID)); - } - - public dispose() { - dispose(this.instanceID); - } -} diff --git a/components/licensor/typescript/ee/src/module.cc b/components/licensor/typescript/ee/src/module.cc deleted file mode 100644 index efa9e6ffb75b83..00000000000000 --- a/components/licensor/typescript/ee/src/module.cc +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (c) 2022 Gitpod GmbH. All rights reserved. -// Licensed under the GNU Affero General Public License (AGPL). -// See License.AGPL.txt in the project root for license information. - - - -#include -// include the header file generated from the Go build -#include "../lib/liblicensor.h" - -using v8::Number; -using v8::FunctionCallbackInfo; -using v8::Isolate; -using v8::Context; -using v8::Local; -using v8::Object; -using v8::Value; -using v8::Exception; -using v8::String; -using v8::Boolean; -using v8::NewStringType; -using v8::PropertyAttribute; - -// Extracts a C string from a V8 Utf8Value. -const char* ToCString(const v8::String::Utf8Value& value) { - return *value ? *value : ""; -} - -void InitM(const FunctionCallbackInfo &args) { - Isolate *isolate = args.GetIsolate(); - - if (args.Length() < 2) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "wrong number of arguments").ToLocalChecked())); - return; - } - - if (!args[0]->IsString()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 0 must be a string").ToLocalChecked())); - return; - } - if (!args[1]->IsString() || args[1]->IsUndefined() || args[1]->IsNull()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 1 must be a string").ToLocalChecked())); - return; - } - - String::Utf8Value nkey(args.GetIsolate(), args[0]); - const char* ckey = ToCString(nkey); - char* key = const_cast(ckey); - - String::Utf8Value ndomain(args.GetIsolate(), args[1]); - if (ndomain.length() == 0) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "domain must not be empty").ToLocalChecked())); - return; - } - const char* cdomain = ToCString(ndomain); - char* domain = const_cast(cdomain); - - // Call exported Go function, which returns a C string - GoInt id = Init(key, domain); - - // return the value - args.GetReturnValue().Set(Number::New(isolate, id)); -} - -void ValidateM(const FunctionCallbackInfo &args) { - Isolate *isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - if (args.Length() < 1) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "wrong number of arguments").ToLocalChecked())); - return; - } - if (!args[0]->IsNumber() || args[0]->IsUndefined()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 0 must be a number").ToLocalChecked())); - return; - } - - double rid = args[0]->NumberValue(context).FromMaybe(0); - int id = static_cast(rid); - - Validate_return r = Validate(id); - - Local obj = Object::New(isolate); - obj->Set(context, - String::NewFromUtf8(isolate, "valid", NewStringType::kNormal).ToLocalChecked(), - Boolean::New(isolate, r.r1) - ).FromJust(); - obj->Set(context, - String::NewFromUtf8(isolate, "msg", NewStringType::kNormal).ToLocalChecked(), - String::NewFromUtf8(isolate, r.r0).ToLocalChecked() - ).FromJust(); - - args.GetReturnValue().Set(obj); -} - -void EnabledM(const FunctionCallbackInfo &args) { - Isolate *isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - if (args.Length() < 3) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "wrong number of arguments").ToLocalChecked())); - return; - } - - if (!args[0]->IsNumber() || args[0]->IsUndefined()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 0 must be a number").ToLocalChecked())); - return; - } - if (!args[1]->IsString() || args[1]->IsUndefined()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 1 must be a string").ToLocalChecked())); - return; - } - if (!args[2]->IsNumber() || args[2]->IsUndefined()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 2 must be a number").ToLocalChecked())); - return; - } - - double rid = args[0]->NumberValue(context).FromMaybe(0); - int id = static_cast(rid); - - String::Utf8Value str(args.GetIsolate(), args[1]); - const char* cstr = ToCString(str); - char* featurestr = const_cast(cstr); - - double rseats = args[2]->NumberValue(context).FromMaybe(-1); - int seats = static_cast(rseats); - if (seats < 0) { - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "cannot convert number of seats").ToLocalChecked())); - return; - } - - // Call exported Go function, which returns a C string - Enabled_return r = Enabled(id, featurestr, seats); - - if (!r.r1) { - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "invalid instance ID").ToLocalChecked())); - return; - } - - // return the value - args.GetReturnValue().Set(Boolean::New(isolate, r.r0)); -} - -void HasEnoughSeatsM(const FunctionCallbackInfo &args) { - Isolate *isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - if (args.Length() < 2) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "wrong number of arguments").ToLocalChecked())); - return; - } - if (!args[0]->IsNumber() || args[0]->IsUndefined()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 0 must be a number").ToLocalChecked())); - return; - } - if (!args[1]->IsNumber() || args[1]->IsUndefined()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 1 must be a number").ToLocalChecked())); - return; - } - - double rid = args[0]->NumberValue(context).FromMaybe(0); - int id = static_cast(rid); - double rseats = args[1]->NumberValue(context).FromMaybe(-1); - int seats = static_cast(rseats); - if (seats < 0) { - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "cannot convert number of seats").ToLocalChecked())); - return; - } - - HasEnoughSeats_return r = HasEnoughSeats(id, seats); - - if (!r.r1) { - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "invalid instance ID").ToLocalChecked())); - return; - } - - // return the value - args.GetReturnValue().Set(Boolean::New(isolate, r.r0)); -} - -void GetLicenseDataM(const FunctionCallbackInfo &args) { - Isolate *isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - if (args.Length() < 1) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "wrong number of arguments").ToLocalChecked())); - return; - } - if (!args[0]->IsNumber() || args[0]->IsUndefined()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 0 must be a number").ToLocalChecked())); - return; - } - - double rid = args[0]->NumberValue(context).FromMaybe(0); - int id = static_cast(rid); - - GetLicenseData_return r = GetLicenseData(id); - if (!r.r1) { - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "invalid instance ID").ToLocalChecked())); - return; - } - - args.GetReturnValue().Set(String::NewFromUtf8(isolate, r.r0).ToLocalChecked()); -} - -void InspectM(const FunctionCallbackInfo &args) { - Isolate *isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - if (args.Length() < 1) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "wrong number of arguments").ToLocalChecked())); - return; - } - if (!args[0]->IsNumber() || args[0]->IsUndefined()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 0 must be a number").ToLocalChecked())); - return; - } - - double rid = args[0]->NumberValue(context).FromMaybe(0); - int id = static_cast(rid); - - Inspect_return r = Inspect(id); - if (!r.r1) { - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "invalid instance ID").ToLocalChecked())); - return; - } - - args.GetReturnValue().Set(String::NewFromUtf8(isolate, r.r0).ToLocalChecked()); -} - -void DisposeM(const FunctionCallbackInfo &args) { - Isolate *isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - if (args.Length() < 1) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "wrong number of arguments").ToLocalChecked())); - return; - } - if (!args[0]->IsNumber() || args[0]->IsUndefined()) { - isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 0 must be a number").ToLocalChecked())); - return; - } - - double rid = args[0]->NumberValue(context).FromMaybe(0); - int id = static_cast(rid); - - Dispose(id); -} - -// add method to exports -void initModule(Local exports) { - NODE_SET_METHOD(exports, "init", InitM); - NODE_SET_METHOD(exports, "validate", ValidateM); - NODE_SET_METHOD(exports, "isEnabled", EnabledM); - NODE_SET_METHOD(exports, "hasEnoughSeats", HasEnoughSeatsM); - NODE_SET_METHOD(exports, "inspect", InspectM); - NODE_SET_METHOD(exports, "dispose", DisposeM); - NODE_SET_METHOD(exports, "getLicenseData", GetLicenseDataM); -} - -// create module -NODE_MODULE(licensor, initModule) diff --git a/components/licensor/typescript/ee/src/nativemodule.d.ts b/components/licensor/typescript/ee/src/nativemodule.d.ts deleted file mode 100644 index 33decba2c1fb4a..00000000000000 --- a/components/licensor/typescript/ee/src/nativemodule.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) 2022 Gitpod GmbH. All rights reserved. - * Licensed under the GNU Affero General Public License (AGPL). - * See License.AGPL.txt in the project root for license information. - */ - -export type Instance = number; - -export function init(key: string, domain: string): Instance; -export function validate(id: Instance): { msg: string; valid: boolean }; -export function isEnabled(id: Instance, feature: Feature, seats: int): boolean; -export function hasEnoughSeats(id: Instance, seats: int): boolean; -export function inspect(id: Instance): string; -export function dispose(id: Instance); -export function getLicenseData(id: Instance): string; diff --git a/components/licensor/typescript/ee/src/nativemodule.js b/components/licensor/typescript/ee/src/nativemodule.js deleted file mode 100644 index fb7a379badbaa0..00000000000000 --- a/components/licensor/typescript/ee/src/nativemodule.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) 2022 Gitpod GmbH. All rights reserved. - * Licensed under the GNU Affero General Public License (AGPL). - * See License.AGPL.txt in the project root for license information. - */ - -module.exports = require("../build/Release/licensor"); diff --git a/components/licensor/typescript/package.json b/components/licensor/typescript/package.json deleted file mode 100644 index aef430f4f31cc6..00000000000000 --- a/components/licensor/typescript/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "private": true, - "name": "@gitpod/licensor", - "version": "0.1.5", - "license": "UNLICENSED", - "files": [ - "build", - "ee/lib", - "lib" - ], - "scripts": { - "preinstall": "if [ ! -e ee/lib/liblicensor.a ]; then cd ee && go mod tidy -compat=1.19 -v && go build -buildmode=c-archive -o lib/liblicensor.a && go run genapi.go; fi", - "build": "yarn preinstall && node-gyp configure build && tsc && cp -f ee/src/*.js lib", - "watch": "leeway exec --package .:lib --transitive-dependencies --filter-type yarn --components --parallel -- tsc -w --preserveWatchOutput" - }, - "devDependencies": { - "node-gyp": "^8.3.0", - "typescript": "~4.4.2" - }, - "typings": "./src/index.d.ts", - "dependencies": { - "inversify": "^5.0.1" - } -} diff --git a/components/licensor/typescript/tsconfig.json b/components/licensor/typescript/tsconfig.json deleted file mode 100644 index 2b1783309a7854..00000000000000 --- a/components/licensor/typescript/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "compilerOptions": { - "rootDir": "ee/src", - "experimentalDecorators": true, - "outDir": "lib", - "lib": [ - "es6", - "esnext.asynciterable", - "DOM" - ], - "strict": true, - "noEmitOnError": false, - "noUnusedLocals": true, - "emitDecoratorMetadata": true, - "strictPropertyInitialization": false, - "downlevelIteration": true, - "module": "commonjs", - "moduleResolution": "node", - "target": "es6", - "jsx": "react", - "sourceMap": true, - "declaration": true, - "declarationMap": true, - "skipLibCheck": true, - "useUnknownInCatchVariables": false - }, - "include": [ - "ee/src" - ] -} \ No newline at end of file diff --git a/components/server/BUILD.yaml b/components/server/BUILD.yaml index e551c5652e830c..6e289607a959ea 100644 --- a/components/server/BUILD.yaml +++ b/components/server/BUILD.yaml @@ -14,7 +14,6 @@ packages: - components/ee/payment-endpoint:lib - components/gitpod-protocol:lib - components/image-builder-api/typescript:lib - - components/licensor/typescript:lib - components/ws-manager-api/typescript:lib - components/supervisor-api/typescript-grpcweb:lib - components/usage-api/typescript:lib @@ -54,7 +53,6 @@ packages: - components/ee/payment-endpoint:lib - components/gitpod-protocol:lib - components/image-builder-api/typescript:lib - - components/licensor/typescript:lib - components/ws-manager-api/typescript:lib - components/supervisor-api/typescript-grpcweb:lib - components/usage-api/typescript:lib @@ -81,7 +79,6 @@ packages: - components/ee/payment-endpoint:lib - components/gitpod-protocol:lib - components/image-builder-api/typescript:lib - - components/licensor/typescript:lib - components/ws-manager-api/typescript:lib - components/supervisor-api/typescript-grpcweb:lib - components/usage-api/typescript:lib diff --git a/components/server/ee/src/billing/entitlement-service-license.ts b/components/server/ee/src/billing/entitlement-service-license.ts index 7d13eea086e052..05dda29524a583 100644 --- a/components/server/ee/src/billing/entitlement-service-license.ts +++ b/components/server/ee/src/billing/entitlement-service-license.ts @@ -11,10 +11,7 @@ import { WorkspaceInstance, WorkspaceTimeoutDuration, WORKSPACE_TIMEOUT_DEFAULT_LONG, - WORKSPACE_TIMEOUT_DEFAULT_SHORT, } from "@gitpod/gitpod-protocol"; -import { LicenseEvaluator } from "@gitpod/licensor/lib"; -import { Feature } from "@gitpod/licensor/lib/api"; import { inject, injectable } from "inversify"; import { EntitlementService, MayStartWorkspaceResult } from "../../../src/billing/entitlement-service"; import { Config } from "../../../src/config"; @@ -23,7 +20,6 @@ import { Config } from "../../../src/config"; export class EntitlementServiceLicense implements EntitlementService { @inject(Config) protected readonly config: Config; @inject(UserDB) protected readonly userDb: UserDB; - @inject(LicenseEvaluator) protected readonly licenseEvaluator: LicenseEvaluator; async mayStartWorkspace( user: User, @@ -41,13 +37,6 @@ export class EntitlementServiceLicense implements EntitlementService { } async getDefaultWorkspaceTimeout(user: User, date: Date): Promise { - const userCount = await this.userDb.getUserCount(true); - - // the self-hosted case - if (!this.licenseEvaluator.isEnabled(Feature.FeatureSetTimeout, userCount)) { - return WORKSPACE_TIMEOUT_DEFAULT_SHORT; - } - return WORKSPACE_TIMEOUT_DEFAULT_LONG; } diff --git a/components/server/ee/src/container-module.ts b/components/server/ee/src/container-module.ts index e7394e647c9cd5..537866be5d4a1c 100644 --- a/components/server/ee/src/container-module.ts +++ b/components/server/ee/src/container-module.ts @@ -9,8 +9,6 @@ import { GitpodServerImpl } from "../../src/workspace/gitpod-server-impl"; import { GitpodServerEEImpl } from "./workspace/gitpod-server-impl"; import { Server } from "../../src/server"; import { ServerEE } from "./server"; -import { LicenseKeySource } from "@gitpod/licensor/lib"; -import { DBLicenseKeySource } from "./license-source"; import { UserService } from "../../src/user/user-service"; import { UserServiceEE } from "./user/user-service"; import { HostContainerMapping } from "../../src/auth/host-container-mapping"; @@ -87,8 +85,6 @@ export const productionEEContainerModule = new ContainerModule((bind, unbind, is bind(UserCounter).toSelf().inSingletonScope(); - bind(LicenseKeySource).to(DBLicenseKeySource).inSingletonScope(); - // GitpodServerImpl (stateful per user) rebind(GitpodServerImpl).to(GitpodServerEEImpl).inRequestScope(); bind(EligibilityService).toSelf().inRequestScope(); diff --git a/components/server/ee/src/license-source.ts b/components/server/ee/src/license-source.ts deleted file mode 100644 index 432811fda4da09..00000000000000 --- a/components/server/ee/src/license-source.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2022 Gitpod GmbH. All rights reserved. - * Licensed under the GNU Affero General Public License (AGPL). - * See License.AGPL.txt in the project root for license information. - */ - -import { LicenseKeySource } from "@gitpod/licensor/lib"; -import { inject, injectable } from "inversify"; -import { LicenseDB } from "@gitpod/gitpod-db/lib"; -import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { Config } from "../../src/config"; - -@injectable() -export class DBLicenseKeySource implements LicenseKeySource { - @inject(Config) protected readonly config: Config; - @inject(LicenseDB) protected readonly licenseDB: LicenseDB; - - async getKey(): Promise<{ key: string; domain: string }> { - let key: string = ""; - try { - key = (await this.licenseDB.get()) || ""; - } catch (err) { - log.error("cannot get license key - even if you have a license, the EE features won't work", err); - } - return { - key: key || this.config.license || "", - domain: this.config.hostUrl.url.host, - }; - } -} diff --git a/components/server/ee/src/user/user-service.ts b/components/server/ee/src/user/user-service.ts index 6ede4c87f10d82..419cedbd9647a0 100644 --- a/components/server/ee/src/user/user-service.ts +++ b/components/server/ee/src/user/user-service.ts @@ -7,31 +7,18 @@ import { UserService, CheckSignUpParams, CheckTermsParams } from "../../../src/user/user-service"; import { User } from "@gitpod/gitpod-protocol"; import { inject } from "inversify"; -import { LicenseEvaluator } from "@gitpod/licensor/lib"; -import { AuthException } from "../../../src/auth/errors"; import { SubscriptionService } from "@gitpod/gitpod-payment-endpoint/lib/accounting"; import { OssAllowListDB } from "@gitpod/gitpod-db/lib/oss-allowlist-db"; import { HostContextProvider } from "../../../src/auth/host-context-provider"; import { Config } from "../../../src/config"; export class UserServiceEE extends UserService { - @inject(LicenseEvaluator) protected readonly licenseEvaluator: LicenseEvaluator; @inject(SubscriptionService) protected readonly subscriptionService: SubscriptionService; @inject(OssAllowListDB) protected readonly OssAllowListDb: OssAllowListDB; @inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider; @inject(Config) protected readonly config: Config; async checkSignUp(params: CheckSignUpParams) { - // todo@at: check if we need an optimization for SaaS here. used to be a no-op there. - - // 1. check the license - const userCount = await this.userDb.getUserCount(true); - if (!this.licenseEvaluator.hasEnoughSeats(userCount)) { - const msg = `Maximum number of users permitted by the license exceeded`; - throw AuthException.create("Cannot sign up", msg, { userCount, params }); - } - - // 2. check defaults await super.checkSignUp(params); } diff --git a/components/server/ee/src/workspace/gitpod-server-impl.ts b/components/server/ee/src/workspace/gitpod-server-impl.ts index 9adfd1e8461cc5..343fb326c267f0 100644 --- a/components/server/ee/src/workspace/gitpod-server-impl.ts +++ b/components/server/ee/src/workspace/gitpod-server-impl.ts @@ -60,9 +60,7 @@ import { import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; import { v4 as uuidv4 } from "uuid"; import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { LicenseKeySource } from "@gitpod/licensor/lib"; -import { Feature } from "@gitpod/licensor/lib/api"; -import { LicenseValidationResult, LicenseFeature } from "@gitpod/gitpod-protocol/lib/license-protocol"; +import { LicenseValidationResult } from "@gitpod/gitpod-protocol/lib/license-protocol"; import { PrebuildManager } from "../prebuilds/prebuild-manager"; import { LicenseDB } from "@gitpod/gitpod-db/lib"; import { GuardedCostCenter, ResourceAccessGuard, ResourceAccessOp } from "../../../src/auth/resource-access"; @@ -131,7 +129,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { @inject(IncrementalPrebuildsService) protected readonly incrementalPrebuildsService: IncrementalPrebuildsService; @inject(ConfigProvider) protected readonly configProvider: ConfigProvider; @inject(LicenseDB) protected readonly licenseDB: LicenseDB; - @inject(LicenseKeySource) protected readonly licenseKeySource: LicenseKeySource; // per-user state @inject(EligibilityService) protected readonly eligibilityService: EligibilityService; @@ -334,38 +331,7 @@ export class GitpodServerEEImpl extends GitpodServerImpl { } } - protected async requireEELicense(feature: Feature) { - const cachedUserCount = this.userCounter.count; - - let userCount: number; - if (cachedUserCount === null) { - userCount = await this.userDB.getUserCount(true); - this.userCounter.count = userCount; - } else { - userCount = cachedUserCount; - } - - if (!this.licenseEvaluator.isEnabled(feature, userCount)) { - throw new ResponseError(ErrorCodes.EE_LICENSE_REQUIRED, "enterprise license required"); - } - } - async validateLicense(ctx: TraceContext): Promise { - const v = this.licenseEvaluator.validate(); - if (!v.valid) { - return v; - } - - const userCount = await this.userDB.getUserCount(true); - const canAnotherUserSignUp = this.licenseEvaluator.hasEnoughSeats(userCount + 1); - if (!canAnotherUserSignUp) { - return { - valid: true, - issue: "seats-exhausted", - msg: "maximum number of users reached", - }; - } - return { valid: true }; } @@ -409,7 +375,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { traceAPIParams(ctx, { workspaceId, duration }); traceWI(ctx, { workspaceId }); - await this.requireEELicense(Feature.FeatureSetTimeout); const user = this.checkUser("setWorkspaceTimeout"); if (!(await this.entitlementService.maySetTimeout(user, new Date()))) { @@ -448,9 +413,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { traceAPIParams(ctx, { workspaceId }); traceWI(ctx, { workspaceId }); - // Allowed in the free version, because it is read only. - // this.requireEELicense(Feature.FeatureSetTimeout); - const user = this.checkUser("getWorkspaceTimeout"); const canChange = await this.entitlementService.maySetTimeout(user, new Date()); @@ -477,9 +439,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { public async isPrebuildDone(ctx: TraceContext, pwsId: string): Promise { traceAPIParams(ctx, { pwsId }); - // Allowed in the free version, because it is read only. - // this.requireEELicense(Feature.FeaturePrebuild); - const pws = await this.workspaceDb.trace(ctx).findPrebuildByID(pwsId); if (!pws) { // there is no prebuild - that's as good one being done @@ -493,7 +452,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { traceAPIParams(ctx, { workspaceId, level }); traceWI(ctx, { workspaceId }); - await this.requireEELicense(Feature.FeatureWorkspaceSharing); this.checkAndBlockUser("controlAdmission"); const lvlmap = new Map(); @@ -529,7 +487,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { const { workspaceId, dontWait } = options; traceWI(ctx, { workspaceId }); - await this.requireEELicense(Feature.FeatureSnapshot); const user = this.checkAndBlockUser("takeSnapshot"); const workspace = await this.guardSnaphotAccess(ctx, user.id, workspaceId); @@ -584,7 +541,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async waitForSnapshot(ctx: TraceContext, snapshotId: string): Promise { traceAPIParams(ctx, { snapshotId }); - await this.requireEELicense(Feature.FeatureSnapshot); const user = this.checkAndBlockUser("waitForSnapshot"); const snapshot = await this.workspaceDb.trace(ctx).findSnapshotById(snapshotId); @@ -608,9 +564,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { traceAPIParams(ctx, { workspaceId }); traceWI(ctx, { workspaceId }); - // Allowed in the free version, because it is read only. - // this.requireEELicense(Feature.FeatureSnapshot); - const user = this.checkAndBlockUser("getSnapshots"); const workspace = await this.workspaceDb.trace(ctx).findById(workspaceId); @@ -627,8 +580,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminGetUsers(ctx: TraceContext, req: AdminGetListRequest): Promise> { traceAPIParams(ctx, { req: censor(req, "searchTerm") }); // searchTerm may contain PII - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminGetUsers", { req }, Permission.ADMIN_USERS); try { @@ -649,8 +600,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminGetUser(ctx: TraceContext, userId: string): Promise { traceAPIParams(ctx, { userId }); - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminGetUser", { id: userId }, Permission.ADMIN_USERS); let result: User | undefined; @@ -669,8 +618,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminBlockUser(ctx: TraceContext, req: AdminBlockUserRequest): Promise { traceAPIParams(ctx, { req }); - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminBlockUser", { req }, Permission.ADMIN_USERS); let targetUser; @@ -701,8 +648,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { } async adminVerifyUser(ctx: TraceContext, userId: string): Promise { - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminVerifyUser", { id: userId }, Permission.ADMIN_USERS); try { const user = await this.userDB.findUserById(userId); @@ -720,8 +665,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminDeleteUser(ctx: TraceContext, userId: string): Promise { traceAPIParams(ctx, { userId }); - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminDeleteUser", { id: userId }, Permission.ADMIN_USERS); try { @@ -736,7 +679,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { req: AdminGetListRequest, ): Promise> { traceAPIParams(ctx, { req: censor(req, "searchTerm") }); // searchTerm may contain PII - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminGetBlockedRepositories", { req }, Permission.ADMIN_USERS); @@ -760,7 +702,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { blockUser: boolean, ): Promise { traceAPIParams(ctx, { urlRegexp, blockUser }); - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminCreateBlockedRepository", { urlRegexp, blockUser }, Permission.ADMIN_USERS); @@ -769,7 +710,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminDeleteBlockedRepository(ctx: TraceContext, id: number): Promise { traceAPIParams(ctx, { id }); - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminDeleteBlockedRepository", { id }, Permission.ADMIN_USERS); @@ -779,8 +719,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminModifyRoleOrPermission(ctx: TraceContext, req: AdminModifyRoleOrPermissionRequest): Promise { traceAPIParams(ctx, { req }); - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminModifyRoleOrPermission", { req }, Permission.ADMIN_USERS); const target = await this.userDB.findUserById(req.id); @@ -811,8 +749,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { ): Promise { traceAPIParams(ctx, { req }); - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminModifyPermanentWorkspaceFeatureFlag", { req }, Permission.ADMIN_USERS); const target = await this.userDB.findUserById(req.id); if (!target) { @@ -839,7 +775,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { } async adminGetTeamMembers(ctx: TraceContext, teamId: string): Promise { - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminGetTeamMembers", { teamId }, Permission.ADMIN_WORKSPACES); const team = await this.teamDB.findTeamById(teamId); @@ -851,7 +786,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { } async adminGetTeams(ctx: TraceContext, req: AdminGetListRequest): Promise> { - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminGetTeams", { req }, Permission.ADMIN_WORKSPACES); return await this.teamDB.findTeams( @@ -864,7 +798,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { } async adminGetTeamById(ctx: TraceContext, id: string): Promise { - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminGetTeamById", { id }, Permission.ADMIN_WORKSPACES); return await this.teamDB.findTeamById(id); } @@ -875,7 +808,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { userId: string, role: TeamMemberRole, ): Promise { - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminSetTeamMemberRole", { teamId, userId, role }, Permission.ADMIN_WORKSPACES); return this.teamDB.setTeamMemberRole(userId, teamId, role); } @@ -886,8 +818,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { ): Promise> { traceAPIParams(ctx, { req }); - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminGetWorkspaces", { req }, Permission.ADMIN_WORKSPACES); return await this.workspaceDb @@ -904,8 +834,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminGetWorkspace(ctx: TraceContext, workspaceId: string): Promise { traceAPIParams(ctx, { workspaceId }); - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminGetWorkspace", { id: workspaceId }, Permission.ADMIN_WORKSPACES); const result = await this.workspaceDb.trace(ctx).findWorkspaceAndInstance(workspaceId); @@ -918,8 +846,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminForceStopWorkspace(ctx: TraceContext, workspaceId: string): Promise { traceAPIParams(ctx, { workspaceId }); - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess("adminForceStopWorkspace", { id: workspaceId }, Permission.ADMIN_WORKSPACES); const workspace = await this.workspaceDb.trace(ctx).findById(workspaceId); @@ -931,8 +857,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminRestoreSoftDeletedWorkspace(ctx: TraceContext, workspaceId: string): Promise { traceAPIParams(ctx, { workspaceId }); - await this.requireEELicense(Feature.FeatureAdminDashboard); - await this.guardAdminAccess( "adminRestoreSoftDeletedWorkspace", { id: workspaceId }, @@ -962,7 +886,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { ctx: TraceContext, req: AdminGetListRequest, ): Promise> { - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminGetProjectsBySearchTerm", { req }, Permission.ADMIN_PROJECTS); return await this.projectDB.findProjectsBySearchTerm( req.offset, @@ -974,7 +897,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { } async adminGetProjectById(ctx: TraceContext, id: string): Promise { - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminGetProjectById", { id }, Permission.ADMIN_PROJECTS); return await this.projectDB.findProjectById(id); } @@ -1174,26 +1096,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { await this.guardAdminAccess("adminGetWorkspaces", { key }, Permission.ADMIN_API); await this.licenseDB.store(uuidv4(), key); - await this.licenseEvaluator.reloadLicense(); - } - - async licenseIncludesFeature(ctx: TraceContext, licenseFeature: LicenseFeature): Promise { - traceAPIParams(ctx, { licenseFeature }); - - this.checkAndBlockUser("licenseIncludesFeature"); - - let feature: Feature | undefined; - switch (licenseFeature) { - case LicenseFeature.CreateSnapshot: - feature = Feature.FeatureSnapshot; - // room for more - default: - } - if (feature) { - const userCount = await this.userDB.getUserCount(true); - return this.licenseEvaluator.isEnabled(feature, userCount); - } - return false; } // (SaaS) – accounting @@ -2999,7 +2901,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl { async adminFindPrebuilds(ctx: TraceContext, params: FindPrebuildsParams): Promise { traceAPIParams(ctx, { params }); - await this.requireEELicense(Feature.FeatureAdminDashboard); await this.guardAdminAccess("adminFindPrebuilds", { params }, Permission.ADMIN_PROJECTS); return this.projectsService.findPrebuilds(params); diff --git a/components/server/ee/src/workspace/workspace-factory.ts b/components/server/ee/src/workspace/workspace-factory.ts index 7011ecb9b37327..24ee5918c158b3 100644 --- a/components/server/ee/src/workspace/workspace-factory.ts +++ b/components/server/ee/src/workspace/workspace-factory.ts @@ -21,10 +21,6 @@ import { Project, } from "@gitpod/gitpod-protocol"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { LicenseEvaluator } from "@gitpod/licensor/lib"; -import { Feature } from "@gitpod/licensor/lib/api"; -import { ResponseError } from "vscode-jsonrpc"; -import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; import { HostContextProvider } from "../../../src/auth/host-context-provider"; import { UserDB } from "@gitpod/gitpod-db/lib"; import { UserCounter } from "../user/user-counter"; @@ -35,7 +31,6 @@ import { IncrementalPrebuildsService } from "../prebuilds/incremental-prebuilds- @injectable() export class WorkspaceFactoryEE extends WorkspaceFactory { - @inject(LicenseEvaluator) protected readonly licenseEvaluator: LicenseEvaluator; @inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider; @inject(UserCounter) protected readonly userCounter: UserCounter; @inject(EntitlementService) protected readonly entitlementService: EntitlementService; @@ -43,22 +38,6 @@ export class WorkspaceFactoryEE extends WorkspaceFactory { @inject(UserDB) protected readonly userDB: UserDB; - protected async requireEELicense(feature: Feature) { - const cachedUserCount = this.userCounter.count; - - let userCount: number; - if (cachedUserCount === null) { - userCount = await this.userDB.getUserCount(true); - this.userCounter.count = userCount; - } else { - userCount = cachedUserCount; - } - - if (!this.licenseEvaluator.isEnabled(feature, userCount)) { - throw new ResponseError(ErrorCodes.EE_LICENSE_REQUIRED, "enterprise license required"); - } - } - public async createForContext( ctx: TraceContext, user: User, @@ -83,7 +62,6 @@ export class WorkspaceFactoryEE extends WorkspaceFactory { context: StartPrebuildContext, normalizedContextURL: string, ): Promise { - await this.requireEELicense(Feature.FeaturePrebuild); const span = TraceContext.startSpan("createForStartPrebuild", ctx); try { @@ -222,7 +200,6 @@ export class WorkspaceFactoryEE extends WorkspaceFactory { context: PrebuiltWorkspaceContext, normalizedContextURL: string, ): Promise { - await this.requireEELicense(Feature.FeaturePrebuild); const span = TraceContext.startSpan("createForPrebuiltWorkspace", ctx); try { diff --git a/components/server/package.json b/components/server/package.json index c3a7a1a2b1c2dc..33fad872be1d7f 100644 --- a/components/server/package.json +++ b/components/server/package.json @@ -36,7 +36,6 @@ "@gitpod/gitpod-protocol": "0.1.5", "@gitpod/ide-service-api": "0.1.5", "@gitpod/image-builder": "0.1.5", - "@gitpod/licensor": "0.1.5", "@gitpod/supervisor-api-grpcweb": "0.1.5", "@gitpod/usage-api": "0.1.5", "@gitpod/ws-manager": "0.1.5", diff --git a/components/server/src/auth/rate-limiter.ts b/components/server/src/auth/rate-limiter.ts index a4b96921c0de94..21a04676ebd3e3 100644 --- a/components/server/src/auth/rate-limiter.ts +++ b/components/server/src/auth/rate-limiter.ts @@ -174,7 +174,6 @@ const defaultFunctions: FunctionsConfig = { validateLicense: { group: "default", points: 1 }, getLicenseInfo: { group: "default", points: 1 }, - licenseIncludesFeature: { group: "default", points: 1 }, accessCodeSyncStorage: { group: "default", points: 1 }, diff --git a/components/server/src/container-module.ts b/components/server/src/container-module.ts index f827a79ff48e9d..be128d59011f2e 100644 --- a/components/server/src/container-module.ts +++ b/components/server/src/container-module.ts @@ -90,7 +90,6 @@ import { LocalMessageBroker, LocalRabbitMQBackedMessageBroker } from "./messagin import { ReferrerPrefixParser } from "./workspace/referrer-prefix-context-parser"; import { InstallationAdminTelemetryDataProvider } from "./installation-admin/telemetry-data-provider"; import { IDEService } from "./ide-service"; -import { LicenseEvaluator } from "@gitpod/licensor/lib"; import { WorkspaceClusterImagebuilderClientProvider } from "./workspace/workspace-cluster-imagebuilder-client-provider"; import { UsageServiceClient, UsageServiceDefinition } from "@gitpod/usage-api/lib/usage/v1/usage.pb"; import { BillingServiceClient, BillingServiceDefinition } from "@gitpod/usage-api/lib/usage/v1/billing.pb"; @@ -227,8 +226,6 @@ export const productionContainerModule = new ContainerModule((bind, unbind, isBo bind(InstallationAdminTelemetryDataProvider).toSelf().inSingletonScope(); - bind(LicenseEvaluator).toSelf().inSingletonScope(); - // binds all content services contentServiceBinder((ctx) => { const config = ctx.container.get(Config); diff --git a/components/server/src/installation-admin/telemetry-data-provider.ts b/components/server/src/installation-admin/telemetry-data-provider.ts index 5aac509a71fc04..6462f49cf89be6 100644 --- a/components/server/src/installation-admin/telemetry-data-provider.ts +++ b/components/server/src/installation-admin/telemetry-data-provider.ts @@ -9,14 +9,12 @@ import { injectable, inject } from "inversify"; import { TelemetryData } from "@gitpod/gitpod-protocol"; import * as opentracing from "opentracing"; import { InstallationAdminDB, UserDB, WorkspaceDB } from "@gitpod/gitpod-db/lib"; -import { LicenseEvaluator } from "@gitpod/licensor/lib"; @injectable() export class InstallationAdminTelemetryDataProvider { @inject(InstallationAdminDB) protected readonly installationAdminDb: InstallationAdminDB; @inject(UserDB) protected readonly userDb: UserDB; @inject(WorkspaceDB) protected readonly workspaceDb: WorkspaceDB; - @inject(LicenseEvaluator) protected readonly licenseEvaluator: LicenseEvaluator; async getTelemetryData(): Promise { const span = opentracing.globalTracer().startSpan("getTelemetryData"); @@ -26,13 +24,8 @@ export class InstallationAdminTelemetryDataProvider { totalUsers: await this.userDb.getUserCount(true), totalWorkspaces: await this.workspaceDb.getWorkspaceCount(), totalInstances: await this.workspaceDb.getInstanceCount(), - licenseType: this.licenseEvaluator.getLicenseData().type, } as TelemetryData; - if (data.installationAdmin.settings.sendCustomerID) { - data.customerID = this.licenseEvaluator.getLicenseData().payload.customerID; - } - return data; } finally { span.finish(); diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index 49553c50a9dc17..f422a37f74056e 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -93,7 +93,6 @@ import { } from "@gitpod/gitpod-protocol/lib/admin-protocol"; import { GetLicenseInfoResult, - LicenseFeature, LicenseInfo, LicenseValidationResult, } from "@gitpod/gitpod-protocol/lib/license-protocol"; @@ -172,8 +171,6 @@ import { import { InstallationAdminSettings, TelemetryData } from "@gitpod/gitpod-protocol"; import { Deferred } from "@gitpod/gitpod-protocol/lib/util/deferred"; import { InstallationAdminTelemetryDataProvider } from "../installation-admin/telemetry-data-provider"; -import { LicenseEvaluator } from "@gitpod/licensor/lib"; -import { Feature } from "@gitpod/licensor/lib/api"; import { ListUsageRequest, ListUsageResponse } from "@gitpod/gitpod-protocol/lib/usage"; import { VerificationService } from "../auth/verification-service"; import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode"; @@ -233,7 +230,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { @inject(InstallationAdminDB) protected readonly installationAdminDb: InstallationAdminDB; @inject(InstallationAdminTelemetryDataProvider) protected readonly telemetryDataProvider: InstallationAdminTelemetryDataProvider; - @inject(LicenseEvaluator) protected readonly licenseEvaluator: LicenseEvaluator; @inject(WorkspaceStarter) protected readonly workspaceStarter: WorkspaceStarter; @inject(WorkspaceManagerClientProvider) @@ -2934,46 +2930,13 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { await this.guardAdminAccess("adminGetLicense", {}, Permission.ADMIN_API); - const licenseData = this.licenseEvaluator.getLicenseData(); - const licensePayload = licenseData.payload; - const licenseValid = this.licenseEvaluator.validate(); - const userCount = await this.userDB.getUserCount(true); - const features = Object.keys(Feature); - const enabledFeatures = await this.licenseFeatures(ctx, features, userCount); - return { - key: licensePayload.id, - seats: licensePayload.seats, userCount: userCount, - plan: licenseData.plan, - fallbackAllowed: licenseData.fallbackAllowed, - valid: licenseValid.valid, - errorMsg: licenseValid.msg, - type: licenseData.type, - validUntil: licensePayload.validUntil, - features: features.map((feat) => Feature[feat as keyof typeof Feature]), - enabledFeatures: enabledFeatures, }; } - protected async licenseFeatures(ctx: TraceContext, features: string[], userCount: number): Promise { - var enabledFeatures: string[] = []; - for (const feature of features) { - const featureName: Feature = Feature[feature as keyof typeof Feature]; - if (this.licenseEvaluator.isEnabled(featureName, userCount)) { - enabledFeatures.push(featureName); - } - } - - return enabledFeatures; - } - - async licenseIncludesFeature(ctx: TraceContext, feature: LicenseFeature): Promise { - return false; - } - protected censorUser(user: User): User { const res = { ...user }; delete res.additionalData; diff --git a/gitpod-ws.code-workspace b/gitpod-ws.code-workspace index 666e5df2b04893..aa07598bc24715 100644 --- a/gitpod-ws.code-workspace +++ b/gitpod-ws.code-workspace @@ -12,7 +12,6 @@ { "path": "components/ide-service" }, { "path": "components/image-builder-bob" }, { "path": "components/image-builder-mk3" }, - { "path": "components/licensor" }, { "path": "components/local-app" }, { "path": "components/registry-facade" }, { "path": "components/service-waiter" }, diff --git a/install/installer/pkg/components/server/configmap.go b/install/installer/pkg/components/server/configmap.go index 71eaceca061baf..1c08ff9a340c71 100644 --- a/install/installer/pkg/components/server/configmap.go +++ b/install/installer/pkg/components/server/configmap.go @@ -36,11 +36,6 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { return nil }) - license := "" - if ctx.Config.License != nil { - license = licenseFilePath - } - workspaceImage := ctx.Config.Workspace.WorkspaceImage if workspaceImage == "" { workspaceImage = ctx.ImageName(common.ThirdPartyContainerRepo(ctx.Config.Repository, ""), workspace.DefaultWorkspaceImage, workspace.DefaultWorkspaceImageVersion) @@ -208,7 +203,6 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { Version: ctx.VersionManifest.Version, HostURL: fmt.Sprintf("https://%s", ctx.Config.Domain), InstallationShortname: ctx.Config.Metadata.InstallationShortname, - LicenseFile: license, WorkspaceHeartbeat: WorkspaceHeartbeat{ IntervalSeconds: 60, TimeoutSeconds: 300, diff --git a/install/installer/pkg/components/server/deployment.go b/install/installer/pkg/components/server/deployment.go index f6415f54782c19..71b1ade4f94d46 100644 --- a/install/installer/pkg/components/server/deployment.go +++ b/install/installer/pkg/components/server/deployment.go @@ -19,7 +19,6 @@ import ( "github.com/gitpod-io/gitpod/installer/pkg/common" wsmanagerbridge "github.com/gitpod-io/gitpod/installer/pkg/components/ws-manager-bridge" - configv1 "github.com/gitpod-io/gitpod/installer/pkg/config/v1" "github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental" appsv1 "k8s.io/api/apps/v1" @@ -90,25 +89,6 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) { Name: "CONFIG_PATH", Value: "/config/config.json", }, - func() corev1.EnvVar { - envvar := corev1.EnvVar{ - Name: "GITPOD_LICENSE_TYPE", - } - - if ctx.Config.License == nil { - envvar.Value = string(configv1.LicensorTypeGitpod) - } else { - envvar.ValueFrom = &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{Name: ctx.Config.License.Name}, - Key: "type", - Optional: pointer.Bool(true), - }, - } - } - - return envvar - }(), { Name: "NODE_ENV", Value: "production", // todo(sje): will we need to change this? @@ -140,22 +120,6 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) { volumes := make([]corev1.Volume, 0) volumeMounts := make([]corev1.VolumeMount, 0) - if ctx.Config.License != nil { - volumes = append(volumes, corev1.Volume{ - Name: "gitpod-license-key", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: ctx.Config.License.Name, - }, - }, - }) - - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: "gitpod-license-key", - MountPath: licenseFilePath, - SubPath: "license", - }) - } if len(ctx.Config.AuthProviders) > 0 { for i, provider := range ctx.Config.AuthProviders { diff --git a/install/installer/pkg/components/server/types.go b/install/installer/pkg/components/server/types.go index 09b20df66f17e4..ab1ae93231e911 100644 --- a/install/installer/pkg/components/server/types.go +++ b/install/installer/pkg/components/server/types.go @@ -19,7 +19,6 @@ type ConfigSerialized struct { DevBranch string `json:"devBranch"` InsecureNoDomain bool `json:"insecureNoDomain"` License string `json:"license"` - LicenseFile string `json:"licenseFile"` DefinitelyGpDisabled bool `json:"definitelyGpDisabled"` EnableLocalApp bool `json:"enableLocalApp"` DisableDynamicAuthProviderLogin bool `json:"disableDynamicAuthProviderLogin"` diff --git a/install/installer/pkg/config/v1/config.go b/install/installer/pkg/config/v1/config.go index afcec5346d9847..e161833b1b02ad 100644 --- a/install/installer/pkg/config/v1/config.go +++ b/install/installer/pkg/config/v1/config.go @@ -145,7 +145,6 @@ type Config struct { AuthProviders []ObjectRef `json:"authProviders" validate:"dive"` BlockNewUsers BlockNewUsers `json:"blockNewUsers"` - License *ObjectRef `json:"license,omitempty"` SSHGatewayHostKey *ObjectRef `json:"sshGatewayHostKey,omitempty"` @@ -388,13 +387,6 @@ type Proxy struct { ServiceAnnotations ServiceAnnotations `json:"serviceAnnotations"` } -type LicensorType string - -const ( - LicensorTypeGitpod LicensorType = "gitpod" - LicensorTypeReplicated LicensorType = "replicated" -) - type FSShiftMethod string const ( diff --git a/install/installer/pkg/config/v1/validation.go b/install/installer/pkg/config/v1/validation.go index e2be040740e2a9..818a65a9ee4c74 100644 --- a/install/installer/pkg/config/v1/validation.go +++ b/install/installer/pkg/config/v1/validation.go @@ -46,11 +46,6 @@ var FSShiftMethodList = map[FSShiftMethod]struct{}{ FSShiftShiftFS: {}, } -var LicensorTypeList = map[LicensorType]struct{}{ - LicensorTypeGitpod: {}, - LicensorTypeReplicated: {}, -} - // LoadValidationFuncs load custom validation functions for this version of the config API func (v version) LoadValidationFuncs(validate *validator.Validate) error { funcs := map[string]validator.Func{ @@ -171,29 +166,6 @@ func (v version) ClusterValidation(rcfg interface{}) cluster.ValidationChecks { res = append(res, cluster.CheckSecret(secretName, cluster.CheckSecretRequiredData("ca.crt"))) } - if cfg.License != nil { - secretName := cfg.License.Name - licensorKey := "type" - res = append(res, cluster.CheckSecret(secretName, cluster.CheckSecretRequiredData("license"), cluster.CheckSecretRecommendedData(licensorKey), cluster.CheckSecretRule(func(s *corev1.Secret) ([]cluster.ValidationError, error) { - errors := make([]cluster.ValidationError, 0) - - licensor := LicensorType(s.Data[licensorKey]) - if licensor != "" { - // This field is optional, so blank is valid - _, ok := LicensorTypeList[licensor] - - if !ok { - errors = append(errors, cluster.ValidationError{ - Message: fmt.Sprintf("Secret '%s' has invalid license type '%s'", secretName, licensor), - Type: cluster.ValidationStatusError, - }) - } - } - - return errors, nil - }))) - } - if len(cfg.AuthProviders) > 0 { for _, provider := range cfg.AuthProviders { secretName := provider.Name diff --git a/yarn.lock b/yarn.lock index 9a9b4c03db2d2b..bfd4789685eff5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3989,22 +3989,13 @@ adjust-sourcemap-loader@3.0.0: loader-utils "^2.0.0" regex-parser "^2.2.11" -agent-base@6, agent-base@^6.0.2: +agent-base@6: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" -agentkeepalive@^4.1.3: - version "4.1.4" - resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz" - integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== - dependencies: - debug "^4.1.0" - depd "^1.1.2" - humanize-ms "^1.2.1" - aggregate-error@^3.0.0, aggregate-error@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" @@ -5234,7 +5225,7 @@ cacache@^12.0.2: unique-filename "^1.1.1" y18n "^4.0.0" -cacache@^15.0.5, cacache@^15.2.0: +cacache@^15.0.5: version "15.3.0" resolved "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz" integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== @@ -7029,7 +7020,7 @@ denque@^1.1.0: resolved "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== -depd@^1.1.2, depd@~1.1.2: +depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -7417,13 +7408,6 @@ encodeurl@~1.0.2: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding@^0.1.12: - version "0.1.13" - resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz" - integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== - dependencies: - iconv-lite "^0.6.2" - end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" @@ -7465,21 +7449,11 @@ entities@^2.0.0: resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - envinfo@^7.7.3: version "7.8.1" resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== -err-code@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz" - integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== - errno@^0.1.3, errno@~0.1.7: version "0.1.8" resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz" @@ -9175,7 +9149,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.8" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== @@ -9543,7 +9517,7 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: +http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== @@ -9678,13 +9652,6 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" - integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= - dependencies: - ms "^2.0.0" - iconv-lite@0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -10240,11 +10207,6 @@ is-ip@^2.0.0: dependencies: ip-regex "^2.0.0" -is-lambda@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz" - integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= - is-map@^2.0.1, is-map@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz" @@ -11850,28 +11812,6 @@ make-error@^1, make-error@^1.1.1: resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^9.1.0: - version "9.1.0" - resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz" - integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== - dependencies: - agentkeepalive "^4.1.3" - cacache "^15.2.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" - minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.2" - promise-retry "^2.0.1" - socks-proxy-agent "^6.0.0" - ssri "^8.0.0" - makeerror@1.0.12: version "1.0.12" resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" @@ -12166,17 +12106,6 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.3.2: - version "1.4.1" - resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz" - integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== - dependencies: - minipass "^3.1.0" - minipass-sized "^1.0.3" - minizlib "^2.0.0" - optionalDependencies: - encoding "^0.1.12" - minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz" @@ -12184,20 +12113,13 @@ minipass-flush@^1.0.5: dependencies: minipass "^3.0.0" -minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: +minipass-pipeline@^1.2.2: version "1.2.4" resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" -minipass-sized@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz" - integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== - dependencies: - minipass "^3.0.0" - minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz" @@ -12206,7 +12128,7 @@ minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" -minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: +minipass@^3.0.0, minipass@^3.1.1: version "3.1.5" resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz" integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw== @@ -12220,7 +12142,7 @@ minizlib@^1.3.3: dependencies: minipass "^2.9.0" -minizlib@^2.0.0, minizlib@^2.1.1: +minizlib@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -12446,7 +12368,7 @@ needle@^2.5.2: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@0.6.2, negotiator@^0.6.2: +negotiator@0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== @@ -12522,22 +12444,6 @@ node-forge@^0.10.0: resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-gyp@^8.3.0: - version "8.3.0" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-8.3.0.tgz" - integrity sha512-e+vmKyTiybKgrmvs4M2REFKCnOd+NcrAAnn99Yko6NQA+zZdMlRvbIUHojfsHrSQ1CddLgZnHicnEVgDHziJzA== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" - npmlog "^4.1.2" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.2" - which "^2.0.2" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" @@ -14711,14 +14617,6 @@ promise-inflight@^1.0.1: resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= -promise-retry@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz" - integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== - dependencies: - err-code "^2.0.2" - retry "^0.12.0" - promise@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz" @@ -16359,11 +16257,6 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -smart-buffer@^4.1.0: - version "4.2.0" - resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== - snakeize@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz" @@ -16420,23 +16313,6 @@ sockjs@^0.3.21: uuid "^3.4.0" websocket-driver "^0.7.4" -socks-proxy-agent@^6.0.0: - version "6.1.0" - resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz" - integrity sha512-57e7lwCN4Tzt3mXz25VxOErJKXlPfXmkMLnk310v/jwW20jWRVcgsOit+xNkN3eIEdB47GwnfAEBLacZ/wVIKg== - dependencies: - agent-base "^6.0.2" - debug "^4.3.1" - socks "^2.6.1" - -socks@^2.6.1: - version "2.6.1" - resolved "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz" - integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== - dependencies: - ip "^1.1.5" - smart-buffer "^4.1.0" - sonic-boom@^1.0.2: version "1.4.1" resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz" @@ -16667,7 +16543,7 @@ ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" -ssri@^8.0.0, ssri@^8.0.1: +ssri@^8.0.1: version "8.0.1" resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz" integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== @@ -17223,7 +17099,7 @@ tar@^4.4.13: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: +tar@^6.0.2, tar@^6.1.11: version "6.1.11" resolved "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== From bcc8bb6b68204b787f852c0d75dcac0f99d435eb Mon Sep 17 00:00:00 2001 From: Manuel Alejandro de Brito Fontes Date: Thu, 23 Mar 2023 09:04:44 +0000 Subject: [PATCH 2/2] Update docker version to v20.10.23 --- WORKSPACE.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WORKSPACE.yaml b/WORKSPACE.yaml index 3ad7364515ffd1..df3ce8060a85f7 100644 --- a/WORKSPACE.yaml +++ b/WORKSPACE.yaml @@ -22,7 +22,7 @@ defaultArgs: jbBackendVersion: "latest" REPLICATED_API_TOKEN: "" REPLICATED_APP: "" - dockerVersion: 20.10.17 + dockerVersion: 20.10.23 dockerComposeVersion: "2.16.0-gitpod.0" provenance: enabled: true