From 82dd96477b6e5e274d8e18bdf2df73ed4dadede8 Mon Sep 17 00:00:00 2001 From: Simon Emms Date: Thu, 17 Feb 2022 08:58:30 +0000 Subject: [PATCH] Extract the evaluator type and create Gitpod and Replicated licensors --- components/licensor/ee/cmd/validate.go | 32 ++- components/licensor/ee/pkg/licensor/gitpod.go | 108 ++++++++++ .../licensor/ee/pkg/licensor/licensor.go | 103 ++------- .../licensor/ee/pkg/licensor/licensor_test.go | 195 +++++++++++++++--- .../licensor/ee/pkg/licensor/replicated.go | 130 ++++++++++++ components/licensor/typescript/ee/main.go | 13 +- 6 files changed, 444 insertions(+), 137 deletions(-) create mode 100644 components/licensor/ee/pkg/licensor/gitpod.go create mode 100644 components/licensor/ee/pkg/licensor/replicated.go diff --git a/components/licensor/ee/cmd/validate.go b/components/licensor/ee/cmd/validate.go index ccc2d54b906c8d..5a4f6774d3b6ce 100644 --- a/components/licensor/ee/cmd/validate.go +++ b/components/licensor/ee/cmd/validate.go @@ -10,6 +10,9 @@ import ( "io" "os" + // "io" + // "os" + "github.com/spf13/cobra" "golang.org/x/xerrors" @@ -22,18 +25,28 @@ var validateCmd = &cobra.Command{ 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) { - var lic []byte - if len(args) == 0 { - lic, err = io.ReadAll(os.Stdin) - if err != nil { - return err + domain, _ := cmd.Flags().GetString("domain") + licensorType, _ := cmd.Flags().GetString("licensor") + + var e licensor.Evaluator + switch licensorType { + case string(licensor.LicenseTypeReplicated): + e = licensor.NewReplicatedEvaluator(domain) + break + default: + var lic []byte + if len(args) == 0 { + lic, err = io.ReadAll(os.Stdin) + if err != nil { + return err + } + } else { + lic = []byte(args[0]) } - } else { - lic = []byte(args[0]) + + e = licensor.NewGitpodEvaluator(lic, domain) } - domain, _ := cmd.Flags().GetString("domain") - e := licensor.NewEvaluator(lic, domain) if msg, valid := e.Validate(); !valid { return xerrors.Errorf(msg) } @@ -47,4 +60,5 @@ var validateCmd = &cobra.Command{ 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 new file mode 100644 index 00000000000000..159e411d3a1256 --- /dev/null +++ b/components/licensor/ee/pkg/licensor/gitpod.go @@ -0,0 +1,108 @@ +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. +// Licensed under the Gitpod Enterprise Source Code License, +// See License.enterprise.txt in the project root folder. + +package licensor + +import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "fmt" + "time" +) + +// GitpodEvaluator determines what a license allows for +type GitpodEvaluator struct { + invalid string + lic LicensePayload +} + +// Validate returns false if the license isn't valid and a message explaining why that is. +func (e *GitpodEvaluator) 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 *GitpodEvaluator) Enabled(feature Feature) bool { + if e.invalid != "" { + return false + } + + _, ok := e.lic.Level.allowance().Features[feature] + return ok +} + +// HasEnoughSeats returns true if the license supports at least the give amount of seats +func (e *GitpodEvaluator) HasEnoughSeats(seats int) bool { + if e.invalid != "" { + return false + } + + return e.lic.Seats == 0 || seats <= e.lic.Seats +} + +// 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 *GitpodEvaluator) Inspect() LicensePayload { + return e.lic +} + +// NewGitpodEvaluator produces a new license evaluator from a license key +func NewGitpodEvaluator(key []byte, domain string) (res *GitpodEvaluator) { + if len(key) == 0 { + // fallback to the default license + return &GitpodEvaluator{ + lic: defaultLicense, + } + } + + deckey := make([]byte, base64.StdEncoding.DecodedLen(len(key))) + n, err := base64.StdEncoding.Decode(deckey, key) + if err != nil { + return &GitpodEvaluator{invalid: fmt.Sprintf("cannot decode key: %q", err)} + } + deckey = deckey[:n] + + var lic licensePayload + err = json.Unmarshal(deckey, &lic) + if err != nil { + return &GitpodEvaluator{invalid: fmt.Sprintf("cannot unmarshal key: %q", err)} + } + + keyWoSig, err := json.Marshal(lic.LicensePayload) + if err != nil { + return &GitpodEvaluator{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 &GitpodEvaluator{invalid: fmt.Sprintf("cannot verify key: %q", err)} + } + + if !matchesDomain(lic.Domain, domain) { + return &GitpodEvaluator{invalid: "wrong domain"} + } + + if lic.ValidUntil.Before(time.Now()) { + return &GitpodEvaluator{invalid: "not valid anymore"} + } + + return &GitpodEvaluator{ + lic: lic.LicensePayload, + } +} diff --git a/components/licensor/ee/pkg/licensor/licensor.go b/components/licensor/ee/pkg/licensor/licensor.go index 3bb5374f02ffe5..468088f6a5dd8c 100644 --- a/components/licensor/ee/pkg/licensor/licensor.go +++ b/components/licensor/ee/pkg/licensor/licensor.go @@ -17,6 +17,13 @@ import ( "time" ) +type LicenseType string + +const ( + LicenseTypeGitpod LicenseType = "gitpod" + LicenseTypeReplicated LicenseType = "replicated" +) + // LicensePayload is the actual license content type LicensePayload struct { ID string `json:"id"` @@ -115,57 +122,6 @@ var defaultLicense = LicensePayload{ // Domain, ValidUntil are free for all } -// NewEvaluator produces a new license evaluator from a license key -func NewEvaluator(key []byte, domain string) (res *Evaluator) { - if len(key) == 0 { - // fallback to the default license - return &Evaluator{ - lic: defaultLicense, - } - } - - 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, - } -} - func matchesDomain(pattern, domain string) bool { if pattern == "" { return true @@ -184,46 +140,11 @@ func matchesDomain(pattern, domain string) bool { return false } -// Evaluator determines what a license allows for -type Evaluator struct { - invalid string - lic LicensePayload -} - -// 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) bool { - if e.invalid != "" { - return false - } - - _, ok := e.lic.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 -} - -// 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 +type Evaluator interface { + Enabled(feature Feature) bool + HasEnoughSeats(seats int) bool + Inspect() LicensePayload + Validate() (msg string, valid bool) } // Sign signs a license so that it can be used with the evaluator diff --git a/components/licensor/ee/pkg/licensor/licensor_test.go b/components/licensor/ee/pkg/licensor/licensor_test.go index 85dfc8161ed27a..3521f1b27854fa 100644 --- a/components/licensor/ee/pkg/licensor/licensor_test.go +++ b/components/licensor/ee/pkg/licensor/licensor_test.go @@ -5,41 +5,135 @@ 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" + 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) + Name string + License *LicensePayload + Validate func(t *testing.T, eval Evaluator) + Type LicenseType + NeverExpires bool +} + +// 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.License == nil { - eval = NewEvaluator(nil, "") - } else { - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatalf("cannot generate key: %q", err) + var eval Evaluator + if test.Type == LicenseTypeGitpod { + if test.NeverExpires { + t.Fatal("gitpod licenses must have an expiry date") } - publicKeys = []*rsa.PublicKey{&priv.PublicKey} - lic, err := Sign(*test.License, priv) - if err != nil { - t.Fatalf("cannot sign license: %q", err) + + 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) + } - eval = NewEvaluator(lic, domain) + payload, err := json.Marshal(replicatedLicensePayload{ + 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: "levelId", + Value: func() LicenseLevel { + if test.License != nil { + return test.License.Level + } + return LevelTeam + }(), + }, + { + 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, domain) + } else { + eval = newReplicatedEvaluator(client, test.License.Domain) + } + } else { + t.Fatalf("unknown license type: '%s'", test.Type) } test.Validate(t, eval) @@ -54,16 +148,36 @@ func TestSeats(t *testing.T) { WithinLimits bool DefaultLicense bool InvalidLicense bool + LicenseType LicenseType + NeverExpires bool }{ - {"unlimited seats", 0, 1000, true, false, false}, - {"within limited seats", 50, 40, true, false, false}, - {"within limited seats (edge)", 50, 50, true, false, false}, - {"beyond limited seats", 50, 150, false, false, false}, - {"beyond limited seats (edge)", 50, 51, false, false, false}, - {"invalid license", 50, 50, false, false, true}, - {"within default license seats", 0, 7, true, true, false}, - {"within default license seats (edge)", 0, 10, true, true, false}, - {"beyond default license seats", 0, 11, false, true, false}, + {"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, false, 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, false, false, true, LicenseTypeReplicated, false}, + {"Replicated: beyond default license seats", 0, 11, false, 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, false, 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 { @@ -81,12 +195,14 @@ func TestSeats(t *testing.T) { Seats: test.Licensed, ValidUntil: validUntil, }, - Validate: func(t *testing.T, eval *Evaluator) { + 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 @@ -101,16 +217,26 @@ func TestFeatures(t *testing.T) { DefaultLicense bool Level LicenseLevel Features []Feature + LicenseType LicenseType }{ - {"no license", true, LicenseLevel(0), []Feature{FeaturePrebuild}}, - {"invalid license level", false, LicenseLevel(666), []Feature{}}, - {"enterprise license", false, LevelEnterprise, []Feature{ + {"Gitpod: no license", true, LicenseLevel(0), []Feature{FeaturePrebuild}, LicenseTypeGitpod}, + {"Gitpod: invalid license level", false, LicenseLevel(666), []Feature{}, LicenseTypeGitpod}, + {"Gitpod: enterprise license", false, LevelEnterprise, []Feature{ + FeatureAdminDashboard, + FeatureSetTimeout, + FeatureWorkspaceSharing, + FeatureSnapshot, + FeaturePrebuild, + }, LicenseTypeGitpod}, + + {"Replicated: invalid license level", false, LicenseLevel(666), []Feature{}, LicenseTypeReplicated}, + {"Replicated: enterprise license", false, LevelEnterprise, []Feature{ FeatureAdminDashboard, FeatureSetTimeout, FeatureWorkspaceSharing, FeatureSnapshot, FeaturePrebuild, - }}, + }, LicenseTypeReplicated}, } for _, test := range tests { @@ -127,7 +253,7 @@ func TestFeatures(t *testing.T) { lt := licenseTest{ Name: test.Name, License: lic, - Validate: func(t *testing.T, eval *Evaluator) { + Validate: func(t *testing.T, eval Evaluator) { unavailableFeatures := featureSet{} for f := range allowanceMap[LevelEnterprise].Features { unavailableFeatures[f] = struct{}{} @@ -146,6 +272,7 @@ func TestFeatures(t *testing.T) { } } }, + Type: test.LicenseType, } lt.Run(t) } @@ -258,7 +385,7 @@ func TestEvalutorKeys(t *testing.T) { } var errmsg string - e := NewEvaluator(lic, dom) + e := NewGitpodEvaluator(lic, dom) if msg, valid := e.Validate(); !valid { errmsg = msg } diff --git a/components/licensor/ee/pkg/licensor/replicated.go b/components/licensor/ee/pkg/licensor/replicated.go new file mode 100644 index 00000000000000..dca77210c64436 --- /dev/null +++ b/components/licensor/ee/pkg/licensor/replicated.go @@ -0,0 +1,130 @@ +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. +// Licensed under the Gitpod Enterprise Source Code License, +// See License.enterprise.txt in the project root folder. + +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 string `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 +} + +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) 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() *ReplicatedEvaluator { + return &ReplicatedEvaluator{ + lic: defaultLicense, + } +} + +// newReplicatedEvaluator exists to allow mocking of client +func newReplicatedEvaluator(client *http.Client, domain string) (res *ReplicatedEvaluator) { + resp, err := client.Get(replicatedLicenseApiEndpoint) + if err != nil { + return &ReplicatedEvaluator{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 &ReplicatedEvaluator{invalid: fmt.Sprintf("cannot decode json data, %q", err)} + } + + lic := LicensePayload{ + ID: replicatedPayload.LicenseID, + } + + // Search for the fields + for _, i := range replicatedPayload.Fields { + switch i.Field { + case "domain": + lic.Domain = i.Value.(string) + + case "levelId": + lic.Level = LicenseLevel(i.Value.(float64)) + + case "seats": + lic.Seats = int(i.Value.(float64)) + } + } + + if !matchesDomain(lic.Domain, domain) { + return defaultReplicatedLicense() + } + + if replicatedPayload.ExpirationTime != nil { + lic.ValidUntil = *replicatedPayload.ExpirationTime + + if lic.ValidUntil.Before(time.Now()) { + return defaultReplicatedLicense() + } + } + + return &ReplicatedEvaluator{ + lic: lic, + } +} + +// NewReplicatedEvaluator gets the license data from the kots admin panel +func NewReplicatedEvaluator(domain string) (res *ReplicatedEvaluator) { + return newReplicatedEvaluator(&http.Client{Timeout: replicatedLicenseApiTimeout}, domain) +} diff --git a/components/licensor/typescript/ee/main.go b/components/licensor/typescript/ee/main.go index cf6b374fb21ed2..607283e608b204 100644 --- a/components/licensor/typescript/ee/main.go +++ b/components/licensor/typescript/ee/main.go @@ -7,6 +7,7 @@ package main import ( "C" "encoding/json" + "os" log "github.com/sirupsen/logrus" @@ -14,15 +15,21 @@ import ( ) var ( - instances map[int]*licensor.Evaluator = make(map[int]*licensor.Evaluator) - nextID int = 1 + 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 - instances[id] = licensor.NewEvaluator([]byte(C.GoString(key)), C.GoString(domain)) + switch os.Getenv("GITPOD_LICENSE_TYPE") { + case string(licensor.LicenseTypeReplicated): + instances[id] = licensor.NewReplicatedEvaluator(C.GoString(domain)) + break + default: + instances[id] = licensor.NewGitpodEvaluator([]byte(C.GoString(key)), C.GoString(domain)) + } nextID++ return id