From e667b05efcff243e3696db92fb9dee3db574afe7 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Thu, 16 Feb 2023 10:45:23 +0530 Subject: [PATCH 01/51] [TT-8045] add migrate id extractor in migration. (#4779) https://tyktech.atlassian.net/browse/TT-8045 --- apidef/migration.go | 12 +++++-- apidef/migration_test.go | 8 +++++ ci/goreleaser/goreleaser-el7.yml | 1 + ci/goreleaser/goreleaser.yml | 1 + internal/reflect/reflect.go | 56 ++++++++++++++++++++++++++++++++ internal/reflect/reflect_test.go | 34 +++++++++++++++++++ 6 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 internal/reflect/reflect.go create mode 100644 internal/reflect/reflect_test.go diff --git a/apidef/migration.go b/apidef/migration.go index 6ce25e2be16..dc83fb9fc28 100644 --- a/apidef/migration.go +++ b/apidef/migration.go @@ -4,11 +4,12 @@ import ( "errors" "net/http" "net/url" - "reflect" "sort" "strings" uuid "github.com/satori/go.uuid" + + "github.com/TykTechnologies/tyk/internal/reflect" ) var ( @@ -230,6 +231,7 @@ func (a *APIDefinition) Migrate() (versions []APIDefinition, err error) { a.migrateCertificatePinning() a.migrateGatewayTags() a.migrateAuthenticationPlugin() + a.migrateIDExtractor() a.migrateCustomDomain() versions, err = a.MigrateVersioning() @@ -281,11 +283,17 @@ func (a *APIDefinition) migrateGatewayTags() { } func (a *APIDefinition) migrateAuthenticationPlugin() { - if reflect.DeepEqual(a.CustomMiddleware.AuthCheck, MiddlewareDefinition{}) { + if reflect.IsEmpty(a.CustomMiddleware.AuthCheck) { a.CustomMiddleware.AuthCheck.Disabled = true } } +func (a *APIDefinition) migrateIDExtractor() { + if reflect.IsEmpty(a.CustomMiddleware.IdExtractor) { + a.CustomMiddleware.IdExtractor.Disabled = true + } +} + func (a *APIDefinition) migrateCustomDomain() { if !a.DomainDisabled && a.Domain == "" { a.DomainDisabled = true diff --git a/apidef/migration_test.go b/apidef/migration_test.go index 63a78f2861c..326457e1fe9 100644 --- a/apidef/migration_test.go +++ b/apidef/migration_test.go @@ -660,3 +660,11 @@ func TestSetDisabledFlags(t *testing.T) { apiDef.SetDisabledFlags() assert.Equal(t, expectedAPIDef, apiDef) } + +func TestAPIDefinition_migrateIDExtractor(t *testing.T) { + base := oldTestAPI() + _, err := base.Migrate() + assert.NoError(t, err) + + assert.True(t, base.CustomMiddleware.IdExtractor.Disabled) +} diff --git a/ci/goreleaser/goreleaser-el7.yml b/ci/goreleaser/goreleaser-el7.yml index 8626365e41d..e3f9944cbb9 100644 --- a/ci/goreleaser/goreleaser-el7.yml +++ b/ci/goreleaser/goreleaser-el7.yml @@ -159,6 +159,7 @@ dockers: - testdata - trace - user + - internal checksum: disable: true diff --git a/ci/goreleaser/goreleaser.yml b/ci/goreleaser/goreleaser.yml index 97f520dbb64..3634b34fef9 100644 --- a/ci/goreleaser/goreleaser.yml +++ b/ci/goreleaser/goreleaser.yml @@ -166,6 +166,7 @@ dockers: - testdata - trace - user + - internal docker_manifests: - name_template: tykio/tyk-gateway:{{ .Tag }} diff --git a/internal/reflect/reflect.go b/internal/reflect/reflect.go new file mode 100644 index 00000000000..abc63770953 --- /dev/null +++ b/internal/reflect/reflect.go @@ -0,0 +1,56 @@ +package reflect + +import ( + "math" + "reflect" +) + +// IsEmpty checks whether a field should be set to empty and omitted from OAS JSON. +func IsEmpty(i interface{}) bool { + return IsZero(reflect.ValueOf(i)) +} + +// IsZero is a customized implementation of reflect.Value.IsZero. The built-in function accepts slice, map and pointer fields +// having 0 length as not zero. In OAS, we would like them to be counted as empty so we separated slice, map and pointer to +// different cases. +func IsZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return math.Float64bits(v.Float()) == 0 + case reflect.Complex64, reflect.Complex128: + c := v.Complex() + return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0 + case reflect.Array: + for i := 0; i < v.Len(); i++ { + if !IsZero(v.Index(i)) { + return false + } + } + return true + case reflect.Chan, reflect.Func, reflect.Interface, reflect.UnsafePointer: + return v.IsNil() + case reflect.Ptr: + return v.IsNil() || IsZero(v.Elem()) + case reflect.Slice, reflect.Map: + return v.Len() == 0 + case reflect.String: + return v.Len() == 0 + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + if !IsZero(v.Field(i)) { + return false + } + } + return true + default: + // This should never happens, but will act as a safeguard for + // later, as a default value doesn't makes sense here. + panic(&reflect.ValueError{Method: "oas.IsZero", Kind: v.Kind()}) + } +} diff --git a/internal/reflect/reflect_test.go b/internal/reflect/reflect_test.go new file mode 100644 index 00000000000..461308464c8 --- /dev/null +++ b/internal/reflect/reflect_test.go @@ -0,0 +1,34 @@ +package reflect + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type testStruct struct { + Bool bool + Array []string + Map map[string]string + SubStruct *subStruct +} + +type subStruct struct { + SubMap map[string]string +} + +func Test_IsEmpty(t *testing.T) { + v1 := testStruct{} + v2 := testStruct{Array: make([]string, 0), Map: make(map[string]string), SubStruct: &subStruct{}} + v3 := testStruct{Array: make([]string, 0), Map: make(map[string]string), SubStruct: &subStruct{SubMap: make(map[string]string)}} + v4 := testStruct{Bool: true} + v5 := testStruct{Array: []string{"a"}} + v6 := testStruct{Map: map[string]string{"a": "b"}} + + assert.True(t, IsEmpty(v1)) + assert.True(t, IsEmpty(v2)) + assert.True(t, IsEmpty(v3)) + assert.False(t, IsEmpty(v4)) + assert.False(t, IsEmpty(v5)) + assert.False(t, IsEmpty(v6)) +} From e66b418808e2446e9d38f00551e2d97a28d985c5 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Thu, 16 Feb 2023 07:17:06 +0100 Subject: [PATCH 02/51] [TT-7285] Add customDomain validation, require enabled and name to set (#4495) Add customDomain required fields to schema. --- apidef/oas/schema/x-tyk-api-gateway.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apidef/oas/schema/x-tyk-api-gateway.json b/apidef/oas/schema/x-tyk-api-gateway.json index 5f050567e43..62ea22ce9af 100644 --- a/apidef/oas/schema/x-tyk-api-gateway.json +++ b/apidef/oas/schema/x-tyk-api-gateway.json @@ -1321,7 +1321,11 @@ "enabled": { "type": "boolean" } - } + }, + "required": [ + "enabled", + "name" + ] }, "X-Tyk-Header": { "type": "object", From 4056d33db88eae8b7555dd70335f440594691b86 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Fri, 17 Feb 2023 12:27:02 +0530 Subject: [PATCH 03/51] [TT-8051] fix broken schema and godoc comments (#4782) https://tyktech.atlassian.net/browse/TT-8051 --- apidef/oas/authentication.go | 2 +- apidef/oas/middleware.go | 22 +++++++++++----------- apidef/oas/schema/x-tyk-api-gateway.json | 5 ++++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apidef/oas/authentication.go b/apidef/oas/authentication.go index 348f1a7a3d6..c930b141869 100644 --- a/apidef/oas/authentication.go +++ b/apidef/oas/authentication.go @@ -690,7 +690,7 @@ type IDExtractorConfig struct { // RegexpMatchIndex is the index from which ID to be extracted after a match. RegexpMatchIndex int `bson:"regexpMatchIndex,omitempty" json:"regexpMatchIndex,omitempty"` // XPathExp is the xpath expression to match ID. - XPathExp string `bson:"xPathExp,omitempty" json:"xPathExp"` + XPathExp string `bson:"xPathExp,omitempty" json:"xPathExp,omitempty"` } // Fill fills IDExtractorConfig from supplied classic APIDefinition. diff --git a/apidef/oas/middleware.go b/apidef/oas/middleware.go index 637e552b0d3..db14939cd03 100644 --- a/apidef/oas/middleware.go +++ b/apidef/oas/middleware.go @@ -166,10 +166,10 @@ func (g *Global) ExtractTo(api *apidef.APIDefinition) { // PluginConfigData configures config data for custom plugins. type PluginConfigData struct { // Enabled enables custom plugin config data. - Enabled bool `bson:"enabled" json:"enabled"` + Enabled bool `bson:"enabled" json:"enabled"` // required. // Value is the value of custom plugin config data. - Value map[string]interface{} `bson:"value" json:"value"` + Value map[string]interface{} `bson:"value" json:"value"` // required. } // Fill fills PluginConfigData from apidef. @@ -1030,17 +1030,17 @@ func (p *ResponsePlugin) ExtractTo(api *apidef.APIDefinition) { // VirtualEndpoint contains virtual endpoint configuration. type VirtualEndpoint struct { // Enabled enables virtual endpoint. - Enabled bool `bson:"enabled" json:"enabled"` + Enabled bool `bson:"enabled" json:"enabled"` // required. // Name is the name of js function. - Name string `bson:"name" json:"name"` + Name string `bson:"name" json:"name"` // required. // Path is the path to js file. - Path string `bson:"path" json:"path"` + Path string `bson:"path,omitempty" json:"path,omitempty"` // Body is the js function to execute encoded in base64 format. - Body string `bson:"body" json:"body"` + Body string `bson:"body,omitempty" json:"body,omitempty"` // ProxyOnError proxies if virtual endpoint errors out. - ProxyOnError bool `bson:"proxyOnError" json:"proxyOnError"` + ProxyOnError bool `bson:"proxyOnError,omitempty" json:"proxyOnError,omitempty"` // RequireSession if enabled passes session to virtual endpoint. - RequireSession bool `bson:"requireSession" json:"requireSession"` + RequireSession bool `bson:"requireSession,omitempty" json:"requireSession,omitempty"` } // Fill fills *VirtualEndpoint from apidef.VirtualMeta. @@ -1076,11 +1076,11 @@ type EndpointPostPlugins []EndpointPostPlugin // EndpointPostPlugin contains endpoint level post plugin configuration. type EndpointPostPlugin struct { // Enabled enables post plugin. - Enabled bool `bson:"enabled" json:"enabled"` + Enabled bool `bson:"enabled" json:"enabled"` // required. // Name is the name of plugin function to be executed. - Name string `bson:"name" json:"name"` + Name string `bson:"name" json:"name"` // required. // Path is the path to plugin. - Path string `bson:"path" json:"path"` + Path string `bson:"path" json:"path"` // required. } // Fill fills *EndpointPostPlugin from apidef.GoPluginMeta. diff --git a/apidef/oas/schema/x-tyk-api-gateway.json b/apidef/oas/schema/x-tyk-api-gateway.json index 62ea22ce9af..773ce8f99e8 100644 --- a/apidef/oas/schema/x-tyk-api-gateway.json +++ b/apidef/oas/schema/x-tyk-api-gateway.json @@ -233,7 +233,10 @@ "data": { "$ref": "#/definitions/X-Tyk-PluginConfigData" } - } + }, + "required": [ + "driver" + ] }, "X-Tyk-PluginConfigData": { "type": "object", From b766f0c8fb674fdf957d9072491b7f7839d8d447 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Mon, 20 Feb 2023 13:45:20 +0530 Subject: [PATCH 04/51] [TT-8051] fix regexpMatchIndex schema to be integer (#4783) https://tyktech.atlassian.net/browse/TT-8051 --- apidef/oas/authentication.go | 1 + apidef/oas/schema/x-tyk-api-gateway.json | 2 +- apidef/oas/schema/x-tyk-gateway.md | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apidef/oas/authentication.go b/apidef/oas/authentication.go index c930b141869..3674daf8ae9 100644 --- a/apidef/oas/authentication.go +++ b/apidef/oas/authentication.go @@ -688,6 +688,7 @@ type IDExtractorConfig struct { // Regexp is the regular expression to match ID. Regexp string `bson:"regexp,omitempty" json:"regexp,omitempty"` // RegexpMatchIndex is the index from which ID to be extracted after a match. + // Default value is 0, ie if regexpMatchIndex is not provided ID is matched from index 0. RegexpMatchIndex int `bson:"regexpMatchIndex,omitempty" json:"regexpMatchIndex,omitempty"` // XPathExp is the xpath expression to match ID. XPathExp string `bson:"xPathExp,omitempty" json:"xPathExp,omitempty"` diff --git a/apidef/oas/schema/x-tyk-api-gateway.json b/apidef/oas/schema/x-tyk-api-gateway.json index 773ce8f99e8..9c6335b4fc8 100644 --- a/apidef/oas/schema/x-tyk-api-gateway.json +++ b/apidef/oas/schema/x-tyk-api-gateway.json @@ -303,7 +303,7 @@ "type": "string" }, "regexpMatchIndex": { - "type": "string" + "type": "integer" }, "xPathExp": { "type": "string" diff --git a/apidef/oas/schema/x-tyk-gateway.md b/apidef/oas/schema/x-tyk-gateway.md index 5ce5b0c6bfd..8b1ceeb8065 100644 --- a/apidef/oas/schema/x-tyk-gateway.md +++ b/apidef/oas/schema/x-tyk-gateway.md @@ -561,6 +561,7 @@ Regexp is the regular expression to match ID. **Field: `regexpMatchIndex` (`int`)** RegexpMatchIndex is the index from which ID to be extracted after a match. +Default value is 0, ie if regexpMatchIndex is not provided ID is matched from index 0. **Field: `xPathExp` (`string`)** XPathExp is the xpath expression to match ID. From 5e80b10dace419c9df4ab51852e052ab81168abf Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Mon, 20 Feb 2023 14:44:10 +0530 Subject: [PATCH 05/51] [TT-8075] Fix endpoint postPlugins (#4785) https://tyktech.atlassian.net/browse/TT-8075 --- apidef/oas/operation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apidef/oas/operation.go b/apidef/oas/operation.go index ac36f2d298c..deab0e32e63 100644 --- a/apidef/oas/operation.go +++ b/apidef/oas/operation.go @@ -651,7 +651,7 @@ func (s *OAS) fillEndpointPostPlugins(endpointMetas []apidef.GoPluginMeta) { } func (o *Operation) extractEndpointPostPluginTo(ep *apidef.ExtendedPathsSet, path string, method string) { - if o.VirtualEndpoint == nil { + if o.PostPlugins == nil { return } From 435ea9aab7518d08c5cb2a662e49eff46e240e48 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Mon, 20 Feb 2023 15:12:58 +0530 Subject: [PATCH 06/51] [TT-8076] fix broken OAS migration (#4786) https://tyktech.atlassian.net/browse/TT-8076 --- apidef/migration.go | 7 +++++++ apidef/migration_test.go | 8 ++++++++ apidef/oas/oas_test.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/apidef/migration.go b/apidef/migration.go index dc83fb9fc28..62bbb04bc8c 100644 --- a/apidef/migration.go +++ b/apidef/migration.go @@ -227,6 +227,7 @@ func (a *APIDefinition) Migrate() (versions []APIDefinition, err error) { a.migrateCustomPluginAuth() a.MigrateAuthentication() a.migratePluginBundle() + a.migratePluginConfigData() a.migrateMutualTLS() a.migrateCertificatePinning() a.migrateGatewayTags() @@ -255,6 +256,12 @@ func (a *APIDefinition) migratePluginBundle() { } } +func (a *APIDefinition) migratePluginConfigData() { + if reflect.IsEmpty(a.ConfigData) { + a.ConfigDataDisabled = true + } +} + // migrateCustomPluginAuth deprecates UseGoPluginAuth and EnableCoProcessAuth in favour of CustomPluginAuthEnabled. func (a *APIDefinition) migrateCustomPluginAuth() { if a.UseGoPluginAuth || a.EnableCoProcessAuth { diff --git a/apidef/migration_test.go b/apidef/migration_test.go index 326457e1fe9..0101b2f993c 100644 --- a/apidef/migration_test.go +++ b/apidef/migration_test.go @@ -668,3 +668,11 @@ func TestAPIDefinition_migrateIDExtractor(t *testing.T) { assert.True(t, base.CustomMiddleware.IdExtractor.Disabled) } + +func TestAPIDefinition_migratePluginConfigData(t *testing.T) { + base := oldTestAPI() + _, err := base.Migrate() + assert.NoError(t, err) + + assert.True(t, base.ConfigDataDisabled) +} diff --git a/apidef/oas/oas_test.go b/apidef/oas/oas_test.go index acdea9a67ee..31b0fd992f2 100644 --- a/apidef/oas/oas_test.go +++ b/apidef/oas/oas_test.go @@ -908,3 +908,32 @@ func TestMigrateAndFillOAS_CustomPlugins(t *testing.T) { assert.Equal(t, apidef.GoPluginDriver, migratedAPI.OAS.GetTykExtension().Middleware.Global.PluginConfig.Driver) }) } + +func TestMigrateAndFillOAS_PluginConfigData(t *testing.T) { + configData := map[string]interface{}{ + "key": "value", + } + + api := apidef.APIDefinition{ + Name: "config data", + Proxy: apidef.ProxyConfig{ + ListenPath: "/", + }, + CustomMiddleware: apidef.MiddlewareSection{ + Driver: apidef.GoPluginDriver, + }, + VersionData: apidef.VersionData{ + NotVersioned: true, + Versions: map[string]apidef.VersionInfo{}, + }, + ConfigData: configData, + } + migratedAPI, _, err := MigrateAndFillOAS(&api) + assert.NoError(t, err) + + expectedPluginConfigData := &PluginConfigData{ + Enabled: true, + Value: configData, + } + assert.Equal(t, expectedPluginConfigData, migratedAPI.OAS.GetTykExtension().Middleware.Global.PluginConfig.Data) +} From 5043b06bb3b340a4bfadc310b8ebd599585732bc Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 21 Feb 2023 14:11:04 +0530 Subject: [PATCH 07/51] [TT-8089] Make authenticationPlugin.path optional in OAS schema considering gRPC usecase (#4788) https://tyktech.atlassian.net/browse/TT-8089 --- apidef/oas/authentication.go | 2 +- apidef/oas/middleware.go | 2 +- apidef/oas/schema/x-tyk-api-gateway.json | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apidef/oas/authentication.go b/apidef/oas/authentication.go index 3674daf8ae9..85075c57e9d 100644 --- a/apidef/oas/authentication.go +++ b/apidef/oas/authentication.go @@ -646,7 +646,7 @@ type AuthenticationPlugin struct { // FunctionName is the name of authentication method. FunctionName string `bson:"functionName" json:"functionName"` // required. // Path is the path to shared object file in case of gopluign mode or path to js code in case of otto auth plugin. - Path string `bson:"path" json:"path"` // required. + Path string `bson:"path" json:"path"` // RawBodyOnly if set to true, do not fill body in request or response object. RawBodyOnly bool `bson:"rawBodyOnly,omitempty" json:"rawBodyOnly,omitempty"` // IDExtractor configures ID extractor with coprocess custom authentication. diff --git a/apidef/oas/middleware.go b/apidef/oas/middleware.go index db14939cd03..27f60a4801d 100644 --- a/apidef/oas/middleware.go +++ b/apidef/oas/middleware.go @@ -874,7 +874,7 @@ type CustomPlugin struct { // FunctionName is the name of authentication method. FunctionName string `bson:"functionName" json:"functionName"` // required. // Path is the path to shared object file in case of gopluign mode or path to js code in case of otto auth plugin. - Path string `bson:"path" json:"path"` // required. + Path string `bson:"path" json:"path"` // RawBodyOnly if set to true, do not fill body in request or response object. RawBodyOnly bool `bson:"rawBodyOnly,omitempty" json:"rawBodyOnly,omitempty"` // RequireSession if set to true passes down the session information for plugins after authentication. diff --git a/apidef/oas/schema/x-tyk-api-gateway.json b/apidef/oas/schema/x-tyk-api-gateway.json index 9c6335b4fc8..a0c32e0a8e3 100644 --- a/apidef/oas/schema/x-tyk-api-gateway.json +++ b/apidef/oas/schema/x-tyk-api-gateway.json @@ -286,8 +286,7 @@ }, "required": [ "enabled", - "functionName", - "path" + "functionName" ] }, "X-Tyk-IDExtractorConfig": { @@ -364,8 +363,7 @@ }, "required": [ "enabled", - "functionName", - "path" + "functionName" ] }, "X-Tyk-PluginBundle": { From 357b73b2a37ef52a3e1635dfa1afe0c1ec5fc278 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Wed, 22 Feb 2023 12:42:24 +0530 Subject: [PATCH 08/51] [TT-8093] bump kin-openapi to v0.114.0 (#4789) https://tyktech.atlassian.net/browse/TT-8093 bump kin-openapi version to latest release v0.114.0 major changes - `Components` is changed to a pointer making it optional - `openapi3.NewLoader()` now by default returns a spec loader with which `LoadFromFile` caches the spec based on filepath. Caching spec could affect gw reloads, hence `loader.ReadFromURIFunc` should be updated to be `openapi3.ReadFromFile` so that spec isn't cached. --- apidef/oas/default_test.go | 38 +++++---- apidef/oas/example_test.go | 17 +++-- apidef/oas/oas_test.go | 27 +++---- apidef/oas/root_test.go | 14 ++-- apidef/oas/security.go | 6 +- apidef/oas/security_test.go | 148 +++++++++++++++++++++--------------- gateway/api_definition.go | 2 + gateway/api_test.go | 32 ++++---- go.mod | 4 +- go.sum | 25 ++++-- 10 files changed, 183 insertions(+), 130 deletions(-) diff --git a/apidef/oas/default_test.go b/apidef/oas/default_test.go index c8177c60352..673d4a1cf3e 100644 --- a/apidef/oas/default_test.go +++ b/apidef/oas/default_test.go @@ -168,7 +168,7 @@ func TestOAS_BuildDefaultTykExtension(t *testing.T) { {testSSMyAuth: []string{}, testSSMyAuthWithAnd: []string{}}, {testSSMyAuthWithOR: []string{}}, }, - Components: openapi3.Components{ + Components: &openapi3.Components{ SecuritySchemes: openapi3.SecuritySchemes{ testSSMyAuth: &openapi3.SecuritySchemeRef{ Value: openapi3.NewSecurityScheme().WithType(typeAPIKey).WithIn(header).WithName(testHeader), @@ -1383,12 +1383,14 @@ func TestOAS_importAuthentication(t *testing.T) { jwtScheme.Scheme = schemeBearer jwtScheme.BearerFormat = bearerFormatJWT - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - testSecurityNameToken: &openapi3.SecuritySchemeRef{ - Value: tokenScheme, - }, - testSecurityNameJWT: &openapi3.SecuritySchemeRef{ - Value: jwtScheme, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + testSecurityNameToken: &openapi3.SecuritySchemeRef{ + Value: tokenScheme, + }, + testSecurityNameJWT: &openapi3.SecuritySchemeRef{ + Value: jwtScheme, + }, }, } @@ -1436,9 +1438,11 @@ func TestOAS_importAuthentication(t *testing.T) { securityScheme.In = cookie securityScheme.Name = testCookieName - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - testSecurityNameToken: &openapi3.SecuritySchemeRef{ - Value: securityScheme, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + testSecurityNameToken: &openapi3.SecuritySchemeRef{ + Value: securityScheme, + }, }, } @@ -1504,12 +1508,14 @@ func TestOAS_importAuthentication(t *testing.T) { jwtScheme.Scheme = schemeBearer jwtScheme.BearerFormat = bearerFormatJWT - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - testSecurityNameToken: &openapi3.SecuritySchemeRef{ - Value: tokenScheme, - }, - testSecurityNameJWT: &openapi3.SecuritySchemeRef{ - Value: jwtScheme, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + testSecurityNameToken: &openapi3.SecuritySchemeRef{ + Value: tokenScheme, + }, + testSecurityNameJWT: &openapi3.SecuritySchemeRef{ + Value: jwtScheme, + }, }, } diff --git a/apidef/oas/example_test.go b/apidef/oas/example_test.go index 7c7b1f1bb1a..b41d4ba0997 100644 --- a/apidef/oas/example_test.go +++ b/apidef/oas/example_test.go @@ -46,9 +46,13 @@ func Test_exampleExtractor(t *testing.T) { "example": "bird" } }, - "example": "duck" + "example": { + "name": "duck" + } }`, - "duck", + map[string]interface{}{ + "name": "duck", + }, }, { "boolean", @@ -93,7 +97,7 @@ func Test_exampleExtractor(t *testing.T) { `{ "type": "string", "example": "bird", - "enum": ["duck"] + "enum": ["duck", "bird"] }`, "bird", }, @@ -144,9 +148,9 @@ func Test_exampleExtractor(t *testing.T) { "type": "string", "example": "bird" }, - "example": "duck" + "example": ["duck"] }`, - "duck", + []interface{}{"duck"}, }, } @@ -155,7 +159,8 @@ func Test_exampleExtractor(t *testing.T) { err := schemaRef.UnmarshalJSON([]byte(c.schema)) assert.NoError(t, err) - assert.NoError(t, schemaRef.Validate(context.Background())) + err = schemaRef.Validate(context.Background()) + assert.NoError(t, err) actualRes := ExampleExtractor(schemaRef) diff --git a/apidef/oas/oas_test.go b/apidef/oas/oas_test.go index 31b0fd992f2..88b80a3b875 100644 --- a/apidef/oas/oas_test.go +++ b/apidef/oas/oas_test.go @@ -20,6 +20,7 @@ func TestOAS(t *testing.T) { t.Parallel() var emptyOASPaths OAS + emptyOASPaths.Components = &openapi3.Components{} emptyOASPaths.Paths = make(openapi3.Paths) emptyOASPaths.SetTykExtension(&XTykAPIGateway{}) @@ -38,6 +39,7 @@ func TestOAS(t *testing.T) { t.Parallel() var nilOASPaths OAS + nilOASPaths.Components = &openapi3.Components{} nilOASPaths.SetTykExtension(&XTykAPIGateway{}) var convertedAPI apidef.APIDefinition @@ -57,6 +59,7 @@ func TestOAS(t *testing.T) { t.Parallel() var oasWithPaths OAS + oasWithPaths.Components = &openapi3.Components{} oasWithPaths.SetTykExtension(&XTykAPIGateway{ Middleware: &Middleware{ Operations: Operations{ @@ -413,12 +416,10 @@ func TestOAS_MarshalJSON(t *testing.T) { Info: &openapi3.Info{ Title: "OAS Doc", }, - ExtensionProps: openapi3.ExtensionProps{ - Extensions: map[string]interface{}{ - ExtensionTykAPIGateway: XTykAPIGateway{ - Info: Info{ - Name: "OAS API", - }, + Extensions: map[string]interface{}{ + ExtensionTykAPIGateway: XTykAPIGateway{ + Info: Info{ + Name: "OAS API", }, }, }, @@ -429,7 +430,7 @@ func TestOAS_MarshalJSON(t *testing.T) { copyOAS := s intVal := 9 byteRep, _ := json.Marshal(intVal) - copyOAS.ExtensionProps.Extensions["x-abcd"] = byteRep + copyOAS.Extensions["x-abcd"] = byteRep data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -440,7 +441,7 @@ func TestOAS_MarshalJSON(t *testing.T) { copyOAS := s floatVal := 9.5 byteRep, _ := json.Marshal(floatVal) - copyOAS.ExtensionProps.Extensions["x-abcd"] = byteRep + copyOAS.Extensions["x-abcd"] = byteRep data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -451,7 +452,7 @@ func TestOAS_MarshalJSON(t *testing.T) { copyOAS := s boolVal := false byteRep, _ := json.Marshal(boolVal) - copyOAS.ExtensionProps.Extensions["x-abcd"] = byteRep + copyOAS.Extensions["x-abcd"] = byteRep data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -460,7 +461,7 @@ func TestOAS_MarshalJSON(t *testing.T) { t.Run("nil", func(t *testing.T) { copyOAS := s - copyOAS.ExtensionProps.Extensions["x-abcd"] = nil + copyOAS.Extensions["x-abcd"] = nil data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -469,7 +470,7 @@ func TestOAS_MarshalJSON(t *testing.T) { t.Run("string", func(t *testing.T) { copyOAS := s - copyOAS.ExtensionProps.Extensions["x-abcd"] = []byte(`"hello"`) + copyOAS.Extensions["x-abcd"] = []byte(`"hello"`) data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -478,7 +479,7 @@ func TestOAS_MarshalJSON(t *testing.T) { t.Run("map", func(t *testing.T) { copyOAS := s - copyOAS.ExtensionProps.Extensions["x-abcd"] = []byte(`{"key":"value"}`) + copyOAS.Extensions["x-abcd"] = []byte(`{"key":"value"}`) data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -487,7 +488,7 @@ func TestOAS_MarshalJSON(t *testing.T) { t.Run("array", func(t *testing.T) { copyOAS := s - copyOAS.ExtensionProps.Extensions["x-abcd"] = []byte(`[{"key":"value"},{"key":"value"}]`) + copyOAS.Extensions["x-abcd"] = []byte(`[{"key":"value"},{"key":"value"}]`) data, err := copyOAS.MarshalJSON() assert.NoError(t, err) diff --git a/apidef/oas/root_test.go b/apidef/oas/root_test.go index e2670eeb2f1..0b7fe726d4c 100644 --- a/apidef/oas/root_test.go +++ b/apidef/oas/root_test.go @@ -36,12 +36,14 @@ func TestXTykAPIGateway(t *testing.T) { }, } - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - "custom": { - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - Name: "x-query", - In: "query", + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + "custom": { + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + Name: "x-query", + In: "query", + }, }, }, } diff --git a/apidef/oas/security.go b/apidef/oas/security.go index 9926d23511b..fa8bd98d987 100644 --- a/apidef/oas/security.go +++ b/apidef/oas/security.go @@ -613,6 +613,10 @@ func (s *OAS) fillSecurity(api apidef.APIDefinition) { tykAuthentication.Fill(api) + if s.Components == nil { + s.Components = &openapi3.Components{} + } + s.fillToken(api) s.fillJWT(api) s.fillBasic(api) @@ -640,7 +644,7 @@ func (s *OAS) extractSecurityTo(api *apidef.APIDefinition) { api.AuthConfigs = make(map[string]apidef.AuthConfig) } - if len(s.Security) == 0 || len(s.Components.SecuritySchemes) == 0 { + if len(s.Security) == 0 || s.Components == nil || len(s.Components.SecuritySchemes) == 0 { return } diff --git a/apidef/oas/security_test.go b/apidef/oas/security_test.go index 738f4b4bfad..511e3a4da93 100644 --- a/apidef/oas/security_test.go +++ b/apidef/oas/security_test.go @@ -49,6 +49,10 @@ func TestOAS_ApiKeyScheme(t *testing.T) { } check := func(in, name string, ac apidef.AuthConfig, s OAS) { + if s.Components == nil { + s.Components = &openapi3.Components{} + } + s.fillAPIKeyScheme(&ac) expectedAC := ac @@ -111,16 +115,17 @@ func TestOAS_ApiKeyScheme(t *testing.T) { }) testOAS := func(in, name string) (oas OAS) { - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - authName: &openapi3.SecuritySchemeRef{ - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - In: in, - Name: name, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + authName: &openapi3.SecuritySchemeRef{ + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + In: in, + Name: name, + }, }, }, } - return } @@ -153,12 +158,14 @@ func TestOAS_Token(t *testing.T) { }, } - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - Name: "x-query", - In: query, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + Name: "x-query", + In: query, + }, }, }, } @@ -183,7 +190,9 @@ func TestOAS_Token(t *testing.T) { oas.extractTokenTo(&api, securityName) var convertedOAS OAS - convertedOAS.Components.SecuritySchemes = oas.Components.SecuritySchemes + convertedOAS.Components = &openapi3.Components{ + SecuritySchemes: oas.Components.SecuritySchemes, + } convertedOAS.SetTykExtension(&XTykAPIGateway{Server: Server{Authentication: &Authentication{SecuritySchemes: SecuritySchemes{}}}}) convertedOAS.fillToken(api) @@ -203,19 +212,21 @@ func TestOAS_Token_MultipleSecuritySchemes(t *testing.T) { }, } - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - Name: "x-query", - In: query, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + Name: "x-query", + In: query, + }, }, - }, - securityName2: { - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - Name: "x-header", - In: header, + securityName2: { + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + Name: "x-header", + In: header, + }, }, }, } @@ -296,12 +307,14 @@ func TestOAS_JWT(t *testing.T) { }, } - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeHTTP, - Scheme: schemeBearer, - BearerFormat: bearerFormatJWT, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeHTTP, + Scheme: schemeBearer, + BearerFormat: bearerFormatJWT, + }, }, }, } @@ -325,6 +338,7 @@ func TestOAS_JWT(t *testing.T) { oas.extractJWTTo(&api, securityName) var convertedOAS OAS + convertedOAS.Components = &openapi3.Components{} convertedOAS.SetTykExtension(&XTykAPIGateway{Server: Server{Authentication: &Authentication{SecuritySchemes: SecuritySchemes{}}}}) convertedOAS.fillJWT(api) @@ -341,11 +355,13 @@ func TestOAS_Basic(t *testing.T) { }, } - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeHTTP, - Scheme: schemeBasic, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeHTTP, + Scheme: schemeBasic, + }, }, }, } @@ -369,6 +385,7 @@ func TestOAS_Basic(t *testing.T) { oas.extractBasicTo(&api, securityName) var convertedOAS OAS + convertedOAS.Components = &openapi3.Components{} convertedOAS.SetTykExtension(&XTykAPIGateway{Server: Server{Authentication: &Authentication{SecuritySchemes: SecuritySchemes{}}}}) convertedOAS.fillBasic(api) @@ -390,18 +407,20 @@ func TestOAS_OAuth(t *testing.T) { }, } - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeOAuth2, - Flows: &openapi3.OAuthFlows{ - AuthorizationCode: &openapi3.OAuthFlow{ - AuthorizationURL: "{api-url}/oauth/authorize", - TokenURL: "{api-url}/oauth/token", - Scopes: scopes, - }, - ClientCredentials: &openapi3.OAuthFlow{ - Scopes: scopes, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeOAuth2, + Flows: &openapi3.OAuthFlows{ + AuthorizationCode: &openapi3.OAuthFlow{ + AuthorizationURL: "{api-url}/oauth/authorize", + TokenURL: "{api-url}/oauth/token", + Scopes: scopes, + }, + ClientCredentials: &openapi3.OAuthFlow{ + Scopes: scopes, + }, }, }, }, @@ -426,7 +445,7 @@ func TestOAS_OAuth(t *testing.T) { oas.ExtractTo(&api) var convertedOAS OAS - convertedOAS.Components.SecuritySchemes = oas.Components.SecuritySchemes + convertedOAS.Components = &openapi3.Components{SecuritySchemes: oas.Components.SecuritySchemes} convertedOAS.Fill(api) flows := convertedOAS.Components.SecuritySchemes[securityName].Value.Flows @@ -452,18 +471,20 @@ func TestOAS_ExternalOAuth(t *testing.T) { }, } - oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeOAuth2, - Flows: &openapi3.OAuthFlows{ - AuthorizationCode: &openapi3.OAuthFlow{ - AuthorizationURL: "{api-url}/oauth/authorize", - TokenURL: "{api-url}/oauth/token", - Scopes: scopes, - }, - ClientCredentials: &openapi3.OAuthFlow{ - Scopes: scopes, + oas.Components = &openapi3.Components{ + SecuritySchemes: openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeOAuth2, + Flows: &openapi3.OAuthFlows{ + AuthorizationCode: &openapi3.OAuthFlow{ + AuthorizationURL: "{api-url}/oauth/authorize", + TokenURL: "{api-url}/oauth/token", + Scopes: scopes, + }, + ClientCredentials: &openapi3.OAuthFlow{ + Scopes: scopes, + }, }, }, }, @@ -488,7 +509,7 @@ func TestOAS_ExternalOAuth(t *testing.T) { oas.ExtractTo(&api) var convertedOAS OAS - convertedOAS.Components.SecuritySchemes = oas.Components.SecuritySchemes + convertedOAS.Components = &openapi3.Components{SecuritySchemes: oas.Components.SecuritySchemes} convertedOAS.Fill(api) flows := convertedOAS.Components.SecuritySchemes[securityName].Value.Flows @@ -563,6 +584,7 @@ func TestOAS_TykAuthentication_NoOASSecurity(t *testing.T) { Fill(t, &hmac, 0) var oas OAS + oas.Components = &openapi3.Components{} oas.Paths = make(openapi3.Paths) oas.Extensions = map[string]interface{}{ ExtensionTykAPIGateway: &XTykAPIGateway{ diff --git a/gateway/api_definition.go b/gateway/api_definition.go index f1fd1f0112d..7fa0d12ccda 100644 --- a/gateway/api_definition.go +++ b/gateway/api_definition.go @@ -685,6 +685,8 @@ func (a APIDefinitionLoader) loadDefFromFilePath(filePath string) (*APISpec, err nestDef := nestedApiDefinition{APIDefinition: &def} if def.IsOAS { loader := openapi3.NewLoader() + // use openapi3.ReadFromFile as ReadFromURIFunc since the default implementation cache spec based on file path. + loader.ReadFromURIFunc = openapi3.ReadFromFile oasDoc, err := loader.LoadFromFile(a.GetOASFilepath(filePath)) if err == nil { nestDef.OAS = &oas.OAS{T: *oasDoc} diff --git a/gateway/api_test.go b/gateway/api_test.go index 5a8d6a729f0..3484c210a1f 100644 --- a/gateway/api_test.go +++ b/gateway/api_test.go @@ -2745,7 +2745,7 @@ func TestOAS(t *testing.T) { }) t.Run("get scope public", func(t *testing.T) { _, _ = ts.Run(t, []test.TestCase{ - {AdminAuth: true, Method: http.MethodGet, Path: oasExportPath + "?mode=public", BodyMatch: `.*components`, + {AdminAuth: true, Method: http.MethodGet, Path: oasExportPath + "?mode=public", BodyMatch: `.*info`, BodyNotMatch: ".*\"x-tyk-api-gateway\":", Code: http.StatusOK, HeadersMatch: matchHeaders}, {AdminAuth: true, Method: http.MethodGet, Path: oasBasePath + "/" + oldAPIID + "/export?mode=public", BodyMatch: apidef.ErrOASGetForOldAPI.Error(), Code: http.StatusBadRequest}, @@ -2767,7 +2767,7 @@ func TestOAS(t *testing.T) { }) t.Run("get scope public", func(t *testing.T) { _, _ = ts.Run(t, []test.TestCase{ - {AdminAuth: true, Method: http.MethodGet, Path: oasBasePath + "/" + oasAPIID + "/export?mode=public", BodyMatch: `components`, BodyNotMatch: ".*\"x-tyk-api-gateway\":", Code: http.StatusOK, HeadersMatch: matchHeaders}, + {AdminAuth: true, Method: http.MethodGet, Path: oasBasePath + "/" + oasAPIID + "/export?mode=public", BodyMatch: `info`, BodyNotMatch: ".*\"x-tyk-api-gateway\":", Code: http.StatusOK, HeadersMatch: matchHeaders}, }...) }) }) @@ -2792,12 +2792,12 @@ func TestOAS(t *testing.T) { // copy OAS API, we need to manipulate tyk extension here copyOAS := func(oasAPI openapi3.T) oas.OAS { apiInOAS := oas.OAS{T: oasAPI} - oasExt := oasAPI.ExtensionProps.Extensions + oasExt := oasAPI.Extensions copyExt := make(map[string]interface{}) for k, v := range oasExt { copyExt[k] = v } - apiInOAS.T.ExtensionProps.Extensions = copyExt + apiInOAS.T.Extensions = copyExt return apiInOAS } @@ -2892,7 +2892,7 @@ func TestOAS(t *testing.T) { fillPaths(&apiInOAS) tykExt := apiInOAS.GetTykExtension() - delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) apiInOAS.T.Info.Title = "patched-oas-doc" testPatchOAS(t, ts, apiInOAS, nil, apiID) @@ -2911,7 +2911,7 @@ func TestOAS(t *testing.T) { fillReqBody(&apiInOAS, "/pets", http.MethodPost) expectedTykExt := apiInOAS.GetTykExtension() - delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) listenPath, upstreamURL, customDomain := "/listen-api/", "https://new-upstream.org", "custom-upstream.com" @@ -3033,7 +3033,7 @@ func TestOAS(t *testing.T) { fillPaths(&apiInOAS) tykExt := apiInOAS.GetTykExtension() - delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) apiInOAS.T.Info.Title = "patched-oas-doc" @@ -3060,7 +3060,7 @@ func TestOAS(t *testing.T) { fillPaths(&apiInOAS) tykExt := apiInOAS.GetTykExtension() - delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) apiInOAS.T.Info.Title = "patched-oas-doc" @@ -3093,7 +3093,7 @@ func TestOAS(t *testing.T) { t.Run("error on invalid upstreamURL", func(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) upstreamURL := "new-upstream.org" @@ -3115,7 +3115,7 @@ func TestOAS(t *testing.T) { t.Run("empty apiID", func(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) patchPath := fmt.Sprintf("/tyk/apis/oas/%s", " ") @@ -3128,7 +3128,7 @@ func TestOAS(t *testing.T) { t.Run("malformed body", func(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) _, _ = ts.Run(t, []test.TestCase{ {AdminAuth: true, Method: http.MethodPatch, Path: patchPath, Data: `oas-body`, @@ -3140,7 +3140,7 @@ func TestOAS(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) nonExistingAPIID := "non-existing-api-id" patchPath := fmt.Sprintf("/tyk/apis/oas/%s", nonExistingAPIID) @@ -3172,7 +3172,7 @@ func TestOAS(t *testing.T) { ts.Gw.SetConfig(conf) }() - delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) _, _ = ts.Run(t, []test.TestCase{ {AdminAuth: true, Method: http.MethodPatch, Path: patchPath, Data: &apiInOAS, @@ -3210,7 +3210,7 @@ func TestOAS(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.T.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.T.Extensions, oas.ExtensionTykAPIGateway) apiInOAS.Paths = nil patchPath := fmt.Sprintf("/tyk/apis/oas/%s", apiID) @@ -3241,8 +3241,8 @@ func TestOAS(t *testing.T) { _, _ = ts.Run(t, []test.TestCase{ {Method: http.MethodGet, Path: listenPath, Code: http.StatusOK}, - {AdminAuth: true, Method: http.MethodGet, Path: path, BodyNotMatch: "components", Code: http.StatusOK}, - {AdminAuth: true, Method: http.MethodGet, Path: oasPath, BodyMatch: `components`, Code: http.StatusOK}, + {AdminAuth: true, Method: http.MethodGet, Path: path, BodyNotMatch: "info", Code: http.StatusOK}, + {AdminAuth: true, Method: http.MethodGet, Path: oasPath, BodyMatch: `info`, Code: http.StatusOK}, {AdminAuth: true, Method: http.MethodDelete, Path: path, BodyMatch: `"action":"deleted"`, Code: http.StatusOK}, }...) diff --git a/go.mod b/go.mod index bfb06a13839..c16f288b2f1 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/clbanning/mxj v1.8.4 github.com/evalphobia/logrus_sentry v0.8.2 github.com/gemnasium/logrus-graylog-hook v2.0.7+incompatible - github.com/getkin/kin-openapi v0.89.0 + github.com/getkin/kin-openapi v0.114.0 github.com/go-redis/redis/v8 v8.11.5 github.com/gocraft/health v0.0.0-20170925182251-8675af27fef0 github.com/golang-jwt/jwt/v4 v4.4.2 @@ -80,7 +80,7 @@ require ( require ( github.com/lonelycode/go-uuid v0.0.0-20141202165402-ed3ca8a15a93 // test github.com/nsf/jsondiff v0.0.0-20210303162244-6ea32392771e // test - github.com/stretchr/testify v1.8.0 // test + github.com/stretchr/testify v1.8.1 // test github.com/valyala/fasthttp v1.43.0 // test google.golang.org/grpc/examples v0.0.0-20220317213542-f95b001a48df // test ) diff --git a/go.sum b/go.sum index bc0dd5272ba..2e43d690fb2 100644 --- a/go.sum +++ b/go.sum @@ -245,11 +245,10 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/gemnasium/logrus-graylog-hook v2.0.7+incompatible h1:lgnKqRfXdYPljf5yj0SOYSH+i29U8E3KKzdOIWsHZno= github.com/gemnasium/logrus-graylog-hook v2.0.7+incompatible/go.mod h1:85jwR23cg8rapnMQj96B9pX4XzmkXMNAPVfnnUNP8Dk= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.89.0 h1:p4nagHchUKGn85z/f+pse4aSh50nIBOYjOhMIku2hiA= -github.com/getkin/kin-openapi v0.89.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/getkin/kin-openapi v0.114.0 h1:ar7QiJpDdlR+zSyPjrLf8mNnpoFP/lI90XcywMCFNe8= +github.com/getkin/kin-openapi v0.114.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -291,8 +290,9 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= @@ -468,6 +468,8 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= +github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= +github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -675,6 +677,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/moesif/moesifapi-go v1.0.6/go.mod h1:wRGgVy0QeiCgnjFEiD13HD2Aa7reI8nZXtCnddNnZGs= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -751,6 +755,8 @@ github.com/paulbellamy/ratecounter v0.2.0 h1:2L/RhJq+HA8gBQImDXtLPrDXK5qAj6ozWVK github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= +github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea h1:sKwxy1H95npauwu8vtF95vG/syrL0p8fSZo/XlDg5gk= github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= @@ -870,6 +876,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -878,8 +885,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v0.0.0-20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4= @@ -897,11 +905,13 @@ github.com/uber/jaeger-client-go v2.20.0+incompatible h1:ttG9wKdl2ikV/BGOtu+eb+V github.com/uber/jaeger-client-go v2.20.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -1320,6 +1330,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= From 7136fb51f45997f697fd53b03906f5493a2c8794 Mon Sep 17 00:00:00 2001 From: JordyBottelier Date: Wed, 22 Feb 2023 08:35:05 +0100 Subject: [PATCH 09/51] [TT-7132] Add hashed parameter to the swagger (#3895) # Add hashed parameter to the swagger.yml ## Description The hashed parameter was missing voor de put/delete/get key operations ## Related Issue https://github.com/TykTechnologies/tyk/issues/3894 ## Motivation and Context Code generated from the swagger will be in sync with the API. Co-authored-by: jordy --- swagger.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/swagger.yml b/swagger.yml index 1590a507801..46f26808614 100644 --- a/swagger.yml +++ b/swagger.yml @@ -1134,6 +1134,12 @@ paths: required: true schema: type: string + - description: Use the hash of the key as input instead of the full key + name: hashed + in: query + required: false + schema: + type: boolean get: summary: Get a Key description: Get session info about the specified key. Should return up to date rate limit and quota usage numbers. From 0dabdb57178f325769e5d08e3e2b300c21507775 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Thu, 23 Feb 2023 12:02:31 +0530 Subject: [PATCH 10/51] Revert "[TT-8093] bump kin-openapi to v0.114.0" (#4791) Reverts TykTechnologies/tyk#4789 This change is causing backwards compatibility issues with storage in dashboard. --- apidef/oas/default_test.go | 38 ++++----- apidef/oas/example_test.go | 17 ++--- apidef/oas/oas_test.go | 27 ++++--- apidef/oas/root_test.go | 14 ++-- apidef/oas/security.go | 6 +- apidef/oas/security_test.go | 148 +++++++++++++++--------------------- gateway/api_definition.go | 2 - gateway/api_test.go | 32 ++++---- go.mod | 4 +- go.sum | 25 ++---- 10 files changed, 130 insertions(+), 183 deletions(-) diff --git a/apidef/oas/default_test.go b/apidef/oas/default_test.go index 673d4a1cf3e..c8177c60352 100644 --- a/apidef/oas/default_test.go +++ b/apidef/oas/default_test.go @@ -168,7 +168,7 @@ func TestOAS_BuildDefaultTykExtension(t *testing.T) { {testSSMyAuth: []string{}, testSSMyAuthWithAnd: []string{}}, {testSSMyAuthWithOR: []string{}}, }, - Components: &openapi3.Components{ + Components: openapi3.Components{ SecuritySchemes: openapi3.SecuritySchemes{ testSSMyAuth: &openapi3.SecuritySchemeRef{ Value: openapi3.NewSecurityScheme().WithType(typeAPIKey).WithIn(header).WithName(testHeader), @@ -1383,14 +1383,12 @@ func TestOAS_importAuthentication(t *testing.T) { jwtScheme.Scheme = schemeBearer jwtScheme.BearerFormat = bearerFormatJWT - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - testSecurityNameToken: &openapi3.SecuritySchemeRef{ - Value: tokenScheme, - }, - testSecurityNameJWT: &openapi3.SecuritySchemeRef{ - Value: jwtScheme, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + testSecurityNameToken: &openapi3.SecuritySchemeRef{ + Value: tokenScheme, + }, + testSecurityNameJWT: &openapi3.SecuritySchemeRef{ + Value: jwtScheme, }, } @@ -1438,11 +1436,9 @@ func TestOAS_importAuthentication(t *testing.T) { securityScheme.In = cookie securityScheme.Name = testCookieName - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - testSecurityNameToken: &openapi3.SecuritySchemeRef{ - Value: securityScheme, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + testSecurityNameToken: &openapi3.SecuritySchemeRef{ + Value: securityScheme, }, } @@ -1508,14 +1504,12 @@ func TestOAS_importAuthentication(t *testing.T) { jwtScheme.Scheme = schemeBearer jwtScheme.BearerFormat = bearerFormatJWT - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - testSecurityNameToken: &openapi3.SecuritySchemeRef{ - Value: tokenScheme, - }, - testSecurityNameJWT: &openapi3.SecuritySchemeRef{ - Value: jwtScheme, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + testSecurityNameToken: &openapi3.SecuritySchemeRef{ + Value: tokenScheme, + }, + testSecurityNameJWT: &openapi3.SecuritySchemeRef{ + Value: jwtScheme, }, } diff --git a/apidef/oas/example_test.go b/apidef/oas/example_test.go index b41d4ba0997..7c7b1f1bb1a 100644 --- a/apidef/oas/example_test.go +++ b/apidef/oas/example_test.go @@ -46,13 +46,9 @@ func Test_exampleExtractor(t *testing.T) { "example": "bird" } }, - "example": { - "name": "duck" - } + "example": "duck" }`, - map[string]interface{}{ - "name": "duck", - }, + "duck", }, { "boolean", @@ -97,7 +93,7 @@ func Test_exampleExtractor(t *testing.T) { `{ "type": "string", "example": "bird", - "enum": ["duck", "bird"] + "enum": ["duck"] }`, "bird", }, @@ -148,9 +144,9 @@ func Test_exampleExtractor(t *testing.T) { "type": "string", "example": "bird" }, - "example": ["duck"] + "example": "duck" }`, - []interface{}{"duck"}, + "duck", }, } @@ -159,8 +155,7 @@ func Test_exampleExtractor(t *testing.T) { err := schemaRef.UnmarshalJSON([]byte(c.schema)) assert.NoError(t, err) - err = schemaRef.Validate(context.Background()) - assert.NoError(t, err) + assert.NoError(t, schemaRef.Validate(context.Background())) actualRes := ExampleExtractor(schemaRef) diff --git a/apidef/oas/oas_test.go b/apidef/oas/oas_test.go index 88b80a3b875..31b0fd992f2 100644 --- a/apidef/oas/oas_test.go +++ b/apidef/oas/oas_test.go @@ -20,7 +20,6 @@ func TestOAS(t *testing.T) { t.Parallel() var emptyOASPaths OAS - emptyOASPaths.Components = &openapi3.Components{} emptyOASPaths.Paths = make(openapi3.Paths) emptyOASPaths.SetTykExtension(&XTykAPIGateway{}) @@ -39,7 +38,6 @@ func TestOAS(t *testing.T) { t.Parallel() var nilOASPaths OAS - nilOASPaths.Components = &openapi3.Components{} nilOASPaths.SetTykExtension(&XTykAPIGateway{}) var convertedAPI apidef.APIDefinition @@ -59,7 +57,6 @@ func TestOAS(t *testing.T) { t.Parallel() var oasWithPaths OAS - oasWithPaths.Components = &openapi3.Components{} oasWithPaths.SetTykExtension(&XTykAPIGateway{ Middleware: &Middleware{ Operations: Operations{ @@ -416,10 +413,12 @@ func TestOAS_MarshalJSON(t *testing.T) { Info: &openapi3.Info{ Title: "OAS Doc", }, - Extensions: map[string]interface{}{ - ExtensionTykAPIGateway: XTykAPIGateway{ - Info: Info{ - Name: "OAS API", + ExtensionProps: openapi3.ExtensionProps{ + Extensions: map[string]interface{}{ + ExtensionTykAPIGateway: XTykAPIGateway{ + Info: Info{ + Name: "OAS API", + }, }, }, }, @@ -430,7 +429,7 @@ func TestOAS_MarshalJSON(t *testing.T) { copyOAS := s intVal := 9 byteRep, _ := json.Marshal(intVal) - copyOAS.Extensions["x-abcd"] = byteRep + copyOAS.ExtensionProps.Extensions["x-abcd"] = byteRep data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -441,7 +440,7 @@ func TestOAS_MarshalJSON(t *testing.T) { copyOAS := s floatVal := 9.5 byteRep, _ := json.Marshal(floatVal) - copyOAS.Extensions["x-abcd"] = byteRep + copyOAS.ExtensionProps.Extensions["x-abcd"] = byteRep data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -452,7 +451,7 @@ func TestOAS_MarshalJSON(t *testing.T) { copyOAS := s boolVal := false byteRep, _ := json.Marshal(boolVal) - copyOAS.Extensions["x-abcd"] = byteRep + copyOAS.ExtensionProps.Extensions["x-abcd"] = byteRep data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -461,7 +460,7 @@ func TestOAS_MarshalJSON(t *testing.T) { t.Run("nil", func(t *testing.T) { copyOAS := s - copyOAS.Extensions["x-abcd"] = nil + copyOAS.ExtensionProps.Extensions["x-abcd"] = nil data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -470,7 +469,7 @@ func TestOAS_MarshalJSON(t *testing.T) { t.Run("string", func(t *testing.T) { copyOAS := s - copyOAS.Extensions["x-abcd"] = []byte(`"hello"`) + copyOAS.ExtensionProps.Extensions["x-abcd"] = []byte(`"hello"`) data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -479,7 +478,7 @@ func TestOAS_MarshalJSON(t *testing.T) { t.Run("map", func(t *testing.T) { copyOAS := s - copyOAS.Extensions["x-abcd"] = []byte(`{"key":"value"}`) + copyOAS.ExtensionProps.Extensions["x-abcd"] = []byte(`{"key":"value"}`) data, err := copyOAS.MarshalJSON() assert.NoError(t, err) @@ -488,7 +487,7 @@ func TestOAS_MarshalJSON(t *testing.T) { t.Run("array", func(t *testing.T) { copyOAS := s - copyOAS.Extensions["x-abcd"] = []byte(`[{"key":"value"},{"key":"value"}]`) + copyOAS.ExtensionProps.Extensions["x-abcd"] = []byte(`[{"key":"value"},{"key":"value"}]`) data, err := copyOAS.MarshalJSON() assert.NoError(t, err) diff --git a/apidef/oas/root_test.go b/apidef/oas/root_test.go index 0b7fe726d4c..e2670eeb2f1 100644 --- a/apidef/oas/root_test.go +++ b/apidef/oas/root_test.go @@ -36,14 +36,12 @@ func TestXTykAPIGateway(t *testing.T) { }, } - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - "custom": { - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - Name: "x-query", - In: "query", - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + "custom": { + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + Name: "x-query", + In: "query", }, }, } diff --git a/apidef/oas/security.go b/apidef/oas/security.go index fa8bd98d987..9926d23511b 100644 --- a/apidef/oas/security.go +++ b/apidef/oas/security.go @@ -613,10 +613,6 @@ func (s *OAS) fillSecurity(api apidef.APIDefinition) { tykAuthentication.Fill(api) - if s.Components == nil { - s.Components = &openapi3.Components{} - } - s.fillToken(api) s.fillJWT(api) s.fillBasic(api) @@ -644,7 +640,7 @@ func (s *OAS) extractSecurityTo(api *apidef.APIDefinition) { api.AuthConfigs = make(map[string]apidef.AuthConfig) } - if len(s.Security) == 0 || s.Components == nil || len(s.Components.SecuritySchemes) == 0 { + if len(s.Security) == 0 || len(s.Components.SecuritySchemes) == 0 { return } diff --git a/apidef/oas/security_test.go b/apidef/oas/security_test.go index 511e3a4da93..738f4b4bfad 100644 --- a/apidef/oas/security_test.go +++ b/apidef/oas/security_test.go @@ -49,10 +49,6 @@ func TestOAS_ApiKeyScheme(t *testing.T) { } check := func(in, name string, ac apidef.AuthConfig, s OAS) { - if s.Components == nil { - s.Components = &openapi3.Components{} - } - s.fillAPIKeyScheme(&ac) expectedAC := ac @@ -115,17 +111,16 @@ func TestOAS_ApiKeyScheme(t *testing.T) { }) testOAS := func(in, name string) (oas OAS) { - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - authName: &openapi3.SecuritySchemeRef{ - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - In: in, - Name: name, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + authName: &openapi3.SecuritySchemeRef{ + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + In: in, + Name: name, }, }, } + return } @@ -158,14 +153,12 @@ func TestOAS_Token(t *testing.T) { }, } - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - Name: "x-query", - In: query, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + Name: "x-query", + In: query, }, }, } @@ -190,9 +183,7 @@ func TestOAS_Token(t *testing.T) { oas.extractTokenTo(&api, securityName) var convertedOAS OAS - convertedOAS.Components = &openapi3.Components{ - SecuritySchemes: oas.Components.SecuritySchemes, - } + convertedOAS.Components.SecuritySchemes = oas.Components.SecuritySchemes convertedOAS.SetTykExtension(&XTykAPIGateway{Server: Server{Authentication: &Authentication{SecuritySchemes: SecuritySchemes{}}}}) convertedOAS.fillToken(api) @@ -212,21 +203,19 @@ func TestOAS_Token_MultipleSecuritySchemes(t *testing.T) { }, } - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - Name: "x-query", - In: query, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + Name: "x-query", + In: query, }, - securityName2: { - Value: &openapi3.SecurityScheme{ - Type: typeAPIKey, - Name: "x-header", - In: header, - }, + }, + securityName2: { + Value: &openapi3.SecurityScheme{ + Type: typeAPIKey, + Name: "x-header", + In: header, }, }, } @@ -307,14 +296,12 @@ func TestOAS_JWT(t *testing.T) { }, } - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeHTTP, - Scheme: schemeBearer, - BearerFormat: bearerFormatJWT, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeHTTP, + Scheme: schemeBearer, + BearerFormat: bearerFormatJWT, }, }, } @@ -338,7 +325,6 @@ func TestOAS_JWT(t *testing.T) { oas.extractJWTTo(&api, securityName) var convertedOAS OAS - convertedOAS.Components = &openapi3.Components{} convertedOAS.SetTykExtension(&XTykAPIGateway{Server: Server{Authentication: &Authentication{SecuritySchemes: SecuritySchemes{}}}}) convertedOAS.fillJWT(api) @@ -355,13 +341,11 @@ func TestOAS_Basic(t *testing.T) { }, } - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeHTTP, - Scheme: schemeBasic, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeHTTP, + Scheme: schemeBasic, }, }, } @@ -385,7 +369,6 @@ func TestOAS_Basic(t *testing.T) { oas.extractBasicTo(&api, securityName) var convertedOAS OAS - convertedOAS.Components = &openapi3.Components{} convertedOAS.SetTykExtension(&XTykAPIGateway{Server: Server{Authentication: &Authentication{SecuritySchemes: SecuritySchemes{}}}}) convertedOAS.fillBasic(api) @@ -407,20 +390,18 @@ func TestOAS_OAuth(t *testing.T) { }, } - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeOAuth2, - Flows: &openapi3.OAuthFlows{ - AuthorizationCode: &openapi3.OAuthFlow{ - AuthorizationURL: "{api-url}/oauth/authorize", - TokenURL: "{api-url}/oauth/token", - Scopes: scopes, - }, - ClientCredentials: &openapi3.OAuthFlow{ - Scopes: scopes, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeOAuth2, + Flows: &openapi3.OAuthFlows{ + AuthorizationCode: &openapi3.OAuthFlow{ + AuthorizationURL: "{api-url}/oauth/authorize", + TokenURL: "{api-url}/oauth/token", + Scopes: scopes, + }, + ClientCredentials: &openapi3.OAuthFlow{ + Scopes: scopes, }, }, }, @@ -445,7 +426,7 @@ func TestOAS_OAuth(t *testing.T) { oas.ExtractTo(&api) var convertedOAS OAS - convertedOAS.Components = &openapi3.Components{SecuritySchemes: oas.Components.SecuritySchemes} + convertedOAS.Components.SecuritySchemes = oas.Components.SecuritySchemes convertedOAS.Fill(api) flows := convertedOAS.Components.SecuritySchemes[securityName].Value.Flows @@ -471,20 +452,18 @@ func TestOAS_ExternalOAuth(t *testing.T) { }, } - oas.Components = &openapi3.Components{ - SecuritySchemes: openapi3.SecuritySchemes{ - securityName: { - Value: &openapi3.SecurityScheme{ - Type: typeOAuth2, - Flows: &openapi3.OAuthFlows{ - AuthorizationCode: &openapi3.OAuthFlow{ - AuthorizationURL: "{api-url}/oauth/authorize", - TokenURL: "{api-url}/oauth/token", - Scopes: scopes, - }, - ClientCredentials: &openapi3.OAuthFlow{ - Scopes: scopes, - }, + oas.Components.SecuritySchemes = openapi3.SecuritySchemes{ + securityName: { + Value: &openapi3.SecurityScheme{ + Type: typeOAuth2, + Flows: &openapi3.OAuthFlows{ + AuthorizationCode: &openapi3.OAuthFlow{ + AuthorizationURL: "{api-url}/oauth/authorize", + TokenURL: "{api-url}/oauth/token", + Scopes: scopes, + }, + ClientCredentials: &openapi3.OAuthFlow{ + Scopes: scopes, }, }, }, @@ -509,7 +488,7 @@ func TestOAS_ExternalOAuth(t *testing.T) { oas.ExtractTo(&api) var convertedOAS OAS - convertedOAS.Components = &openapi3.Components{SecuritySchemes: oas.Components.SecuritySchemes} + convertedOAS.Components.SecuritySchemes = oas.Components.SecuritySchemes convertedOAS.Fill(api) flows := convertedOAS.Components.SecuritySchemes[securityName].Value.Flows @@ -584,7 +563,6 @@ func TestOAS_TykAuthentication_NoOASSecurity(t *testing.T) { Fill(t, &hmac, 0) var oas OAS - oas.Components = &openapi3.Components{} oas.Paths = make(openapi3.Paths) oas.Extensions = map[string]interface{}{ ExtensionTykAPIGateway: &XTykAPIGateway{ diff --git a/gateway/api_definition.go b/gateway/api_definition.go index 7fa0d12ccda..f1fd1f0112d 100644 --- a/gateway/api_definition.go +++ b/gateway/api_definition.go @@ -685,8 +685,6 @@ func (a APIDefinitionLoader) loadDefFromFilePath(filePath string) (*APISpec, err nestDef := nestedApiDefinition{APIDefinition: &def} if def.IsOAS { loader := openapi3.NewLoader() - // use openapi3.ReadFromFile as ReadFromURIFunc since the default implementation cache spec based on file path. - loader.ReadFromURIFunc = openapi3.ReadFromFile oasDoc, err := loader.LoadFromFile(a.GetOASFilepath(filePath)) if err == nil { nestDef.OAS = &oas.OAS{T: *oasDoc} diff --git a/gateway/api_test.go b/gateway/api_test.go index 3484c210a1f..5a8d6a729f0 100644 --- a/gateway/api_test.go +++ b/gateway/api_test.go @@ -2745,7 +2745,7 @@ func TestOAS(t *testing.T) { }) t.Run("get scope public", func(t *testing.T) { _, _ = ts.Run(t, []test.TestCase{ - {AdminAuth: true, Method: http.MethodGet, Path: oasExportPath + "?mode=public", BodyMatch: `.*info`, + {AdminAuth: true, Method: http.MethodGet, Path: oasExportPath + "?mode=public", BodyMatch: `.*components`, BodyNotMatch: ".*\"x-tyk-api-gateway\":", Code: http.StatusOK, HeadersMatch: matchHeaders}, {AdminAuth: true, Method: http.MethodGet, Path: oasBasePath + "/" + oldAPIID + "/export?mode=public", BodyMatch: apidef.ErrOASGetForOldAPI.Error(), Code: http.StatusBadRequest}, @@ -2767,7 +2767,7 @@ func TestOAS(t *testing.T) { }) t.Run("get scope public", func(t *testing.T) { _, _ = ts.Run(t, []test.TestCase{ - {AdminAuth: true, Method: http.MethodGet, Path: oasBasePath + "/" + oasAPIID + "/export?mode=public", BodyMatch: `info`, BodyNotMatch: ".*\"x-tyk-api-gateway\":", Code: http.StatusOK, HeadersMatch: matchHeaders}, + {AdminAuth: true, Method: http.MethodGet, Path: oasBasePath + "/" + oasAPIID + "/export?mode=public", BodyMatch: `components`, BodyNotMatch: ".*\"x-tyk-api-gateway\":", Code: http.StatusOK, HeadersMatch: matchHeaders}, }...) }) }) @@ -2792,12 +2792,12 @@ func TestOAS(t *testing.T) { // copy OAS API, we need to manipulate tyk extension here copyOAS := func(oasAPI openapi3.T) oas.OAS { apiInOAS := oas.OAS{T: oasAPI} - oasExt := oasAPI.Extensions + oasExt := oasAPI.ExtensionProps.Extensions copyExt := make(map[string]interface{}) for k, v := range oasExt { copyExt[k] = v } - apiInOAS.T.Extensions = copyExt + apiInOAS.T.ExtensionProps.Extensions = copyExt return apiInOAS } @@ -2892,7 +2892,7 @@ func TestOAS(t *testing.T) { fillPaths(&apiInOAS) tykExt := apiInOAS.GetTykExtension() - delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) apiInOAS.T.Info.Title = "patched-oas-doc" testPatchOAS(t, ts, apiInOAS, nil, apiID) @@ -2911,7 +2911,7 @@ func TestOAS(t *testing.T) { fillReqBody(&apiInOAS, "/pets", http.MethodPost) expectedTykExt := apiInOAS.GetTykExtension() - delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) listenPath, upstreamURL, customDomain := "/listen-api/", "https://new-upstream.org", "custom-upstream.com" @@ -3033,7 +3033,7 @@ func TestOAS(t *testing.T) { fillPaths(&apiInOAS) tykExt := apiInOAS.GetTykExtension() - delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) apiInOAS.T.Info.Title = "patched-oas-doc" @@ -3060,7 +3060,7 @@ func TestOAS(t *testing.T) { fillPaths(&apiInOAS) tykExt := apiInOAS.GetTykExtension() - delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) apiInOAS.T.Info.Title = "patched-oas-doc" @@ -3093,7 +3093,7 @@ func TestOAS(t *testing.T) { t.Run("error on invalid upstreamURL", func(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) upstreamURL := "new-upstream.org" @@ -3115,7 +3115,7 @@ func TestOAS(t *testing.T) { t.Run("empty apiID", func(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) patchPath := fmt.Sprintf("/tyk/apis/oas/%s", " ") @@ -3128,7 +3128,7 @@ func TestOAS(t *testing.T) { t.Run("malformed body", func(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) _, _ = ts.Run(t, []test.TestCase{ {AdminAuth: true, Method: http.MethodPatch, Path: patchPath, Data: `oas-body`, @@ -3140,7 +3140,7 @@ func TestOAS(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) nonExistingAPIID := "non-existing-api-id" patchPath := fmt.Sprintf("/tyk/apis/oas/%s", nonExistingAPIID) @@ -3172,7 +3172,7 @@ func TestOAS(t *testing.T) { ts.Gw.SetConfig(conf) }() - delete(apiInOAS.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) _, _ = ts.Run(t, []test.TestCase{ {AdminAuth: true, Method: http.MethodPatch, Path: patchPath, Data: &apiInOAS, @@ -3210,7 +3210,7 @@ func TestOAS(t *testing.T) { apiInOAS := copyOAS(oasAPI) fillPaths(&apiInOAS) - delete(apiInOAS.T.Extensions, oas.ExtensionTykAPIGateway) + delete(apiInOAS.T.ExtensionProps.Extensions, oas.ExtensionTykAPIGateway) apiInOAS.Paths = nil patchPath := fmt.Sprintf("/tyk/apis/oas/%s", apiID) @@ -3241,8 +3241,8 @@ func TestOAS(t *testing.T) { _, _ = ts.Run(t, []test.TestCase{ {Method: http.MethodGet, Path: listenPath, Code: http.StatusOK}, - {AdminAuth: true, Method: http.MethodGet, Path: path, BodyNotMatch: "info", Code: http.StatusOK}, - {AdminAuth: true, Method: http.MethodGet, Path: oasPath, BodyMatch: `info`, Code: http.StatusOK}, + {AdminAuth: true, Method: http.MethodGet, Path: path, BodyNotMatch: "components", Code: http.StatusOK}, + {AdminAuth: true, Method: http.MethodGet, Path: oasPath, BodyMatch: `components`, Code: http.StatusOK}, {AdminAuth: true, Method: http.MethodDelete, Path: path, BodyMatch: `"action":"deleted"`, Code: http.StatusOK}, }...) diff --git a/go.mod b/go.mod index c16f288b2f1..bfb06a13839 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/clbanning/mxj v1.8.4 github.com/evalphobia/logrus_sentry v0.8.2 github.com/gemnasium/logrus-graylog-hook v2.0.7+incompatible - github.com/getkin/kin-openapi v0.114.0 + github.com/getkin/kin-openapi v0.89.0 github.com/go-redis/redis/v8 v8.11.5 github.com/gocraft/health v0.0.0-20170925182251-8675af27fef0 github.com/golang-jwt/jwt/v4 v4.4.2 @@ -80,7 +80,7 @@ require ( require ( github.com/lonelycode/go-uuid v0.0.0-20141202165402-ed3ca8a15a93 // test github.com/nsf/jsondiff v0.0.0-20210303162244-6ea32392771e // test - github.com/stretchr/testify v1.8.1 // test + github.com/stretchr/testify v1.8.0 // test github.com/valyala/fasthttp v1.43.0 // test google.golang.org/grpc/examples v0.0.0-20220317213542-f95b001a48df // test ) diff --git a/go.sum b/go.sum index 2e43d690fb2..bc0dd5272ba 100644 --- a/go.sum +++ b/go.sum @@ -245,10 +245,11 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/gemnasium/logrus-graylog-hook v2.0.7+incompatible h1:lgnKqRfXdYPljf5yj0SOYSH+i29U8E3KKzdOIWsHZno= github.com/gemnasium/logrus-graylog-hook v2.0.7+incompatible/go.mod h1:85jwR23cg8rapnMQj96B9pX4XzmkXMNAPVfnnUNP8Dk= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.114.0 h1:ar7QiJpDdlR+zSyPjrLf8mNnpoFP/lI90XcywMCFNe8= -github.com/getkin/kin-openapi v0.114.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.89.0 h1:p4nagHchUKGn85z/f+pse4aSh50nIBOYjOhMIku2hiA= +github.com/getkin/kin-openapi v0.89.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -290,9 +291,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= @@ -468,8 +468,6 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -677,8 +675,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/moesif/moesifapi-go v1.0.6/go.mod h1:wRGgVy0QeiCgnjFEiD13HD2Aa7reI8nZXtCnddNnZGs= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -755,8 +751,6 @@ github.com/paulbellamy/ratecounter v0.2.0 h1:2L/RhJq+HA8gBQImDXtLPrDXK5qAj6ozWVK github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= -github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= -github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea h1:sKwxy1H95npauwu8vtF95vG/syrL0p8fSZo/XlDg5gk= github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= @@ -876,7 +870,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -885,9 +878,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v0.0.0-20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4= @@ -905,13 +897,11 @@ github.com/uber/jaeger-client-go v2.20.0+incompatible h1:ttG9wKdl2ikV/BGOtu+eb+V github.com/uber/jaeger-client-go v2.20.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -1330,7 +1320,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= From b595779b7b32cf2680f5d2741266e4ec283e19a8 Mon Sep 17 00:00:00 2001 From: Esteban Ricardo Mirizio Date: Fri, 24 Feb 2023 02:52:25 -0300 Subject: [PATCH 11/51] update backend conf (#4775) Change remote backend to be managed by terraform cloud instead of S3 bucket --- .github/workflows/pac.yml | 46 ++++++++++++++++++++- ci/repo-policy/main.tf | 11 ++--- ci/repo-policy/modules/github-repos/repo.tf | 1 + 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pac.yml b/.github/workflows/pac.yml index 3a78e2daef3..328a23cd700 100644 --- a/.github/workflows/pac.yml +++ b/.github/workflows/pac.yml @@ -7,17 +7,19 @@ on: env: TERRAFORM_DIR: "./ci/repo-policy" - GITHUB_TOKEN: ${{ secrets.ITS_GH_TOKEN }} jobs: terraform: runs-on: ubuntu-latest permissions: id-token: write + pull-requests: write steps: - name: Checkout uses: actions/checkout@v3 + with: + fetch-depth: 1 - uses: aws-actions/configure-aws-credentials@v1 with: @@ -28,16 +30,56 @@ jobs: - uses: hashicorp/setup-terraform@v2 with: terraform_version: 1.3.0 + cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} - name: Terraform Init working-directory: ${{ env.TERRAFORM_DIR }} id: init run: terraform init -input=false + - name: Terraform Validate + id: validate + run: terraform validate -no-color + - name: Terraform Plan working-directory: ${{ env.TERRAFORM_DIR }} id: plan run: | echo "::group::Terraform Plan" - terraform validate && terraform plan + terraform plan -no-color -input=false echo "::endgroup::" + continue-on-error: true + + - name: Update Pull Request + uses: actions/github-script@v6 + if: github.event_name == 'pull_request' + env: + PLAN: "terraform\n${{ steps.plan.outputs.stdout }}" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` + #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` + #### Terraform Plan 📖\`${{ steps.plan.outcome }}\` + #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` + +
Show Plan + + \`\`\`\n + ${process.env.PLAN} + \`\`\` + +
+ + *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }) + + - name: Terraform Plan Status + if: steps.plan.outcome == 'failure' + run: exit 1 \ No newline at end of file diff --git a/ci/repo-policy/main.tf b/ci/repo-policy/main.tf index 9aebd61d78e..80f98827842 100644 --- a/ci/repo-policy/main.tf +++ b/ci/repo-policy/main.tf @@ -1,11 +1,12 @@ terraform { #Being used until TFCloud can be used - backend "s3" { - bucket = "terraform-state-devenv" - key = "github-policy/tyk" - region = "eu-central-1" - dynamodb_table = "terraform-state-locks" + backend "remote" { + hostname = "app.terraform.io" + organization = "Tyk" + workspaces { + name = "repo-policy-tyk" + } } required_providers { diff --git a/ci/repo-policy/modules/github-repos/repo.tf b/ci/repo-policy/modules/github-repos/repo.tf index d3b9250ffc9..d8c273ba0d5 100644 --- a/ci/repo-policy/modules/github-repos/repo.tf +++ b/ci/repo-policy/modules/github-repos/repo.tf @@ -20,6 +20,7 @@ resource "github_repository" "repository" { allow_auto_merge = true delete_branch_on_merge = var.delete_branch_on_merge vulnerability_alerts = var.vulnerability_alerts + allow_update_branch = true has_downloads = true has_issues = true has_wiki = var.wiki From 0947a402b98cb8758c03ea27782db85efbae7ad8 Mon Sep 17 00:00:00 2001 From: Sedky Shamalah Date: Fri, 24 Feb 2023 07:20:37 -0500 Subject: [PATCH 12/51] Remove the false deprecation warning on the hard-sync limiters (non DRL) (#4401) --- checkup/checkup.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/checkup/checkup.go b/checkup/checkup.go index bada721c7f1..140af643825 100644 --- a/checkup/checkup.go +++ b/checkup/checkup.go @@ -26,7 +26,6 @@ const ( ) func Run(c *config.Config) { - legacyRateLimiters(c) allowInsecureConfigs(c) healthCheck(c) sessionLifetimeCheck(c) @@ -37,16 +36,6 @@ func Run(c *config.Config) { defaultAnalytics(c) } -func legacyRateLimiters(c *config.Config) { - if c.ManagementNode { - return - } - - if c.EnableSentinelRateLimiter || c.EnableRedisRollingLimiter { - log.Warning("SentinelRateLimiter & RedisRollingLimiter are deprecated") - } -} - func allowInsecureConfigs(c *config.Config) { if c.AllowInsecureConfigs { log.WithField("config.allow_insecure_configs", true). From 21c846cd8f6232960f54130b2a2a87795476b8cd Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 24 Feb 2023 14:03:12 +0100 Subject: [PATCH 13/51] [TT-7873] Remove swagger entry for /hello endpoint (#4798) ## Description ## Related Issue https://tyktech.atlassian.net/browse/TT-7873 Closes #4705 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why Co-authored-by: Tit Petric --- swagger.yml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/swagger.yml b/swagger.yml index 46f26808614..f4f6228cf6d 100644 --- a/swagger.yml +++ b/swagger.yml @@ -940,28 +940,6 @@ paths: schema: type: string example: "Hello Tiki" - '/tyk/{listenPath}/hello': - parameters: - - in: path - name: listenPath - required: true - description: "Listen path of loaded API" - schema: - type: string - get: - summary: Check the Health of the API - description: Should point to API domain if it has its own - tags: - - Health Checking - operationId: helloAPI - responses: - '200': - description: Success - content: - text/html: - schema: - type: string - example: "Hello Tiki" '/tyk/certs': get: parameters: From b23b83a0d159458f544e96a81c9af919aabaea9c Mon Sep 17 00:00:00 2001 From: Leonid Bugaev Date: Fri, 24 Feb 2023 16:04:20 +0300 Subject: [PATCH 14/51] [TT-7959] Add option to skip ClientCA announcement during mTLS (#4762) At the moment we announce ClientCA to the client, which is a default Go way to do mTLS, and it also gives client a hint which certificate to use, if it has many. However if you have a lot of certificates in ClientCA, like 100, it can overflow TLS handshake buffer of some clients (for example default JVM buffer is 32kb). This PR adds an option `http_server_options.skip_client_ca_announcement`, which stops announcing ClientCAs during handshake. However, if you just pass empty ClientCA, and at the same time set TLS mode to `RequireAndVerifyClientCert` it will error, because non empty ClientCA required for default mTLS verification, which is built in to Go TLS library. Which means that we have to "disable" default client cert validation by setting TLS mode to `RequestClientCert`, and implement our "own" verification. Thankfully Go provides TLS hook `VerifyPeerCertificate` where you can add custom validation. It does not have access to client hello message, and other dynamic variables, so it needs to use a closure (this way is recommended by Go team). Inside this check duplicate exactly the same code which Go TLS library has, but instead of depending on `config.ClientCA` it use certificate pool passed via closure. mTLS unit test now runs 2 times, with and without clientCA announcement. I was afraid to make new way "default" because mTLS is weird, and better have a lot of options to tune it. But despite new config variable is added, this PR still can count as bugfix. ## Motivation and Context We have a client who is hitting overflow issue, without a way to overcome it. And number of used certificates only will grow. ## Types of changes - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --------- Co-authored-by: jeff --- cli/linter/schema.json | 3 +++ config/config.go | 5 +++++ gateway/cert.go | 33 +++++++++++++++++++++++++++++++++ gateway/cert_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ gateway/testutil.go | 10 ++++++++-- 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/cli/linter/schema.json b/cli/linter/schema.json index c1ab4bbf01c..605ab4ba949 100644 --- a/cli/linter/schema.json +++ b/cli/linter/schema.json @@ -563,6 +563,9 @@ "override_defaults": { "type": "boolean" }, + "skip_client_ca_announcement": { + "type": "boolean" + }, "read_timeout": { "type": "integer" }, diff --git a/config/config.go b/config/config.go index c59bb3950db..cabb316c7d4 100644 --- a/config/config.go +++ b/config/config.go @@ -394,6 +394,11 @@ type HttpServerOptionsConfig struct { // Maximum TLS version. MaxVersion uint16 `json:"max_version"` + // When mTLS enabled, this option allows to skip client CA announcement in the TLS handshake. + // This option is useful when you have a lot of ClientCAs and you want to reduce the handshake overhead, as some clients can hit TLS handshake limits. + // This option does not give any hints to the client, on which certificate to pick (but this is very rare situation when it is required) + SkipClientCAAnnouncement bool `json:"skip_client_ca_announcement"` + // Set this to the number of seconds that Tyk uses to flush content from the proxied upstream connection to the open downstream connection. // This option needed be set for streaming protocols like Server Side Events, or gRPC streaming. FlushInterval int `json:"flush_interval"` diff --git a/gateway/cert.go b/gateway/cert.go index 161a01949b1..19121ee5618 100644 --- a/gateway/cert.go +++ b/gateway/cert.go @@ -281,6 +281,30 @@ var tlsConfigCache = cache.New(60*time.Second, 60*time.Minute) var tlsConfigMu sync.Mutex +func getClientValidator(helloInfo *tls.ClientHelloInfo, certPool *x509.CertPool) func([][]byte, [][]*x509.Certificate) error { + return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + if len(rawCerts) == 0 { + return errors.New("x509: missing client certificate") + } + + cert, certErr := x509.ParseCertificate(rawCerts[0]) + if certErr != nil { + return certErr + } + + opts := x509.VerifyOptions{ + Roots: certPool, + CurrentTime: time.Now(), + Intermediates: x509.NewCertPool(), + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + + _, err := cert.Verify(opts) + + return err + } +} + func (gw *Gateway) getTLSConfigForClient(baseConfig *tls.Config, listenPort int) func(hello *tls.ClientHelloInfo) (*tls.Config, error) { gwConfig := gw.GetConfig() // Supporting legacy certificate configuration @@ -446,8 +470,17 @@ func (gw *Gateway) getTLSConfigForClient(baseConfig *tls.Config, listenPort int) newConfig.ClientAuth = domainRequireCert[""] } + if gwConfig.HttpServerOptions.SkipClientCAAnnouncement { + if newConfig.ClientAuth == tls.RequireAndVerifyClientCert { + newConfig.VerifyPeerCertificate = getClientValidator(hello, newConfig.ClientCAs) + } + newConfig.ClientCAs = x509.NewCertPool() + newConfig.ClientAuth = tls.RequestClientCert + } + // Cache the config tlsConfigCache.Set(hello.ServerName+listenPortStr, newConfig, cache.DefaultExpiration) + return newConfig, nil } } diff --git a/gateway/cert_test.go b/gateway/cert_test.go index 852e8ad4a80..d4f18fcc7c2 100644 --- a/gateway/cert_test.go +++ b/gateway/cert_test.go @@ -281,7 +281,19 @@ func TestGatewayControlAPIMutualTLS(t *testing.T) { }) } +// Run 2 times to ensure that both methods backward compatible func TestAPIMutualTLS(t *testing.T) { + t.Run("Skip ClientCA announce", func(t *testing.T) { + testAPIMutualTLSHelper(t, true) + }) + + t.Run("Announce ClientCA", func(t *testing.T) { + testAPIMutualTLSHelper(t, false) + }) +} + +func testAPIMutualTLSHelper(t *testing.T, skipCAAnnounce bool) { + t.Helper() serverCertPem, _, combinedPEM, _ := certs.GenServerCertificate() certID, _, _ := certs.GetCertIDAndChainPEM(combinedPEM, "") @@ -290,6 +302,7 @@ func TestAPIMutualTLS(t *testing.T) { globalConf.EnableCustomDomains = true globalConf.HttpServerOptions.UseSSL = true globalConf.HttpServerOptions.SSLCertificates = []string{certID} + globalConf.HttpServerOptions.SkipClientCAAnnouncement = skipCAAnnounce } ts := StartTest(conf) defer ts.Close() @@ -305,6 +318,34 @@ func TestAPIMutualTLS(t *testing.T) { clientCertPem, _, _, clientCert := certs.GenCertificate(&x509.Certificate{}, false) clientCertPem2, _, _, clientCert2 := certs.GenCertificate(&x509.Certificate{}, false) + t.Run("acceptable CAs from server", func(t *testing.T) { + tlsConfig := GetTLSConfig(&clientCert, serverCertPem) + tlsConfig.GetClientCertificate = func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) { + assert.Equal(t, skipCAAnnounce, len(info.AcceptableCAs) == 0) + return &clientCert, nil + } + + transport := &http.Transport{TLSClientConfig: tlsConfig} + httpClient := &http.Client{Transport: transport} + clientCertID, err := ts.Gw.CertificateManager.Add(clientCertPem, "") + assert.NoError(t, err) + + ts.Gw.BuildAndLoadAPI(func(spec *APISpec) { + spec.Domain = "localhost" + spec.Proxy.ListenPath = "/" + spec.UseMutualTLSAuth = true + spec.ClientCertificates = []string{clientCertID} + }) + + _, _ = ts.Run(t, test.TestCase{ + Code: 200, Client: httpClient, Domain: "localhost", + }) + + ts.Gw.CertificateManager.Delete(clientCertID, "") + ts.Gw.CertificateManager.FlushCache() + tlsConfigCache.Flush() + }) + t.Run("SNI and domain per API", func(t *testing.T) { t.Run("API without mutual TLS", func(t *testing.T) { client := GetTLSClient(&clientCert, serverCertPem) diff --git a/gateway/testutil.go b/gateway/testutil.go index 26f6c880c30..94882806124 100644 --- a/gateway/testutil.go +++ b/gateway/testutil.go @@ -1289,8 +1289,7 @@ func (s *Test) RunExt(t testing.TB, testCases ...test.TestCase) { } } -func GetTLSClient(cert *tls.Certificate, caCert []byte) *http.Client { - // Setup HTTPS client +func GetTLSConfig(cert *tls.Certificate, caCert []byte) *tls.Config { tlsConfig := &tls.Config{} if cert != nil { @@ -1306,6 +1305,13 @@ func GetTLSClient(cert *tls.Certificate, caCert []byte) *http.Client { tlsConfig.InsecureSkipVerify = true } + return tlsConfig +} + +func GetTLSClient(cert *tls.Certificate, caCert []byte) *http.Client { + // Setup HTTPS client + tlsConfig := GetTLSConfig(cert, caCert) + transport := &http.Transport{TLSClientConfig: tlsConfig} return &http.Client{Transport: transport} From aeb800437b7ca650d9c2141d18e751a729a57796 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Mon, 27 Feb 2023 12:05:54 +0100 Subject: [PATCH 15/51] [TT-7972] Fix/jaegertracing import path (#4760) Bump uber/jaegertracing to @master versions to hopefully resolve the following issue: ``` task: [plugins-gowork-testplugin:test-plugin] ./tyk plugin load -f ../plugin/plugin.so -s MyPluginPre tyk: error: unexpected error: plugin.Open("../plugin/plugin"): plugin was built with a different version of package github.com/uber/jaeger-client-go/log, try --help task: Failed to run task "plugins-gowork-testplugin:default": task: Failed to run task "plugins-gowork-testplugin:test-plugin": exit status 1 ``` https://tyktech.atlassian.net/browse/TT-7972 --------- Co-authored-by: Tit Petric Co-authored-by: tbuchaillot --- config/testdata/expect.env.jaeger.json | 9 +++++++-- config/testdata/expect.jaeger.json | 9 +++++++-- config/testdata/jaeger.json | 8 ++++++-- go.mod | 17 ++++++++--------- go.sum | 18 +++++++++++++----- trace/jaeger/config_test.go | 4 ++++ trace/jaeger/testdata/jaeger.json | 5 ++++- 7 files changed, 49 insertions(+), 21 deletions(-) diff --git a/config/testdata/expect.env.jaeger.json b/config/testdata/expect.env.jaeger.json index bab9289e913..cf1e8429dbc 100644 --- a/config/testdata/expect.env.jaeger.json +++ b/config/testdata/expect.env.jaeger.json @@ -18,11 +18,15 @@ "logSpans": false, "password": "", "queueSize": 0, - "user": "" + "disableAttemptReconnecting":false, + "attemptreconnectinterval":"0s", + "user": "", + "http_headers":{} }, "rpc_metrics": false, "sampler": { "maxOperations": 0, + "operationNameLateBinding":false, "options": [ ], "param": 0, @@ -36,5 +40,6 @@ "hostPort": "", "refreshInterval": "0s", "synchronousInitialization": false - } + }, + "traceid_128bit":false } \ No newline at end of file diff --git a/config/testdata/expect.jaeger.json b/config/testdata/expect.jaeger.json index a07731fbad5..c7946f59955 100644 --- a/config/testdata/expect.jaeger.json +++ b/config/testdata/expect.jaeger.json @@ -18,11 +18,15 @@ "logSpans": true, "password": "", "queueSize": 0, - "user": "" + "disableAttemptReconnecting":false, + "attemptreconnectinterval":"0s", + "user": "", + "http_headers":{} }, "rpc_metrics": false, "sampler": { "maxOperations": 0, + "operationNameLateBinding":false, "options": [ ], "param": 1, @@ -36,5 +40,6 @@ "hostPort": "", "refreshInterval": "0s", "synchronousInitialization": false - } + }, + "traceid_128bit": false } \ No newline at end of file diff --git a/config/testdata/jaeger.json b/config/testdata/jaeger.json index 018d7ba916f..fa70e54a943 100644 --- a/config/testdata/jaeger.json +++ b/config/testdata/jaeger.json @@ -22,7 +22,10 @@ "logSpans": true, "password": "", "queueSize": 0, - "user": "" + "disableAttemptReconnecting":false, + "attemptreconnectinterval":"0s", + "user": "", + "http_headers":{} }, "rpc_metrics": false, "sampler": { @@ -40,7 +43,8 @@ "hostPort": "", "refreshInterval": "0s", "synchronousInitialization": false - } + }, + "traceid_128bit": false } } } \ No newline at end of file diff --git a/go.mod b/go.mod index bfb06a13839..e4203f92da5 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/TykTechnologies/tyk go 1.16 require ( + github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Jeffail/gabs v1.4.0 github.com/Jeffail/tunny v0.0.0-20171107125207-452a8e97d6a3 github.com/Masterminds/sprig/v3 v3.2.2 @@ -43,6 +44,7 @@ require ( github.com/jensneuse/abstractlogger v0.0.4 github.com/justinas/alice v1.2.0 github.com/kelseyhightower/envconfig v1.4.0 + github.com/lonelycode/go-uuid v0.0.0-20141202165402-ed3ca8a15a93 // test github.com/lonelycode/osin v0.0.0-20160423095202-da239c9dacb6 github.com/mavricknz/ldap v0.0.0-20160227184754-f5a958005e43 github.com/miekg/dns v1.0.14 @@ -50,6 +52,7 @@ require ( github.com/mitchellh/mapstructure v1.4.1 github.com/nats-io/nats-server/v2 v2.3.4 // indirect github.com/newrelic/go-agent v2.13.0+incompatible + github.com/nsf/jsondiff v0.0.0-20210303162244-6ea32392771e // test github.com/opentracing/opentracing-go v1.2.0 github.com/openzipkin/zipkin-go v0.2.2 github.com/oschwald/maxminddb-golang v1.5.0 @@ -62,13 +65,17 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/spf13/afero v1.6.0 github.com/square/go-jose v2.4.1+incompatible - github.com/uber/jaeger-client-go v2.20.0+incompatible + github.com/stretchr/testify v1.8.1 // test + github.com/uber/jaeger-client-go v2.30.1-0.20220110192849-8d8e8fcfd04d+incompatible + github.com/uber/jaeger-lib v2.4.2-0.20210604143007-135cf5605a6d+incompatible // indirect + github.com/valyala/fasthttp v1.43.0 // test github.com/vmihailenco/msgpack v4.0.4+incompatible github.com/xeipuuv/gojsonschema v1.2.0 golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 golang.org/x/net v0.0.0-20220906165146-f3363e06e74c golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 google.golang.org/grpc v1.36.0 + google.golang.org/grpc/examples v0.0.0-20220317213542-f95b001a48df // test gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 gopkg.in/vmihailenco/msgpack.v2 v2.9.1 @@ -77,14 +84,6 @@ require ( rsc.io/letsencrypt v0.0.2 ) -require ( - github.com/lonelycode/go-uuid v0.0.0-20141202165402-ed3ca8a15a93 // test - github.com/nsf/jsondiff v0.0.0-20210303162244-6ea32392771e // test - github.com/stretchr/testify v1.8.0 // test - github.com/valyala/fasthttp v1.43.0 // test - google.golang.org/grpc/examples v0.0.0-20220317213542-f95b001a48df // test -) - replace gorm.io/gorm => github.com/TykTechnologies/gorm v1.20.7-0.20210409171139-b5c340f85ed0 //replace github.com/TykTechnologies/graphql-go-tools => ../graphql-go-tools diff --git a/go.sum b/go.sum index bc0dd5272ba..73a0bd1e02b 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v4.7.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo= github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= github.com/Jeffail/tunny v0.0.0-20171107125207-452a8e97d6a3 h1:FALdZx01H5t7P6YcNsAOwKh6rme30R7h8cjgtEhcd4s= @@ -171,7 +173,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= @@ -696,6 +697,7 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/newrelic/go-agent v2.13.0+incompatible h1:Dl6m75MHAzfB0kicv9GiLxzQatRjTLUAdrnYyoT8s4M= github.com/newrelic/go-agent v2.13.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nsf/jsondiff v0.0.0-20210303162244-6ea32392771e h1:S+/ptYdZtpK/MDstwCyt+ZHdXEpz86RJZ5gyZU4txJY= github.com/nsf/jsondiff v0.0.0-20210303162244-6ea32392771e/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -870,6 +872,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -878,8 +882,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v0.0.0-20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4= @@ -893,10 +898,11 @@ github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7V github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/uber/jaeger-client-go v2.19.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.20.0+incompatible h1:ttG9wKdl2ikV/BGOtu+eb+VPp+R7jMeuM177Ihs5Fdc= -github.com/uber/jaeger-client-go v2.20.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= +github.com/uber/jaeger-client-go v2.30.1-0.20220110192849-8d8e8fcfd04d+incompatible h1:Nriupf3YYKvA5oxiej8Bb+ao/oanuw9lvahjIQc4nEc= +github.com/uber/jaeger-client-go v2.30.1-0.20220110192849-8d8e8fcfd04d+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/uber/jaeger-lib v2.4.2-0.20210604143007-135cf5605a6d+incompatible h1:73eb49SfAfRZEhxIKR0tz5MUMu2zjJxJUZlFCHInV34= +github.com/uber/jaeger-lib v2.4.2-0.20210604143007-135cf5605a6d+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -1213,6 +1219,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= @@ -1287,6 +1294,7 @@ gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UD gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/trace/jaeger/config_test.go b/trace/jaeger/config_test.go index 884e47992bf..df86c866a79 100644 --- a/trace/jaeger/config_test.go +++ b/trace/jaeger/config_test.go @@ -27,6 +27,9 @@ func TestLoad(t *testing.T) { Reporter: &config.ReporterConfig{ LogSpans: true, LocalAgentHostPort: "jaeger:6831", + HTTPHeaders: map[string]string{ + "test": "1", + }, }, } @@ -49,4 +52,5 @@ func TestLoad(t *testing.T) { t.Errorf("%v: expected %#v got %#v", v.field, v.expect, v.got) } } + } diff --git a/trace/jaeger/testdata/jaeger.json b/trace/jaeger/testdata/jaeger.json index 32f48b56695..5a44b8061c8 100644 --- a/trace/jaeger/testdata/jaeger.json +++ b/trace/jaeger/testdata/jaeger.json @@ -22,7 +22,10 @@ "logSpans": true, "password": "", "queueSize": 0, - "user": "" + "user": "", + "http_headers":{ + "test":"1" + } }, "rpc_metrics": false, "sampler": { From 7a58e2732612df7926927cdacabb5216a94dd0cf Mon Sep 17 00:00:00 2001 From: Tomas Buchaillot Date: Mon, 27 Feb 2023 12:36:01 +0100 Subject: [PATCH 16/51] [TT-7973] Updating tunny lib (#4758) ## Description Update `tunny` lib ( work pool library used for `hostcheck`) to the latest version. ## Related Issue https://tyktech.atlassian.net/browse/TT-7973 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --- gateway/host_checker.go | 8 ++++---- go.mod | 2 +- go.sum | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/gateway/host_checker.go b/gateway/host_checker.go index 6c1050a537d..2e7481a128d 100644 --- a/gateway/host_checker.go +++ b/gateway/host_checker.go @@ -56,7 +56,7 @@ type HostUptimeChecker struct { checkTimeout int HostList map[string]HostData unHealthyList map[string]bool - pool *tunny.WorkPool + pool *tunny.Pool errorChan chan HostHealthReport okChan chan HostHealthReport @@ -133,7 +133,7 @@ func (h *HostUptimeChecker) execCheck() { } h.resetListMu.Unlock() for _, host := range h.HostList { - _, err := h.pool.SendWork(host) + _, err := h.pool.ProcessCtx(h.Gw.ctx, host) if err != nil && err != tunny.ErrPoolNotRunning { log.Warnf("[HOST CHECKER] could not send work, error: %v", err) } @@ -369,11 +369,11 @@ func (h *HostUptimeChecker) Init(workers, triggerLimit, timeout int, hostList ma log.Debug("[HOST CHECKER] Config:WorkerPool: ", h.workerPoolSize) var err error - h.pool, err = tunny.CreatePool(h.workerPoolSize, func(hostData interface{}) interface{} { + h.pool = tunny.NewFunc(h.workerPoolSize, func(hostData interface{}) interface{} { input, _ := hostData.(HostData) h.CheckHost(input) return nil - }).Open() + }) log.Debug("[HOST CHECKER] Init complete") diff --git a/go.mod b/go.mod index e4203f92da5..3df927b47b7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Jeffail/gabs v1.4.0 - github.com/Jeffail/tunny v0.0.0-20171107125207-452a8e97d6a3 + github.com/Jeffail/tunny v0.1.4 github.com/Masterminds/sprig/v3 v3.2.2 github.com/TykTechnologies/again v0.0.0-20190805133618-6ad301e7eaed github.com/TykTechnologies/circuitbreaker v2.2.2+incompatible diff --git a/go.sum b/go.sum index 73a0bd1e02b..745febeed5c 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,9 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo= github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= -github.com/Jeffail/tunny v0.0.0-20171107125207-452a8e97d6a3 h1:FALdZx01H5t7P6YcNsAOwKh6rme30R7h8cjgtEhcd4s= github.com/Jeffail/tunny v0.0.0-20171107125207-452a8e97d6a3/go.mod h1:BX3q3G70XX0UmIkDWfDHoDRquDS1xFJA5VTbMf+14wM= +github.com/Jeffail/tunny v0.1.4 h1:chtpdz+nUtaYQeCKlNBg6GycFF/kGVHOr6A3cmzTJXs= +github.com/Jeffail/tunny v0.1.4/go.mod h1:P8xAx4XQl0xsuhjX1DtfaMDCSuavzdb2rwbd0lk+fvo= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= From c7bff4974805f41a0e1dd120501abe119df55010 Mon Sep 17 00:00:00 2001 From: Leonid Bugaev Date: Mon, 27 Feb 2023 17:54:23 +0300 Subject: [PATCH 17/51] Release 5 branches (#4800) - Add release-5 release branches --------- Co-authored-by: Esteban Ricardo Mirizio --- ci/repo-policy/main.tf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ci/repo-policy/main.tf b/ci/repo-policy/main.tf index 80f98827842..488ce243449 100644 --- a/ci/repo-policy/main.tf +++ b/ci/repo-policy/main.tf @@ -77,5 +77,15 @@ module "tyk" { convos = "false", source_branch = "release-4.3", required_tests = ["Go 1.16 Redis 5","1.16","1.16-el7"]}, +{ branch = "release-5-lts", + reviewers = "0", + convos = "false", + source_branch = "master", + required_tests = ["Go 1.16 Redis 5","1.16","1.16-el7"]}, +{ branch = "release-5.0", + reviewers = "0", + convos = "false", + source_branch = "master", + required_tests = ["Go 1.16 Redis 5","1.16","1.16-el7"]}, ] } \ No newline at end of file From bea03b12281eed361d61eccd580e16a89cc5f5b0 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 28 Feb 2023 13:43:12 +0530 Subject: [PATCH 18/51] [TT-7661] reload all APIs having a plugin defined in API definition (#4731) ## Description Currently APIs are not reloaded unless API definition checksum isn't changed. However this could lead to API not reloading when custom middlewares are used, since when plugin shared objects or js files etc changes doesn't make the checksum change. As a solution to this, we can reload all APIs which have custom middleware enabled. Also gRPC plugins need not be reloaded unless there's a change in API definition, so that should be skipped. ## Related Issue https://tyktech.atlassian.net/browse/TT-7661 ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --- gateway/api_definition.go | 8 +- gateway/api_loader.go | 4 +- gateway/util.go | 41 +++++ gateway/util_test.go | 214 +++++++++++++++++++++++++ internal/middleware/middleware.go | 14 ++ internal/middleware/middleware_test.go | 62 +++++++ 6 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 internal/middleware/middleware.go create mode 100644 internal/middleware/middleware_test.go diff --git a/gateway/api_definition.go b/gateway/api_definition.go index f1fd1f0112d..47484165522 100644 --- a/gateway/api_definition.go +++ b/gateway/api_definition.go @@ -318,8 +318,10 @@ func (a APIDefinitionLoader) MakeSpec(def *nestedApiDefinition, logger *logrus.E // Unique API content ID, to check if we already have if it changed from previous sync spec.Checksum = base64.URLEncoding.EncodeToString(sha256hash[:]) - if currSpec := a.Gw.getApiSpec(def.APIID); currSpec != nil && currSpec.Checksum == spec.Checksum { - return a.Gw.getApiSpec(def.APIID) + spec.APIDefinition = def.APIDefinition + + if currSpec := a.Gw.getApiSpec(def.APIID); !shouldReloadSpec(currSpec, spec) { + return currSpec } if logger == nil { @@ -351,8 +353,6 @@ func (a APIDefinitionLoader) MakeSpec(def *nestedApiDefinition, logger *logrus.E } } - spec.APIDefinition = def.APIDefinition - // We'll push the default HealthChecker: spec.Health = &DefaultHealthChecker{ Gw: a.Gw, diff --git a/gateway/api_loader.go b/gateway/api_loader.go index 636af5776e3..f1f432c93a8 100644 --- a/gateway/api_loader.go +++ b/gateway/api_loader.go @@ -740,7 +740,7 @@ func (gw *Gateway) loadHTTPService(spec *APISpec, apisByListen map[string]int, g var chainObj *ChainObject - if curSpec := gw.getApiSpec(spec.APIID); curSpec != nil && curSpec.Checksum == spec.Checksum { + if curSpec := gw.getApiSpec(spec.APIID); !shouldReloadSpec(curSpec, spec) { if chain, found := gw.apisHandlesByID.Load(spec.APIID); found { chainObj = chain.(*ChainObject) } @@ -919,7 +919,7 @@ func (gw *Gateway) loadApps(specs []*APISpec) { spec.Proxy.ListenPath = converted } - if currSpec := gw.getApiSpec(spec.APIID); currSpec != nil && spec.Checksum == currSpec.Checksum { + if currSpec := gw.getApiSpec(spec.APIID); !shouldReloadSpec(currSpec, spec) { tmpSpecRegister[spec.APIID] = currSpec } else { tmpSpecRegister[spec.APIID] = spec diff --git a/gateway/util.go b/gateway/util.go index 6164ee93075..63ecc9d9af5 100644 --- a/gateway/util.go +++ b/gateway/util.go @@ -7,6 +7,7 @@ import ( "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/config" + "github.com/TykTechnologies/tyk/internal/middleware" ) // appendIfMissing ensures dest slice is unique with new items. @@ -148,3 +149,43 @@ func getAPIURL(apiDef apidef.APIDefinition, gwConfig config.Config) string { return result.String() } + +func shouldReloadSpec(existingSpec, newSpec *APISpec) bool { + if existingSpec == nil { + return true + } + + if existingSpec.Checksum != newSpec.Checksum { + return true + } + + if newSpec.hasVirtualEndpoint() { + return true + } + + if newSpec.CustomMiddleware.Driver == apidef.GrpcDriver { + return false + } + + if middleware.Enabled(newSpec.CustomMiddleware.AuthCheck) { + return true + } + + if middleware.Enabled(newSpec.CustomMiddleware.Pre...) { + return true + } + + if middleware.Enabled(newSpec.CustomMiddleware.PostKeyAuth...) { + return true + } + + if middleware.Enabled(newSpec.CustomMiddleware.Post...) { + return true + } + + if middleware.Enabled(newSpec.CustomMiddleware.Response...) { + return true + } + + return false +} diff --git a/gateway/util_test.go b/gateway/util_test.go index 6beecdb5a39..e1172bf00a1 100644 --- a/gateway/util_test.go +++ b/gateway/util_test.go @@ -236,3 +236,217 @@ func Test_getAPIURL(t *testing.T) { }) } } + +func Test_shouldReloadSpec(t *testing.T) { + t.Parallel() + t.Run("empty curr spec", func(t *testing.T) { + t.Parallel() + assert.True(t, shouldReloadSpec(nil, &APISpec{})) + }) + + t.Run("checksum mismatch", func(t *testing.T) { + t.Parallel() + existingSpec, newSpec := &APISpec{Checksum: "1"}, &APISpec{Checksum: "2"} + assert.True(t, shouldReloadSpec(existingSpec, newSpec)) + }) + + type testCase struct { + name string + spec *APISpec + want bool + } + + assertionHelper := func(t *testing.T, tcs []testCase) { + t.Helper() + for i := 0; i < len(tcs); i++ { + tc := tcs[i] + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if got := shouldReloadSpec(&APISpec{}, tc.spec); got != tc.want { + t.Errorf("shouldReloadSpec() = %v, want %v", got, tc.want) + } + }) + } + } + + t.Run("virtual endpoint", func(t *testing.T) { + t.Parallel() + tcs := []testCase{ + { + name: "disabled", + spec: &APISpec{APIDefinition: &apidef.APIDefinition{ + VersionData: apidef.VersionData{ + Versions: map[string]apidef.VersionInfo{ + "": { + ExtendedPaths: apidef.ExtendedPathsSet{ + Virtual: []apidef.VirtualMeta{ + { + Disabled: false, + }, + }, + }, + }, + }, + }, + }, + }, + want: true, + }, + { + name: "enabled", + spec: &APISpec{APIDefinition: &apidef.APIDefinition{ + VersionData: apidef.VersionData{ + Versions: map[string]apidef.VersionInfo{ + "": { + ExtendedPaths: apidef.ExtendedPathsSet{ + Virtual: []apidef.VirtualMeta{ + { + Disabled: true, + }, + }, + }, + }, + }, + }, + }, + }, + want: false, + }, + } + + assertionHelper(t, tcs) + }) + + t.Run("driver", func(t *testing.T) { + t.Parallel() + tcs := []testCase{ + { + name: "grpc", + spec: &APISpec{ + APIDefinition: &apidef.APIDefinition{ + CustomMiddleware: apidef.MiddlewareSection{ + Driver: apidef.GrpcDriver, + Pre: []apidef.MiddlewareDefinition{ + { + Disabled: false, + Name: "funcName", + }, + }, + }, + }, + }, + want: false, + }, + { + name: "goplugin", + spec: &APISpec{ + APIDefinition: &apidef.APIDefinition{ + CustomMiddleware: apidef.MiddlewareSection{ + Driver: apidef.GoPluginDriver, + Pre: []apidef.MiddlewareDefinition{ + { + Disabled: false, + Name: "funcName", + }, + }, + }, + }, + }, + want: true, + }, + } + + assertionHelper(t, tcs) + }) + + t.Run("mw enabled", func(t *testing.T) { + t.Parallel() + tcs := []testCase{ + { + name: "auth", + spec: &APISpec{ + APIDefinition: &apidef.APIDefinition{ + CustomMiddleware: apidef.MiddlewareSection{ + AuthCheck: apidef.MiddlewareDefinition{ + Disabled: false, + Name: "auth", + Path: "path", + }, + }, + }, + }, + want: true, + }, + { + name: "pre", + spec: &APISpec{ + APIDefinition: &apidef.APIDefinition{ + CustomMiddleware: apidef.MiddlewareSection{ + Pre: []apidef.MiddlewareDefinition{ + { + Disabled: false, + Name: "pre", + Path: "path", + }, + }, + }, + }, + }, + want: true, + }, + { + name: "postKeyAuth", + spec: &APISpec{ + APIDefinition: &apidef.APIDefinition{ + CustomMiddleware: apidef.MiddlewareSection{ + PostKeyAuth: []apidef.MiddlewareDefinition{ + { + Disabled: false, + Name: "postAuth", + Path: "path", + }, + }, + }, + }, + }, + want: true, + }, + { + name: "post", + spec: &APISpec{ + APIDefinition: &apidef.APIDefinition{ + CustomMiddleware: apidef.MiddlewareSection{ + Post: []apidef.MiddlewareDefinition{ + { + Disabled: false, + Name: "post", + Path: "path", + }, + }, + }, + }, + }, + want: true, + }, + { + name: "response", + spec: &APISpec{ + APIDefinition: &apidef.APIDefinition{ + CustomMiddleware: apidef.MiddlewareSection{ + Response: []apidef.MiddlewareDefinition{ + { + Disabled: false, + Name: "response", + Path: "path", + }, + }, + }, + }, + }, + want: true, + }, + } + + assertionHelper(t, tcs) + }) +} diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go new file mode 100644 index 00000000000..6fb90454005 --- /dev/null +++ b/internal/middleware/middleware.go @@ -0,0 +1,14 @@ +package middleware + +import "github.com/TykTechnologies/tyk/apidef" + +// Enabled returns whether middlewares are enabled or not. +func Enabled(defs ...apidef.MiddlewareDefinition) bool { + for _, def := range defs { + if !def.Disabled && def.Name != "" { + return true + } + } + + return false +} diff --git a/internal/middleware/middleware_test.go b/internal/middleware/middleware_test.go new file mode 100644 index 00000000000..530c670e7ea --- /dev/null +++ b/internal/middleware/middleware_test.go @@ -0,0 +1,62 @@ +package middleware + +import ( + "testing" + + "github.com/TykTechnologies/tyk/apidef" +) + +func TestEnabled(t *testing.T) { + tests := []struct { + name string + mws []apidef.MiddlewareDefinition + want bool + }{ + { + name: "disabled", + mws: []apidef.MiddlewareDefinition{ + { + Disabled: true, + Name: "mwFunc", + Path: "path", + }, + }, + want: false, + }, + { + name: "enabled with empty name and path", + mws: []apidef.MiddlewareDefinition{ + { + Disabled: false, + }, + }, + want: false, + }, + { + name: "enabled with empty name and path", + mws: []apidef.MiddlewareDefinition{ + { + Disabled: false, + }, + }, + want: false, + }, + { + name: "enabled", + mws: []apidef.MiddlewareDefinition{ + { + Disabled: false, + Name: "mwFunc", + }, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Enabled(tt.mws...); got != tt.want { + t.Errorf("Enabled() = %v, want %v", got, tt.want) + } + }) + } +} From bfaf1a3b27f560fd33bbba3007dd33cf81a53fa2 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 28 Feb 2023 13:56:44 +0530 Subject: [PATCH 19/51] [TT-8134] remove driver from required field in OAS schema (#4812) https://tyktech.atlassian.net/browse/TT-8134 --- apidef/oas/schema/x-tyk-api-gateway.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apidef/oas/schema/x-tyk-api-gateway.json b/apidef/oas/schema/x-tyk-api-gateway.json index a0c32e0a8e3..632b5bf3ed3 100644 --- a/apidef/oas/schema/x-tyk-api-gateway.json +++ b/apidef/oas/schema/x-tyk-api-gateway.json @@ -233,10 +233,7 @@ "data": { "$ref": "#/definitions/X-Tyk-PluginConfigData" } - }, - "required": [ - "driver" - ] + } }, "X-Tyk-PluginConfigData": { "type": "object", From 8347f442024c22ae97d3e3f00dd5d8687406d515 Mon Sep 17 00:00:00 2001 From: Asutosh <1187055+asutosh@users.noreply.github.com> Date: Wed, 1 Mar 2023 20:54:22 +0530 Subject: [PATCH 20/51] [TD-1474]: Update sync-automation with version support. (#4819) Add version support to the sync-automation workflow. ## Related Issue TD-1474 --- .github/workflows/sync-automation.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sync-automation.yml b/.github/workflows/sync-automation.yml index 57d61235756..9b44cc18de1 100644 --- a/.github/workflows/sync-automation.yml +++ b/.github/workflows/sync-automation.yml @@ -1,5 +1,5 @@ # Generated by: gromit policy -# Generated on: Thu Dec 15 07:17:30 UTC 2022 +# Generated on: Wed Mar 1 11:28:57 UTC 2023 name: Sync automation @@ -16,6 +16,7 @@ on: jobs: sync: runs-on: ubuntu-latest + container: tykio/gromit:v1.5 strategy: fail-fast: false @@ -33,6 +34,7 @@ jobs: - name: sync ${{matrix.branch}} from master id: sync-changes run: | + git config --global --add safe.directory "$GITHUB_WORKSPACE" git config --local user.email "policy@gromit" git config --local user.name "Bender" git fetch origin ${{ matrix.branch }} @@ -53,6 +55,21 @@ jobs: echo "::debug::Commit ${{ github.sha }} syncd for ${{matrix.branch}}" exit 0 + - name: Generate releng bundle using latest gromit templates for ${{matrix.branch}} + id: gromit-bundle-gen + run: | + # add gh cli to gromit container + apk add --no-cache github-cli + # get the tarball for the latest gromit master - it will have the latest version of + # the templates. + mkdir /tmp/gromit-src && gh api -H "Accept: application/vnd.github+json" /repos/TykTechnologies/gromit/tarball/master | tar --strip-components 1 -C /tmp/gromit-src -xzf - + gromit bundle gen --branch ${{ matrix.branch }} --bundle /tmp/gromit-src/policy/templates/releng --repo tyk . + git add -A && git commit -m "[CI]: Automated releng bundle sync by sync-automation" + git push origin ${{ steps.sync-changes.outputs.prbranch }} + exit 0 + env: + GITHUB_TOKEN: ${{ secrets.ORG_GH_TOKEN }} + - name: Create PR from the branch. id: create-pr uses: actions/github-script@v6 From 96df8d064f934ca0c13d724ca902679438b86c41 Mon Sep 17 00:00:00 2001 From: Patric Vormstein Date: Thu, 2 Mar 2023 09:47:36 +0100 Subject: [PATCH 21/51] move adapter helper functions to proper util files (TT-7999) (#4792) This PR moves helper functions from the GraphQL adapter to some proper util files. It also adds some missing tests for those helper functions. *Please note, that the remaining functions inside the adapter will be refactored in the next step.* ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [x] Refactoring or add test (improvements in base code or adds test coverage to functionality) --- apidef/adapter/engine_v2_utils.go | 137 +++++++++ apidef/adapter/engine_v2_utils_test.go | 282 ++++++++++++++++++ apidef/adapter/graphql_config_adapter.go | 229 ++------------ apidef/adapter/graphql_config_adapter_test.go | 64 +--- apidef/adapter/utils.go | 87 ++++++ apidef/adapter/utils_test.go | 227 ++++++++++++++ 6 files changed, 761 insertions(+), 265 deletions(-) create mode 100644 apidef/adapter/engine_v2_utils.go create mode 100644 apidef/adapter/engine_v2_utils_test.go create mode 100644 apidef/adapter/utils.go create mode 100644 apidef/adapter/utils_test.go diff --git a/apidef/adapter/engine_v2_utils.go b/apidef/adapter/engine_v2_utils.go new file mode 100644 index 00000000000..37f67f8bea9 --- /dev/null +++ b/apidef/adapter/engine_v2_utils.go @@ -0,0 +1,137 @@ +package adapter + +import ( + "errors" + "net/http" + neturl "net/url" + "sort" + "strings" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + restDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/rest_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" + + "github.com/TykTechnologies/tyk/apidef" +) + +type createGraphQLDataSourceFactoryParams struct { + graphqlConfig apidef.GraphQLEngineDataSourceConfigGraphQL + subscriptionClientFactory graphqlDataSource.GraphQLSubscriptionClientFactory + httpClient *http.Client + streamingClient *http.Client +} + +func graphqlDataSourceConfiguration(url string, method string, headers map[string]string, subscriptionType apidef.SubscriptionType) graphqlDataSource.Configuration { + dataSourceHeaders := make(map[string]string) + for name, value := range headers { + dataSourceHeaders[name] = value + } + + if strings.HasPrefix(url, "tyk://") { + url = strings.ReplaceAll(url, "tyk://", "http://") + dataSourceHeaders[apidef.TykInternalApiHeader] = "true" + } + + cfg := graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: url, + Method: method, + Header: convertApiDefinitionHeadersToHttpHeaders(dataSourceHeaders), + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: url, + UseSSE: subscriptionType == apidef.GQLSubscriptionSSE, + }, + } + + return cfg +} + +func createArgumentConfigurationsForArgumentNames(argumentNames ...string) plan.ArgumentsConfigurations { + argConfs := plan.ArgumentsConfigurations{} + for _, argName := range argumentNames { + argConf := plan.ArgumentConfiguration{ + Name: argName, + SourceType: plan.FieldArgumentSource, + } + + argConfs = append(argConfs, argConf) + } + + return argConfs +} + +func extractURLQueryParamsForEngineV2(url string, providedApiDefQueries []apidef.QueryVariable) (urlWithoutParams string, engineV2Queries []restDataSource.QueryConfiguration, err error) { + urlParts := strings.Split(url, "?") + urlWithoutParams = urlParts[0] + + queryPart := "" + if len(urlParts) == 2 { + queryPart = urlParts[1] + } + // Parse only query part as URL could contain templating {{.argument.id}} which should not be escaped + values, err := neturl.ParseQuery(queryPart) + if err != nil { + return "", nil, err + } + + engineV2Queries = make([]restDataSource.QueryConfiguration, 0) + appendURLQueryParamsToEngineV2Queries(&engineV2Queries, values) + appendApiDefQueriesConfigToEngineV2Queries(&engineV2Queries, providedApiDefQueries) + + if len(engineV2Queries) == 0 { + return urlWithoutParams, nil, nil + } + + return urlWithoutParams, engineV2Queries, nil +} + +func appendURLQueryParamsToEngineV2Queries(engineV2Queries *[]restDataSource.QueryConfiguration, queryValues neturl.Values) { + for queryKey, queryValue := range queryValues { + *engineV2Queries = append(*engineV2Queries, restDataSource.QueryConfiguration{ + Name: queryKey, + Value: strings.Join(queryValue, ","), + }) + } + + sort.Slice(*engineV2Queries, func(i, j int) bool { + return (*engineV2Queries)[i].Name < (*engineV2Queries)[j].Name + }) +} + +func appendApiDefQueriesConfigToEngineV2Queries(engineV2Queries *[]restDataSource.QueryConfiguration, apiDefQueries []apidef.QueryVariable) { + if len(apiDefQueries) == 0 { + return + } + + for _, apiDefQueryVar := range apiDefQueries { + engineV2Query := restDataSource.QueryConfiguration{ + Name: apiDefQueryVar.Name, + Value: apiDefQueryVar.Value, + } + + *engineV2Queries = append(*engineV2Queries, engineV2Query) + } +} + +func createGraphQLDataSourceFactory(params createGraphQLDataSourceFactoryParams) (*graphqlDataSource.Factory, error) { + factory := &graphqlDataSource.Factory{ + HTTPClient: params.httpClient, + StreamingClient: params.streamingClient, + } + + wsProtocol := graphqlDataSourceWebSocketProtocol(params.graphqlConfig.SubscriptionType) + graphqlSubscriptionClient := params.subscriptionClientFactory.NewSubscriptionClient( + params.httpClient, + params.streamingClient, + nil, + graphqlDataSource.WithWSSubProtocol(wsProtocol), + ) + + subscriptionClient, ok := graphqlSubscriptionClient.(*graphqlDataSource.SubscriptionClient) + if !ok { + return nil, errors.New("incorrect SubscriptionClient has been created") + } + factory.SubscriptionClient = subscriptionClient + return factory, nil +} diff --git a/apidef/adapter/engine_v2_utils_test.go b/apidef/adapter/engine_v2_utils_test.go new file mode 100644 index 00000000000..594e4b0ad3e --- /dev/null +++ b/apidef/adapter/engine_v2_utils_test.go @@ -0,0 +1,282 @@ +package adapter + +import ( + "net/http" + neturl "net/url" + "testing" + + "github.com/stretchr/testify/assert" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + restDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/rest_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" + + "github.com/TykTechnologies/tyk/apidef" +) + +func TestGraphqlDataSourceConfiguration(t *testing.T) { + type testInput struct { + url string + method string + headers map[string]string + subscriptionType apidef.SubscriptionType + } + + t.Run("with internal data source url and sse", func(t *testing.T) { + internalDataSource := testInput{ + url: "tyk://data-source.fake", + method: http.MethodGet, + headers: map[string]string{ + "Authorization": "token", + "X-Tyk-Key": "value", + }, + subscriptionType: apidef.GQLSubscriptionSSE, + } + + expectedGraphqlDataSourceConfiguration := graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://data-source.fake", + Method: http.MethodGet, + Header: http.Header{ + "Authorization": {"token"}, + http.CanonicalHeaderKey(apidef.TykInternalApiHeader): {"true"}, + "X-Tyk-Key": {"value"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://data-source.fake", + UseSSE: true, + SSEMethodPost: false, + }, + } + + actualGraphqlDataSourceConfiguration := graphqlDataSourceConfiguration( + internalDataSource.url, + internalDataSource.method, + internalDataSource.headers, + internalDataSource.subscriptionType, + ) + + assert.Equal(t, expectedGraphqlDataSourceConfiguration, actualGraphqlDataSourceConfiguration) + }) + + t.Run("with external data source url and no sse", func(t *testing.T) { + externalDataSource := testInput{ + url: "http://data-source.fake", + method: http.MethodGet, + headers: map[string]string{ + "Authorization": "token", + "X-Tyk-Key": "value", + }, + subscriptionType: apidef.GQLSubscriptionTransportWS, + } + + expectedGraphqlDataSourceConfiguration := graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://data-source.fake", + Method: http.MethodGet, + Header: http.Header{ + "Authorization": {"token"}, + "X-Tyk-Key": {"value"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://data-source.fake", + UseSSE: false, + SSEMethodPost: false, + }, + } + + actualGraphqlDataSourceConfiguration := graphqlDataSourceConfiguration( + externalDataSource.url, + externalDataSource.method, + externalDataSource.headers, + externalDataSource.subscriptionType, + ) + + assert.Equal(t, expectedGraphqlDataSourceConfiguration, actualGraphqlDataSourceConfiguration) + }) + +} + +func TestCreateArgumentConfigurationsForArgumentNames(t *testing.T) { + expectedArgumentConfigurations := plan.ArgumentsConfigurations{ + { + Name: "argument1", + SourceType: plan.FieldArgumentSource, + }, + { + Name: "argument2", + SourceType: plan.FieldArgumentSource, + }, + } + + actualArgumentConfigurations := createArgumentConfigurationsForArgumentNames("argument1", "argument2") + assert.Equal(t, expectedArgumentConfigurations, actualArgumentConfigurations) +} + +func TestExtractURLQueryParamsForEngineV2(t *testing.T) { + type expectedOutput struct { + urlWithoutParams string + engineV2Queries []restDataSource.QueryConfiguration + err error + } + + providedApiDefQueries := []apidef.QueryVariable{ + { + Name: "providedQueryName1", + Value: "providedQueryValue1", + }, + { + Name: "providedQueryName2", + Value: "providedQueryValue2", + }, + } + + t.Run("without query params in url", func(t *testing.T) { + inputUrl := "http://rest-data-source.fake" + expected := expectedOutput{ + urlWithoutParams: "http://rest-data-source.fake", + engineV2Queries: []restDataSource.QueryConfiguration{ + { + Name: "providedQueryName1", + Value: "providedQueryValue1", + }, + { + Name: "providedQueryName2", + Value: "providedQueryValue2", + }, + }, + err: nil, + } + actualUrlWithoutParams, actualEngineV2Queries, actualErr := extractURLQueryParamsForEngineV2(inputUrl, providedApiDefQueries) + assert.Equal(t, expected.urlWithoutParams, actualUrlWithoutParams) + assert.Equal(t, expected.engineV2Queries, actualEngineV2Queries) + assert.Equal(t, expected.err, actualErr) + }) + + t.Run("with query params in url", func(t *testing.T) { + inputUrl := "http://rest-data-source.fake?urlParam=urlParamValue" + expected := expectedOutput{ + urlWithoutParams: "http://rest-data-source.fake", + engineV2Queries: []restDataSource.QueryConfiguration{ + { + Name: "urlParam", + Value: "urlParamValue", + }, + { + Name: "providedQueryName1", + Value: "providedQueryValue1", + }, + { + Name: "providedQueryName2", + Value: "providedQueryValue2", + }, + }, + err: nil, + } + actualUrlWithoutParams, actualEngineV2Queries, actualErr := extractURLQueryParamsForEngineV2(inputUrl, providedApiDefQueries) + assert.Equal(t, expected.urlWithoutParams, actualUrlWithoutParams) + assert.Equal(t, expected.engineV2Queries, actualEngineV2Queries) + assert.Equal(t, expected.err, actualErr) + }) +} + +func TestAppendURLQueryParamsToEngineV2Queries(t *testing.T) { + existingEngineV2Queries := &[]restDataSource.QueryConfiguration{ + { + Name: "existingName", + Value: "existingValue", + }, + } + + queryValues := neturl.Values{ + "newKey1": {"newKey1Value1"}, + "newKey2": {"newKey2Value1", "newKey2Value2"}, + } + + expectedEngineV2Queries := &[]restDataSource.QueryConfiguration{ + { + Name: "existingName", + Value: "existingValue", + }, + { + Name: "newKey1", + Value: "newKey1Value1", + }, + { + Name: "newKey2", + Value: "newKey2Value1,newKey2Value2", + }, + } + + appendURLQueryParamsToEngineV2Queries(existingEngineV2Queries, queryValues) + assert.Equal(t, expectedEngineV2Queries, existingEngineV2Queries) +} + +func TestAppendApiDefQueriesConfigToEngineV2Queries(t *testing.T) { + existingEngineV2Queries := &[]restDataSource.QueryConfiguration{ + { + Name: "existingName", + Value: "existingValue", + }, + } + + apiDefQueryVariables := []apidef.QueryVariable{ + { + Name: "newName1", + Value: "newValue2", + }, + { + Name: "newName2", + Value: "newValue2", + }, + } + + expectedEngineV2Queries := &[]restDataSource.QueryConfiguration{ + { + Name: "existingName", + Value: "existingValue", + }, + { + Name: "newName1", + Value: "newValue2", + }, + { + Name: "newName2", + Value: "newValue2", + }, + } + + appendApiDefQueriesConfigToEngineV2Queries(existingEngineV2Queries, apiDefQueryVariables) + assert.Equal(t, expectedEngineV2Queries, existingEngineV2Queries) +} + +func TestCreateGraphQLDataSourceFactory(t *testing.T) { + inputParams := createGraphQLDataSourceFactoryParams{ + graphqlConfig: apidef.GraphQLEngineDataSourceConfigGraphQL{ + SubscriptionType: apidef.GQLSubscriptionSSE, + }, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + httpClient: &http.Client{ + Timeout: 0, + }, + streamingClient: &http.Client{ + Timeout: 1, + }, + } + + expectedGraphQLDataSourceFactory := &graphqlDataSource.Factory{ + HTTPClient: &http.Client{ + Timeout: 0, + }, + StreamingClient: &http.Client{ + Timeout: 1, + }, + SubscriptionClient: mockSubscriptionClient, + } + + actualGraphQLDataSourceFactory, err := createGraphQLDataSourceFactory(inputParams) + assert.Nil(t, err) + assert.Equal(t, expectedGraphQLDataSourceFactory, actualGraphQLDataSourceFactory) +} diff --git a/apidef/adapter/graphql_config_adapter.go b/apidef/adapter/graphql_config_adapter.go index 4816954754a..6de5c68f3d4 100644 --- a/apidef/adapter/graphql_config_adapter.go +++ b/apidef/adapter/graphql_config_adapter.go @@ -4,8 +4,6 @@ import ( "encoding/json" "errors" "net/http" - neturl "net/url" - "sort" "strings" graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" @@ -72,11 +70,11 @@ func (g *GraphQLConfigAdapter) EngineConfigV2() (*graphql.EngineV2Configuration, return nil, ErrUnsupportedGraphQLConfigVersion } - if g.isProxyOnlyAPIDefinition() { + if isProxyOnlyAPIDefinition(g.apiDefinition) { return g.createV2ConfigForProxyOnlyExecutionMode() } - if g.isSupergraphAPIDefinition() { + if isSupergraphAPIDefinition(g.apiDefinition) { return g.createV2ConfigForSupergraphExecutionMode() } @@ -95,7 +93,7 @@ func (g *GraphQLConfigAdapter) createV2ConfigForProxyOnlyExecutionMode() (*graph upstreamConfig := graphql.ProxyUpstreamConfig{ URL: url, StaticHeaders: staticHeaders, - SubscriptionType: g.graphqlSubscriptionType(g.apiDefinition.GraphQL.Proxy.SubscriptionType), + SubscriptionType: graphqlSubscriptionType(g.apiDefinition.GraphQL.Proxy.SubscriptionType), } if g.schema == nil { @@ -159,7 +157,9 @@ func (g *GraphQLConfigAdapter) createV2ConfigForSupergraphExecutionMode() (*grap } func (g *GraphQLConfigAdapter) createV2ConfigForEngineExecutionMode() (*graphql.EngineV2Configuration, error) { - if err := g.parseSchema(); err != nil { + var err error + g.schema, err = parseSchema(g.apiDefinition.GraphQL.Schema) + if err != nil { return nil, err } @@ -178,28 +178,6 @@ func (g *GraphQLConfigAdapter) createV2ConfigForEngineExecutionMode() (*graphql. return &conf, nil } -func (g *GraphQLConfigAdapter) parseSchema() (err error) { - if g.schema != nil { - return nil - } - - g.schema, err = graphql.NewSchemaFromString(g.apiDefinition.GraphQL.Schema) - if err != nil { - return err - } - - normalizationResult, err := g.schema.Normalize() - if err != nil { - return err - } - - if !normalizationResult.Successful && normalizationResult.Errors != nil { - return normalizationResult.Errors - } - - return nil -} - func (g *GraphQLConfigAdapter) engineConfigV2FieldConfigs() (planFieldConfigs plan.FieldConfigurations) { for _, fc := range g.apiDefinition.GraphQL.Engine.FieldConfigs { planFieldConfig := plan.FieldConfiguration{ @@ -246,7 +224,7 @@ func (g *GraphQLConfigAdapter) engineConfigV2DataSources() (planDataSources []pl Client: g.getHttpClient(), } - urlWithoutQueryParams, queryConfigs, err := g.extractURLQueryParamsForEngineV2(restConfig.URL, restConfig.Query) + urlWithoutQueryParams, queryConfigs, err := extractURLQueryParamsForEngineV2(restConfig.URL, restConfig.Query) if err != nil { return nil, err } @@ -257,7 +235,7 @@ func (g *GraphQLConfigAdapter) engineConfigV2DataSources() (planDataSources []pl Method: restConfig.Method, Body: restConfig.Body, Query: queryConfigs, - Header: g.convertHeadersToHttpHeaders(restConfig.Headers), + Header: convertApiDefinitionHeadersToHttpHeaders(restConfig.Headers), }, }) @@ -268,12 +246,17 @@ func (g *GraphQLConfigAdapter) engineConfigV2DataSources() (planDataSources []pl return nil, err } - planDataSource.Factory, err = g.createGraphQLDataSourceFactory(graphqlConfig) + planDataSource.Factory, err = createGraphQLDataSourceFactory(createGraphQLDataSourceFactoryParams{ + graphqlConfig: graphqlConfig, + subscriptionClientFactory: g.subscriptionClientFactory, + httpClient: g.getHttpClient(), + streamingClient: g.getStreamingClient(), + }) if err != nil { return nil, err } - planDataSource.Custom = graphqlDataSource.ConfigJson(g.graphqlDataSourceConfiguration( + planDataSource.Custom = graphqlDataSource.ConfigJson(graphqlDataSourceConfiguration( graphqlConfig.URL, graphqlConfig.Method, graphqlConfig.Headers, @@ -320,8 +303,8 @@ func (g *GraphQLConfigAdapter) subgraphDataSourceConfigs() []graphqlDataSource.C if len(apiDefSubgraphConf.SDL) == 0 { continue } - hdr := g.removeDuplicateHeaders(apiDefSubgraphConf.Headers, g.apiDefinition.GraphQL.Supergraph.GlobalHeaders) - conf := g.graphqlDataSourceConfiguration( + hdr := removeDuplicateApiDefinitionHeaders(apiDefSubgraphConf.Headers, g.apiDefinition.GraphQL.Supergraph.GlobalHeaders) + conf := graphqlDataSourceConfiguration( apiDefSubgraphConf.URL, http.MethodPost, hdr, @@ -337,32 +320,6 @@ func (g *GraphQLConfigAdapter) subgraphDataSourceConfigs() []graphqlDataSource.C return confs } -func (g *GraphQLConfigAdapter) graphqlDataSourceConfiguration(url string, method string, headers map[string]string, subscriptionType apidef.SubscriptionType) graphqlDataSource.Configuration { - dataSourceHeaders := make(map[string]string) - for name, value := range headers { - dataSourceHeaders[name] = value - } - - if strings.HasPrefix(url, "tyk://") { - url = strings.ReplaceAll(url, "tyk://", "http://") - dataSourceHeaders[apidef.TykInternalApiHeader] = "true" - } - - cfg := graphqlDataSource.Configuration{ - Fetch: graphqlDataSource.FetchConfiguration{ - URL: url, - Method: method, - Header: g.convertHeadersToHttpHeaders(dataSourceHeaders), - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: url, - UseSSE: subscriptionType == apidef.GQLSubscriptionSSE, - }, - } - - return cfg -} - func (g *GraphQLConfigAdapter) engineConfigV2Arguments(fieldConfs *plan.FieldConfigurations, generatedArgs map[graphql.TypeFieldLookupKey]graphql.TypeFieldArguments) { for i := range *fieldConfs { if len(generatedArgs) == 0 { @@ -375,7 +332,7 @@ func (g *GraphQLConfigAdapter) engineConfigV2Arguments(fieldConfs *plan.FieldCon continue } - (*fieldConfs)[i].Arguments = g.createArgumentConfigurationsForArgumentNames(currentArgs.ArgumentNames) + (*fieldConfs)[i].Arguments = createArgumentConfigurationsForArgumentNames(currentArgs.ArgumentNames...) delete(generatedArgs, lookupKey) } @@ -383,107 +340,11 @@ func (g *GraphQLConfigAdapter) engineConfigV2Arguments(fieldConfs *plan.FieldCon *fieldConfs = append(*fieldConfs, plan.FieldConfiguration{ TypeName: genArgs.TypeName, FieldName: genArgs.FieldName, - Arguments: g.createArgumentConfigurationsForArgumentNames(genArgs.ArgumentNames), + Arguments: createArgumentConfigurationsForArgumentNames(genArgs.ArgumentNames...), }) } } -func (g *GraphQLConfigAdapter) createArgumentConfigurationsForArgumentNames(argumentNames []string) plan.ArgumentsConfigurations { - argConfs := plan.ArgumentsConfigurations{} - for _, argName := range argumentNames { - argConf := plan.ArgumentConfiguration{ - Name: argName, - SourceType: plan.FieldArgumentSource, - } - - argConfs = append(argConfs, argConf) - } - - return argConfs -} - -func (g *GraphQLConfigAdapter) extractURLQueryParamsForEngineV2(url string, providedApiDefQueries []apidef.QueryVariable) (urlWithoutParams string, engineV2Queries []restDataSource.QueryConfiguration, err error) { - urlParts := strings.Split(url, "?") - urlWithoutParams = urlParts[0] - - queryPart := "" - if len(urlParts) == 2 { - queryPart = urlParts[1] - } - // Parse only query part as URL could contain templating {{.argument.id}} which should not be escaped - values, err := neturl.ParseQuery(queryPart) - if err != nil { - return "", nil, err - } - - engineV2Queries = make([]restDataSource.QueryConfiguration, 0) - g.convertURLQueryParamsIntoEngineV2Queries(&engineV2Queries, values) - g.convertApiDefQueriesConfigIntoEngineV2Queries(&engineV2Queries, providedApiDefQueries) - - if len(engineV2Queries) == 0 { - return urlWithoutParams, nil, nil - } - - return urlWithoutParams, engineV2Queries, nil -} - -func (g *GraphQLConfigAdapter) convertURLQueryParamsIntoEngineV2Queries(engineV2Queries *[]restDataSource.QueryConfiguration, queryValues neturl.Values) { - for queryKey, queryValue := range queryValues { - *engineV2Queries = append(*engineV2Queries, restDataSource.QueryConfiguration{ - Name: queryKey, - Value: strings.Join(queryValue, ","), - }) - } - - sort.Slice(*engineV2Queries, func(i, j int) bool { - return (*engineV2Queries)[i].Name < (*engineV2Queries)[j].Name - }) -} - -func (g *GraphQLConfigAdapter) convertApiDefQueriesConfigIntoEngineV2Queries(engineV2Queries *[]restDataSource.QueryConfiguration, apiDefQueries []apidef.QueryVariable) { - if len(apiDefQueries) == 0 { - return - } - - for _, apiDefQueryVar := range apiDefQueries { - engineV2Query := restDataSource.QueryConfiguration{ - Name: apiDefQueryVar.Name, - Value: apiDefQueryVar.Value, - } - - *engineV2Queries = append(*engineV2Queries, engineV2Query) - } -} - -func (g *GraphQLConfigAdapter) convertHeadersToHttpHeaders(apiDefHeaders map[string]string) http.Header { - if len(apiDefHeaders) == 0 { - return nil - } - - engineV2Headers := make(http.Header) - for apiDefHeaderKey, apiDefHeaderValue := range apiDefHeaders { - engineV2Headers.Add(apiDefHeaderKey, apiDefHeaderValue) - } - - return engineV2Headers -} - -func (g *GraphQLConfigAdapter) removeDuplicateHeaders(headers ...map[string]string) map[string]string { - hdr := make(map[string]string) - // headers priority depends on the order of arguments - for _, header := range headers { - for k, v := range header { - keyCanonical := http.CanonicalHeaderKey(k) - if _, ok := hdr[keyCanonical]; ok { - // skip because header is present - continue - } - hdr[keyCanonical] = v - } - } - return hdr -} - func (g *GraphQLConfigAdapter) determineChildNodes(planDataSources []plan.DataSourceConfiguration) error { for i := range planDataSources { for j := range planDataSources[i].RootNodes { @@ -507,15 +368,6 @@ func (g *GraphQLConfigAdapter) determineChildNodes(planDataSources []plan.DataSo return nil } -func (g *GraphQLConfigAdapter) isSupergraphAPIDefinition() bool { - return g.apiDefinition.GraphQL.Enabled && g.apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeSupergraph -} - -func (g *GraphQLConfigAdapter) isProxyOnlyAPIDefinition() bool { - return g.apiDefinition.GraphQL.Enabled && - (g.apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeProxyOnly || g.apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeSubgraph) -} - func (g *GraphQLConfigAdapter) getHttpClient() *http.Client { if g.httpClient == nil { g.httpClient = httpclient.DefaultNetHttpClient @@ -532,46 +384,3 @@ func (g *GraphQLConfigAdapter) getStreamingClient() *http.Client { return g.streamingClient } - -func (g *GraphQLConfigAdapter) createGraphQLDataSourceFactory(graphqlConfig apidef.GraphQLEngineDataSourceConfigGraphQL) (*graphqlDataSource.Factory, error) { - factory := &graphqlDataSource.Factory{ - HTTPClient: g.getHttpClient(), - StreamingClient: g.getStreamingClient(), - } - - wsProtocol := g.graphqlDataSourceWebSocketProtocol(graphqlConfig.SubscriptionType) - graphqlSubscriptionClient := g.subscriptionClientFactory.NewSubscriptionClient( - g.getHttpClient(), - g.getStreamingClient(), - nil, - graphqlDataSource.WithWSSubProtocol(wsProtocol), - ) - - subscriptionClient, ok := graphqlSubscriptionClient.(*graphqlDataSource.SubscriptionClient) - if !ok { - return nil, errors.New("incorrect SubscriptionClient has been created") - } - factory.SubscriptionClient = subscriptionClient - return factory, nil -} - -func (g *GraphQLConfigAdapter) graphqlDataSourceWebSocketProtocol(subscriptionType apidef.SubscriptionType) string { - wsProtocol := graphqlDataSource.ProtocolGraphQLWS - if subscriptionType == apidef.GQLSubscriptionTransportWS { - wsProtocol = graphqlDataSource.ProtocolGraphQLTWS - } - return wsProtocol -} - -func (g *GraphQLConfigAdapter) graphqlSubscriptionType(subscriptionType apidef.SubscriptionType) graphql.SubscriptionType { - switch subscriptionType { - case apidef.GQLSubscriptionWS: - return graphql.SubscriptionTypeGraphQLWS - case apidef.GQLSubscriptionTransportWS: - return graphql.SubscriptionTypeGraphQLTransportWS - case apidef.GQLSubscriptionSSE: - return graphql.SubscriptionTypeSSE - default: - return graphql.SubscriptionTypeUnknown - } -} diff --git a/apidef/adapter/graphql_config_adapter_test.go b/apidef/adapter/graphql_config_adapter_test.go index f2a0e5d4f9a..aa1af52e1a7 100644 --- a/apidef/adapter/graphql_config_adapter_test.go +++ b/apidef/adapter/graphql_config_adapter_test.go @@ -14,7 +14,7 @@ import ( kafkaDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/kafka_datasource" restDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/rest_datasource" "github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" - "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" + "github.com/TykTechnologies/tyk/apidef" ) @@ -494,7 +494,10 @@ func TestGraphQLConfigAdapter_engineConfigV2FieldConfigs(t *testing.T) { } adapter := NewGraphQLConfigAdapter(apiDef) - require.NoError(t, adapter.parseSchema()) + + var err error + adapter.schema, err = parseSchema(gqlConfig.Schema) + require.NoError(t, err) actualFieldCfgs := adapter.engineConfigV2FieldConfigs() assert.ElementsMatch(t, expectedFieldCfgs, actualFieldCfgs) @@ -816,7 +819,10 @@ func TestGraphQLConfigAdapter_engineConfigV2DataSources(t *testing.T) { WithStreamingClient(streamingClient), withGraphQLSubscriptionClientFactory(&MockSubscriptionClientFactory{}), ) - require.NoError(t, adapter.parseSchema()) + + var err error + adapter.schema, err = parseSchema(gqlConfig.Schema) + require.NoError(t, err) actualDataSources, err := adapter.engineConfigV2DataSources() assert.NoError(t, err) @@ -824,58 +830,6 @@ func TestGraphQLConfigAdapter_engineConfigV2DataSources(t *testing.T) { //assert.ElementsMatch(t, expectedDataSources, actualDataSources) } -func TestGraphQLConfigAdapter_GraphqlDataSourceWebSocketProtocol(t *testing.T) { - run := func(subscriptionType apidef.SubscriptionType, expectedWebSocketProtocol string) func(t *testing.T) { - return func(t *testing.T) { - adapter := NewGraphQLConfigAdapter(nil) - actualProtocol := adapter.graphqlDataSourceWebSocketProtocol(subscriptionType) - assert.Equal(t, expectedWebSocketProtocol, actualProtocol) - } - } - - t.Run("should return 'graphql-ws' for undefined subscription type", - run(apidef.GQLSubscriptionUndefined, graphqlDataSource.ProtocolGraphQLWS), - ) - - t.Run("should return 'graphql-ws' for graphql-ws subscription type", - run(apidef.GQLSubscriptionWS, graphqlDataSource.ProtocolGraphQLWS), - ) - - t.Run("should return 'graphql-ws' for sse subscription type as websocket protocol is irrelevant in that case", - run(apidef.GQLSubscriptionSSE, graphqlDataSource.ProtocolGraphQLWS), - ) - - t.Run("should return 'graphql-transport-ws' for graphql-transport-ws subscription type", - run(apidef.GQLSubscriptionTransportWS, graphqlDataSource.ProtocolGraphQLTWS), - ) -} - -func TestGraphQLConfigAdapter_GraphqlSubscriptionType(t *testing.T) { - run := func(subscriptionType apidef.SubscriptionType, expectedGraphQLSubscriptionType graphql.SubscriptionType) func(t *testing.T) { - return func(t *testing.T) { - adapter := NewGraphQLConfigAdapter(nil) - actualSubscriptionType := adapter.graphqlSubscriptionType(subscriptionType) - assert.Equal(t, expectedGraphQLSubscriptionType, actualSubscriptionType) - } - } - - t.Run("should return 'Unknown' for undefined subscription type", - run(apidef.GQLSubscriptionUndefined, graphql.SubscriptionTypeUnknown), - ) - - t.Run("should return 'SSE' for sse subscription type as websocket protocol is irrelevant in that case", - run(apidef.GQLSubscriptionSSE, graphql.SubscriptionTypeSSE), - ) - - t.Run("should return 'GraphQLWS' for graphql-ws subscription type", - run(apidef.GQLSubscriptionWS, graphql.SubscriptionTypeGraphQLWS), - ) - - t.Run("should return 'GraphQLTransportWS' for graphql-transport-ws subscription type", - run(apidef.GQLSubscriptionTransportWS, graphql.SubscriptionTypeGraphQLTransportWS), - ) -} - var mockSubscriptionClient = &graphqlDataSource.SubscriptionClient{} type MockSubscriptionClientFactory struct{} diff --git a/apidef/adapter/utils.go b/apidef/adapter/utils.go new file mode 100644 index 00000000000..e615518f3d8 --- /dev/null +++ b/apidef/adapter/utils.go @@ -0,0 +1,87 @@ +package adapter + +import ( + "net/http" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" + + "github.com/TykTechnologies/tyk/apidef" +) + +func parseSchema(schemaAsString string) (parsedSchema *graphql.Schema, err error) { + parsedSchema, err = graphql.NewSchemaFromString(schemaAsString) + if err != nil { + return nil, err + } + + normalizationResult, err := parsedSchema.Normalize() + if err != nil { + return nil, err + } + + if !normalizationResult.Successful && normalizationResult.Errors != nil { + return nil, normalizationResult.Errors + } + + return parsedSchema, nil +} + +func isSupergraphAPIDefinition(apiDefinition *apidef.APIDefinition) bool { + return apiDefinition.GraphQL.Enabled && apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeSupergraph +} + +func isProxyOnlyAPIDefinition(apiDefinition *apidef.APIDefinition) bool { + return apiDefinition.GraphQL.Enabled && + (apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeProxyOnly || apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeSubgraph) +} + +func graphqlDataSourceWebSocketProtocol(subscriptionType apidef.SubscriptionType) string { + wsProtocol := graphqlDataSource.ProtocolGraphQLWS + if subscriptionType == apidef.GQLSubscriptionTransportWS { + wsProtocol = graphqlDataSource.ProtocolGraphQLTWS + } + return wsProtocol +} + +func graphqlSubscriptionType(subscriptionType apidef.SubscriptionType) graphql.SubscriptionType { + switch subscriptionType { + case apidef.GQLSubscriptionWS: + return graphql.SubscriptionTypeGraphQLWS + case apidef.GQLSubscriptionTransportWS: + return graphql.SubscriptionTypeGraphQLTransportWS + case apidef.GQLSubscriptionSSE: + return graphql.SubscriptionTypeSSE + default: + return graphql.SubscriptionTypeUnknown + } +} + +func convertApiDefinitionHeadersToHttpHeaders(apiDefHeaders map[string]string) http.Header { + if len(apiDefHeaders) == 0 { + return nil + } + + engineV2Headers := make(http.Header) + for apiDefHeaderKey, apiDefHeaderValue := range apiDefHeaders { + engineV2Headers.Add(apiDefHeaderKey, apiDefHeaderValue) + } + + return engineV2Headers +} + +func removeDuplicateApiDefinitionHeaders(headers ...map[string]string) map[string]string { + hdr := make(map[string]string) + // headers priority depends on the order of arguments + for _, header := range headers { + for k, v := range header { + keyCanonical := http.CanonicalHeaderKey(k) + if _, ok := hdr[keyCanonical]; ok { + // skip because header is present + continue + } + hdr[keyCanonical] = v + } + } + return hdr +} diff --git a/apidef/adapter/utils_test.go b/apidef/adapter/utils_test.go new file mode 100644 index 00000000000..2002562b73d --- /dev/null +++ b/apidef/adapter/utils_test.go @@ -0,0 +1,227 @@ +package adapter + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" + + "github.com/TykTechnologies/tyk/apidef" +) + +func TestParseSchema(t *testing.T) { + inputSchema := ` + type Query { + hello: String! + }` + + // We are pretty confident that the schema parsing works, as it is tested in the library already. + // We just want to make sure that there is no weird error happening. + parsedSchema, err := parseSchema(inputSchema) + assert.NotNil(t, parsedSchema) + assert.NoError(t, err) +} + +func TestIsSupergraphAPIDefinition(t *testing.T) { + type testInput struct { + graphQLEnabled bool + executionModes []apidef.GraphQLExecutionMode + expectedResult bool + } + run := func(input testInput) func(t *testing.T) { + return func(t *testing.T) { + for _, executionMode := range input.executionModes { + apiDef := &apidef.APIDefinition{ + GraphQL: apidef.GraphQLConfig{ + Enabled: input.graphQLEnabled, + ExecutionMode: executionMode, + }, + } + assert.Equal(t, input.expectedResult, isSupergraphAPIDefinition(apiDef)) + } + } + } + + t.Run("false if graphql is disabled", run( + testInput{ + graphQLEnabled: false, + executionModes: []apidef.GraphQLExecutionMode{ + apidef.GraphQLExecutionModeSupergraph, + apidef.GraphQLExecutionModeProxyOnly, + apidef.GraphQLExecutionModeExecutionEngine, + apidef.GraphQLExecutionModeSubgraph, + }, + expectedResult: false, + }, + )) + + t.Run("false if execution mode is not supergraph", run( + testInput{ + graphQLEnabled: true, + executionModes: []apidef.GraphQLExecutionMode{ + apidef.GraphQLExecutionModeProxyOnly, + apidef.GraphQLExecutionModeExecutionEngine, + apidef.GraphQLExecutionModeSubgraph, + }, + expectedResult: false, + }, + )) + + t.Run("true if graphql is enabled and execution mode is supergraph", run( + testInput{ + graphQLEnabled: true, + executionModes: []apidef.GraphQLExecutionMode{ + apidef.GraphQLExecutionModeSupergraph, + }, + expectedResult: true, + }, + )) +} + +func TestIsProxyOnlyAPIDefinition(t *testing.T) { + type testInput struct { + graphQLEnabled bool + executionModes []apidef.GraphQLExecutionMode + expectedResult bool + } + run := func(input testInput) func(t *testing.T) { + return func(t *testing.T) { + for _, executionMode := range input.executionModes { + apiDef := &apidef.APIDefinition{ + GraphQL: apidef.GraphQLConfig{ + Enabled: input.graphQLEnabled, + ExecutionMode: executionMode, + }, + } + assert.Equal(t, input.expectedResult, isProxyOnlyAPIDefinition(apiDef)) + } + } + } + + t.Run("false if graphql is disabled", run( + testInput{ + graphQLEnabled: false, + executionModes: []apidef.GraphQLExecutionMode{ + apidef.GraphQLExecutionModeProxyOnly, + apidef.GraphQLExecutionModeExecutionEngine, + apidef.GraphQLExecutionModeSubgraph, + apidef.GraphQLExecutionModeSupergraph, + }, + expectedResult: false, + }, + )) + + t.Run("false if execution mode is not proxyOnly or subgraph", run( + testInput{ + graphQLEnabled: true, + executionModes: []apidef.GraphQLExecutionMode{ + apidef.GraphQLExecutionModeExecutionEngine, + apidef.GraphQLExecutionModeSupergraph, + }, + expectedResult: false, + }, + )) + + t.Run("true if graphql is enabled and execution mode is proxyOnly", run( + testInput{ + graphQLEnabled: true, + executionModes: []apidef.GraphQLExecutionMode{ + apidef.GraphQLExecutionModeProxyOnly, + apidef.GraphQLExecutionModeSubgraph, + }, + expectedResult: true, + }, + )) +} + +func TestGraphqlDataSourceWebSocketProtocol(t *testing.T) { + run := func(subscriptionType apidef.SubscriptionType, expectedWebSocketProtocol string) func(t *testing.T) { + return func(t *testing.T) { + actualProtocol := graphqlDataSourceWebSocketProtocol(subscriptionType) + assert.Equal(t, expectedWebSocketProtocol, actualProtocol) + } + } + + t.Run("should return 'graphql-ws' for undefined subscription type", + run(apidef.GQLSubscriptionUndefined, graphqlDataSource.ProtocolGraphQLWS), + ) + + t.Run("should return 'graphql-ws' for graphql-ws subscription type", + run(apidef.GQLSubscriptionWS, graphqlDataSource.ProtocolGraphQLWS), + ) + + t.Run("should return 'graphql-ws' for sse subscription type as websocket protocol is irrelevant in that case", + run(apidef.GQLSubscriptionSSE, graphqlDataSource.ProtocolGraphQLWS), + ) + + t.Run("should return 'graphql-transport-ws' for graphql-transport-ws subscription type", + run(apidef.GQLSubscriptionTransportWS, graphqlDataSource.ProtocolGraphQLTWS), + ) +} + +func TestGraphqlSubscriptionType(t *testing.T) { + run := func(subscriptionType apidef.SubscriptionType, expectedGraphQLSubscriptionType graphql.SubscriptionType) func(t *testing.T) { + return func(t *testing.T) { + actualSubscriptionType := graphqlSubscriptionType(subscriptionType) + assert.Equal(t, expectedGraphQLSubscriptionType, actualSubscriptionType) + } + } + + t.Run("should return 'Unknown' for undefined subscription type", + run(apidef.GQLSubscriptionUndefined, graphql.SubscriptionTypeUnknown), + ) + + t.Run("should return 'SSE' for sse subscription type as websocket protocol is irrelevant in that case", + run(apidef.GQLSubscriptionSSE, graphql.SubscriptionTypeSSE), + ) + + t.Run("should return 'GraphQLWS' for graphql-ws subscription type", + run(apidef.GQLSubscriptionWS, graphql.SubscriptionTypeGraphQLWS), + ) + + t.Run("should return 'GraphQLTransportWS' for graphql-transport-ws subscription type", + run(apidef.GQLSubscriptionTransportWS, graphql.SubscriptionTypeGraphQLTransportWS), + ) +} + +func TestConvertApiDefinitionHeadersToHttpHeaders(t *testing.T) { + t.Run("should return nil for empty input", func(t *testing.T) { + assert.Nil(t, convertApiDefinitionHeadersToHttpHeaders(nil)) + }) + + t.Run("should successfully convert API Definition header to Http Headers", func(t *testing.T) { + apiDefinitionHeaders := map[string]string{ + "Authorization": "token", + "X-Tyk-Key": "value", + } + + expectedHttpHeaders := http.Header{ + "Authorization": {"token"}, + "X-Tyk-Key": {"value"}, + } + + actualHttpHeaders := convertApiDefinitionHeadersToHttpHeaders(apiDefinitionHeaders) + assert.Equal(t, expectedHttpHeaders, actualHttpHeaders) + }) +} + +func TestRemoveDuplicateApiDefinitionHeaders(t *testing.T) { + apiDefinitionHeadersFirstArgument := map[string]string{ + "duplicate-header": "value", + } + apiDefinitionHeadersSecondArgument := map[string]string{ + "Duplicate-Header": "value", + "Non-Duplicate-Header": "another_value", + } + + expectedDeduplicatedHeaders := map[string]string{ + "Duplicate-Header": "value", + "Non-Duplicate-Header": "another_value", + } + + actualDeduplicatedHeaders := removeDuplicateApiDefinitionHeaders(apiDefinitionHeadersFirstArgument, apiDefinitionHeadersSecondArgument) + assert.Equal(t, expectedDeduplicatedHeaders, actualDeduplicatedHeaders) +} From 907d42b0143304499f1f82ef3e1500513c705789 Mon Sep 17 00:00:00 2001 From: Furkan Senharputlu Date: Fri, 3 Mar 2023 13:10:12 +0300 Subject: [PATCH 22/51] [TT-2584] Fix panic in JWT when JWK source and no kid in token (#4823) --- gateway/mw_jwt.go | 11 ++++++++--- gateway/mw_jwt_test.go | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/gateway/mw_jwt.go b/gateway/mw_jwt.go index 5ac92208904..a1cc119b671 100644 --- a/gateway/mw_jwt.go +++ b/gateway/mw_jwt.go @@ -139,7 +139,12 @@ func (k *JWTMiddleware) legacyGetSecretFromURL(url, kid, keyType string) (interf return nil, errors.New("No matching KID could be found") } -func (k *JWTMiddleware) getSecretFromURL(url, kid, keyType string) (interface{}, error) { +func (k *JWTMiddleware) getSecretFromURL(url string, kidVal interface{}, keyType string) (interface{}, error) { + kid, ok := kidVal.(string) + if !ok { + return nil, ErrKIDNotAString + } + // Implement a cache if JWKCache == nil { k.Logger().Debug("Creating JWK Cache") @@ -197,7 +202,7 @@ func (k *JWTMiddleware) getSecretToVerifySignature(r *http.Request, token *jwt.T if config.JWTSource != "" { // Is it a URL? if httpScheme.MatchString(config.JWTSource) { - return k.getSecretFromURL(config.JWTSource, token.Header[KID].(string), k.Spec.JWTSigningMethod) + return k.getSecretFromURL(config.JWTSource, token.Header[KID], k.Spec.JWTSigningMethod) } // If not, return the actual value @@ -208,7 +213,7 @@ func (k *JWTMiddleware) getSecretToVerifySignature(r *http.Request, token *jwt.T // Is decoded url too? if httpScheme.MatchString(string(decodedCert)) { - secret, err := k.getSecretFromURL(string(decodedCert), token.Header[KID].(string), k.Spec.JWTSigningMethod) + secret, err := k.getSecretFromURL(string(decodedCert), token.Header[KID], k.Spec.JWTSigningMethod) if err != nil { return nil, err } diff --git a/gateway/mw_jwt_test.go b/gateway/mw_jwt_test.go index 3f8facfeab6..051162a77b9 100644 --- a/gateway/mw_jwt_test.go +++ b/gateway/mw_jwt_test.go @@ -2291,3 +2291,21 @@ func TestGetUserIDFromClaim(t *testing.T) { assert.Equal(t, fmt.Sprintf("found an empty user ID in predefined base field claim %s", userIDKey), err.Error()) }) } + +func TestJWTMiddleware_getSecretToVerifySignature_JWKNoKID(t *testing.T) { + const jwkURL = "https://jwk.com" + + m := JWTMiddleware{} + api := &apidef.APIDefinition{JWTSource: jwkURL} + m.Spec = &APISpec{APIDefinition: api} + + token := &jwt.Token{Header: make(map[string]interface{})} + _, err := m.getSecretToVerifySignature(nil, token) + assert.ErrorIs(t, err, ErrKIDNotAString) + + t.Run("base64 encoded JWK URL", func(t *testing.T) { + api.JWTSource = base64.StdEncoding.EncodeToString([]byte(api.JWTSource)) + _, err := m.getSecretToVerifySignature(nil, token) + assert.ErrorIs(t, err, ErrKIDNotAString) + }) +} From 4d3ba1f66a35f4bb54e4a385262362c4d96d477e Mon Sep 17 00:00:00 2001 From: Asutosh <1187055+asutosh@users.noreply.github.com> Date: Fri, 3 Mar 2023 20:40:33 +0530 Subject: [PATCH 23/51] [TD-1474]: Sync releng templates. (#4821) ## Description This is generated from the WIP branch `fix/td-1528/update-releng-templates` on gromit. The gromit branch/PR should be merged to master before this PR gets merged,as sync automation would run on tyk once this is merged. For all the versioned releng templates to work with sync-automation - the sync automation script requires the PR that changed the templates be merged to gromit master. --- .github/dependabot.yml | 2 +- .github/workflows/del-env.yml | 2 +- .github/workflows/release.yml | 3 +-- ci/Dockerfile.std | 5 ++--- ci/aws/byol.pkr.hcl | 2 +- ci/goreleaser/goreleaser-el7.yml | 7 +++---- ci/goreleaser/goreleaser.yml | 7 +++---- ci/install/before_install.sh | 2 +- ci/install/post_install.sh | 2 +- ci/install/post_remove.sh | 2 +- ci/install/post_trans.sh | 2 +- ci/terraform/outputs.tf | 2 +- 12 files changed, 17 insertions(+), 21 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index aeb3afd5b92..ba8c81f2ded 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 version: 2 updates: diff --git a/.github/workflows/del-env.yml b/.github/workflows/del-env.yml index 4cea5b19755..236a7bb09b9 100644 --- a/.github/workflows/del-env.yml +++ b/.github/workflows/del-env.yml @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 name: Retiring dev env diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3576fdddd9b..77e14bf6f40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 # Distribution channels covered by this workflow @@ -69,7 +69,6 @@ jobs: with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Login to Cloudsmith if: startsWith(github.ref, 'refs/tags') uses: docker/login-action@v2 diff --git a/ci/Dockerfile.std b/ci/Dockerfile.std index 0ec9381f7d9..bbd2250268c 100644 --- a/ci/Dockerfile.std +++ b/ci/Dockerfile.std @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 FROM debian:bullseye-slim ARG TARGETARCH @@ -9,7 +9,6 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update \ && apt-get dist-upgrade -y ca-certificates - # Install curl and python3 RUN apt-get install -y curl python3-setuptools libpython3.9 python3.9-dev \ && curl https://bootstrap.pypa.io/get-pip.py | python3 \ @@ -17,7 +16,7 @@ RUN apt-get install -y curl python3-setuptools libpython3.9 python3.9-dev \ # Remove some things to decrease CVE surface -RUN apt-get remove -y --auto-remove --allow-remove-essential curl libtiff5 ncurses-base \ +RUN apt-get remove -y --allow-remove-essential --auto-remove curl libtiff5 ncurses-base \ && rm /usr/bin/passwd && rm /usr/sbin/adduser # Clean up caches, unwanted .a and .o files diff --git a/ci/aws/byol.pkr.hcl b/ci/aws/byol.pkr.hcl index d2f699e2d1a..32cb8060d45 100644 --- a/ci/aws/byol.pkr.hcl +++ b/ci/aws/byol.pkr.hcl @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 packer { required_plugins { diff --git a/ci/goreleaser/goreleaser-el7.yml b/ci/goreleaser/goreleaser-el7.yml index e3f9944cbb9..b4d2385bba5 100644 --- a/ci/goreleaser/goreleaser-el7.yml +++ b/ci/goreleaser/goreleaser-el7.yml @@ -1,5 +1,5 @@ # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 # Check the documentation at http://goreleaser.com # This project needs CGO_ENABLED=1 and the cross-compiler toolchains for @@ -35,8 +35,7 @@ nfpms: vendor: "Tyk Technologies Ltd" homepage: "https://tyk.io" maintainer: "Tyk " - description: Tyk API Gateway - + description: Tyk Open Source API Gateway written in Go, supporting REST, GraphQL, TCP and gRPC protocols package_name: tyk-gateway builds: - std-linux @@ -147,6 +146,7 @@ dockers: - gateway - goplugin - header + - internal - log - regexp - request @@ -159,7 +159,6 @@ dockers: - testdata - trace - user - - internal checksum: disable: true diff --git a/ci/goreleaser/goreleaser.yml b/ci/goreleaser/goreleaser.yml index 3634b34fef9..e11d65035b0 100644 --- a/ci/goreleaser/goreleaser.yml +++ b/ci/goreleaser/goreleaser.yml @@ -1,5 +1,5 @@ # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 # Check the documentation at http://goreleaser.com # This project needs CGO_ENABLED=1 and the cross-compiler toolchains for @@ -154,6 +154,7 @@ dockers: - gateway - goplugin - header + - internal - log - regexp - request @@ -166,7 +167,6 @@ dockers: - testdata - trace - user - - internal docker_manifests: - name_template: tykio/tyk-gateway:{{ .Tag }} @@ -187,8 +187,7 @@ nfpms: vendor: "Tyk Technologies Ltd" homepage: "https://tyk.io" maintainer: "Tyk " - description: Tyk API Gateway - + description: Tyk Open Source API Gateway written in Go, supporting REST, GraphQL, TCP and gRPC protocols package_name: tyk-gateway builds: - std-linux diff --git a/ci/install/before_install.sh b/ci/install/before_install.sh index 57be3301c49..7ef35697c92 100755 --- a/ci/install/before_install.sh +++ b/ci/install/before_install.sh @@ -1,7 +1,7 @@ #!/bin/bash # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 echo "Creating user and group..." GROUPNAME="tyk" diff --git a/ci/install/post_install.sh b/ci/install/post_install.sh index ec0f9885c33..956244a821e 100755 --- a/ci/install/post_install.sh +++ b/ci/install/post_install.sh @@ -2,7 +2,7 @@ # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 # If "True" the install directory ownership will be changed to "tyk:tyk" change_ownership="False" diff --git a/ci/install/post_remove.sh b/ci/install/post_remove.sh index 4a0b5b3f367..417bc666069 100755 --- a/ci/install/post_remove.sh +++ b/ci/install/post_remove.sh @@ -1,7 +1,7 @@ #!/bin/sh # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 cleanRemove() { diff --git a/ci/install/post_trans.sh b/ci/install/post_trans.sh index 6466cb4ee40..14421b6c66f 100644 --- a/ci/install/post_trans.sh +++ b/ci/install/post_trans.sh @@ -1,7 +1,7 @@ #!/bin/sh # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 if command -V systemctl >/dev/null 2>&1; then if [ ! -f /lib/systemd/system/tyk-gateway.service ]; then diff --git a/ci/terraform/outputs.tf b/ci/terraform/outputs.tf index 9de187a05ad..00fadadeeb9 100644 --- a/ci/terraform/outputs.tf +++ b/ci/terraform/outputs.tf @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Wed Dec 14 19:54:02 UTC 2022 +# Generated on: Thu Mar 2 07:07:43 UTC 2023 From d9158ccd7ebd48949561a7693da68a1e0595897a Mon Sep 17 00:00:00 2001 From: Patric Vormstein Date: Mon, 6 Mar 2023 11:20:41 +0100 Subject: [PATCH 24/51] create interface for adapters and add implementations (#4824) This PR adds a GraphQLEngineAdapter interface which is implemented by `gqlengineadapter.ProxyOnly`, `gqlengineadapter.Supergraph` and `gqlengineadapter.UniversalDataGraph`. This also means that those implementations moved to a subpackage called `gqlengineadapter`. The `adapter` package is meant to be a general package for API definition adapters (like AsyncAPI), so it makes sense not to clutter it with GraphQL Engine Adapters. ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [x] Refactoring or add test (improvements in base code or adds test coverage to functionality) --- .../gqlengineadapter/adapter_proxy_only.go | 56 + .../adapter_proxy_only_test.go | 269 ++++ .../gqlengineadapter/adapter_supergraph.go | 84 ++ .../adapter_supergraph_test.go | 235 ++++ .../adapter/gqlengineadapter/adapter_udg.go | 210 +++ .../gqlengineadapter/adapter_udg_test.go | 737 +++++++++++ apidef/adapter/gqlengineadapter/utils.go | 78 ++ .../utils_engine_v2.go} | 9 +- .../utils_engine_v2_test.go} | 14 +- apidef/adapter/gqlengineadapter/utils_test.go | 124 ++ apidef/adapter/graphql_config_adapter.go | 337 +---- apidef/adapter/graphql_config_adapter_test.go | 1169 ++--------------- apidef/adapter/utils.go | 78 +- apidef/adapter/utils_test.go | 175 ++- 14 files changed, 2036 insertions(+), 1539 deletions(-) create mode 100644 apidef/adapter/gqlengineadapter/adapter_proxy_only.go create mode 100644 apidef/adapter/gqlengineadapter/adapter_proxy_only_test.go create mode 100644 apidef/adapter/gqlengineadapter/adapter_supergraph.go create mode 100644 apidef/adapter/gqlengineadapter/adapter_supergraph_test.go create mode 100644 apidef/adapter/gqlengineadapter/adapter_udg.go create mode 100644 apidef/adapter/gqlengineadapter/adapter_udg_test.go create mode 100644 apidef/adapter/gqlengineadapter/utils.go rename apidef/adapter/{engine_v2_utils.go => gqlengineadapter/utils_engine_v2.go} (92%) rename apidef/adapter/{engine_v2_utils_test.go => gqlengineadapter/utils_engine_v2_test.go} (92%) create mode 100644 apidef/adapter/gqlengineadapter/utils_test.go diff --git a/apidef/adapter/gqlengineadapter/adapter_proxy_only.go b/apidef/adapter/gqlengineadapter/adapter_proxy_only.go new file mode 100644 index 00000000000..5cfd82f88e1 --- /dev/null +++ b/apidef/adapter/gqlengineadapter/adapter_proxy_only.go @@ -0,0 +1,56 @@ +package gqlengineadapter + +import ( + "net/http" + "strings" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" + + "github.com/TykTechnologies/tyk/apidef" +) + +type ProxyOnly struct { + ApiDefinition *apidef.APIDefinition + HttpClient *http.Client + StreamingClient *http.Client + Schema *graphql.Schema + + subscriptionClientFactory graphqlDataSource.GraphQLSubscriptionClientFactory +} + +func (p *ProxyOnly) EngineConfig() (*graphql.EngineV2Configuration, error) { + var err error + if p.Schema == nil { + p.Schema, err = parseSchema(p.ApiDefinition.GraphQL.Schema) + if err != nil { + return nil, err + } + } + + staticHeaders := make(http.Header) + + url := p.ApiDefinition.Proxy.TargetURL + if strings.HasPrefix(url, "tyk://") { + url = strings.ReplaceAll(url, "tyk://", "http://") + staticHeaders.Set(apidef.TykInternalApiHeader, "true") + } + + upstreamConfig := graphql.ProxyUpstreamConfig{ + URL: url, + StaticHeaders: staticHeaders, + SubscriptionType: graphqlSubscriptionType(p.ApiDefinition.GraphQL.Proxy.SubscriptionType), + } + + v2Config, err := graphql.NewProxyEngineConfigFactory( + p.Schema, + upstreamConfig, + graphqlDataSource.NewBatchFactory(), + graphql.WithProxyHttpClient(p.HttpClient), + graphql.WithProxyStreamingClient(p.StreamingClient), + graphql.WithProxySubscriptionClientFactory(subscriptionClientFactoryOrDefault(p.subscriptionClientFactory)), + ).EngineV2Configuration() + + v2Config.EnableSingleFlight(true) + return &v2Config, err +} diff --git a/apidef/adapter/gqlengineadapter/adapter_proxy_only_test.go b/apidef/adapter/gqlengineadapter/adapter_proxy_only_test.go new file mode 100644 index 00000000000..94a1eb58b74 --- /dev/null +++ b/apidef/adapter/gqlengineadapter/adapter_proxy_only_test.go @@ -0,0 +1,269 @@ +package gqlengineadapter + +import ( + "encoding/json" + "net/http" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" + + "github.com/TykTechnologies/tyk/apidef" +) + +func TestProxyOnly_EngineConfig(t *testing.T) { + t.Run("should create v2 config for proxy-only mode", func(t *testing.T) { + var gqlConfig apidef.GraphQLConfig + require.NoError(t, json.Unmarshal([]byte(graphqlProxyOnlyConfig), &gqlConfig)) + + apiDef := &apidef.APIDefinition{ + GraphQL: gqlConfig, + Proxy: apidef.ProxyConfig{ + TargetURL: "http://localhost:8080", + }, + } + + httpClient := &http.Client{} + streamingClient := &http.Client{} + adapter := ProxyOnly{ + ApiDefinition: apiDef, + HttpClient: httpClient, + StreamingClient: streamingClient, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + } + + engineV2Config, err := adapter.EngineConfig() + assert.NoError(t, err) + + expectedDataSource := plan.DataSourceConfiguration{ + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"hello"}, + }, + }, + ChildNodes: []plan.TypeField{}, + Factory: &graphqlDataSource.Factory{ + BatchFactory: graphqlDataSource.NewBatchFactory(), + HTTPClient: httpClient, + StreamingClient: streamingClient, + SubscriptionClient: mockSubscriptionClient, + }, + Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://localhost:8080", + Header: map[string][]string{}, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://localhost:8080", + UseSSE: true, + }, + }), + } + + expectedFieldConfig := plan.FieldConfiguration{ + TypeName: "Query", + FieldName: "hello", + Arguments: plan.ArgumentsConfigurations{ + { + Name: "name", + SourceType: plan.FieldArgumentSource, + }, + }, + } + + assert.Containsf(t, engineV2Config.DataSources(), expectedDataSource, "engine configuration does not contain proxy-only data source") + assert.Containsf(t, engineV2Config.FieldConfigurations(), expectedFieldConfig, "engine configuration does not contain expected field config") + }) + + t.Run("should create v2 config for internal proxy-only api", func(t *testing.T) { + var gqlConfig apidef.GraphQLConfig + require.NoError(t, json.Unmarshal([]byte(graphqlProxyOnlyConfig), &gqlConfig)) + + apiDef := &apidef.APIDefinition{ + GraphQL: gqlConfig, + Proxy: apidef.ProxyConfig{ + TargetURL: "tyk://api-name", + }, + } + + httpClient := &http.Client{} + streamingClient := &http.Client{} + adapter := ProxyOnly{ + ApiDefinition: apiDef, + HttpClient: httpClient, + StreamingClient: streamingClient, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + } + + engineV2Config, err := adapter.EngineConfig() + assert.NoError(t, err) + + expectedDataSource := plan.DataSourceConfiguration{ + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"hello"}, + }, + }, + ChildNodes: []plan.TypeField{}, + Factory: &graphqlDataSource.Factory{ + BatchFactory: graphqlDataSource.NewBatchFactory(), + HTTPClient: httpClient, + StreamingClient: streamingClient, + SubscriptionClient: mockSubscriptionClient, + }, + Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://api-name", + Header: http.Header{ + "X-Tyk-Internal": []string{"true"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://api-name", + UseSSE: true, + }, + }), + } + + expectedFieldConfig := plan.FieldConfiguration{ + TypeName: "Query", + FieldName: "hello", + Arguments: plan.ArgumentsConfigurations{ + { + Name: "name", + SourceType: plan.FieldArgumentSource, + }, + }, + } + + assert.Containsf(t, engineV2Config.DataSources(), expectedDataSource, "engine configuration does not contain proxy-only data source") + assert.Containsf(t, engineV2Config.FieldConfigurations(), expectedFieldConfig, "engine configuration does not contain expected field config") + }) + + t.Run("should create v2 config for subgraph as proxy-only without error", func(t *testing.T) { + var gqlConfig apidef.GraphQLConfig + require.NoError(t, json.Unmarshal([]byte(graphqlSubgraphConfig), &gqlConfig)) + + apiDef := &apidef.APIDefinition{ + GraphQL: gqlConfig, + Proxy: apidef.ProxyConfig{ + TargetURL: "http://localhost:8080", + }, + } + + httpClient := &http.Client{} + streamingClient := &http.Client{} + adapter := ProxyOnly{ + ApiDefinition: apiDef, + HttpClient: httpClient, + StreamingClient: streamingClient, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + } + + engineV2Config, err := adapter.EngineConfig() + assert.NoError(t, err) + + expectedDataSource := plan.DataSourceConfiguration{ + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"me", "_entities", "_service"}, + }, + }, + ChildNodes: []plan.TypeField{ + { + TypeName: "_Service", + FieldNames: []string{"sdl"}, + }, + { + TypeName: "User", + FieldNames: []string{"id", "username"}, + }, + }, + Factory: &graphqlDataSource.Factory{ + BatchFactory: graphqlDataSource.NewBatchFactory(), + HTTPClient: httpClient, + StreamingClient: streamingClient, + SubscriptionClient: mockSubscriptionClient, + }, + Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://localhost:8080", + Header: http.Header{}, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://localhost:8080", + UseSSE: false, + }, + }), + } + + expectedFieldConfig := plan.FieldConfiguration{ + TypeName: "Query", + FieldName: "_entities", + Arguments: plan.ArgumentsConfigurations{ + { + Name: "representations", + SourceType: plan.FieldArgumentSource, + }, + }, + } + + assert.Containsf(t, engineV2Config.DataSources(), expectedDataSource, "engine configuration does not contain proxy-only data source") + assert.Containsf(t, engineV2Config.FieldConfigurations(), expectedFieldConfig, "engine configuration does not contain expected field config") + }) +} + +var graphqlProxyOnlyConfig = `{ + "enabled": true, + "execution_mode": "proxyOnly", + "version": "2", + "schema": "type Query { hello(name: String!): String! }", + "last_schema_update": "2020-11-11T11:11:11.000+01:00", + "proxy": { + "auth_headers": { + "Authorization": "123abc" + }, + "subscription_type": "sse" + }, + "engine": { + "field_configs": [], + "data_sources": [] + }, + "supergraph": { + "subgraphs": [], + "global_headers": {}, + "merged_sdl": "" + }, + "playground": {} +}` + +var graphqlSubgraphConfig = `{ + "enabled": true, + "execution_mode": "subgraph", + "version": "2", + "schema": ` + strconv.Quote(graphqlSubgraphSchema) + `, + "last_schema_update": "2020-11-11T11:11:11.000+01:00", + "proxy": { + "auth_headers": { + "Authorization": "123abc" + } + }, + "engine": { + "field_configs": [], + "data_sources": [] + }, + "subgraph": { + "sdl": ` + strconv.Quote(federationAccountsServiceSDL) + `, + "subscription_type": "graphql-transport-ws" + }, + "playground": {} +}` + +const graphqlSubgraphSchema = `scalar _Any scalar _FieldSet union _Entity = User type _Service { sdl: String } type Query { me: User _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type User { id: ID! username: String! }` diff --git a/apidef/adapter/gqlengineadapter/adapter_supergraph.go b/apidef/adapter/gqlengineadapter/adapter_supergraph.go new file mode 100644 index 00000000000..c7e712e553b --- /dev/null +++ b/apidef/adapter/gqlengineadapter/adapter_supergraph.go @@ -0,0 +1,84 @@ +package gqlengineadapter + +import ( + "net/http" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" + + "github.com/TykTechnologies/tyk/apidef" +) + +type Supergraph struct { + ApiDefinition *apidef.APIDefinition + HttpClient *http.Client + StreamingClient *http.Client + + subscriptionClientFactory graphqlDataSource.GraphQLSubscriptionClientFactory +} + +func (s *Supergraph) EngineConfig() (*graphql.EngineV2Configuration, error) { + dataSourceConfs := s.subgraphDataSourceConfigs() + var federationConfigV2Factory *graphql.FederationEngineConfigFactory + if s.ApiDefinition.GraphQL.Supergraph.DisableQueryBatching { + federationConfigV2Factory = graphql.NewFederationEngineConfigFactory( + dataSourceConfs, + nil, + graphql.WithFederationHttpClient(s.HttpClient), + graphql.WithFederationStreamingClient(s.StreamingClient), + graphql.WithFederationSubscriptionClientFactory(subscriptionClientFactoryOrDefault(s.subscriptionClientFactory)), + ) + } else { + federationConfigV2Factory = graphql.NewFederationEngineConfigFactory( + dataSourceConfs, + graphqlDataSource.NewBatchFactory(), + graphql.WithFederationHttpClient(s.HttpClient), + graphql.WithFederationStreamingClient(s.StreamingClient), + graphql.WithFederationSubscriptionClientFactory(subscriptionClientFactoryOrDefault(s.subscriptionClientFactory)), + ) + } + + err := federationConfigV2Factory.SetMergedSchemaFromString(s.ApiDefinition.GraphQL.Supergraph.MergedSDL) + if err != nil { + return nil, err + } + + conf, err := federationConfigV2Factory.EngineV2Configuration() + if err != nil { + return nil, err + } + + conf.EnableSingleFlight(true) + if !s.ApiDefinition.GraphQL.Supergraph.DisableQueryBatching { + conf.EnableDataLoader(true) + } + + return &conf, nil +} + +func (s *Supergraph) subgraphDataSourceConfigs() []graphqlDataSource.Configuration { + confs := make([]graphqlDataSource.Configuration, 0) + if len(s.ApiDefinition.GraphQL.Supergraph.Subgraphs) == 0 { + return confs + } + + for _, apiDefSubgraphConf := range s.ApiDefinition.GraphQL.Supergraph.Subgraphs { + if len(apiDefSubgraphConf.SDL) == 0 { + continue + } + hdr := removeDuplicateApiDefinitionHeaders(apiDefSubgraphConf.Headers, s.ApiDefinition.GraphQL.Supergraph.GlobalHeaders) + conf := graphqlDataSourceConfiguration( + apiDefSubgraphConf.URL, + http.MethodPost, + hdr, + apiDefSubgraphConf.SubscriptionType) + conf.Federation = graphqlDataSource.FederationConfiguration{ + Enabled: true, + ServiceSDL: apiDefSubgraphConf.SDL, + } + + confs = append(confs, conf) + } + + return confs +} diff --git a/apidef/adapter/gqlengineadapter/adapter_supergraph_test.go b/apidef/adapter/gqlengineadapter/adapter_supergraph_test.go new file mode 100644 index 00000000000..75c1b4afda5 --- /dev/null +++ b/apidef/adapter/gqlengineadapter/adapter_supergraph_test.go @@ -0,0 +1,235 @@ +package gqlengineadapter + +import ( + "encoding/json" + "net/http" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" + + "github.com/TykTechnologies/tyk/apidef" +) + +func TestSupergraph_EngineConfig(t *testing.T) { + t.Run("should create v2 config for supergraph execution mode without error", func(t *testing.T) { + var gqlConfig apidef.GraphQLConfig + require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2SupergraphConfigJson), &gqlConfig)) + + apiDef := &apidef.APIDefinition{ + GraphQL: gqlConfig, + } + + httpClient := &http.Client{} + adapter := Supergraph{ + ApiDefinition: apiDef, + HttpClient: httpClient, + StreamingClient: nil, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + } + + _, err := adapter.EngineConfig() + assert.NoError(t, err) + }) + t.Run("should create v2 config for supergraph with batching disabled", func(t *testing.T) { + var gqlConfig apidef.GraphQLConfig + require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2SupergraphConfigJson), &gqlConfig)) + + apiDef := &apidef.APIDefinition{ + GraphQL: gqlConfig, + } + apiDef.GraphQL.Supergraph.DisableQueryBatching = true + + httpClient := &http.Client{} + streamingClient := &http.Client{} + adapter := Supergraph{ + ApiDefinition: apiDef, + HttpClient: httpClient, + StreamingClient: streamingClient, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + } + + v2Config, err := adapter.EngineConfig() + assert.NoError(t, err) + expectedDataSource := plan.DataSourceConfiguration{ + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"me"}, + }, + { + TypeName: "User", + FieldNames: []string{"id", "username"}, + }, + }, + ChildNodes: []plan.TypeField{ + { + TypeName: "User", + FieldNames: []string{"id", "username"}, + }, + }, + Factory: &graphqlDataSource.Factory{ + HTTPClient: httpClient, + StreamingClient: streamingClient, + SubscriptionClient: mockSubscriptionClient, + }, + Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://accounts.service", + Method: http.MethodPost, + Header: http.Header{ + "Auth": []string{"appended_header"}, + "Header1": []string{"override_global"}, + "Header2": []string{"value2"}, + "X-Tyk-Internal": []string{"true"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://accounts.service", + UseSSE: true, + }, + Federation: graphqlDataSource.FederationConfiguration{ + Enabled: true, + ServiceSDL: `extend type Query {me: User} type User @key(fields: "id"){ id: ID! username: String!}`, + }, + }), + } + assert.Containsf(t, v2Config.DataSources(), expectedDataSource, "engine configuration does not contain proxy-only data source") + + }) +} + +func TestSupergraph_supergraphDataSourceConfigs(t *testing.T) { + expectedDataSourceConfigs := []graphqlDataSource.Configuration{ + { + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://accounts.service", + Method: http.MethodPost, + Header: http.Header{ + "Header1": []string{"override_global"}, + "Header2": []string{"value2"}, + "X-Tyk-Internal": []string{"true"}, + "Auth": []string{"appended_header"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://accounts.service", + UseSSE: true, + }, + Federation: graphqlDataSource.FederationConfiguration{ + Enabled: true, + ServiceSDL: federationAccountsServiceSDL, + }, + }, + { + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://products.service", + Method: http.MethodPost, + Header: http.Header{ + "Header1": []string{"value1"}, + "Header2": []string{"value2"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://products.service", + }, + Federation: graphqlDataSource.FederationConfiguration{ + Enabled: true, + ServiceSDL: federationProductsServiceSDL, + }, + }, + { + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://reviews.service", + Method: http.MethodPost, + Header: http.Header{ + "Header1": []string{"override_global"}, + "Auth": []string{"appended_header"}, + "Header2": []string{"value2"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://reviews.service", + }, + Federation: graphqlDataSource.FederationConfiguration{ + Enabled: true, + ServiceSDL: federationReviewsServiceSDL, + }, + }, + } + + var gqlConfig apidef.GraphQLConfig + require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2SupergraphConfigJson), &gqlConfig)) + + apiDef := &apidef.APIDefinition{ + GraphQL: gqlConfig, + } + + adapter := Supergraph{ + ApiDefinition: apiDef, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + } + actualGraphQLConfigs := adapter.subgraphDataSourceConfigs() + assert.Equal(t, expectedDataSourceConfigs, actualGraphQLConfigs) +} + +const federationAccountsServiceSDL = `extend type Query {me: User} type User @key(fields: "id"){ id: ID! username: String!}` +const federationProductsServiceSDL = `extend type Query {topProducts(first: Int = 5): [Product]} type Product @key(fields: "upc") {upc: String! name: String! price: Int!}` +const federationReviewsServiceSDL = `type Review { body: String! author: User! @provides(fields: "username") product: Product! } extend type User @key(fields: "id") { id: ID! @external reviews: [Review] } extend type Product @key(fields: "upc") { upc: String! @external reviews: [Review] }` +const federationMergedSDL = `type Query { me: User topProducts(first: Int = 5): [Product] } type User { id: ID! username: String! reviews: [Review] } type Product { upc: String! name: String! price: Int! reviews: [Review] } type Review { body: String! author: User! product: Product! }` + +var graphqlEngineV2SupergraphConfigJson = `{ + "enabled": true, + "execution_mode": "supergraph", + "version": "2", + "schema": "", + "last_schema_update": "2020-11-11T11:11:11.000+01:00", + "engine": { + "field_configs": [], + "data_sources": [] + }, + "supergraph": { + "subgraphs": [ + { + "api_id": "", + "url": "tyk://accounts.service", + "sdl": ` + strconv.Quote(federationAccountsServiceSDL) + `, + "headers": { + "header1": "override_global", + "Auth": "appended_header" + }, + "subscription_type": "sse" + }, + { + "api_id": "", + "url": "http://products.service", + "sdl": ` + strconv.Quote(federationProductsServiceSDL) + ` + }, + { + "api_id": "", + "url": "http://ignored.service", + "sdl": "" + }, + { + "api_id": "", + "url": "http://reviews.service", + "sdl": ` + strconv.Quote(federationReviewsServiceSDL) + `, + "headers": { + "header1": "override_global", + "header2": "value2", + "Auth": "appended_header" + } + } + ], + "global_headers": { + "header1": "value1", + "header2": "value2" + }, + "merged_sdl": "` + federationMergedSDL + `" + }, + "playground": {} +}` diff --git a/apidef/adapter/gqlengineadapter/adapter_udg.go b/apidef/adapter/gqlengineadapter/adapter_udg.go new file mode 100644 index 00000000000..a179b1d1d5d --- /dev/null +++ b/apidef/adapter/gqlengineadapter/adapter_udg.go @@ -0,0 +1,210 @@ +package gqlengineadapter + +import ( + "encoding/json" + "net/http" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + kafkaDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/kafka_datasource" + restDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/rest_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" + "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" + + "github.com/TykTechnologies/tyk/apidef" +) + +type UniversalDataGraph struct { + ApiDefinition *apidef.APIDefinition + HttpClient *http.Client + StreamingClient *http.Client + Schema *graphql.Schema + + subscriptionClientFactory graphqlDataSource.GraphQLSubscriptionClientFactory +} + +func (u *UniversalDataGraph) EngineConfig() (*graphql.EngineV2Configuration, error) { + var err error + if u.Schema == nil { + u.Schema, err = parseSchema(u.ApiDefinition.GraphQL.Schema) + if err != nil { + return nil, err + } + } + + conf := graphql.NewEngineV2Configuration(u.Schema) + conf.EnableSingleFlight(true) + + fieldConfigs := u.engineConfigV2FieldConfigs() + datsSources, err := u.engineConfigV2DataSources() + if err != nil { + return nil, err + } + + conf.SetFieldConfigurations(fieldConfigs) + conf.SetDataSources(datsSources) + + return &conf, nil +} + +func (u *UniversalDataGraph) engineConfigV2FieldConfigs() (planFieldConfigs plan.FieldConfigurations) { + for _, fc := range u.ApiDefinition.GraphQL.Engine.FieldConfigs { + planFieldConfig := plan.FieldConfiguration{ + TypeName: fc.TypeName, + FieldName: fc.FieldName, + DisableDefaultMapping: fc.DisableDefaultMapping, + Path: fc.Path, + } + + planFieldConfigs = append(planFieldConfigs, planFieldConfig) + } + + generatedArgs := u.Schema.GetAllFieldArguments(graphql.NewSkipReservedNamesFunc()) + generatedArgsAsLookupMap := graphql.CreateTypeFieldArgumentsLookupMap(generatedArgs) + u.engineConfigV2Arguments(&planFieldConfigs, generatedArgsAsLookupMap) + + return planFieldConfigs +} + +func (u *UniversalDataGraph) engineConfigV2DataSources() (planDataSources []plan.DataSourceConfiguration, err error) { + for _, ds := range u.ApiDefinition.GraphQL.Engine.DataSources { + planDataSource := plan.DataSourceConfiguration{ + RootNodes: []plan.TypeField{}, + } + + for _, typeField := range ds.RootFields { + planTypeField := plan.TypeField{ + TypeName: typeField.Type, + FieldNames: typeField.Fields, + } + + planDataSource.RootNodes = append(planDataSource.RootNodes, planTypeField) + } + + switch ds.Kind { + case apidef.GraphQLEngineDataSourceKindREST: + var restConfig apidef.GraphQLEngineDataSourceConfigREST + err = json.Unmarshal(ds.Config, &restConfig) + if err != nil { + return nil, err + } + + planDataSource.Factory = &restDataSource.Factory{ + Client: u.HttpClient, + } + + urlWithoutQueryParams, queryConfigs, err := extractURLQueryParamsForEngineV2(restConfig.URL, restConfig.Query) + if err != nil { + return nil, err + } + + planDataSource.Custom = restDataSource.ConfigJSON(restDataSource.Configuration{ + Fetch: restDataSource.FetchConfiguration{ + URL: urlWithoutQueryParams, + Method: restConfig.Method, + Body: restConfig.Body, + Query: queryConfigs, + Header: convertApiDefinitionHeadersToHttpHeaders(restConfig.Headers), + }, + }) + + case apidef.GraphQLEngineDataSourceKindGraphQL: + var graphqlConfig apidef.GraphQLEngineDataSourceConfigGraphQL + err = json.Unmarshal(ds.Config, &graphqlConfig) + if err != nil { + return nil, err + } + + planDataSource.Factory, err = createGraphQLDataSourceFactory(createGraphQLDataSourceFactoryParams{ + graphqlConfig: graphqlConfig, + subscriptionClientFactory: subscriptionClientFactoryOrDefault(u.subscriptionClientFactory), + httpClient: u.HttpClient, + streamingClient: u.StreamingClient, + }) + if err != nil { + return nil, err + } + + planDataSource.Custom = graphqlDataSource.ConfigJson(graphqlDataSourceConfiguration( + graphqlConfig.URL, + graphqlConfig.Method, + graphqlConfig.Headers, + graphqlConfig.SubscriptionType, + )) + + case apidef.GraphQLEngineDataSourceKindKafka: + var kafkaConfig apidef.GraphQLEngineDataSourceConfigKafka + err = json.Unmarshal(ds.Config, &kafkaConfig) + if err != nil { + return nil, err + } + + planDataSource.Factory = &kafkaDataSource.Factory{} + planDataSource.Custom = kafkaDataSource.ConfigJSON(kafkaDataSource.Configuration{ + Subscription: kafkaDataSource.SubscriptionConfiguration{ + BrokerAddresses: kafkaConfig.BrokerAddresses, + Topics: kafkaConfig.Topics, + GroupID: kafkaConfig.GroupID, + ClientID: kafkaConfig.ClientID, + KafkaVersion: kafkaConfig.KafkaVersion, + StartConsumingLatest: kafkaConfig.StartConsumingLatest, + BalanceStrategy: kafkaConfig.BalanceStrategy, + IsolationLevel: kafkaConfig.IsolationLevel, + SASL: kafkaConfig.SASL, + }, + }) + } + + planDataSources = append(planDataSources, planDataSource) + } + + err = u.determineChildNodes(planDataSources) + return planDataSources, err +} + +func (u *UniversalDataGraph) engineConfigV2Arguments(fieldConfs *plan.FieldConfigurations, generatedArgs map[graphql.TypeFieldLookupKey]graphql.TypeFieldArguments) { + for i := range *fieldConfs { + if len(generatedArgs) == 0 { + return + } + + lookupKey := graphql.CreateTypeFieldLookupKey((*fieldConfs)[i].TypeName, (*fieldConfs)[i].FieldName) + currentArgs, ok := generatedArgs[lookupKey] + if !ok { + continue + } + + (*fieldConfs)[i].Arguments = createArgumentConfigurationsForArgumentNames(currentArgs.ArgumentNames...) + delete(generatedArgs, lookupKey) + } + + for _, genArgs := range generatedArgs { + *fieldConfs = append(*fieldConfs, plan.FieldConfiguration{ + TypeName: genArgs.TypeName, + FieldName: genArgs.FieldName, + Arguments: createArgumentConfigurationsForArgumentNames(genArgs.ArgumentNames...), + }) + } +} + +func (u *UniversalDataGraph) determineChildNodes(planDataSources []plan.DataSourceConfiguration) error { + for i := range planDataSources { + for j := range planDataSources[i].RootNodes { + typeName := planDataSources[i].RootNodes[j].TypeName + for k := range planDataSources[i].RootNodes[j].FieldNames { + fieldName := planDataSources[i].RootNodes[j].FieldNames[k] + typeFields := u.Schema.GetAllNestedFieldChildrenFromTypeField(typeName, fieldName, graphql.NewIsDataSourceConfigV2RootFieldSkipFunc(planDataSources)) + + children := make([]plan.TypeField, 0) + for _, tf := range typeFields { + childNode := plan.TypeField{ + TypeName: tf.TypeName, + FieldNames: tf.FieldNames, + } + children = append(children, childNode) + } + planDataSources[i].ChildNodes = append(planDataSources[i].ChildNodes, children...) + } + } + } + return nil +} diff --git a/apidef/adapter/gqlengineadapter/adapter_udg_test.go b/apidef/adapter/gqlengineadapter/adapter_udg_test.go new file mode 100644 index 00000000000..63a1dd7cc7b --- /dev/null +++ b/apidef/adapter/gqlengineadapter/adapter_udg_test.go @@ -0,0 +1,737 @@ +package gqlengineadapter + +import ( + "encoding/json" + "net/http" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + kafkaDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/kafka_datasource" + restDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/rest_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" + + "github.com/TykTechnologies/tyk/apidef" +) + +func TestUniversalDataGraph_EngineConfig(t *testing.T) { + t.Run("should create v2 config for engine execution mode without error", func(t *testing.T) { + var gqlConfig apidef.GraphQLConfig + require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2ConfigJson), &gqlConfig)) + + apiDef := &apidef.APIDefinition{ + GraphQL: gqlConfig, + } + + httpClient := &http.Client{} + streamingClient := &http.Client{} + adapter := UniversalDataGraph{ + ApiDefinition: apiDef, + HttpClient: httpClient, + StreamingClient: streamingClient, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + } + + _, err := adapter.EngineConfig() + assert.NoError(t, err) + }) +} + +func TestUniversalDataGraph_engineConfigV2FieldConfigs(t *testing.T) { + expectedFieldCfgs := plan.FieldConfigurations{ + { + TypeName: "Query", + FieldName: "rest", + DisableDefaultMapping: false, + Path: []string{"my_rest"}, + }, + { + TypeName: "Query", + FieldName: "gql", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "id", + SourceType: plan.FieldArgumentSource, + }, + { + Name: "name", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "Query", + FieldName: "restWithQueryParams", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "q", + SourceType: plan.FieldArgumentSource, + }, + { + Name: "order", + SourceType: plan.FieldArgumentSource, + }, + { + Name: "limit", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "Query", + FieldName: "restWithPathParams", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "id", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "Query", + FieldName: "restWithFullUrlAsParam", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "url", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "DeepGQL", + FieldName: "query", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "code", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "Subscription", + FieldName: "foobarTopicWithVariable", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "name", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + } + + var gqlConfig apidef.GraphQLConfig + require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2ConfigJson), &gqlConfig)) + + apiDef := &apidef.APIDefinition{ + GraphQL: gqlConfig, + } + + httpClient := &http.Client{} + streamingClient := &http.Client{} + adapter := UniversalDataGraph{ + ApiDefinition: apiDef, + HttpClient: httpClient, + StreamingClient: streamingClient, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + } + + var err error + adapter.Schema, err = parseSchema(gqlConfig.Schema) + require.NoError(t, err) + + actualFieldCfgs := adapter.engineConfigV2FieldConfigs() + assert.ElementsMatch(t, expectedFieldCfgs, actualFieldCfgs) +} + +func TestUniversalDataGraph_engineConfigV2DataSources(t *testing.T) { + httpClient := &http.Client{} + streamingClient := &http.Client{} + + expectedDataSources := []plan.DataSourceConfiguration{ + { + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"rest"}, + }, + }, + Factory: &restDataSource.Factory{ + Client: httpClient, + }, + Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ + Fetch: restDataSource.FetchConfiguration{ + URL: "tyk://rest-example", + Method: "POST", + Header: map[string][]string{ + "Authorization": {"123"}, + "X-Custom": {"A, B"}, + }, + Body: "body", + Query: []restDataSource.QueryConfiguration{ + { + Name: "q", + Value: "val1,val2", + }, + { + Name: "repeat", + Value: "val1", + }, + { + Name: "repeat", + Value: "val2", + }, + }, + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"gql"}, + }, + }, + Factory: &graphqlDataSource.Factory{ + HTTPClient: httpClient, + StreamingClient: streamingClient, + SubscriptionClient: mockSubscriptionClient, + }, + Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "http://graphql-example", + Method: "POST", + Header: http.Header{ + "X-Tyk-Internal": []string{"true"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "http://graphql-example", + UseSSE: true, + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"withChildren"}, + }, + }, + ChildNodes: []plan.TypeField{ + { + TypeName: "WithChildren", + FieldNames: []string{"id", "name", "__typename"}, + }, + }, + Factory: &restDataSource.Factory{ + Client: httpClient, + }, + Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ + Fetch: restDataSource.FetchConfiguration{ + URL: "https://rest.example.com", + Method: "POST", + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "WithChildren", + FieldNames: []string{"nested"}, + }, + }, + ChildNodes: []plan.TypeField{ + { + TypeName: "Nested", + FieldNames: []string{"id", "name", "__typename"}, + }, + }, + Factory: &restDataSource.Factory{ + Client: httpClient, + }, + Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ + Fetch: restDataSource.FetchConfiguration{ + URL: "https://rest.example.com", + Method: "POST", + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"multiRoot1", "multiRoot2"}, + }, + }, + ChildNodes: []plan.TypeField{ + { + TypeName: "MultiRoot1", + FieldNames: []string{"id", "__typename"}, + }, + { + TypeName: "MultiRoot2", + FieldNames: []string{"name", "__typename"}, + }, + }, + Factory: &graphqlDataSource.Factory{ + HTTPClient: httpClient, + StreamingClient: streamingClient, + SubscriptionClient: mockSubscriptionClient, + }, + Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "https://graphql.example.com", + Method: "POST", + Header: map[string][]string{ + "Auth": {"123"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "https://graphql.example.com", + UseSSE: false, + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"restWithQueryParams"}, + }, + }, + Factory: &restDataSource.Factory{ + Client: httpClient, + }, + Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ + Fetch: restDataSource.FetchConfiguration{ + URL: "https://rest-with-query-params.example.com", + Method: "POST", + Query: []restDataSource.QueryConfiguration{ + { + Name: "order", + Value: "{{.arguments.order}}", + }, + { + Name: "q", + Value: "{{.arguments.q}}", + }, + { + Name: "limit", + Value: "{{.arguments.limit}}", + }, + }, + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"restWithPathParams"}, + }, + }, + Factory: &restDataSource.Factory{ + Client: httpClient, + }, + Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ + Fetch: restDataSource.FetchConfiguration{ + URL: "https://rest-with-path-params.example.com/{{.arguments.id}}", + Method: "POST", + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"restWithFullUrlAsParam"}, + }, + }, + Factory: &restDataSource.Factory{ + Client: httpClient, + }, + Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ + Fetch: restDataSource.FetchConfiguration{ + URL: "{{.arguments.url}}", + Method: "POST", + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "Query", + FieldNames: []string{"idType"}, + }, + }, + ChildNodes: []plan.TypeField{ + { + TypeName: "WithChildren", + FieldNames: []string{"id", "name", "__typename"}, + }, + { + TypeName: "IDType", + FieldNames: []string{"id", "__typename"}, + }, + }, + Factory: &graphqlDataSource.Factory{ + HTTPClient: httpClient, + StreamingClient: streamingClient, + SubscriptionClient: mockSubscriptionClient, + }, + Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ + Fetch: graphqlDataSource.FetchConfiguration{ + URL: "https://graphql.example.com", + Method: "POST", + Header: map[string][]string{ + "Auth": {"123"}, + }, + }, + Subscription: graphqlDataSource.SubscriptionConfiguration{ + URL: "https://graphql.example.com", + UseSSE: false, + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "Subscription", + FieldNames: []string{"foobar"}, + }, + }, + Factory: &kafkaDataSource.Factory{}, + Custom: kafkaDataSource.ConfigJSON(kafkaDataSource.Configuration{ + Subscription: kafkaDataSource.SubscriptionConfiguration{ + BrokerAddresses: []string{"localhost:9092"}, + Topics: []string{"test.topic"}, + GroupID: "test.consumer.group", + ClientID: "test.client.id", + KafkaVersion: "V2_8_0_0", + StartConsumingLatest: true, + BalanceStrategy: kafkaDataSource.BalanceStrategySticky, + IsolationLevel: kafkaDataSource.IsolationLevelReadCommitted, + SASL: kafkaDataSource.SASL{ + Enable: true, + User: "admin", + Password: "admin-secret", + }, + }, + }), + }, + { + RootNodes: []plan.TypeField{ + { + TypeName: "Subscription", + FieldNames: []string{"foobarTopicWithVariable"}, + }, + }, + Factory: &kafkaDataSource.Factory{}, + Custom: kafkaDataSource.ConfigJSON(kafkaDataSource.Configuration{ + Subscription: kafkaDataSource.SubscriptionConfiguration{ + BrokerAddresses: []string{"localhost:9092"}, + Topics: []string{"test.topic.{{.arguments.name}}"}, + GroupID: "test.consumer.group", + ClientID: "test.client.id", + KafkaVersion: "V2_8_0_0", + StartConsumingLatest: true, + BalanceStrategy: kafkaDataSource.BalanceStrategySticky, + IsolationLevel: kafkaDataSource.IsolationLevelReadCommitted, + SASL: kafkaDataSource.SASL{ + Enable: true, + User: "admin", + Password: "admin-secret", + }, + }, + }), + }, + } + + var gqlConfig apidef.GraphQLConfig + require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2ConfigJson), &gqlConfig)) + + apiDef := &apidef.APIDefinition{ + GraphQL: gqlConfig, + } + + adapter := UniversalDataGraph{ + ApiDefinition: apiDef, + HttpClient: httpClient, + StreamingClient: streamingClient, + subscriptionClientFactory: &MockSubscriptionClientFactory{}, + } + + var err error + adapter.Schema, err = parseSchema(gqlConfig.Schema) + require.NoError(t, err) + + actualDataSources, err := adapter.engineConfigV2DataSources() + assert.NoError(t, err) + assert.Equal(t, expectedDataSources, actualDataSources) + //assert.ElementsMatch(t, expectedDataSources, actualDataSources) +} + +var v2Schema = strconv.Quote(`type Query { + rest: String + gql(id: ID!, name: String): String + deepGQL: DeepGQL + withChildren: WithChildren + multiRoot1: MultiRoot1 + multiRoot2: MultiRoot2 + restWithQueryParams(q: String, order: String, limit: Int): [String] + restWithPathParams(id: String): [String] + restWithFullUrlAsParam(url: String): [String] + idType: IDType! +} +interface IDType { + id: ID! +} +type WithChildren implements IDType { + id: ID! + name: String + nested: Nested +} +type Nested { + id: ID! + name: String! +} +type MultiRoot1 { + id: ID! +} +type MultiRoot2 { + name: String! +} +type DeepGQL { + query(code: String!): String +} +type Subscription { + foobar: Int + foobarTopicWithVariable(name: String): Int +}`) + +var graphqlEngineV2ConfigJson = `{ + "enabled": true, + "execution_mode": "executionEngine", + "version": "2", + "schema": ` + v2Schema + `, + "last_schema_update": "2020-11-11T11:11:11.000+01:00", + "engine": { + "field_configs": [ + { + "type_name": "Query", + "field_name": "rest", + "disable_default_mapping": false, + "path": ["my_rest"] + } + ], + "data_sources": [ + { + "kind": "REST", + "name": "", + "internal": true, + "root_fields": [ + { "type": "Query", "fields": ["rest"] } + ], + "config": { + "url": "tyk://rest-example", + "method": "POST", + "headers": { + "Authorization": "123", + "X-Custom": "A, B" + }, + "query": [ + { + "name": "q", + "value": "val1,val2" + }, + { + "name": "repeat", + "value": "val1" + }, + { + "name": "repeat", + "value": "val2" + } + ], + "body": "body" + } + }, + { + "kind": "GraphQL", + "internal": true, + "root_fields": [ + { "type": "Query", "fields": ["gql"] } + ], + "config": { + "url": "tyk://graphql-example", + "method": "POST", + "subscription_type": "sse" + } + }, + { + "kind": "REST", + "name": "", + "internal": true, + "root_fields": [ + { "type": "Query", "fields": ["withChildren"] } + ], + "config": { + "url": "https://rest.example.com", + "method": "POST", + "headers": {}, + "query": [], + "body": "" + } + }, + { + "kind": "REST", + "name": "", + "internal": true, + "root_fields": [ + { "type": "WithChildren", "fields": ["nested"] } + ], + "config": { + "url": "https://rest.example.com", + "method": "POST", + "headers": {}, + "query": [], + "body": "" + } + }, + { + "kind": "GraphQL", + "internal": false, + "root_fields": [ + { "type": "Query", "fields": ["multiRoot1","multiRoot2"] } + ], + "config": { + "url": "https://graphql.example.com", + "method": "POST", + "headers": { + "Auth": "123" + } + } + }, + { + "kind": "REST", + "name": "restWithQueryParams", + "internal": true, + "root_fields": [ + { "type": "Query", "fields": ["restWithQueryParams"] } + ], + "config": { + "url": "https://rest-with-query-params.example.com?q={{.arguments.q}}&order={{.arguments.order}}", + "method": "POST", + "headers": {}, + "query": [ + { + "name": "limit", + "value": "{{.arguments.limit}}" + } + ], + "body": "" + } + }, + { + "kind": "REST", + "name": "restWithPathParams", + "internal": true, + "root_fields": [ + { "type": "Query", "fields": ["restWithPathParams"] } + ], + "config": { + "url": "https://rest-with-path-params.example.com/{{.arguments.id}}", + "method": "POST", + "headers": {}, + "query": [], + "body": "" + } + }, + { + "kind": "REST", + "name": "restWithFullUrlAsParam", + "internal": true, + "root_fields": [ + { "type": "Query", "fields": ["restWithFullUrlAsParam"] } + ], + "config": { + "url": "{{.arguments.url}}", + "method": "POST", + "headers": {}, + "query": [], + "body": "" + } + }, + { + "kind": "GraphQL", + "internal": false, + "root_fields": [ + { "type": "Query", "fields": ["idType"] } + ], + "config": { + "url": "https://graphql.example.com", + "method": "POST", + "headers": { + "Auth": "123" + } + } + }, + { + "kind": "Kafka", + "name": "kafka-consumer-group", + "internal": false, + "root_fields": [{ + "type": "Subscription", + "fields": [ + "foobar" + ] + }], + "config": { + "broker_addresses": ["localhost:9092"], + "topics": ["test.topic"], + "group_id": "test.consumer.group", + "client_id": "test.client.id", + "kafka_version": "V2_8_0_0", + "start_consuming_latest": true, + "balance_strategy": "BalanceStrategySticky", + "isolation_level": "ReadCommitted", + "sasl": { + "enable": true, + "user": "admin", + "password": "admin-secret" + } + } + }, + { + "kind": "Kafka", + "name": "kafka-consumer-group-with-variable", + "internal": false, + "root_fields": [{ + "type": "Subscription", + "fields": [ + "foobarTopicWithVariable" + ] + }], + "config": { + "broker_addresses": ["localhost:9092"], + "topics": ["test.topic.{{.arguments.name}}"], + "group_id": "test.consumer.group", + "client_id": "test.client.id", + "kafka_version": "V2_8_0_0", + "start_consuming_latest": true, + "balance_strategy": "BalanceStrategySticky", + "isolation_level": "ReadCommitted", + "sasl": { + "enable": true, + "user": "admin", + "password": "admin-secret" + } + } + } + ] + }, + "playground": {} +}` diff --git a/apidef/adapter/gqlengineadapter/utils.go b/apidef/adapter/gqlengineadapter/utils.go new file mode 100644 index 00000000000..13c3c008c57 --- /dev/null +++ b/apidef/adapter/gqlengineadapter/utils.go @@ -0,0 +1,78 @@ +package gqlengineadapter + +import ( + "net/http" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" + + "github.com/TykTechnologies/tyk/apidef" +) + +func parseSchema(schemaAsString string) (parsedSchema *graphql.Schema, err error) { + parsedSchema, err = graphql.NewSchemaFromString(schemaAsString) + if err != nil { + return nil, err + } + + normalizationResult, err := parsedSchema.Normalize() + if err != nil { + return nil, err + } + + if !normalizationResult.Successful && normalizationResult.Errors != nil { + return nil, normalizationResult.Errors + } + + return parsedSchema, nil +} + +func graphqlDataSourceWebSocketProtocol(subscriptionType apidef.SubscriptionType) string { + wsProtocol := graphqlDataSource.ProtocolGraphQLWS + if subscriptionType == apidef.GQLSubscriptionTransportWS { + wsProtocol = graphqlDataSource.ProtocolGraphQLTWS + } + return wsProtocol +} + +func graphqlSubscriptionType(subscriptionType apidef.SubscriptionType) graphql.SubscriptionType { + switch subscriptionType { + case apidef.GQLSubscriptionWS: + return graphql.SubscriptionTypeGraphQLWS + case apidef.GQLSubscriptionTransportWS: + return graphql.SubscriptionTypeGraphQLTransportWS + case apidef.GQLSubscriptionSSE: + return graphql.SubscriptionTypeSSE + default: + return graphql.SubscriptionTypeUnknown + } +} + +func convertApiDefinitionHeadersToHttpHeaders(apiDefHeaders map[string]string) http.Header { + if len(apiDefHeaders) == 0 { + return nil + } + + engineV2Headers := make(http.Header) + for apiDefHeaderKey, apiDefHeaderValue := range apiDefHeaders { + engineV2Headers.Add(apiDefHeaderKey, apiDefHeaderValue) + } + + return engineV2Headers +} + +func removeDuplicateApiDefinitionHeaders(headers ...map[string]string) map[string]string { + hdr := make(map[string]string) + // headers priority depends on the order of arguments + for _, header := range headers { + for k, v := range header { + keyCanonical := http.CanonicalHeaderKey(k) + if _, ok := hdr[keyCanonical]; ok { + // skip because header is present + continue + } + hdr[keyCanonical] = v + } + } + return hdr +} diff --git a/apidef/adapter/engine_v2_utils.go b/apidef/adapter/gqlengineadapter/utils_engine_v2.go similarity index 92% rename from apidef/adapter/engine_v2_utils.go rename to apidef/adapter/gqlengineadapter/utils_engine_v2.go index 37f67f8bea9..cb867dc743d 100644 --- a/apidef/adapter/engine_v2_utils.go +++ b/apidef/adapter/gqlengineadapter/utils_engine_v2.go @@ -1,4 +1,4 @@ -package adapter +package gqlengineadapter import ( "errors" @@ -135,3 +135,10 @@ func createGraphQLDataSourceFactory(params createGraphQLDataSourceFactoryParams) factory.SubscriptionClient = subscriptionClient return factory, nil } + +func subscriptionClientFactoryOrDefault(providedSubscriptionClientFactory graphqlDataSource.GraphQLSubscriptionClientFactory) graphqlDataSource.GraphQLSubscriptionClientFactory { + if providedSubscriptionClientFactory != nil { + return providedSubscriptionClientFactory + } + return &graphqlDataSource.DefaultSubscriptionClientFactory{} +} diff --git a/apidef/adapter/engine_v2_utils_test.go b/apidef/adapter/gqlengineadapter/utils_engine_v2_test.go similarity index 92% rename from apidef/adapter/engine_v2_utils_test.go rename to apidef/adapter/gqlengineadapter/utils_engine_v2_test.go index 594e4b0ad3e..f6b2c5af70e 100644 --- a/apidef/adapter/engine_v2_utils_test.go +++ b/apidef/adapter/gqlengineadapter/utils_engine_v2_test.go @@ -1,4 +1,4 @@ -package adapter +package gqlengineadapter import ( "net/http" @@ -280,3 +280,15 @@ func TestCreateGraphQLDataSourceFactory(t *testing.T) { assert.Nil(t, err) assert.Equal(t, expectedGraphQLDataSourceFactory, actualGraphQLDataSourceFactory) } + +func TestSubscriptionClientFactoryOrDefault(t *testing.T) { + t.Run("should return the provided subscriptionClientFactory if not nil", func(t *testing.T) { + factory := &MockSubscriptionClientFactory{} + actualFactory := subscriptionClientFactoryOrDefault(factory) + assert.Equal(t, factory, actualFactory) + }) + t.Run("should return default subscriptionClientFactory if provided one is nil", func(t *testing.T) { + actualFactory := subscriptionClientFactoryOrDefault(nil) + assert.Equal(t, &graphqlDataSource.DefaultSubscriptionClientFactory{}, actualFactory) + }) +} diff --git a/apidef/adapter/gqlengineadapter/utils_test.go b/apidef/adapter/gqlengineadapter/utils_test.go new file mode 100644 index 00000000000..5322e9368aa --- /dev/null +++ b/apidef/adapter/gqlengineadapter/utils_test.go @@ -0,0 +1,124 @@ +package gqlengineadapter + +import ( + "context" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + + graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" + + "github.com/TykTechnologies/tyk/apidef" +) + +func TestParseSchema(t *testing.T) { + inputSchema := ` + type Query { + hello: String! + }` + + // We are pretty confident that the schema parsing works, as it is tested in the library already. + // We just want to make sure that there is no weird error happening. + parsedSchema, err := parseSchema(inputSchema) + assert.NotNil(t, parsedSchema) + assert.NoError(t, err) +} + +func TestGraphqlDataSourceWebSocketProtocol(t *testing.T) { + run := func(subscriptionType apidef.SubscriptionType, expectedWebSocketProtocol string) func(t *testing.T) { + return func(t *testing.T) { + actualProtocol := graphqlDataSourceWebSocketProtocol(subscriptionType) + assert.Equal(t, expectedWebSocketProtocol, actualProtocol) + } + } + + t.Run("should return 'graphql-ws' for undefined subscription type", + run(apidef.GQLSubscriptionUndefined, graphqlDataSource.ProtocolGraphQLWS), + ) + + t.Run("should return 'graphql-ws' for graphql-ws subscription type", + run(apidef.GQLSubscriptionWS, graphqlDataSource.ProtocolGraphQLWS), + ) + + t.Run("should return 'graphql-ws' for sse subscription type as websocket protocol is irrelevant in that case", + run(apidef.GQLSubscriptionSSE, graphqlDataSource.ProtocolGraphQLWS), + ) + + t.Run("should return 'graphql-transport-ws' for graphql-transport-ws subscription type", + run(apidef.GQLSubscriptionTransportWS, graphqlDataSource.ProtocolGraphQLTWS), + ) +} + +func TestGraphqlSubscriptionType(t *testing.T) { + run := func(subscriptionType apidef.SubscriptionType, expectedGraphQLSubscriptionType graphql.SubscriptionType) func(t *testing.T) { + return func(t *testing.T) { + actualSubscriptionType := graphqlSubscriptionType(subscriptionType) + assert.Equal(t, expectedGraphQLSubscriptionType, actualSubscriptionType) + } + } + + t.Run("should return 'Unknown' for undefined subscription type", + run(apidef.GQLSubscriptionUndefined, graphql.SubscriptionTypeUnknown), + ) + + t.Run("should return 'SSE' for sse subscription type as websocket protocol is irrelevant in that case", + run(apidef.GQLSubscriptionSSE, graphql.SubscriptionTypeSSE), + ) + + t.Run("should return 'GraphQLWS' for graphql-ws subscription type", + run(apidef.GQLSubscriptionWS, graphql.SubscriptionTypeGraphQLWS), + ) + + t.Run("should return 'GraphQLTransportWS' for graphql-transport-ws subscription type", + run(apidef.GQLSubscriptionTransportWS, graphql.SubscriptionTypeGraphQLTransportWS), + ) +} + +func TestConvertApiDefinitionHeadersToHttpHeaders(t *testing.T) { + t.Run("should return nil for empty input", func(t *testing.T) { + assert.Nil(t, convertApiDefinitionHeadersToHttpHeaders(nil)) + }) + + t.Run("should successfully convert API Definition header to Http Headers", func(t *testing.T) { + apiDefinitionHeaders := map[string]string{ + "Authorization": "token", + "X-Tyk-Key": "value", + } + + expectedHttpHeaders := http.Header{ + "Authorization": {"token"}, + "X-Tyk-Key": {"value"}, + } + + actualHttpHeaders := convertApiDefinitionHeadersToHttpHeaders(apiDefinitionHeaders) + assert.Equal(t, expectedHttpHeaders, actualHttpHeaders) + }) +} + +func TestRemoveDuplicateApiDefinitionHeaders(t *testing.T) { + apiDefinitionHeadersFirstArgument := map[string]string{ + "duplicate-header": "value", + } + apiDefinitionHeadersSecondArgument := map[string]string{ + "Duplicate-Header": "value", + "Non-Duplicate-Header": "another_value", + } + + expectedDeduplicatedHeaders := map[string]string{ + "Duplicate-Header": "value", + "Non-Duplicate-Header": "another_value", + } + + actualDeduplicatedHeaders := removeDuplicateApiDefinitionHeaders(apiDefinitionHeadersFirstArgument, apiDefinitionHeadersSecondArgument) + assert.Equal(t, expectedDeduplicatedHeaders, actualDeduplicatedHeaders) +} + +var mockSubscriptionClient = &graphqlDataSource.SubscriptionClient{} + +type MockSubscriptionClientFactory struct{} + +func (m *MockSubscriptionClientFactory) NewSubscriptionClient(httpClient, streamingClient *http.Client, engineCtx context.Context, options ...graphqlDataSource.Options) graphqlDataSource.GraphQLSubscriptionClient { + return mockSubscriptionClient +} diff --git a/apidef/adapter/graphql_config_adapter.go b/apidef/adapter/graphql_config_adapter.go index 6de5c68f3d4..c2bfcd9d741 100644 --- a/apidef/adapter/graphql_config_adapter.go +++ b/apidef/adapter/graphql_config_adapter.go @@ -1,22 +1,22 @@ package adapter import ( - "encoding/json" "errors" "net/http" - "strings" - graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/httpclient" - kafkaDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/kafka_datasource" - restDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/rest_datasource" - "github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" "github.com/TykTechnologies/tyk/apidef" + "github.com/TykTechnologies/tyk/apidef/adapter/gqlengineadapter" ) var ErrUnsupportedGraphQLConfigVersion = errors.New("provided version of GraphQL config is not supported for this operation") +var ErrUnsupportedGraphQLExecutionMode = errors.New("provided execution mode of GraphQL config is not supported for this operation") + +type GraphQLEngineAdapter interface { + EngineConfig() (*graphql.EngineV2Configuration, error) +} type GraphQLConfigAdapterOption func(adapter *GraphQLConfigAdapter) @@ -38,25 +38,16 @@ func WithStreamingClient(streamingClient *http.Client) GraphQLConfigAdapterOptio } } -func withGraphQLSubscriptionClientFactory(factory graphqlDataSource.GraphQLSubscriptionClientFactory) GraphQLConfigAdapterOption { - return func(adapter *GraphQLConfigAdapter) { - adapter.subscriptionClientFactory = factory - } -} - type GraphQLConfigAdapter struct { apiDefinition *apidef.APIDefinition httpClient *http.Client streamingClient *http.Client schema *graphql.Schema - - subscriptionClientFactory graphqlDataSource.GraphQLSubscriptionClientFactory } func NewGraphQLConfigAdapter(apiDefinition *apidef.APIDefinition, options ...GraphQLConfigAdapterOption) GraphQLConfigAdapter { adapter := GraphQLConfigAdapter{ - apiDefinition: apiDefinition, - subscriptionClientFactory: &graphqlDataSource.DefaultSubscriptionClientFactory{}, + apiDefinition: apiDefinition, } for _, option := range options { option(&adapter) @@ -70,302 +61,34 @@ func (g *GraphQLConfigAdapter) EngineConfigV2() (*graphql.EngineV2Configuration, return nil, ErrUnsupportedGraphQLConfigVersion } - if isProxyOnlyAPIDefinition(g.apiDefinition) { - return g.createV2ConfigForProxyOnlyExecutionMode() - } - - if isSupergraphAPIDefinition(g.apiDefinition) { - return g.createV2ConfigForSupergraphExecutionMode() - } - - return g.createV2ConfigForEngineExecutionMode() -} - -func (g *GraphQLConfigAdapter) createV2ConfigForProxyOnlyExecutionMode() (*graphql.EngineV2Configuration, error) { - staticHeaders := make(http.Header) - - url := g.apiDefinition.Proxy.TargetURL - if strings.HasPrefix(url, "tyk://") { - url = strings.ReplaceAll(url, "tyk://", "http://") - staticHeaders.Set(apidef.TykInternalApiHeader, "true") - } - - upstreamConfig := graphql.ProxyUpstreamConfig{ - URL: url, - StaticHeaders: staticHeaders, - SubscriptionType: graphqlSubscriptionType(g.apiDefinition.GraphQL.Proxy.SubscriptionType), - } - - if g.schema == nil { - var err error - g.schema, err = graphql.NewSchemaFromString(g.apiDefinition.GraphQL.Schema) - if err != nil { - return nil, err - } - } - - v2Config, err := graphql.NewProxyEngineConfigFactory( - g.schema, - upstreamConfig, - graphqlDataSource.NewBatchFactory(), - graphql.WithProxyHttpClient(g.httpClient), - graphql.WithProxyStreamingClient(g.streamingClient), - graphql.WithProxySubscriptionClientFactory(g.subscriptionClientFactory), - ).EngineV2Configuration() - - v2Config.EnableSingleFlight(true) - return &v2Config, err -} - -func (g *GraphQLConfigAdapter) createV2ConfigForSupergraphExecutionMode() (*graphql.EngineV2Configuration, error) { - dataSourceConfs := g.subgraphDataSourceConfigs() - var federationConfigV2Factory *graphql.FederationEngineConfigFactory - if g.apiDefinition.GraphQL.Supergraph.DisableQueryBatching { - federationConfigV2Factory = graphql.NewFederationEngineConfigFactory( - dataSourceConfs, - nil, - graphql.WithFederationHttpClient(g.getHttpClient()), - graphql.WithFederationStreamingClient(g.getStreamingClient()), - graphql.WithFederationSubscriptionClientFactory(g.subscriptionClientFactory), - ) - } else { - federationConfigV2Factory = graphql.NewFederationEngineConfigFactory( - dataSourceConfs, - graphqlDataSource.NewBatchFactory(), - graphql.WithFederationHttpClient(g.getHttpClient()), - graphql.WithFederationStreamingClient(g.getStreamingClient()), - graphql.WithFederationSubscriptionClientFactory(g.subscriptionClientFactory), - ) - } - - err := federationConfigV2Factory.SetMergedSchemaFromString(g.apiDefinition.GraphQL.Supergraph.MergedSDL) - if err != nil { - return nil, err - } - - conf, err := federationConfigV2Factory.EngineV2Configuration() - if err != nil { - return nil, err - } - - conf.EnableSingleFlight(true) - if !g.apiDefinition.GraphQL.Supergraph.DisableQueryBatching { - conf.EnableDataLoader(true) - } - - return &conf, nil -} - -func (g *GraphQLConfigAdapter) createV2ConfigForEngineExecutionMode() (*graphql.EngineV2Configuration, error) { - var err error - g.schema, err = parseSchema(g.apiDefinition.GraphQL.Schema) - if err != nil { - return nil, err - } - - conf := graphql.NewEngineV2Configuration(g.schema) - conf.EnableSingleFlight(true) - - fieldConfigs := g.engineConfigV2FieldConfigs() - datsSources, err := g.engineConfigV2DataSources() - if err != nil { - return nil, err - } - - conf.SetFieldConfigurations(fieldConfigs) - conf.SetDataSources(datsSources) - - return &conf, nil -} - -func (g *GraphQLConfigAdapter) engineConfigV2FieldConfigs() (planFieldConfigs plan.FieldConfigurations) { - for _, fc := range g.apiDefinition.GraphQL.Engine.FieldConfigs { - planFieldConfig := plan.FieldConfiguration{ - TypeName: fc.TypeName, - FieldName: fc.FieldName, - DisableDefaultMapping: fc.DisableDefaultMapping, - Path: fc.Path, - } - - planFieldConfigs = append(planFieldConfigs, planFieldConfig) - } - - generatedArgs := g.schema.GetAllFieldArguments(graphql.NewSkipReservedNamesFunc()) - generatedArgsAsLookupMap := graphql.CreateTypeFieldArgumentsLookupMap(generatedArgs) - g.engineConfigV2Arguments(&planFieldConfigs, generatedArgsAsLookupMap) - - return planFieldConfigs -} - -func (g *GraphQLConfigAdapter) engineConfigV2DataSources() (planDataSources []plan.DataSourceConfiguration, err error) { - for _, ds := range g.apiDefinition.GraphQL.Engine.DataSources { - planDataSource := plan.DataSourceConfiguration{ - RootNodes: []plan.TypeField{}, - } - - for _, typeField := range ds.RootFields { - planTypeField := plan.TypeField{ - TypeName: typeField.Type, - FieldNames: typeField.Fields, - } - - planDataSource.RootNodes = append(planDataSource.RootNodes, planTypeField) - } - - switch ds.Kind { - case apidef.GraphQLEngineDataSourceKindREST: - var restConfig apidef.GraphQLEngineDataSourceConfigREST - err = json.Unmarshal(ds.Config, &restConfig) - if err != nil { - return nil, err - } - - planDataSource.Factory = &restDataSource.Factory{ - Client: g.getHttpClient(), - } - - urlWithoutQueryParams, queryConfigs, err := extractURLQueryParamsForEngineV2(restConfig.URL, restConfig.Query) - if err != nil { - return nil, err - } - - planDataSource.Custom = restDataSource.ConfigJSON(restDataSource.Configuration{ - Fetch: restDataSource.FetchConfiguration{ - URL: urlWithoutQueryParams, - Method: restConfig.Method, - Body: restConfig.Body, - Query: queryConfigs, - Header: convertApiDefinitionHeadersToHttpHeaders(restConfig.Headers), - }, - }) - - case apidef.GraphQLEngineDataSourceKindGraphQL: - var graphqlConfig apidef.GraphQLEngineDataSourceConfigGraphQL - err = json.Unmarshal(ds.Config, &graphqlConfig) - if err != nil { - return nil, err - } - - planDataSource.Factory, err = createGraphQLDataSourceFactory(createGraphQLDataSourceFactoryParams{ - graphqlConfig: graphqlConfig, - subscriptionClientFactory: g.subscriptionClientFactory, - httpClient: g.getHttpClient(), - streamingClient: g.getStreamingClient(), - }) - if err != nil { - return nil, err - } - - planDataSource.Custom = graphqlDataSource.ConfigJson(graphqlDataSourceConfiguration( - graphqlConfig.URL, - graphqlConfig.Method, - graphqlConfig.Headers, - graphqlConfig.SubscriptionType, - )) - - case apidef.GraphQLEngineDataSourceKindKafka: - var kafkaConfig apidef.GraphQLEngineDataSourceConfigKafka - err = json.Unmarshal(ds.Config, &kafkaConfig) - if err != nil { - return nil, err - } - - planDataSource.Factory = &kafkaDataSource.Factory{} - planDataSource.Custom = kafkaDataSource.ConfigJSON(kafkaDataSource.Configuration{ - Subscription: kafkaDataSource.SubscriptionConfiguration{ - BrokerAddresses: kafkaConfig.BrokerAddresses, - Topics: kafkaConfig.Topics, - GroupID: kafkaConfig.GroupID, - ClientID: kafkaConfig.ClientID, - KafkaVersion: kafkaConfig.KafkaVersion, - StartConsumingLatest: kafkaConfig.StartConsumingLatest, - BalanceStrategy: kafkaConfig.BalanceStrategy, - IsolationLevel: kafkaConfig.IsolationLevel, - SASL: kafkaConfig.SASL, - }, - }) - } - - planDataSources = append(planDataSources, planDataSource) - } - - err = g.determineChildNodes(planDataSources) - return planDataSources, err -} - -func (g *GraphQLConfigAdapter) subgraphDataSourceConfigs() []graphqlDataSource.Configuration { - confs := make([]graphqlDataSource.Configuration, 0) - if len(g.apiDefinition.GraphQL.Supergraph.Subgraphs) == 0 { - return confs - } - - for _, apiDefSubgraphConf := range g.apiDefinition.GraphQL.Supergraph.Subgraphs { - if len(apiDefSubgraphConf.SDL) == 0 { - continue + var engineAdapter GraphQLEngineAdapter + adapterType := graphqlEngineAdapterTypeFromApiDefinition(g.apiDefinition) + switch adapterType { + case GraphQLEngineAdapterTypeProxyOnly: + engineAdapter = &gqlengineadapter.ProxyOnly{ + ApiDefinition: g.apiDefinition, + HttpClient: g.getHttpClient(), + StreamingClient: g.getStreamingClient(), + Schema: g.schema, } - hdr := removeDuplicateApiDefinitionHeaders(apiDefSubgraphConf.Headers, g.apiDefinition.GraphQL.Supergraph.GlobalHeaders) - conf := graphqlDataSourceConfiguration( - apiDefSubgraphConf.URL, - http.MethodPost, - hdr, - apiDefSubgraphConf.SubscriptionType) - conf.Federation = graphqlDataSource.FederationConfiguration{ - Enabled: true, - ServiceSDL: apiDefSubgraphConf.SDL, + case GraphQLEngineAdapterTypeSupergraph: + engineAdapter = &gqlengineadapter.Supergraph{ + ApiDefinition: g.apiDefinition, + HttpClient: g.getHttpClient(), + StreamingClient: g.getStreamingClient(), } - - confs = append(confs, conf) - } - - return confs -} - -func (g *GraphQLConfigAdapter) engineConfigV2Arguments(fieldConfs *plan.FieldConfigurations, generatedArgs map[graphql.TypeFieldLookupKey]graphql.TypeFieldArguments) { - for i := range *fieldConfs { - if len(generatedArgs) == 0 { - return - } - - lookupKey := graphql.CreateTypeFieldLookupKey((*fieldConfs)[i].TypeName, (*fieldConfs)[i].FieldName) - currentArgs, ok := generatedArgs[lookupKey] - if !ok { - continue + case GraphQLEngineAdapterTypeUniversalDataGraph: + engineAdapter = &gqlengineadapter.UniversalDataGraph{ + ApiDefinition: g.apiDefinition, + HttpClient: g.getHttpClient(), + StreamingClient: g.getStreamingClient(), + Schema: g.schema, } - - (*fieldConfs)[i].Arguments = createArgumentConfigurationsForArgumentNames(currentArgs.ArgumentNames...) - delete(generatedArgs, lookupKey) - } - - for _, genArgs := range generatedArgs { - *fieldConfs = append(*fieldConfs, plan.FieldConfiguration{ - TypeName: genArgs.TypeName, - FieldName: genArgs.FieldName, - Arguments: createArgumentConfigurationsForArgumentNames(genArgs.ArgumentNames...), - }) + default: + return nil, ErrUnsupportedGraphQLExecutionMode } -} - -func (g *GraphQLConfigAdapter) determineChildNodes(planDataSources []plan.DataSourceConfiguration) error { - for i := range planDataSources { - for j := range planDataSources[i].RootNodes { - typeName := planDataSources[i].RootNodes[j].TypeName - for k := range planDataSources[i].RootNodes[j].FieldNames { - fieldName := planDataSources[i].RootNodes[j].FieldNames[k] - typeFields := g.schema.GetAllNestedFieldChildrenFromTypeField(typeName, fieldName, graphql.NewIsDataSourceConfigV2RootFieldSkipFunc(planDataSources)) - children := make([]plan.TypeField, 0) - for _, tf := range typeFields { - childNode := plan.TypeField{ - TypeName: tf.TypeName, - FieldNames: tf.FieldNames, - } - children = append(children, childNode) - } - planDataSources[i].ChildNodes = append(planDataSources[i].ChildNodes, children...) - } - } - } - return nil + return engineAdapter.EngineConfig() } func (g *GraphQLConfigAdapter) getHttpClient() *http.Client { diff --git a/apidef/adapter/graphql_config_adapter_test.go b/apidef/adapter/graphql_config_adapter_test.go index aa1af52e1a7..b5b651007b3 100644 --- a/apidef/adapter/graphql_config_adapter_test.go +++ b/apidef/adapter/graphql_config_adapter_test.go @@ -1,323 +1,88 @@ package adapter import ( - "context" "encoding/json" - "net/http" - "strconv" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" - kafkaDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/kafka_datasource" - restDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/rest_datasource" - "github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" - "github.com/TykTechnologies/tyk/apidef" ) func TestGraphQLConfigAdapter_EngineConfigV2(t *testing.T) { - t.Run("should create v2 config for proxy-only mode", func(t *testing.T) { + t.Run("should return no error when having a proxy-only config", func(t *testing.T) { var gqlConfig apidef.GraphQLConfig - require.NoError(t, json.Unmarshal([]byte(graphqlProxyOnlyConfig), &gqlConfig)) + require.NoError(t, json.Unmarshal([]byte(graphqlMinimalProxyOnlyConfig), &gqlConfig)) apiDef := &apidef.APIDefinition{ GraphQL: gqlConfig, - Proxy: apidef.ProxyConfig{ - TargetURL: "http://localhost:8080", - }, } - httpClient := &http.Client{} - streamingClient := &http.Client{} - adapter := NewGraphQLConfigAdapter(apiDef, - WithHttpClient(httpClient), - WithStreamingClient(streamingClient), - withGraphQLSubscriptionClientFactory(&MockSubscriptionClientFactory{}), - ) + adapter := NewGraphQLConfigAdapter(apiDef) + _, err := adapter.EngineConfigV2() - engineV2Config, err := adapter.EngineConfigV2() assert.NoError(t, err) - - expectedDataSource := plan.DataSourceConfiguration{ - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"hello"}, - }, - }, - ChildNodes: []plan.TypeField{}, - Factory: &graphqlDataSource.Factory{ - BatchFactory: graphqlDataSource.NewBatchFactory(), - HTTPClient: httpClient, - StreamingClient: streamingClient, - SubscriptionClient: mockSubscriptionClient, - }, - Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "http://localhost:8080", - Header: map[string][]string{}, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "http://localhost:8080", - UseSSE: true, - }, - }), - } - - expectedFieldConfig := plan.FieldConfiguration{ - TypeName: "Query", - FieldName: "hello", - Arguments: plan.ArgumentsConfigurations{ - { - Name: "name", - SourceType: plan.FieldArgumentSource, - }, - }, - } - - assert.Containsf(t, engineV2Config.DataSources(), expectedDataSource, "engine configuration does not contain proxy-only data source") - assert.Containsf(t, engineV2Config.FieldConfigurations(), expectedFieldConfig, "engine configuration does not contain expected field config") }) - t.Run("should create v2 config for internal proxy-only api", func(t *testing.T) { + t.Run("should return no error when having a subgraph config", func(t *testing.T) { var gqlConfig apidef.GraphQLConfig - require.NoError(t, json.Unmarshal([]byte(graphqlProxyOnlyConfig), &gqlConfig)) + require.NoError(t, json.Unmarshal([]byte(graphqlMinimalSubgraphConfig), &gqlConfig)) apiDef := &apidef.APIDefinition{ GraphQL: gqlConfig, - Proxy: apidef.ProxyConfig{ - TargetURL: "tyk://api-name", - }, } - httpClient := &http.Client{} - streamingClient := &http.Client{} - adapter := NewGraphQLConfigAdapter( - apiDef, - WithHttpClient(httpClient), - WithStreamingClient(streamingClient), - withGraphQLSubscriptionClientFactory(&MockSubscriptionClientFactory{}), - ) + adapter := NewGraphQLConfigAdapter(apiDef) + _, err := adapter.EngineConfigV2() - engineV2Config, err := adapter.EngineConfigV2() assert.NoError(t, err) - - expectedDataSource := plan.DataSourceConfiguration{ - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"hello"}, - }, - }, - ChildNodes: []plan.TypeField{}, - Factory: &graphqlDataSource.Factory{ - BatchFactory: graphqlDataSource.NewBatchFactory(), - HTTPClient: httpClient, - StreamingClient: streamingClient, - SubscriptionClient: mockSubscriptionClient, - }, - Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "http://api-name", - Header: http.Header{ - "X-Tyk-Internal": []string{"true"}, - }, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "http://api-name", - UseSSE: true, - }, - }), - } - - expectedFieldConfig := plan.FieldConfiguration{ - TypeName: "Query", - FieldName: "hello", - Arguments: plan.ArgumentsConfigurations{ - { - Name: "name", - SourceType: plan.FieldArgumentSource, - }, - }, - } - - assert.Containsf(t, engineV2Config.DataSources(), expectedDataSource, "engine configuration does not contain proxy-only data source") - assert.Containsf(t, engineV2Config.FieldConfigurations(), expectedFieldConfig, "engine configuration does not contain expected field config") }) - t.Run("should create v2 config for engine execution mode without error", func(t *testing.T) { + t.Run("should return no error when having a supergraph config", func(t *testing.T) { var gqlConfig apidef.GraphQLConfig - require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2ConfigJson), &gqlConfig)) + require.NoError(t, json.Unmarshal([]byte(graphqlMinimalSupergraphConfig), &gqlConfig)) apiDef := &apidef.APIDefinition{ GraphQL: gqlConfig, } - httpClient := &http.Client{} - adapter := NewGraphQLConfigAdapter(apiDef, WithHttpClient(httpClient)) - + adapter := NewGraphQLConfigAdapter(apiDef) _, err := adapter.EngineConfigV2() + assert.NoError(t, err) }) - t.Run("should create v2 config for supergraph execution mode without error", func(t *testing.T) { + t.Run("should return no error when having a udg config", func(t *testing.T) { var gqlConfig apidef.GraphQLConfig - require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2SupergraphConfigJson), &gqlConfig)) + require.NoError(t, json.Unmarshal([]byte(graphqlMinimalUniversalDataGraphConfig), &gqlConfig)) apiDef := &apidef.APIDefinition{ GraphQL: gqlConfig, } - httpClient := &http.Client{} - adapter := NewGraphQLConfigAdapter(apiDef, WithHttpClient(httpClient)) - + adapter := NewGraphQLConfigAdapter(apiDef) _, err := adapter.EngineConfigV2() - assert.NoError(t, err) - }) - t.Run("should create v2 config for supergraph with batching disabled", func(t *testing.T) { - var gqlConfig apidef.GraphQLConfig - require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2SupergraphConfigJson), &gqlConfig)) - - apiDef := &apidef.APIDefinition{ - GraphQL: gqlConfig, - } - apiDef.GraphQL.Supergraph.DisableQueryBatching = true - httpClient := &http.Client{} - streamingClient := &http.Client{} - adapter := NewGraphQLConfigAdapter( - apiDef, - WithHttpClient(httpClient), - WithStreamingClient(streamingClient), - withGraphQLSubscriptionClientFactory(&MockSubscriptionClientFactory{}), - ) - - v2Config, err := adapter.EngineConfigV2() assert.NoError(t, err) - expectedDataSource := plan.DataSourceConfiguration{ - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"me"}, - }, - { - TypeName: "User", - FieldNames: []string{"id", "username"}, - }, - }, - ChildNodes: []plan.TypeField{ - { - TypeName: "User", - FieldNames: []string{"id", "username"}, - }, - }, - Factory: &graphqlDataSource.Factory{ - HTTPClient: httpClient, - StreamingClient: streamingClient, - SubscriptionClient: mockSubscriptionClient, - }, - Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "http://accounts.service", - Method: http.MethodPost, - Header: http.Header{ - "Auth": []string{"appended_header"}, - "Header1": []string{"override_global"}, - "Header2": []string{"value2"}, - "X-Tyk-Internal": []string{"true"}, - }, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "http://accounts.service", - UseSSE: true, - }, - Federation: graphqlDataSource.FederationConfiguration{ - Enabled: true, - ServiceSDL: `extend type Query {me: User} type User @key(fields: "id"){ id: ID! username: String!}`, - }, - }), - } - assert.Containsf(t, v2Config.DataSources(), expectedDataSource, "engine configuration does not contain proxy-only data source") - }) - t.Run("should create v2 config for subgraph without error", func(t *testing.T) { + t.Run("should return an error for unsupported execution mode", func(t *testing.T) { var gqlConfig apidef.GraphQLConfig - require.NoError(t, json.Unmarshal([]byte(graphqlSubgraphConfig), &gqlConfig)) + require.NoError(t, json.Unmarshal([]byte(graphqlEngineUnknownExecutionMode), &gqlConfig)) apiDef := &apidef.APIDefinition{ GraphQL: gqlConfig, - Proxy: apidef.ProxyConfig{ - TargetURL: "http://localhost:8080", - }, - } - - httpClient := &http.Client{} - streamingClient := &http.Client{} - adapter := NewGraphQLConfigAdapter( - apiDef, - WithHttpClient(httpClient), - WithStreamingClient(streamingClient), - withGraphQLSubscriptionClientFactory(&MockSubscriptionClientFactory{}), - ) - - engineV2Config, err := adapter.EngineConfigV2() - assert.NoError(t, err) - - expectedDataSource := plan.DataSourceConfiguration{ - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"me", "_entities", "_service"}, - }, - }, - ChildNodes: []plan.TypeField{ - { - TypeName: "_Service", - FieldNames: []string{"sdl"}, - }, - { - TypeName: "User", - FieldNames: []string{"id", "username"}, - }, - }, - Factory: &graphqlDataSource.Factory{ - BatchFactory: graphqlDataSource.NewBatchFactory(), - HTTPClient: httpClient, - StreamingClient: streamingClient, - SubscriptionClient: mockSubscriptionClient, - }, - Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "http://localhost:8080", - Header: http.Header{}, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "http://localhost:8080", - UseSSE: false, - }, - }), } - expectedFieldConfig := plan.FieldConfiguration{ - TypeName: "Query", - FieldName: "_entities", - Arguments: plan.ArgumentsConfigurations{ - { - Name: "representations", - SourceType: plan.FieldArgumentSource, - }, - }, - } + adapter := NewGraphQLConfigAdapter(apiDef) + _, err := adapter.EngineConfigV2() - assert.Containsf(t, engineV2Config.DataSources(), expectedDataSource, "engine configuration does not contain proxy-only data source") - assert.Containsf(t, engineV2Config.FieldConfigurations(), expectedFieldConfig, "engine configuration does not contain expected field config") + assert.Error(t, err) + assert.Equal(t, ErrUnsupportedGraphQLExecutionMode, err) }) - t.Run("should return an error for unsupported config", func(t *testing.T) { + t.Run("should return an error for legacy config", func(t *testing.T) { var gqlConfig apidef.GraphQLConfig require.NoError(t, json.Unmarshal([]byte(graphqlEngineV1ConfigJson), &gqlConfig)) @@ -333,511 +98,6 @@ func TestGraphQLConfigAdapter_EngineConfigV2(t *testing.T) { }) } -func TestGraphQLConfigAdapter_supergraphDataSourceConfigs(t *testing.T) { - expectedDataSourceConfigs := []graphqlDataSource.Configuration{ - { - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "http://accounts.service", - Method: http.MethodPost, - Header: http.Header{ - "Header1": []string{"override_global"}, - "Header2": []string{"value2"}, - "X-Tyk-Internal": []string{"true"}, - "Auth": []string{"appended_header"}, - }, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "http://accounts.service", - UseSSE: true, - }, - Federation: graphqlDataSource.FederationConfiguration{ - Enabled: true, - ServiceSDL: federationAccountsServiceSDL, - }, - }, - { - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "http://products.service", - Method: http.MethodPost, - Header: http.Header{ - "Header1": []string{"value1"}, - "Header2": []string{"value2"}, - }, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "http://products.service", - }, - Federation: graphqlDataSource.FederationConfiguration{ - Enabled: true, - ServiceSDL: federationProductsServiceSDL, - }, - }, - { - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "http://reviews.service", - Method: http.MethodPost, - Header: http.Header{ - "Header1": []string{"override_global"}, - "Auth": []string{"appended_header"}, - "Header2": []string{"value2"}, - }, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "http://reviews.service", - }, - Federation: graphqlDataSource.FederationConfiguration{ - Enabled: true, - ServiceSDL: federationReviewsServiceSDL, - }, - }, - } - - var gqlConfig apidef.GraphQLConfig - require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2SupergraphConfigJson), &gqlConfig)) - - apiDef := &apidef.APIDefinition{ - GraphQL: gqlConfig, - } - - adapter := NewGraphQLConfigAdapter(apiDef) - actualGraphQLConfigs := adapter.subgraphDataSourceConfigs() - assert.Equal(t, expectedDataSourceConfigs, actualGraphQLConfigs) -} - -func TestGraphQLConfigAdapter_engineConfigV2FieldConfigs(t *testing.T) { - expectedFieldCfgs := plan.FieldConfigurations{ - { - TypeName: "Query", - FieldName: "rest", - DisableDefaultMapping: false, - Path: []string{"my_rest"}, - }, - { - TypeName: "Query", - FieldName: "gql", - Arguments: []plan.ArgumentConfiguration{ - { - Name: "id", - SourceType: plan.FieldArgumentSource, - }, - { - Name: "name", - SourceType: plan.FieldArgumentSource, - }, - }, - }, - { - TypeName: "Query", - FieldName: "restWithQueryParams", - Arguments: []plan.ArgumentConfiguration{ - { - Name: "q", - SourceType: plan.FieldArgumentSource, - }, - { - Name: "order", - SourceType: plan.FieldArgumentSource, - }, - { - Name: "limit", - SourceType: plan.FieldArgumentSource, - }, - }, - }, - { - TypeName: "Query", - FieldName: "restWithPathParams", - Arguments: []plan.ArgumentConfiguration{ - { - Name: "id", - SourceType: plan.FieldArgumentSource, - }, - }, - }, - { - TypeName: "Query", - FieldName: "restWithFullUrlAsParam", - Arguments: []plan.ArgumentConfiguration{ - { - Name: "url", - SourceType: plan.FieldArgumentSource, - }, - }, - }, - { - TypeName: "DeepGQL", - FieldName: "query", - Arguments: []plan.ArgumentConfiguration{ - { - Name: "code", - SourceType: plan.FieldArgumentSource, - }, - }, - }, - { - TypeName: "Subscription", - FieldName: "foobarTopicWithVariable", - Arguments: []plan.ArgumentConfiguration{ - { - Name: "name", - SourceType: plan.FieldArgumentSource, - }, - }, - }, - } - - var gqlConfig apidef.GraphQLConfig - require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2ConfigJson), &gqlConfig)) - - apiDef := &apidef.APIDefinition{ - GraphQL: gqlConfig, - } - - adapter := NewGraphQLConfigAdapter(apiDef) - - var err error - adapter.schema, err = parseSchema(gqlConfig.Schema) - require.NoError(t, err) - - actualFieldCfgs := adapter.engineConfigV2FieldConfigs() - assert.ElementsMatch(t, expectedFieldCfgs, actualFieldCfgs) -} - -func TestGraphQLConfigAdapter_engineConfigV2DataSources(t *testing.T) { - httpClient := &http.Client{} - streamingClient := &http.Client{} - - expectedDataSources := []plan.DataSourceConfiguration{ - { - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"rest"}, - }, - }, - Factory: &restDataSource.Factory{ - Client: httpClient, - }, - Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ - Fetch: restDataSource.FetchConfiguration{ - URL: "tyk://rest-example", - Method: "POST", - Header: map[string][]string{ - "Authorization": {"123"}, - "X-Custom": {"A, B"}, - }, - Body: "body", - Query: []restDataSource.QueryConfiguration{ - { - Name: "q", - Value: "val1,val2", - }, - { - Name: "repeat", - Value: "val1", - }, - { - Name: "repeat", - Value: "val2", - }, - }, - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"gql"}, - }, - }, - Factory: &graphqlDataSource.Factory{ - HTTPClient: httpClient, - StreamingClient: streamingClient, - SubscriptionClient: mockSubscriptionClient, - }, - Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "http://graphql-example", - Method: "POST", - Header: http.Header{ - "X-Tyk-Internal": []string{"true"}, - }, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "http://graphql-example", - UseSSE: true, - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"withChildren"}, - }, - }, - ChildNodes: []plan.TypeField{ - { - TypeName: "WithChildren", - FieldNames: []string{"id", "name", "__typename"}, - }, - }, - Factory: &restDataSource.Factory{ - Client: httpClient, - }, - Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ - Fetch: restDataSource.FetchConfiguration{ - URL: "https://rest.example.com", - Method: "POST", - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "WithChildren", - FieldNames: []string{"nested"}, - }, - }, - ChildNodes: []plan.TypeField{ - { - TypeName: "Nested", - FieldNames: []string{"id", "name", "__typename"}, - }, - }, - Factory: &restDataSource.Factory{ - Client: httpClient, - }, - Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ - Fetch: restDataSource.FetchConfiguration{ - URL: "https://rest.example.com", - Method: "POST", - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"multiRoot1", "multiRoot2"}, - }, - }, - ChildNodes: []plan.TypeField{ - { - TypeName: "MultiRoot1", - FieldNames: []string{"id", "__typename"}, - }, - { - TypeName: "MultiRoot2", - FieldNames: []string{"name", "__typename"}, - }, - }, - Factory: &graphqlDataSource.Factory{ - HTTPClient: httpClient, - StreamingClient: streamingClient, - SubscriptionClient: mockSubscriptionClient, - }, - Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "https://graphql.example.com", - Method: "POST", - Header: map[string][]string{ - "Auth": {"123"}, - }, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "https://graphql.example.com", - UseSSE: false, - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"restWithQueryParams"}, - }, - }, - Factory: &restDataSource.Factory{ - Client: httpClient, - }, - Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ - Fetch: restDataSource.FetchConfiguration{ - URL: "https://rest-with-query-params.example.com", - Method: "POST", - Query: []restDataSource.QueryConfiguration{ - { - Name: "order", - Value: "{{.arguments.order}}", - }, - { - Name: "q", - Value: "{{.arguments.q}}", - }, - { - Name: "limit", - Value: "{{.arguments.limit}}", - }, - }, - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"restWithPathParams"}, - }, - }, - Factory: &restDataSource.Factory{ - Client: httpClient, - }, - Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ - Fetch: restDataSource.FetchConfiguration{ - URL: "https://rest-with-path-params.example.com/{{.arguments.id}}", - Method: "POST", - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"restWithFullUrlAsParam"}, - }, - }, - Factory: &restDataSource.Factory{ - Client: httpClient, - }, - Custom: restDataSource.ConfigJSON(restDataSource.Configuration{ - Fetch: restDataSource.FetchConfiguration{ - URL: "{{.arguments.url}}", - Method: "POST", - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "Query", - FieldNames: []string{"idType"}, - }, - }, - ChildNodes: []plan.TypeField{ - { - TypeName: "WithChildren", - FieldNames: []string{"id", "name", "__typename"}, - }, - { - TypeName: "IDType", - FieldNames: []string{"id", "__typename"}, - }, - }, - Factory: &graphqlDataSource.Factory{ - HTTPClient: httpClient, - StreamingClient: streamingClient, - SubscriptionClient: mockSubscriptionClient, - }, - Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{ - Fetch: graphqlDataSource.FetchConfiguration{ - URL: "https://graphql.example.com", - Method: "POST", - Header: map[string][]string{ - "Auth": {"123"}, - }, - }, - Subscription: graphqlDataSource.SubscriptionConfiguration{ - URL: "https://graphql.example.com", - UseSSE: false, - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "Subscription", - FieldNames: []string{"foobar"}, - }, - }, - Factory: &kafkaDataSource.Factory{}, - Custom: kafkaDataSource.ConfigJSON(kafkaDataSource.Configuration{ - Subscription: kafkaDataSource.SubscriptionConfiguration{ - BrokerAddresses: []string{"localhost:9092"}, - Topics: []string{"test.topic"}, - GroupID: "test.consumer.group", - ClientID: "test.client.id", - KafkaVersion: "V2_8_0_0", - StartConsumingLatest: true, - BalanceStrategy: kafkaDataSource.BalanceStrategySticky, - IsolationLevel: kafkaDataSource.IsolationLevelReadCommitted, - SASL: kafkaDataSource.SASL{ - Enable: true, - User: "admin", - Password: "admin-secret", - }, - }, - }), - }, - { - RootNodes: []plan.TypeField{ - { - TypeName: "Subscription", - FieldNames: []string{"foobarTopicWithVariable"}, - }, - }, - Factory: &kafkaDataSource.Factory{}, - Custom: kafkaDataSource.ConfigJSON(kafkaDataSource.Configuration{ - Subscription: kafkaDataSource.SubscriptionConfiguration{ - BrokerAddresses: []string{"localhost:9092"}, - Topics: []string{"test.topic.{{.arguments.name}}"}, - GroupID: "test.consumer.group", - ClientID: "test.client.id", - KafkaVersion: "V2_8_0_0", - StartConsumingLatest: true, - BalanceStrategy: kafkaDataSource.BalanceStrategySticky, - IsolationLevel: kafkaDataSource.IsolationLevelReadCommitted, - SASL: kafkaDataSource.SASL{ - Enable: true, - User: "admin", - Password: "admin-secret", - }, - }, - }), - }, - } - - var gqlConfig apidef.GraphQLConfig - require.NoError(t, json.Unmarshal([]byte(graphqlEngineV2ConfigJson), &gqlConfig)) - - apiDef := &apidef.APIDefinition{ - GraphQL: gqlConfig, - } - - adapter := NewGraphQLConfigAdapter( - apiDef, WithHttpClient(httpClient), - WithStreamingClient(streamingClient), - withGraphQLSubscriptionClientFactory(&MockSubscriptionClientFactory{}), - ) - - var err error - adapter.schema, err = parseSchema(gqlConfig.Schema) - require.NoError(t, err) - - actualDataSources, err := adapter.engineConfigV2DataSources() - assert.NoError(t, err) - assert.Equal(t, expectedDataSources, actualDataSources) - //assert.ElementsMatch(t, expectedDataSources, actualDataSources) -} - -var mockSubscriptionClient = &graphqlDataSource.SubscriptionClient{} - -type MockSubscriptionClientFactory struct{} - -func (m *MockSubscriptionClientFactory) NewSubscriptionClient(httpClient, streamingClient *http.Client, engineCtx context.Context, options ...graphqlDataSource.Options) graphqlDataSource.GraphQLSubscriptionClient { - return mockSubscriptionClient -} - const graphqlEngineV1ConfigJson = `{ "enabled": true, "execution_mode": "executionEngine", @@ -846,326 +106,16 @@ const graphqlEngineV1ConfigJson = `{ "playground": {} }` -var v2Schema = strconv.Quote(`type Query { - rest: String - gql(id: ID!, name: String): String - deepGQL: DeepGQL - withChildren: WithChildren - multiRoot1: MultiRoot1 - multiRoot2: MultiRoot2 - restWithQueryParams(q: String, order: String, limit: Int): [String] - restWithPathParams(id: String): [String] - restWithFullUrlAsParam(url: String): [String] - idType: IDType! -} -interface IDType { - id: ID! -} -type WithChildren implements IDType { - id: ID! - name: String - nested: Nested -} -type Nested { - id: ID! - name: String! -} -type MultiRoot1 { - id: ID! -} -type MultiRoot2 { - name: String! -} -type DeepGQL { - query(code: String!): String -} -type Subscription { - foobar: Int - foobarTopicWithVariable(name: String): Int -}`) - -var graphqlEngineV2ConfigJson = `{ +const graphqlEngineUnknownExecutionMode = `{ "enabled": true, - "execution_mode": "executionEngine", + "execution_mode": "unknown", "version": "2", - "schema": ` + v2Schema + `, - "last_schema_update": "2020-11-11T11:11:11.000+01:00", - "engine": { - "field_configs": [ - { - "type_name": "Query", - "field_name": "rest", - "disable_default_mapping": false, - "path": ["my_rest"] - } - ], - "data_sources": [ - { - "kind": "REST", - "name": "", - "internal": true, - "root_fields": [ - { "type": "Query", "fields": ["rest"] } - ], - "config": { - "url": "tyk://rest-example", - "method": "POST", - "headers": { - "Authorization": "123", - "X-Custom": "A, B" - }, - "query": [ - { - "name": "q", - "value": "val1,val2" - }, - { - "name": "repeat", - "value": "val1" - }, - { - "name": "repeat", - "value": "val2" - } - ], - "body": "body" - } - }, - { - "kind": "GraphQL", - "internal": true, - "root_fields": [ - { "type": "Query", "fields": ["gql"] } - ], - "config": { - "url": "tyk://graphql-example", - "method": "POST", - "subscription_type": "sse" - } - }, - { - "kind": "REST", - "name": "", - "internal": true, - "root_fields": [ - { "type": "Query", "fields": ["withChildren"] } - ], - "config": { - "url": "https://rest.example.com", - "method": "POST", - "headers": {}, - "query": [], - "body": "" - } - }, - { - "kind": "REST", - "name": "", - "internal": true, - "root_fields": [ - { "type": "WithChildren", "fields": ["nested"] } - ], - "config": { - "url": "https://rest.example.com", - "method": "POST", - "headers": {}, - "query": [], - "body": "" - } - }, - { - "kind": "GraphQL", - "internal": false, - "root_fields": [ - { "type": "Query", "fields": ["multiRoot1","multiRoot2"] } - ], - "config": { - "url": "https://graphql.example.com", - "method": "POST", - "headers": { - "Auth": "123" - } - } - }, - { - "kind": "REST", - "name": "restWithQueryParams", - "internal": true, - "root_fields": [ - { "type": "Query", "fields": ["restWithQueryParams"] } - ], - "config": { - "url": "https://rest-with-query-params.example.com?q={{.arguments.q}}&order={{.arguments.order}}", - "method": "POST", - "headers": {}, - "query": [ - { - "name": "limit", - "value": "{{.arguments.limit}}" - } - ], - "body": "" - } - }, - { - "kind": "REST", - "name": "restWithPathParams", - "internal": true, - "root_fields": [ - { "type": "Query", "fields": ["restWithPathParams"] } - ], - "config": { - "url": "https://rest-with-path-params.example.com/{{.arguments.id}}", - "method": "POST", - "headers": {}, - "query": [], - "body": "" - } - }, - { - "kind": "REST", - "name": "restWithFullUrlAsParam", - "internal": true, - "root_fields": [ - { "type": "Query", "fields": ["restWithFullUrlAsParam"] } - ], - "config": { - "url": "{{.arguments.url}}", - "method": "POST", - "headers": {}, - "query": [], - "body": "" - } - }, - { - "kind": "GraphQL", - "internal": false, - "root_fields": [ - { "type": "Query", "fields": ["idType"] } - ], - "config": { - "url": "https://graphql.example.com", - "method": "POST", - "headers": { - "Auth": "123" - } - } - }, - { - "kind": "Kafka", - "name": "kafka-consumer-group", - "internal": false, - "root_fields": [{ - "type": "Subscription", - "fields": [ - "foobar" - ] - }], - "config": { - "broker_addresses": ["localhost:9092"], - "topics": ["test.topic"], - "group_id": "test.consumer.group", - "client_id": "test.client.id", - "kafka_version": "V2_8_0_0", - "start_consuming_latest": true, - "balance_strategy": "BalanceStrategySticky", - "isolation_level": "ReadCommitted", - "sasl": { - "enable": true, - "user": "admin", - "password": "admin-secret" - } - } - }, - { - "kind": "Kafka", - "name": "kafka-consumer-group-with-variable", - "internal": false, - "root_fields": [{ - "type": "Subscription", - "fields": [ - "foobarTopicWithVariable" - ] - }], - "config": { - "broker_addresses": ["localhost:9092"], - "topics": ["test.topic.{{.arguments.name}}"], - "group_id": "test.consumer.group", - "client_id": "test.client.id", - "kafka_version": "V2_8_0_0", - "start_consuming_latest": true, - "balance_strategy": "BalanceStrategySticky", - "isolation_level": "ReadCommitted", - "sasl": { - "enable": true, - "user": "admin", - "password": "admin-secret" - } - } - } - ] - }, - "playground": {} -}` - -const federationAccountsServiceSDL = `extend type Query {me: User} type User @key(fields: "id"){ id: ID! username: String!}` -const federationProductsServiceSDL = `extend type Query {topProducts(first: Int = 5): [Product]} type Product @key(fields: "upc") {upc: String! name: String! price: Int!}` -const federationReviewsServiceSDL = `type Review { body: String! author: User! @provides(fields: "username") product: Product! } extend type User @key(fields: "id") { id: ID! @external reviews: [Review] } extend type Product @key(fields: "upc") { upc: String! @external reviews: [Review] }` -const federationMergedSDL = `type Query { me: User topProducts(first: Int = 5): [Product] } type User { id: ID! username: String! reviews: [Review] } type Product { upc: String! name: String! price: Int! reviews: [Review] } type Review { body: String! author: User! product: Product! }` - -var graphqlEngineV2SupergraphConfigJson = `{ - "enabled": true, - "execution_mode": "supergraph", - "version": "2", - "schema": "", + "schema": "type Query { rest: String, gql: String }", "last_schema_update": "2020-11-11T11:11:11.000+01:00", - "engine": { - "field_configs": [], - "data_sources": [] - }, - "supergraph": { - "subgraphs": [ - { - "api_id": "", - "url": "tyk://accounts.service", - "sdl": ` + strconv.Quote(federationAccountsServiceSDL) + `, - "headers": { - "header1": "override_global", - "Auth": "appended_header" - }, - "subscription_type": "sse" - }, - { - "api_id": "", - "url": "http://products.service", - "sdl": ` + strconv.Quote(federationProductsServiceSDL) + ` - }, - { - "api_id": "", - "url": "http://ignored.service", - "sdl": "" - }, - { - "api_id": "", - "url": "http://reviews.service", - "sdl": ` + strconv.Quote(federationReviewsServiceSDL) + `, - "headers": { - "header1": "override_global", - "header2": "value2", - "Auth": "appended_header" - } - } - ], - "global_headers": { - "header1": "value1", - "header2": "value2" - }, - "merged_sdl": "` + federationMergedSDL + `" - }, "playground": {} }` -var graphqlProxyOnlyConfig = `{ +var graphqlMinimalProxyOnlyConfig = `{ "enabled": true, "execution_mode": "proxyOnly", "version": "2", @@ -1189,11 +139,11 @@ var graphqlProxyOnlyConfig = `{ "playground": {} }` -var graphqlSubgraphConfig = `{ +var graphqlMinimalSubgraphConfig = `{ "enabled": true, "execution_mode": "subgraph", "version": "2", - "schema": ` + strconv.Quote(graphqlSubgraphSchema) + `, + "schema": "type Query { hello: String }", "last_schema_update": "2020-11-11T11:11:11.000+01:00", "proxy": { "auth_headers": { @@ -1205,10 +155,69 @@ var graphqlSubgraphConfig = `{ "data_sources": [] }, "subgraph": { - "sdl": ` + strconv.Quote(federationAccountsServiceSDL) + `, + "sdl": "extend type Query { hello: String }", "subscription_type": "graphql-transport-ws" }, "playground": {} }` -const graphqlSubgraphSchema = `scalar _Any scalar _FieldSet union _Entity = User type _Service { sdl: String } type Query { me: User _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } type User { id: ID! username: String! }` +var graphqlMinimalSupergraphConfig = `{ + "enabled": true, + "execution_mode": "supergraph", + "version": "2", + "schema": "", + "last_schema_update": "2020-11-11T11:11:11.000+01:00", + "engine": { + "field_configs": [], + "data_sources": [] + }, + "supergraph": { + "subgraphs": [ + { + "api_id": "", + "url": "tyk://accounts.service", + "sdl": "extend type Query {me: User}", + "headers": {}, + "subscription_type": "sse" + } + ], + "global_headers": {}, + "merged_sdl": "type Query { me: User }" + }, + "playground": {} +}` + +var graphqlMinimalUniversalDataGraphConfig = `{ + "enabled": true, + "execution_mode": "executionEngine", + "version": "2", + "schema": "type Query { hello: String }", + "last_schema_update": "2020-11-11T11:11:11.000+01:00", + "engine": { + "field_configs": [ + { + "type_name": "Query", + "field_name": "hello", + "disable_default_mapping": false + } + ], + "data_sources": [ + { + "kind": "REST", + "name": "", + "internal": true, + "root_fields": [ + { "type": "Query", "fields": ["hello"] } + ], + "config": { + "url": "tyk://rest-example", + "method": "POST", + "headers": {}, + "query": [], + "body": "" + } + } + ] + }, + "playground": {} +}` diff --git a/apidef/adapter/utils.go b/apidef/adapter/utils.go index e615518f3d8..7c4c72cc5bf 100644 --- a/apidef/adapter/utils.go +++ b/apidef/adapter/utils.go @@ -1,31 +1,17 @@ package adapter import ( - "net/http" - - graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" - "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" - "github.com/TykTechnologies/tyk/apidef" ) -func parseSchema(schemaAsString string) (parsedSchema *graphql.Schema, err error) { - parsedSchema, err = graphql.NewSchemaFromString(schemaAsString) - if err != nil { - return nil, err - } - - normalizationResult, err := parsedSchema.Normalize() - if err != nil { - return nil, err - } - - if !normalizationResult.Successful && normalizationResult.Errors != nil { - return nil, normalizationResult.Errors - } +type GraphQLEngineAdapterType int - return parsedSchema, nil -} +const ( + GraphQLEngineAdapterTypeUnknown = iota + GraphQLEngineAdapterTypeProxyOnly + GraphQLEngineAdapterTypeSupergraph + GraphQLEngineAdapterTypeUniversalDataGraph +) func isSupergraphAPIDefinition(apiDefinition *apidef.APIDefinition) bool { return apiDefinition.GraphQL.Enabled && apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeSupergraph @@ -36,52 +22,22 @@ func isProxyOnlyAPIDefinition(apiDefinition *apidef.APIDefinition) bool { (apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeProxyOnly || apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeSubgraph) } -func graphqlDataSourceWebSocketProtocol(subscriptionType apidef.SubscriptionType) string { - wsProtocol := graphqlDataSource.ProtocolGraphQLWS - if subscriptionType == apidef.GQLSubscriptionTransportWS { - wsProtocol = graphqlDataSource.ProtocolGraphQLTWS - } - return wsProtocol +func isUniversalDataGraphAPIDefinition(apiDefinition *apidef.APIDefinition) bool { + return apiDefinition.GraphQL.Enabled && apiDefinition.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeExecutionEngine } -func graphqlSubscriptionType(subscriptionType apidef.SubscriptionType) graphql.SubscriptionType { - switch subscriptionType { - case apidef.GQLSubscriptionWS: - return graphql.SubscriptionTypeGraphQLWS - case apidef.GQLSubscriptionTransportWS: - return graphql.SubscriptionTypeGraphQLTransportWS - case apidef.GQLSubscriptionSSE: - return graphql.SubscriptionTypeSSE - default: - return graphql.SubscriptionTypeUnknown +func graphqlEngineAdapterTypeFromApiDefinition(apiDefinition *apidef.APIDefinition) GraphQLEngineAdapterType { + if isProxyOnlyAPIDefinition(apiDefinition) { + return GraphQLEngineAdapterTypeProxyOnly } -} -func convertApiDefinitionHeadersToHttpHeaders(apiDefHeaders map[string]string) http.Header { - if len(apiDefHeaders) == 0 { - return nil + if isSupergraphAPIDefinition(apiDefinition) { + return GraphQLEngineAdapterTypeSupergraph } - engineV2Headers := make(http.Header) - for apiDefHeaderKey, apiDefHeaderValue := range apiDefHeaders { - engineV2Headers.Add(apiDefHeaderKey, apiDefHeaderValue) + if isUniversalDataGraphAPIDefinition(apiDefinition) { + return GraphQLEngineAdapterTypeUniversalDataGraph } - return engineV2Headers -} - -func removeDuplicateApiDefinitionHeaders(headers ...map[string]string) map[string]string { - hdr := make(map[string]string) - // headers priority depends on the order of arguments - for _, header := range headers { - for k, v := range header { - keyCanonical := http.CanonicalHeaderKey(k) - if _, ok := hdr[keyCanonical]; ok { - // skip because header is present - continue - } - hdr[keyCanonical] = v - } - } - return hdr + return GraphQLEngineAdapterTypeUnknown } diff --git a/apidef/adapter/utils_test.go b/apidef/adapter/utils_test.go index 2002562b73d..42603011be4 100644 --- a/apidef/adapter/utils_test.go +++ b/apidef/adapter/utils_test.go @@ -1,30 +1,13 @@ package adapter import ( - "net/http" "testing" "github.com/stretchr/testify/assert" - graphqlDataSource "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/graphql_datasource" - "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" - "github.com/TykTechnologies/tyk/apidef" ) -func TestParseSchema(t *testing.T) { - inputSchema := ` - type Query { - hello: String! - }` - - // We are pretty confident that the schema parsing works, as it is tested in the library already. - // We just want to make sure that there is no weird error happening. - parsedSchema, err := parseSchema(inputSchema) - assert.NotNil(t, parsedSchema) - assert.NoError(t, err) -} - func TestIsSupergraphAPIDefinition(t *testing.T) { type testInput struct { graphQLEnabled bool @@ -137,91 +120,105 @@ func TestIsProxyOnlyAPIDefinition(t *testing.T) { )) } -func TestGraphqlDataSourceWebSocketProtocol(t *testing.T) { - run := func(subscriptionType apidef.SubscriptionType, expectedWebSocketProtocol string) func(t *testing.T) { - return func(t *testing.T) { - actualProtocol := graphqlDataSourceWebSocketProtocol(subscriptionType) - assert.Equal(t, expectedWebSocketProtocol, actualProtocol) - } +func TestIsUniversalDataGraphAPIDefinition(t *testing.T) { + type testInput struct { + graphQLEnabled bool + executionModes []apidef.GraphQLExecutionMode + expectedResult bool } - - t.Run("should return 'graphql-ws' for undefined subscription type", - run(apidef.GQLSubscriptionUndefined, graphqlDataSource.ProtocolGraphQLWS), - ) - - t.Run("should return 'graphql-ws' for graphql-ws subscription type", - run(apidef.GQLSubscriptionWS, graphqlDataSource.ProtocolGraphQLWS), - ) - - t.Run("should return 'graphql-ws' for sse subscription type as websocket protocol is irrelevant in that case", - run(apidef.GQLSubscriptionSSE, graphqlDataSource.ProtocolGraphQLWS), - ) - - t.Run("should return 'graphql-transport-ws' for graphql-transport-ws subscription type", - run(apidef.GQLSubscriptionTransportWS, graphqlDataSource.ProtocolGraphQLTWS), - ) -} - -func TestGraphqlSubscriptionType(t *testing.T) { - run := func(subscriptionType apidef.SubscriptionType, expectedGraphQLSubscriptionType graphql.SubscriptionType) func(t *testing.T) { + run := func(input testInput) func(t *testing.T) { return func(t *testing.T) { - actualSubscriptionType := graphqlSubscriptionType(subscriptionType) - assert.Equal(t, expectedGraphQLSubscriptionType, actualSubscriptionType) + for _, executionMode := range input.executionModes { + apiDef := &apidef.APIDefinition{ + GraphQL: apidef.GraphQLConfig{ + Enabled: input.graphQLEnabled, + ExecutionMode: executionMode, + }, + } + assert.Equal(t, input.expectedResult, isUniversalDataGraphAPIDefinition(apiDef)) + } } } - t.Run("should return 'Unknown' for undefined subscription type", - run(apidef.GQLSubscriptionUndefined, graphql.SubscriptionTypeUnknown), - ) - - t.Run("should return 'SSE' for sse subscription type as websocket protocol is irrelevant in that case", - run(apidef.GQLSubscriptionSSE, graphql.SubscriptionTypeSSE), - ) + t.Run("false if graphql is disabled", run( + testInput{ + graphQLEnabled: false, + executionModes: []apidef.GraphQLExecutionMode{ + apidef.GraphQLExecutionModeProxyOnly, + apidef.GraphQLExecutionModeExecutionEngine, + apidef.GraphQLExecutionModeSubgraph, + apidef.GraphQLExecutionModeSupergraph, + }, + expectedResult: false, + }, + )) - t.Run("should return 'GraphQLWS' for graphql-ws subscription type", - run(apidef.GQLSubscriptionWS, graphql.SubscriptionTypeGraphQLWS), - ) + t.Run("false if execution mode is not executionEngine", run( + testInput{ + graphQLEnabled: true, + executionModes: []apidef.GraphQLExecutionMode{ + apidef.GraphQLExecutionModeProxyOnly, + apidef.GraphQLExecutionModeSupergraph, + apidef.GraphQLExecutionModeSubgraph, + }, + expectedResult: false, + }, + )) - t.Run("should return 'GraphQLTransportWS' for graphql-transport-ws subscription type", - run(apidef.GQLSubscriptionTransportWS, graphql.SubscriptionTypeGraphQLTransportWS), - ) + t.Run("true if graphql is enabled and execution mode is executionEngine", run( + testInput{ + graphQLEnabled: true, + executionModes: []apidef.GraphQLExecutionMode{ + apidef.GraphQLExecutionModeExecutionEngine, + }, + expectedResult: true, + }, + )) } -func TestConvertApiDefinitionHeadersToHttpHeaders(t *testing.T) { - t.Run("should return nil for empty input", func(t *testing.T) { - assert.Nil(t, convertApiDefinitionHeadersToHttpHeaders(nil)) - }) - - t.Run("should successfully convert API Definition header to Http Headers", func(t *testing.T) { - apiDefinitionHeaders := map[string]string{ - "Authorization": "token", - "X-Tyk-Key": "value", - } +func TestGraphqlEngineAdapterTypeFromApiDefinition(t *testing.T) { + type testInput struct { + executionMode apidef.GraphQLExecutionMode + expectedResult GraphQLEngineAdapterType + } - expectedHttpHeaders := http.Header{ - "Authorization": {"token"}, - "X-Tyk-Key": {"value"}, + run := func(input testInput) func(t *testing.T) { + return func(t *testing.T) { + apiDef := &apidef.APIDefinition{ + GraphQL: apidef.GraphQLConfig{ + Enabled: true, + ExecutionMode: input.executionMode, + }, + } + assert.Equal(t, input.expectedResult, graphqlEngineAdapterTypeFromApiDefinition(apiDef)) } + } - actualHttpHeaders := convertApiDefinitionHeadersToHttpHeaders(apiDefinitionHeaders) - assert.Equal(t, expectedHttpHeaders, actualHttpHeaders) - }) -} + t.Run("should return adapter type proxy only for execution mode proxy only", run( + testInput{ + executionMode: apidef.GraphQLExecutionModeProxyOnly, + expectedResult: GraphQLEngineAdapterTypeProxyOnly, + }, + )) -func TestRemoveDuplicateApiDefinitionHeaders(t *testing.T) { - apiDefinitionHeadersFirstArgument := map[string]string{ - "duplicate-header": "value", - } - apiDefinitionHeadersSecondArgument := map[string]string{ - "Duplicate-Header": "value", - "Non-Duplicate-Header": "another_value", - } + t.Run("should return adapter type proxy only for execution mode subgraph", run( + testInput{ + executionMode: apidef.GraphQLExecutionModeSubgraph, + expectedResult: GraphQLEngineAdapterTypeProxyOnly, + }, + )) - expectedDeduplicatedHeaders := map[string]string{ - "Duplicate-Header": "value", - "Non-Duplicate-Header": "another_value", - } + t.Run("should return adapter type supergraph for execution mode supergraph", run( + testInput{ + executionMode: apidef.GraphQLExecutionModeSupergraph, + expectedResult: GraphQLEngineAdapterTypeSupergraph, + }, + )) - actualDeduplicatedHeaders := removeDuplicateApiDefinitionHeaders(apiDefinitionHeadersFirstArgument, apiDefinitionHeadersSecondArgument) - assert.Equal(t, expectedDeduplicatedHeaders, actualDeduplicatedHeaders) + t.Run("should return adapter type proxy only for execution mode proxy only", run( + testInput{ + executionMode: apidef.GraphQLExecutionModeExecutionEngine, + expectedResult: GraphQLEngineAdapterTypeUniversalDataGraph, + }, + )) } From 7fa4a7ef6fabd1a3c5b88d20a8a2cdfc2cb50e1d Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Mon, 6 Mar 2023 15:34:52 +0100 Subject: [PATCH 25/51] Improve path joining tests with disable strip slash (#4813) ## Description Better implementation of 'disable_strip_slash' - extends tests and adjusts behaviour to be expected for the flag. ## Related Issue https://tyktech.atlassian.net/browse/TT-7784 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [x] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --------- Co-authored-by: Tit Petric --- gateway/reverse_proxy.go | 18 +++++++++-------- gateway/reverse_proxy_test.go | 38 ++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/gateway/reverse_proxy.go b/gateway/reverse_proxy.go index 3d1666fd9e1..ac27bdb3b66 100644 --- a/gateway/reverse_proxy.go +++ b/gateway/reverse_proxy.go @@ -418,16 +418,18 @@ func (p *ReverseProxy) defaultTransport(dialerTimeout float64) *http.Transport { return transport } -func singleJoiningSlash(a, b string, disableStripSlash bool) string { - if disableStripSlash && (len(b) == 0 || b == "/") { - return a +func singleJoiningSlash(targetPath, subPath string, disableStripSlash bool) string { + if disableStripSlash && (len(subPath) == 0 || subPath == "/") { + return targetPath } - a = strings.TrimRight(a, "/") - b = strings.TrimLeft(b, "/") - if len(b) > 0 { - return a + "/" + b + + targetPath = strings.TrimRight(targetPath, "/") + subPath = strings.TrimLeft(subPath, "/") + + if len(subPath) > 0 { + return targetPath + "/" + subPath } - return a + return targetPath } func removeDuplicateCORSHeader(dst, src http.Header) { diff --git a/gateway/reverse_proxy_test.go b/gateway/reverse_proxy_test.go index 50f4a8cb70b..d5895939567 100644 --- a/gateway/reverse_proxy_test.go +++ b/gateway/reverse_proxy_test.go @@ -545,35 +545,49 @@ func TestSingleJoiningSlash(t *testing.T) { testsFalse := []struct { a, b, want string }{ + {"", "", ""}, + {"/", "", ""}, + {"", "/", ""}, + {"/", "/", ""}, {"foo", "", "foo"}, + {"foo", "/", "foo"}, {"foo", "bar", "foo/bar"}, {"foo/", "bar", "foo/bar"}, {"foo", "/bar", "foo/bar"}, {"foo/", "/bar", "foo/bar"}, {"foo//", "//bar", "foo/bar"}, + {"foo", "bar/", "foo/bar/"}, + {"foo/", "bar/", "foo/bar/"}, + {"foo", "/bar/", "foo/bar/"}, + {"foo/", "/bar/", "foo/bar/"}, + {"foo//", "//bar/", "foo/bar/"}, } - for _, tc := range testsFalse { - t.Run(fmt.Sprintf("%s+%s", tc.a, tc.b), func(t *testing.T) { + for i, tc := range testsFalse { + t.Run(fmt.Sprintf("enabled StripSlashes #%d", i), func(t *testing.T) { got := singleJoiningSlash(tc.a, tc.b, false) - if got != tc.want { - t.Fatalf("want %s, got %s", tc.want, got) - } + assert.Equal(t, tc.want, got) }) } testsTrue := []struct { a, b, want string }{ + {"", "", ""}, + {"/", "", "/"}, + {"", "/", ""}, + {"/", "/", "/"}, + {"foo", "", "foo"}, + {"foo", "/", "foo"}, {"foo/", "", "foo/"}, - {"foo/", "/name", "foo/name"}, {"foo/", "/", "foo/"}, - {"foo", "", "foo"}, + {"foo/", "/name", "foo/name"}, + {"foo/", "/name/", "foo/name/"}, + {"foo/", "//name", "foo/name"}, + {"foo/", "//name/", "foo/name/"}, } - for _, tc := range testsTrue { - t.Run(fmt.Sprintf("%s+%s", tc.a, tc.b), func(t *testing.T) { + for i, tc := range testsTrue { + t.Run(fmt.Sprintf("disabled StripSlashes #%d", i), func(t *testing.T) { got := singleJoiningSlash(tc.a, tc.b, true) - if got != tc.want { - t.Fatalf("want %s, got %s", tc.want, got) - } + assert.Equal(t, tc.want, got, fmt.Sprintf("a: %s, b: %s, out: %s, expected %s", tc.a, tc.b, got, tc.want)) }) } } From 3d8dd1aad6791c432d82c0a163d8626a44e14b5e Mon Sep 17 00:00:00 2001 From: itachi sasuke <8012032+Keithwachira@users.noreply.github.com> Date: Tue, 7 Mar 2023 12:44:46 +0300 Subject: [PATCH 26/51] make policy access_rights field be a hashmap instead of an object (#4832) - The access_right field in the policy object is supposed to be a HashMaps (Dictionaries) of AccessDefinition. However, this field is currently an object of type AccessDefinition instead. This pull request fixes that issue. Here is a prove of that it should be a HashMaps : https://github.com/TykTechnologies/tyk/blob/abc14c84c7a85e3f22d38673239ea28388af05fd/user/policy.go#L24 ## Description ## Related Issue ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [X] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [X] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --------- Co-authored-by: Yaara --- swagger.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swagger.yml b/swagger.yml index f4f6228cf6d..2ad91c30527 100644 --- a/swagger.yml +++ b/swagger.yml @@ -3008,9 +3008,10 @@ components: type: number x-go-name: MaxQueryDepth access_rights: - $ref: '#/components/schemas/AccessDefinition' type: object x-go-name: AccessRights + additionalProperties: + $ref: '#/components/schemas/AccessDefinition' hmac_enabled: type: boolean x-go-name: HMACEnabled From feb826c153767acff73f940ff4faf336c49725c2 Mon Sep 17 00:00:00 2001 From: Tomas Buchaillot Date: Tue, 7 Mar 2023 14:03:13 +0100 Subject: [PATCH 27/51] TT-8150 Fix CI Tunny panic (#4833) ## Description Fix flaky test - check before trying to close an already closed channel ## Related Issue https://tyktech.atlassian.net/browse/TT-8150 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --- gateway/host_checker.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gateway/host_checker.go b/gateway/host_checker.go index 2e7481a128d..a4f65cd9acb 100644 --- a/gateway/host_checker.go +++ b/gateway/host_checker.go @@ -408,7 +408,10 @@ func (h *HostUptimeChecker) Stop() { eraseSyncMap(h.samples) log.Info("[HOST CHECKER] Stopping poller") - h.pool.Close() + + if h.pool != nil && h.pool.GetSize() > 0 { + h.pool.Close() + } } } From 8b8a9f441ee5268e98e62657ff72431b8e77e085 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Wed, 8 Mar 2023 10:26:14 +0100 Subject: [PATCH 28/51] [TT-7920] additional routes with APIID (Oauth) (#4796) ## Description In addition to the API listenPath, this PR makes the APIs available by /apiID. This makes the API endpoints accessible by the ID, in particular the OAuth endpoints below. ## Related Issue https://tyktech.atlassian.net/browse/TT-7920 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [x] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [x] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why Co-authored-by: Tit Petric --- gateway/api_loader.go | 59 ++++++++++++++++++++++++++------------ gateway/api_loader_test.go | 18 ++++++------ gateway/server.go | 20 ------------- gateway/tracing.go | 3 +- 4 files changed, 53 insertions(+), 47 deletions(-) diff --git a/gateway/api_loader.go b/gateway/api_loader.go index f1f432c93a8..b9cd035ce30 100644 --- a/gateway/api_loader.go +++ b/gateway/api_loader.go @@ -17,6 +17,7 @@ import ( "github.com/gorilla/mux" "github.com/justinas/alice" + "github.com/rs/cors" "github.com/sirupsen/logrus" "github.com/TykTechnologies/tyk/apidef" @@ -104,22 +105,33 @@ func fixFuncPath(pathPrefix string, funcs []apidef.MiddlewareDefinition) { } } -func (gw *Gateway) generateSubRoutes(spec *APISpec, subRouter *mux.Router, logger *logrus.Entry) { +func (gw *Gateway) generateSubRoutes(spec *APISpec, router *mux.Router, logger *logrus.Entry) { if spec.GraphQL.GraphQLPlayground.Enabled { - gw.loadGraphQLPlayground(spec, subRouter) + gw.loadGraphQLPlayground(spec, router) } if spec.EnableBatchRequestSupport { - gw.addBatchEndpoint(spec, subRouter) + gw.addBatchEndpoint(spec, router) } if spec.UseOauth2 { - logger.Debug("Loading OAuth Manager") - oauthManager := gw.addOAuthHandlers(spec, subRouter) - logger.Debug("-- Added OAuth Handlers") - + oauthManager := gw.addOAuthHandlers(spec, router) spec.OAuthManager = oauthManager - logger.Debug("Done loading OAuth Manager") + } + + if spec.CORS.Enable { + c := cors.New(cors.Options{ + AllowedOrigins: spec.CORS.AllowedOrigins, + AllowedMethods: spec.CORS.AllowedMethods, + AllowedHeaders: spec.CORS.AllowedHeaders, + ExposedHeaders: spec.CORS.ExposedHeaders, + AllowCredentials: spec.CORS.AllowCredentials, + MaxAge: spec.CORS.MaxAge, + OptionsPassthrough: spec.CORS.OptionsPassthrough, + Debug: spec.CORS.Debug, + }) + + router.Use(c.Handler) } } @@ -736,10 +748,7 @@ func (gw *Gateway) loadHTTPService(spec *APISpec, apisByListen map[string]int, g router = router.Host(hostname).Subrouter() } - subrouter := router.PathPrefix(spec.Proxy.ListenPath).Subrouter() - var chainObj *ChainObject - if curSpec := gw.getApiSpec(spec.APIID); !shouldReloadSpec(curSpec, spec) { if chain, found := gw.apisHandlesByID.Load(spec.APIID); found { chainObj = chain.(*ChainObject) @@ -748,19 +757,33 @@ func (gw *Gateway) loadHTTPService(spec *APISpec, apisByListen map[string]int, g chainObj = gw.processSpec(spec, apisByListen, gs, logrus.NewEntry(log)) } - gw.generateSubRoutes(spec, subrouter, logrus.NewEntry(log)) - handleCORS(subrouter, spec) - if chainObj.Skip { return chainObj } - if !chainObj.Open { - subrouter.Handle(rateLimitEndpoint, chainObj.RateLimitChain) + // Prefixes are multiple paths that the API endpoints are listening on. + prefixes := []string{ + // API definition UUID + "/" + spec.APIID + "/", + // User defined listen path + spec.Proxy.ListenPath, } - httpHandler := explicitRouteSubpaths(spec.Proxy.ListenPath, chainObj.ThisHandler, muxer, gwConfig.HttpServerOptions.EnableStrictRoutes) - subrouter.NewRoute().Handler(httpHandler) + // Register routes for each prefixe + for _, prefix := range prefixes { + subrouter := router.PathPrefix(prefix).Subrouter() + + gw.generateSubRoutes(spec, subrouter, logrus.NewEntry(log)) + + if !chainObj.Open { + subrouter.Handle(rateLimitEndpoint, chainObj.RateLimitChain) + } + + httpHandler := explicitRouteSubpaths(prefix, chainObj.ThisHandler, muxer, gwConfig.HttpServerOptions.EnableStrictRoutes) + + // Attach handlers + subrouter.NewRoute().Handler(httpHandler) + } return chainObj } diff --git a/gateway/api_loader_test.go b/gateway/api_loader_test.go index aebccb5ff2f..b311bdea215 100644 --- a/gateway/api_loader_test.go +++ b/gateway/api_loader_test.go @@ -8,13 +8,13 @@ import ( "sync/atomic" "testing" - "github.com/TykTechnologies/tyk/apidef" - "github.com/TykTechnologies/tyk/user" - + "github.com/lonelycode/go-uuid/uuid" "github.com/stretchr/testify/assert" + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/test" "github.com/TykTechnologies/tyk/trace" + "github.com/TykTechnologies/tyk/user" ) func TestOpenTracing(t *testing.T) { @@ -266,16 +266,19 @@ func TestCORS(t *testing.T) { g := StartTest(nil) defer g.Close() + api1ID := uuid.New() + api2ID := uuid.New() + apis := g.Gw.BuildAndLoadAPI(func(spec *APISpec) { spec.Name = "CORS test API" - spec.APIID = "cors-api" + spec.APIID = api1ID spec.Proxy.ListenPath = "/cors-api/" spec.CORS.Enable = false spec.CORS.ExposedHeaders = []string{"Custom-Header"} spec.CORS.AllowedOrigins = []string{"*"} }, func(spec *APISpec) { spec.Name = "Another API" - spec.APIID = "another-api" + spec.APIID = api2ID spec.Proxy.ListenPath = "/another-api/" spec.CORS.ExposedHeaders = []string{"Custom-Header"} spec.CORS.AllowedOrigins = []string{"*"} @@ -302,10 +305,9 @@ func TestCORS(t *testing.T) { _, _ = g.Run(t, []test.TestCase{ {Path: "/cors-api/", Headers: headers, HeadersMatch: headersMatch, Code: http.StatusOK}, - }...) - - _, _ = g.Run(t, []test.TestCase{ {Path: "/another-api/", Headers: headers, HeadersNotMatch: headersMatch, Code: http.StatusOK}, + {Path: "/" + api1ID + "/", Headers: headers, HeadersMatch: headersMatch, Code: http.StatusOK}, + {Path: "/" + api2ID + "/", Headers: headers, HeadersNotMatch: headersMatch, Code: http.StatusOK}, }...) }) diff --git a/gateway/server.go b/gateway/server.go index d9d98aeaabe..79f6f40acd2 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -34,7 +34,6 @@ import ( "github.com/lonelycode/osin" newrelic "github.com/newrelic/go-agent" "github.com/pmylund/go-cache" - "github.com/rs/cors" uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" @@ -920,25 +919,6 @@ func (gw *Gateway) createResponseMiddlewareChain(spec *APISpec, responseFuncs [] spec.ResponseChain = responseChain } -func handleCORS(router *mux.Router, spec *APISpec) { - - if spec.CORS.Enable { - mainLog.Debug("CORS ENABLED") - c := cors.New(cors.Options{ - AllowedOrigins: spec.CORS.AllowedOrigins, - AllowedMethods: spec.CORS.AllowedMethods, - AllowedHeaders: spec.CORS.AllowedHeaders, - ExposedHeaders: spec.CORS.ExposedHeaders, - AllowCredentials: spec.CORS.AllowCredentials, - MaxAge: spec.CORS.MaxAge, - OptionsPassthrough: spec.CORS.OptionsPassthrough, - Debug: spec.CORS.Debug, - }) - - router.Use(c.Handler) - } -} - func (gw *Gateway) isRPCMode() bool { return gw.GetConfig().AuthOverride.ForceAuthProvider && gw.GetConfig().AuthOverride.AuthProvider.StorageEngine == RPCStorageEngine diff --git a/gateway/tracing.go b/gateway/tracing.go index 7dcd360d9a3..d1c35aa8a4b 100644 --- a/gateway/tracing.go +++ b/gateway/tracing.go @@ -119,8 +119,9 @@ func (gw *Gateway) traceHandler(w http.ResponseWriter, r *http.Request) { spec := loader.MakeSpec(&nestedApiDefinition{APIDefinition: traceReq.Spec}, logrus.NewEntry(logger)) chainObj := gw.processSpec(spec, nil, &gs, logrus.NewEntry(logger)) + gw.generateSubRoutes(spec, subrouter, logrus.NewEntry(logger)) - handleCORS(subrouter, spec) + spec.middlewareChain = chainObj if chainObj.ThisHandler == nil { From eb54adb24ce9704647c72d0192e205ddde9ee9dd Mon Sep 17 00:00:00 2001 From: Leonid Bugaev Date: Wed, 8 Mar 2023 18:25:44 +0300 Subject: [PATCH 29/51] Fix mTLS ClientCA leak (#4473) If there are mTLS apis with **EMPTY** domain and we have **DIRECT** domain match, we add certificates of "empty domain apis" to Client CAs anyway. In other words, if no domain set, their certificates will be **ALWAYS** exposed to TLS handshake, which in case of having massive list of CAs can overflow proxy buffers (we had issues with F5). It should also improve security as well (do not forget that we also have second layer of mTLS check on HTTP level). **Worth noticing that having mTLS without domain is a bad practice anyway, which is needed only if you have old TLS clients who do not work with SNI extension (e.g. you do not have access to domain during TLS handshake).** ## How to test - Use -vvv with curl, or use openssl cli client (`openssl 2>&1 s_client -connect localhost:8080`), to have TLS debug output. - Create an API without the domain, and another with domain, with mTLS enabled, and having **different** list of client certificates attached. - Create a request to the API with domain, by domain, and in TLS logs you should see that announced Client Certificate Authority will contain certificates from both APIs. WIth this PR, it should contain only CAs from direct domain match API. ## Related Issue ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --------- Co-authored-by: Tit Petric --- gateway/cert.go | 12 ++++++++++-- gateway/cert_test.go | 17 +++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/gateway/cert.go b/gateway/cert.go index 19121ee5618..53d7b382642 100644 --- a/gateway/cert.go +++ b/gateway/cert.go @@ -381,8 +381,16 @@ func (gw *Gateway) getTLSConfigForClient(baseConfig *tls.Config, listenPort int) defer gw.apisMu.RUnlock() newConfig.ClientCAs = x509.NewCertPool() - domainRequireCert := map[string]tls.ClientAuthType{} + + directMTLSDomainMatch := false + for _, spec := range gw.apiSpecs { + if spec.UseMutualTLSAuth && spec.Domain == hello.ServerName { + directMTLSDomainMatch = true + break + } + } + for _, spec := range gw.apiSpecs { switch { case spec.UseMutualTLSAuth: @@ -395,7 +403,7 @@ func (gw *Gateway) getTLSConfigForClient(baseConfig *tls.Config, listenPort int) } // If current domain match or empty, whitelist client certificates - if spec.Domain == "" || spec.Domain == hello.ServerName { + if (!directMTLSDomainMatch && spec.Domain == "") || spec.Domain == hello.ServerName { certIDs := append(spec.ClientCertificates, gwConfig.Security.Certificates.API...) for _, cert := range gw.CertificateManager.List(certIDs, certs.CertificatePublic) { diff --git a/gateway/cert_test.go b/gateway/cert_test.go index d4f18fcc7c2..123549a01ea 100644 --- a/gateway/cert_test.go +++ b/gateway/cert_test.go @@ -321,13 +321,19 @@ func testAPIMutualTLSHelper(t *testing.T, skipCAAnnounce bool) { t.Run("acceptable CAs from server", func(t *testing.T) { tlsConfig := GetTLSConfig(&clientCert, serverCertPem) tlsConfig.GetClientCertificate = func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) { - assert.Equal(t, skipCAAnnounce, len(info.AcceptableCAs) == 0) + if skipCAAnnounce { + assert.Equal(t, 0, len(info.AcceptableCAs)) + } else { + // Even if we are loading 2 mTLS APIs where one with empty domain, because we have direct domain match it should show only cert of matching API + assert.Equal(t, 1, len(info.AcceptableCAs)) + } return &clientCert, nil } transport := &http.Transport{TLSClientConfig: tlsConfig} httpClient := &http.Client{Transport: transport} clientCertID, err := ts.Gw.CertificateManager.Add(clientCertPem, "") + clientCertID2, err := ts.Gw.CertificateManager.Add(clientCertPem2, "") assert.NoError(t, err) ts.Gw.BuildAndLoadAPI(func(spec *APISpec) { @@ -335,13 +341,20 @@ func testAPIMutualTLSHelper(t *testing.T, skipCAAnnounce bool) { spec.Proxy.ListenPath = "/" spec.UseMutualTLSAuth = true spec.ClientCertificates = []string{clientCertID} - }) + }, + func(spec *APISpec) { + spec.Domain = "" + spec.Proxy.ListenPath = "/test" + spec.UseMutualTLSAuth = true + spec.ClientCertificates = []string{clientCertID2} + }) _, _ = ts.Run(t, test.TestCase{ Code: 200, Client: httpClient, Domain: "localhost", }) ts.Gw.CertificateManager.Delete(clientCertID, "") + ts.Gw.CertificateManager.Delete(clientCertID2, "") ts.Gw.CertificateManager.FlushCache() tlsConfigCache.Flush() }) From 4416b5a4f8a78a68d09efe509741009358f606bf Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Wed, 8 Mar 2023 22:48:12 +0100 Subject: [PATCH 30/51] Github actions: don't escape content for GITHUB_OUTPUT anymore (#4843) ## Description Since ::set-output has been replaced with GITHUB_OUTPUT, the git-state comment from CI has been inaccurate, or rather the newlines have been escaped according to old format. New output set according to [official docs](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings). ## Related Issue ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --------- Co-authored-by: Tit Petric --- .github/workflows/ci-tests.yml | 12 ++++++------ main.go | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 007a49f1015..108fc96ef4a 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -52,13 +52,17 @@ jobs: make lint git add --all + git diff HEAD > git-state.log - echo "git-state=$(sed -ze 's/%/%25/g;s/\n/%0A/g' git-state.log)" >> $GITHUB_OUTPUT git_state_count=$(wc -l < git-state.log) if [[ $git_state_count -ne 0 ]] then + echo "git-state<> $GITHUB_OUTPUT + cat git-state.log >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "make lint made git state dirty, please run make lint locally and update PR" exit 1 fi @@ -71,7 +75,6 @@ jobs: with: redis-version: ${{ matrix.redis-version }} - - name: Cache uses: actions/cache@v2 with: @@ -83,10 +86,7 @@ jobs: - name: Run Gateway Tests id: ci-tests run: | - ./bin/ci-tests.sh 2>&1 | tee test.log - result_code=${PIPESTATUS[0]} - echo "log=$(sed -ze 's/%/%25/g;s/\n/%0A/g' test.log)" >> $GITHUB_OUTPUT - exit $result_code + ./bin/ci-tests.sh 2>&1 - name: Notify status if: ${{ failure() && github.event.pull_request.number }} diff --git a/main.go b/main.go index 5ff7c896297..cf0238cfdd5 100644 --- a/main.go +++ b/main.go @@ -5,5 +5,6 @@ import ( ) func main() { + gateway.Start() } From 52591f5eeafbd59a13509d7b3a73d36db4c6dba8 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 10 Mar 2023 11:24:01 +0100 Subject: [PATCH 31/51] Remove lonelycode/go-uuid (test dependency) (#4797) ## Description lonelycode/go-uuid is a test dependency; remove it to use internal uuid package. ## Related Issue ## Motivation and Context Outdatated dependency, multiple dependencies for same purpose. ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [x] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --------- Co-authored-by: Tit Petric --- .golangci.yml | 4 ++++ Makefile | 2 +- gateway/api_loader_test.go | 3 ++- gateway/looping_test.go | 1 - gateway/multiauth_test.go | 3 ++- gateway/mw_auth_key_test.go | 6 +++--- gateway/mw_http_signature_validation_test.go | 6 +++--- gateway/mw_jwt_test.go | 7 +++---- gateway/policy_test.go | 9 ++++----- go.mod | 1 - internal/uuid/uuid.go | 16 ++++++++++++++++ internal/uuid/uuid_test.go | 16 ++++++++++++++++ 12 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 internal/uuid/uuid.go create mode 100644 internal/uuid/uuid_test.go diff --git a/.golangci.yml b/.golangci.yml index d3ea974f254..772dc8e410e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -82,8 +82,12 @@ linters: - dupl - nilerr - misspell + - goimports linters-settings: + goimports: + local-prefixes: github.com/TykTechnologies,github.com/TykTechnologies/tyk/internal + errcheck: check-type-assertions: true check-blank: true diff --git a/Makefile b/Makefile index 82fc2e07921..21f393d2ec2 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ test: # lint runs all local linters that must pass before pushing .PHONY: lint lint-install lint-fast lint: lint-install - goimports -local github.com/TykTechnologies -w . + goimports -local github.com/TykTechnologies,github.com/TykTechnologies/tyk/internal -w . gofmt -w . faillint -ignore-tests -paths "$(shell grep -v '^#' .faillint | xargs echo | sed 's/ /,/g')" ./... diff --git a/gateway/api_loader_test.go b/gateway/api_loader_test.go index b311bdea215..85480150d0e 100644 --- a/gateway/api_loader_test.go +++ b/gateway/api_loader_test.go @@ -8,13 +8,14 @@ import ( "sync/atomic" "testing" - "github.com/lonelycode/go-uuid/uuid" "github.com/stretchr/testify/assert" "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/test" "github.com/TykTechnologies/tyk/trace" "github.com/TykTechnologies/tyk/user" + + "github.com/TykTechnologies/tyk/internal/uuid" ) func TestOpenTracing(t *testing.T) { diff --git a/gateway/looping_test.go b/gateway/looping_test.go index 476fd25921b..7970deeeae9 100644 --- a/gateway/looping_test.go +++ b/gateway/looping_test.go @@ -13,7 +13,6 @@ import ( "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/apidef/oas" - "github.com/TykTechnologies/tyk/test" "github.com/TykTechnologies/tyk/user" ) diff --git a/gateway/multiauth_test.go b/gateway/multiauth_test.go index ee9a44c6cd8..2f34b9f399b 100644 --- a/gateway/multiauth_test.go +++ b/gateway/multiauth_test.go @@ -12,11 +12,12 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/justinas/alice" - "github.com/lonelycode/go-uuid/uuid" "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/test" "github.com/TykTechnologies/tyk/user" + + "github.com/TykTechnologies/tyk/internal/uuid" ) const multiAuthDev = `{ diff --git a/gateway/mw_auth_key_test.go b/gateway/mw_auth_key_test.go index 96a299a3089..8737f76efaf 100644 --- a/gateway/mw_auth_key_test.go +++ b/gateway/mw_auth_key_test.go @@ -9,15 +9,15 @@ import ( "testing" "time" - "github.com/TykTechnologies/tyk/apidef" - "github.com/justinas/alice" - "github.com/lonelycode/go-uuid/uuid" + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/signature_validator" "github.com/TykTechnologies/tyk/storage" "github.com/TykTechnologies/tyk/test" "github.com/TykTechnologies/tyk/user" + + "github.com/TykTechnologies/tyk/internal/uuid" ) func TestMurmur3CharBug(t *testing.T) { diff --git a/gateway/mw_http_signature_validation_test.go b/gateway/mw_http_signature_validation_test.go index eca44d646a8..599650a2c57 100644 --- a/gateway/mw_http_signature_validation_test.go +++ b/gateway/mw_http_signature_validation_test.go @@ -22,14 +22,14 @@ import ( "testing" "time" - "github.com/TykTechnologies/tyk/certs" - "github.com/justinas/alice" - "github.com/lonelycode/go-uuid/uuid" "github.com/TykTechnologies/tyk/apidef" + "github.com/TykTechnologies/tyk/certs" "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/user" + + "github.com/TykTechnologies/tyk/internal/uuid" ) const hmacAuthDef = `{ diff --git a/gateway/mw_jwt_test.go b/gateway/mw_jwt_test.go index 051162a77b9..044e59a07e9 100644 --- a/gateway/mw_jwt_test.go +++ b/gateway/mw_jwt_test.go @@ -11,15 +11,14 @@ import ( "testing" "time" - "github.com/TykTechnologies/tyk/apidef" - "github.com/golang-jwt/jwt/v4" - - "github.com/lonelycode/go-uuid/uuid" "github.com/stretchr/testify/assert" + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/test" "github.com/TykTechnologies/tyk/user" + + "github.com/TykTechnologies/tyk/internal/uuid" ) // openssl rsa -in app.rsa -pubout > app.rsa.pub diff --git a/gateway/policy_test.go b/gateway/policy_test.go index 4e97814d523..7f2b28c7255 100644 --- a/gateway/policy_test.go +++ b/gateway/policy_test.go @@ -12,17 +12,16 @@ import ( "testing" "time" - "github.com/TykTechnologies/tyk/config" - - "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" - - "github.com/lonelycode/go-uuid/uuid" "github.com/stretchr/testify/assert" + "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" "github.com/TykTechnologies/tyk/apidef" + "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/header" "github.com/TykTechnologies/tyk/test" "github.com/TykTechnologies/tyk/user" + + "github.com/TykTechnologies/tyk/internal/uuid" ) func TestLoadPoliciesFromDashboardReLogin(t *testing.T) { diff --git a/go.mod b/go.mod index 3df927b47b7..f827b5d42a8 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,6 @@ require ( github.com/jensneuse/abstractlogger v0.0.4 github.com/justinas/alice v1.2.0 github.com/kelseyhightower/envconfig v1.4.0 - github.com/lonelycode/go-uuid v0.0.0-20141202165402-ed3ca8a15a93 // test github.com/lonelycode/osin v0.0.0-20160423095202-da239c9dacb6 github.com/mavricknz/ldap v0.0.0-20160227184754-f5a958005e43 github.com/miekg/dns v1.0.14 diff --git a/internal/uuid/uuid.go b/internal/uuid/uuid.go new file mode 100644 index 00000000000..588d3630145 --- /dev/null +++ b/internal/uuid/uuid.go @@ -0,0 +1,16 @@ +package uuid + +import ( + uuid "github.com/satori/go.uuid" +) + +// New returns a V4 UUID. +func New() string { + return uuid.NewV4().String() +} + +// Valid returns true if id is parsed as UUID without error. +func Valid(id string) bool { + _, err := uuid.FromString(id) + return err == nil +} diff --git a/internal/uuid/uuid_test.go b/internal/uuid/uuid_test.go new file mode 100644 index 00000000000..919c82630a1 --- /dev/null +++ b/internal/uuid/uuid_test.go @@ -0,0 +1,16 @@ +package uuid_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/TykTechnologies/tyk/internal/uuid" +) + +func TestUUID(t *testing.T) { + id := uuid.New() + + assert.NotEmpty(t, id) + assert.True(t, uuid.Valid(id)) +} From 0e3ab5f56abf06c45c21bd1a944dc179a00fdf8f Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 10 Mar 2023 18:21:52 +0100 Subject: [PATCH 32/51] [TT-8265] Drop satori go.uuid package (#4849) ## Description This PR removes satori/go.uuid in favor of gofrs/uuid. ## Related Issue https://tyktech.atlassian.net/browse/TT-8265 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --------- Co-authored-by: Tit Petric --- apidef/api_definitions.go | 15 ++++++--------- apidef/importer/blueprint.go | 6 +++--- apidef/importer/swagger.go | 6 +++--- apidef/importer/wsdl.go | 6 +++--- apidef/migration.go | 6 ++---- gateway/api.go | 13 ++++++------- gateway/api_test.go | 6 ++++-- gateway/auth_manager.go | 7 +++---- gateway/host_checker_manager.go | 5 +++-- gateway/host_checker_manager_test.go | 7 ------- gateway/host_checker_test.go | 12 ++++++++---- gateway/mw_api_rate_limit_test.go | 6 ++++-- gateway/mw_context_vars.go | 4 ++-- gateway/mw_organisation_activity_test.go | 5 +++-- gateway/oauth_manager.go | 6 +++--- gateway/oauth_manager_test.go | 9 ++++++--- gateway/server.go | 8 +++++--- gateway/testutil.go | 5 +++-- go.mod | 2 +- go.sum | 1 + internal/uuid/uuid.go | 23 +++++++++++++++++++++-- internal/uuid/uuid_test.go | 9 +++++++++ rpc/rpc_client.go | 10 +++++----- storage/redis_cluster.go | 8 +++----- storage/storage.go | 5 +++-- 25 files changed, 110 insertions(+), 80 deletions(-) diff --git a/apidef/api_definitions.go b/apidef/api_definitions.go index 09149d4e396..5cdcb31ddac 100644 --- a/apidef/api_definitions.go +++ b/apidef/api_definitions.go @@ -8,23 +8,20 @@ import ( "errors" "fmt" "net/http" - "strings" "text/template" "time" - uuid "github.com/satori/go.uuid" - - "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/kafka_datasource" - "github.com/TykTechnologies/graphql-go-tools/pkg/execution/datasource" - "github.com/clbanning/mxj" - "github.com/lonelycode/osin" "gopkg.in/mgo.v2/bson" - "github.com/TykTechnologies/gojsonschema" + "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/kafka_datasource" + "github.com/TykTechnologies/graphql-go-tools/pkg/execution/datasource" + "github.com/TykTechnologies/gojsonschema" "github.com/TykTechnologies/tyk/regexp" + + "github.com/TykTechnologies/tyk/internal/uuid" ) type AuthProviderCode string @@ -1124,7 +1121,7 @@ func (s *StringRegexMap) Init() error { } func (a *APIDefinition) GenerateAPIID() { - a.APIID = strings.Replace(uuid.NewV4().String(), "-", "", -1) + a.APIID = uuid.NewHex() } func (a *APIDefinition) GetAPIDomain() string { diff --git a/apidef/importer/blueprint.go b/apidef/importer/blueprint.go index 3cdd8917993..a086f75a0d5 100644 --- a/apidef/importer/blueprint.go +++ b/apidef/importer/blueprint.go @@ -7,9 +7,9 @@ import ( "strconv" "strings" - uuid "github.com/satori/go.uuid" - "github.com/TykTechnologies/tyk/apidef" + + "github.com/TykTechnologies/tyk/internal/uuid" ) const ApiaryBluePrint APIImporterSource = "blueprint" @@ -164,7 +164,7 @@ func (b *BluePrintAST) ToAPIDefinition(orgID, upstreamURL string, asMock bool) ( Name: b.Name, Active: true, UseKeylessAccess: true, - APIID: uuid.NewV4().String(), + APIID: uuid.NewHex(), OrgID: orgID, } ad.VersionDefinition.Key = "version" diff --git a/apidef/importer/swagger.go b/apidef/importer/swagger.go index 43d857dc2b2..ae71a5794ba 100644 --- a/apidef/importer/swagger.go +++ b/apidef/importer/swagger.go @@ -7,9 +7,9 @@ import ( "net/http" "strings" - uuid "github.com/satori/go.uuid" - "github.com/TykTechnologies/tyk/apidef" + + "github.com/TykTechnologies/tyk/internal/uuid" ) const SwaggerSource APIImporterSource = "swagger" @@ -145,7 +145,7 @@ func (s *SwaggerAST) ToAPIDefinition(orgId, upstreamURL string, as_mock bool) (* Name: s.Info.Title, Active: true, UseKeylessAccess: true, - APIID: uuid.NewV4().String(), + APIID: uuid.NewHex(), OrgID: orgId, } ad.VersionDefinition.Key = "version" diff --git a/apidef/importer/wsdl.go b/apidef/importer/wsdl.go index 2f05fa45697..87e5f0529ba 100644 --- a/apidef/importer/wsdl.go +++ b/apidef/importer/wsdl.go @@ -7,9 +7,9 @@ import ( "net/http" "strings" - uuid "github.com/satori/go.uuid" - "github.com/TykTechnologies/tyk/apidef" + + "github.com/TykTechnologies/tyk/internal/uuid" ) const WSDLSource APIImporterSource = "wsdl" @@ -279,7 +279,7 @@ func (def *WSDLDef) ToAPIDefinition(orgId, upstreamURL string, as_mock bool) (*a Active: true, UseKeylessAccess: true, OrgID: orgId, - APIID: uuid.NewV4().String(), + APIID: uuid.NewHex(), } ad.VersionDefinition.Key = "version" diff --git a/apidef/migration.go b/apidef/migration.go index 62bbb04bc8c..e4423350b72 100644 --- a/apidef/migration.go +++ b/apidef/migration.go @@ -7,9 +7,8 @@ import ( "sort" "strings" - uuid "github.com/satori/go.uuid" - "github.com/TykTechnologies/tyk/internal/reflect" + "github.com/TykTechnologies/tyk/internal/uuid" ) var ( @@ -60,8 +59,7 @@ func (a *APIDefinition) MigrateVersioning() (versions []APIDefinition, err error for vName, vInfo := range a.VersionData.Versions { newAPI := *a - newID := uuid.NewV4() - apiID := strings.Replace(newID.String(), "-", "", -1) + apiID := uuid.NewHex() newAPI.APIID = apiID newAPI.Id = "" diff --git a/gateway/api.go b/gateway/api.go index 18ca3a89dbd..f76f83157ed 100644 --- a/gateway/api.go +++ b/gateway/api.go @@ -47,7 +47,7 @@ import ( "github.com/TykTechnologies/tyk/config" - uuid "github.com/satori/go.uuid" + "github.com/TykTechnologies/tyk/internal/uuid" "github.com/TykTechnologies/tyk/apidef/oas" @@ -2140,11 +2140,10 @@ func (gw *Gateway) createOauthClient(w http.ResponseWriter, r *http.Request) { } // Allow the client ID to be set - cleanSting := newOauthClient.ClientID + clientID := newOauthClient.ClientID if newOauthClient.ClientID == "" { - u5 := uuid.NewV4() - cleanSting = strings.Replace(u5.String(), "-", "", -1) + clientID = uuid.NewHex() } // Allow the secret to be set @@ -2154,7 +2153,7 @@ func (gw *Gateway) createOauthClient(w http.ResponseWriter, r *http.Request) { } newClient := OAuthClient{ - ClientID: cleanSting, + ClientID: clientID, ClientRedirectURI: newOauthClient.ClientRedirectURI, ClientSecret: secret, PolicyID: newOauthClient.PolicyID, @@ -3386,8 +3385,8 @@ func ctxGetOperation(r *http.Request) (op *Operation) { } var createOauthClientSecret = func() string { - secret := uuid.NewV4() - return base64.StdEncoding.EncodeToString([]byte(secret.String())) + secret := uuid.New() + return base64.StdEncoding.EncodeToString([]byte(secret)) } // invalidate tokens if we had a new policy diff --git a/gateway/api_test.go b/gateway/api_test.go index 5a8d6a729f0..6e7b1c3d71e 100644 --- a/gateway/api_test.go +++ b/gateway/api_test.go @@ -21,11 +21,12 @@ import ( "github.com/getkin/kin-openapi/openapi3" "github.com/go-redis/redis/v8" - uuid "github.com/satori/go.uuid" "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/TykTechnologies/tyk/internal/uuid" + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/apidef/oas" "github.com/TykTechnologies/tyk/certs" @@ -1836,7 +1837,8 @@ func TestApiLoaderLongestPathFirst(t *testing.T) { for hp := range inputs { apis = append(apis, BuildAPI(func(spec *APISpec) { - spec.APIID = uuid.NewV4().String() + spec.APIID = uuid.New() + spec.Domain = hp.host spec.Proxy.ListenPath = "/" + hp.path })[0]) diff --git a/gateway/auth_manager.go b/gateway/auth_manager.go index 1b38bb47fe1..d757baeb6a4 100644 --- a/gateway/auth_manager.go +++ b/gateway/auth_manager.go @@ -6,9 +6,10 @@ import ( "strings" "time" - uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" + "github.com/TykTechnologies/tyk/internal/uuid" + "github.com/TykTechnologies/tyk/storage" "github.com/TykTechnologies/tyk/user" ) @@ -219,7 +220,5 @@ func (d DefaultKeyGenerator) GenerateAuthKey(orgID string) string { // GenerateHMACSecret is a utility function for generating new auth keys. Returns the storage key name and the actual key func (DefaultKeyGenerator) GenerateHMACSecret() string { - u5 := uuid.NewV4() - cleanSting := strings.Replace(u5.String(), "-", "", -1) - return base64.StdEncoding.EncodeToString([]byte(cleanSting)) + return base64.StdEncoding.EncodeToString([]byte(uuid.NewHex())) } diff --git a/gateway/host_checker_manager.go b/gateway/host_checker_manager.go index ec09f9943c6..fdd39c7c297 100644 --- a/gateway/host_checker_manager.go +++ b/gateway/host_checker_manager.go @@ -10,10 +10,11 @@ import ( "sync" "time" - uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" msgpack "gopkg.in/vmihailenco/msgpack.v2" + "github.com/TykTechnologies/tyk/internal/uuid" + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/storage" ) @@ -87,7 +88,7 @@ func (hc *HostCheckerManager) Start(ctx context.Context) { } func (hc *HostCheckerManager) GenerateCheckerId() { - hc.Id = uuid.NewV4().String() + hc.Id = uuid.New() } // CheckActivePollerLoop manages the state of the HostCheckerManager UptimeTest diff --git a/gateway/host_checker_manager_test.go b/gateway/host_checker_manager_test.go index f5284eb85a4..171f87722ef 100644 --- a/gateway/host_checker_manager_test.go +++ b/gateway/host_checker_manager_test.go @@ -4,8 +4,6 @@ import ( "net/http" "testing" - uuid "github.com/satori/go.uuid" - "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/storage" "github.com/TykTechnologies/tyk/test" @@ -99,11 +97,6 @@ func TestGenerateCheckerId(t *testing.T) { if hc.Id == "" { t.Error("HostCheckerManager should generate an Id on GenerateCheckerId") } - - uuid, _ := uuid.FromString(hc.Id) - if uuid.Version() != 4 { - t.Error("HostCheckerManager should generate an uuid.v4 id") - } } func TestCheckActivePollerLoop(t *testing.T) { diff --git a/gateway/host_checker_test.go b/gateway/host_checker_test.go index 2781b6dbb36..327c7a067ba 100644 --- a/gateway/host_checker_test.go +++ b/gateway/host_checker_test.go @@ -15,9 +15,10 @@ import ( "time" proxyproto "github.com/pires/go-proxyproto" - uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/assert" + "github.com/TykTechnologies/tyk/internal/uuid" + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/storage" @@ -71,7 +72,8 @@ func (w *testEventHandler) HandleEvent(em config.EventMessage) { // // ToDo check why it blocks func TestHostChecker(t *testing.T) { ts := StartTest(func(globalConf *config.Config) { - globalConf.UptimeTests.PollerGroup = uuid.NewV4().String() + globalConf.UptimeTests.PollerGroup = uuid.New() + }) defer ts.Close() @@ -178,7 +180,8 @@ func TestHostChecker(t *testing.T) { func TestReverseProxyAllDown(t *testing.T) { ts := StartTest(func(globalConf *config.Config) { - globalConf.UptimeTests.PollerGroup = uuid.NewV4().String() + globalConf.UptimeTests.PollerGroup = uuid.New() + }) defer ts.Close() @@ -458,7 +461,8 @@ func TestProxyWhenHostIsDown(t *testing.T) { conf.UptimeTests.Config.FailureTriggerSampleSize = 1 conf.UptimeTests.Config.TimeWait = 5 conf.UptimeTests.Config.EnableUptimeAnalytics = true - conf.UptimeTests.PollerGroup = uuid.NewV4().String() + conf.UptimeTests.PollerGroup = uuid.New() + } ts := StartTest(conf) defer ts.Close() diff --git a/gateway/mw_api_rate_limit_test.go b/gateway/mw_api_rate_limit_test.go index 947499d09f8..2db117f64c9 100644 --- a/gateway/mw_api_rate_limit_test.go +++ b/gateway/mw_api_rate_limit_test.go @@ -8,7 +8,8 @@ import ( "time" "github.com/justinas/alice" - uuid "github.com/satori/go.uuid" + + "github.com/TykTechnologies/tyk/internal/uuid" "github.com/TykTechnologies/tyk/test" "github.com/TykTechnologies/tyk/user" @@ -224,7 +225,8 @@ func TestRLClosed(t *testing.T) { req := TestReq(t, "GET", "/rl_closed_test/", nil) session := createRLSession() - customToken := uuid.NewV4().String() + customToken := uuid.New() + // AuthKey sessions are stored by {token} err := ts.Gw.GlobalSessionManager.UpdateSession(customToken, session, 60, false) if err != nil { diff --git a/gateway/mw_context_vars.go b/gateway/mw_context_vars.go index 7e17dc1fb14..94f617c3420 100644 --- a/gateway/mw_context_vars.go +++ b/gateway/mw_context_vars.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - uuid "github.com/satori/go.uuid" + "github.com/TykTechnologies/tyk/internal/uuid" "github.com/TykTechnologies/tyk/request" ) @@ -33,7 +33,7 @@ func (m *MiddlewareContextVars) ProcessRequest(w http.ResponseWriter, r *http.Re "path_parts": strings.Split(r.URL.Path, "/"), // Path parts "path": r.URL.Path, // path data "remote_addr": request.RealIP(r), // IP - "request_id": uuid.NewV4().String(), //Correlation ID + "request_id": uuid.New(), //Correlation ID } for hname, vals := range r.Header { diff --git a/gateway/mw_organisation_activity_test.go b/gateway/mw_organisation_activity_test.go index 1491a5c762a..8442495dfa5 100644 --- a/gateway/mw_organisation_activity_test.go +++ b/gateway/mw_organisation_activity_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - uuid "github.com/satori/go.uuid" + "github.com/TykTechnologies/tyk/internal/uuid" "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/test" @@ -17,7 +17,8 @@ import ( func (ts *Test) testPrepareProcessRequestQuotaLimit(tb testing.TB, data map[string]interface{}) { // load API - orgID := "test-org-" + uuid.NewV4().String() + orgID := "test-org-" + uuid.New() + ts.Gw.BuildAndLoadAPI(func(spec *APISpec) { spec.UseKeylessAccess = true spec.OrgID = orgID diff --git a/gateway/oauth_manager.go b/gateway/oauth_manager.go index 7999a48cbb8..dd1e6edfc0e 100644 --- a/gateway/oauth_manager.go +++ b/gateway/oauth_manager.go @@ -17,9 +17,10 @@ import ( "github.com/TykTechnologies/tyk/request" "github.com/lonelycode/osin" - uuid "github.com/satori/go.uuid" "golang.org/x/crypto/bcrypt" + "github.com/TykTechnologies/tyk/internal/uuid" + "strconv" "github.com/TykTechnologies/tyk/header" @@ -1138,8 +1139,7 @@ func (a accessTokenGen) GenerateAccessToken(data *osin.AccessData, generaterefre accesstoken = a.Gw.keyGen.GenerateAuthKey(newSession.OrgID) if generaterefresh { - u6 := uuid.NewV4() - refreshtoken = base64.StdEncoding.EncodeToString([]byte(u6.String())) + refreshtoken = base64.StdEncoding.EncodeToString([]byte(uuid.New())) } return } diff --git a/gateway/oauth_manager_test.go b/gateway/oauth_manager_test.go index 8ed5b10fba8..c3ed2e10699 100644 --- a/gateway/oauth_manager_test.go +++ b/gateway/oauth_manager_test.go @@ -21,7 +21,8 @@ import ( "time" "github.com/lonelycode/osin" - uuid "github.com/satori/go.uuid" + + "github.com/TykTechnologies/tyk/internal/uuid" "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/storage" @@ -637,7 +638,8 @@ func TestGetPaginatedClientTokens(t *testing.T) { spec := ts.LoadTestOAuthSpec() - clientID := uuid.NewV4().String() + clientID := uuid.New() + ts.createTestOAuthClient(spec, clientID) tokensID := map[string]bool{} @@ -763,7 +765,8 @@ func testGetClientTokens(t *testing.T, hashed bool) { spec := ts.LoadTestOAuthSpec() - clientID := uuid.NewV4().String() + clientID := uuid.New() + ts.createTestOAuthClient(spec, clientID) // make three tokens diff --git a/gateway/server.go b/gateway/server.go index 79f6f40acd2..c40e23cf403 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -34,11 +34,12 @@ import ( "github.com/lonelycode/osin" newrelic "github.com/newrelic/go-agent" "github.com/pmylund/go-cache" - uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" "rsc.io/letsencrypt" + "github.com/TykTechnologies/tyk/internal/uuid" + "github.com/TykTechnologies/again" "github.com/TykTechnologies/drl" gas "github.com/TykTechnologies/goautosocket" @@ -1502,9 +1503,10 @@ func Start() { // ToDo:Config replace for get default conf gw := NewGateway(config.Default, ctx) - gw.SetNodeID("solo-" + uuid.NewV4().String()) + gw.SetNodeID("solo-" + uuid.New()) + + gw.SessionID = uuid.New() - gw.SessionID = uuid.NewV4().String() if err := gw.initialiseSystem(); err != nil { mainLog.Fatalf("Error initialising system: %v", err) } diff --git a/gateway/testutil.go b/gateway/testutil.go index 94882806124..2ed8441112c 100644 --- a/gateway/testutil.go +++ b/gateway/testutil.go @@ -34,9 +34,10 @@ import ( "github.com/gorilla/mux" "github.com/gorilla/websocket" - uuid "github.com/satori/go.uuid" "golang.org/x/net/context" + "github.com/TykTechnologies/tyk/internal/uuid" + "github.com/TykTechnologies/graphql-go-tools/pkg/execution/datasource" "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" @@ -280,7 +281,7 @@ func (s *Test) RegisterBundle(name string, files map[string]string) string { s.Gw.TestBundleMu.Lock() defer s.Gw.TestBundleMu.Unlock() - bundleID := name + "-" + uuid.NewV4().String() + ".zip" + bundleID := name + "-" + uuid.NewHex() + ".zip" s.Gw.TestBundles[bundleID] = files return bundleID diff --git a/go.mod b/go.mod index f827b5d42a8..4f886e82017 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/getkin/kin-openapi v0.89.0 github.com/go-redis/redis/v8 v8.11.5 github.com/gocraft/health v0.0.0-20170925182251-8675af27fef0 + github.com/gofrs/uuid v3.3.0+incompatible github.com/golang-jwt/jwt/v4 v4.4.2 github.com/golang/protobuf v1.5.2 github.com/google/uuid v1.3.0 // indirect @@ -60,7 +61,6 @@ require ( github.com/pmylund/go-cache v2.1.0+incompatible github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d github.com/rs/cors v1.7.0 - github.com/satori/go.uuid v1.2.0 github.com/sirupsen/logrus v1.8.1 github.com/spf13/afero v1.6.0 github.com/square/go-jose v2.4.1+incompatible diff --git a/go.sum b/go.sum index 745febeed5c..86cd993c322 100644 --- a/go.sum +++ b/go.sum @@ -309,6 +309,7 @@ github.com/gocraft/health v0.0.0-20170925182251-8675af27fef0/go.mod h1:rWibcVfwb github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= diff --git a/internal/uuid/uuid.go b/internal/uuid/uuid.go index 588d3630145..038c1e21a63 100644 --- a/internal/uuid/uuid.go +++ b/internal/uuid/uuid.go @@ -1,12 +1,31 @@ package uuid import ( - uuid "github.com/satori/go.uuid" + "strings" + + "github.com/gofrs/uuid" ) // New returns a V4 UUID. func New() string { - return uuid.NewV4().String() + id, err := uuid.NewV4() + checkErrAndPanic(err, "Error generating UUID") + return id.String() +} + +func checkErrAndPanic(err error, message string) { + if err != nil { + // This is unfortunate, but UUID generation is used for DB + // record IDs and similar. If we can't generate an UUID, we + // can't reasonably recover from that situation. We should + // never reach this code. + panic(message + " " + err.Error()) + } +} + +// NewHex returns a V4 UUID without dashes. +func NewHex() string { + return strings.ReplaceAll(New(), "-", "") } // Valid returns true if id is parsed as UUID without error. diff --git a/internal/uuid/uuid_test.go b/internal/uuid/uuid_test.go index 919c82630a1..b075fbabfeb 100644 --- a/internal/uuid/uuid_test.go +++ b/internal/uuid/uuid_test.go @@ -13,4 +13,13 @@ func TestUUID(t *testing.T) { assert.NotEmpty(t, id) assert.True(t, uuid.Valid(id)) + assert.Contains(t, id, "-") +} + +func TestUUIDHex(t *testing.T) { + id := uuid.NewHex() + + assert.NotEmpty(t, id) + assert.True(t, uuid.Valid(id)) + assert.NotContains(t, id, "-") } diff --git a/rpc/rpc_client.go b/rpc/rpc_client.go index 932e9842bb3..b149f946460 100644 --- a/rpc/rpc_client.go +++ b/rpc/rpc_client.go @@ -10,15 +10,15 @@ import ( "testing" "time" - "github.com/TykTechnologies/tyk-pump/serializer" - "github.com/cenkalti/backoff/v4" "github.com/gocraft/health" - uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" "golang.org/x/sync/singleflight" "github.com/TykTechnologies/gorpc" + "github.com/TykTechnologies/tyk-pump/serializer" + + "github.com/TykTechnologies/tyk/internal/uuid" ) var ( @@ -226,7 +226,7 @@ func Connect(connConfig Config, suppressRegister bool, dispatcherFuncs map[strin // Set up the cache Log.Info("Setting new RPC connection!") - connID := uuid.NewV4().String() + connID := uuid.New() // Length should fit into 1 byte. Protection if we decide change uuid in future. if len(connID) > 255 { @@ -506,7 +506,7 @@ func Disconnect() bool { } func register() { - id = uuid.NewV4().String() + id = uuid.New() Log.Debug("RPC Client registered") } diff --git a/storage/redis_cluster.go b/storage/redis_cluster.go index 6a421a988f3..407cea4b4f8 100644 --- a/storage/redis_cluster.go +++ b/storage/redis_cluster.go @@ -9,13 +9,12 @@ import ( "strings" "time" - uuid "github.com/satori/go.uuid" - redis "github.com/go-redis/redis/v8" - "github.com/sirupsen/logrus" "github.com/TykTechnologies/tyk/config" + + "github.com/TykTechnologies/tyk/internal/uuid" ) // ------------------- REDIS CLUSTER STORAGE MANAGER ------------------------------- @@ -116,9 +115,8 @@ func getRedisAddrs(config config.StorageOptionsConf) (addrs []string) { } func clusterConnectionIsOpen(cluster *RedisCluster) bool { - c := cluster.RedisController.singleton(cluster.IsCache, cluster.IsAnalytics) - testKey := "redis-test-" + uuid.NewV4().String() + testKey := "redis-test-" + uuid.New() if err := c.Set(cluster.RedisController.ctx, testKey, "test", time.Second).Err(); err != nil { return false } diff --git a/storage/storage.go b/storage/storage.go index 8fd0d3b4d52..706e4b0999d 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -10,10 +10,11 @@ import ( "strings" "github.com/buger/jsonparser" - uuid "github.com/satori/go.uuid" "github.com/TykTechnologies/murmur3" logger "github.com/TykTechnologies/tyk/log" + + "github.com/TykTechnologies/tyk/internal/uuid" ) var log = logger.Get() @@ -71,7 +72,7 @@ const defaultHashAlgorithm = "murmur64" // If hashing algorithm is empty, use legacy key generation func GenerateToken(orgID, keyID, hashAlgorithm string) (string, error) { if keyID == "" { - keyID = strings.Replace(uuid.NewV4().String(), "-", "", -1) + keyID = uuid.NewHex() } if hashAlgorithm != "" { From 3fda7f84612552531ee550f50ecd6bf6633fe85a Mon Sep 17 00:00:00 2001 From: Asutosh <1187055+asutosh@users.noreply.github.com> Date: Mon, 13 Mar 2023 16:03:10 +0530 Subject: [PATCH 33/51] Update sync-automation (#4846) This updates the sync automation from the latest templates with changes that use GH_TOKEN for all the steps using gh cli. --- .github/workflows/sync-automation.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sync-automation.yml b/.github/workflows/sync-automation.yml index 9b44cc18de1..53a6e196da9 100644 --- a/.github/workflows/sync-automation.yml +++ b/.github/workflows/sync-automation.yml @@ -1,5 +1,5 @@ # Generated by: gromit policy -# Generated on: Wed Mar 1 11:28:57 UTC 2023 +# Generated on: Thu Mar 9 16:52:31 UTC 2023 name: Sync automation @@ -68,7 +68,7 @@ jobs: git push origin ${{ steps.sync-changes.outputs.prbranch }} exit 0 env: - GITHUB_TOKEN: ${{ secrets.ORG_GH_TOKEN }} + GH_TOKEN: ${{ secrets.ORG_GH_TOKEN }} - name: Create PR from the branch. id: create-pr @@ -106,6 +106,6 @@ jobs: run: | gh pr merge $PULL --auto --squash --subject "[CI] Sync automation: Syncing commits from master" --body "Picking CI changes from the commit $COMMIT" env: - GITHUB_TOKEN: ${{ secrets.ORG_GH_TOKEN }} + GH_TOKEN: ${{ secrets.ORG_GH_TOKEN }} PULL: ${{ steps.create-pr.outputs.result }} COMMIT: ${{ github.sha }} From a6a278709533d2b652776a3d0bb0d274853fdd57 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Mon, 13 Mar 2023 15:23:09 +0100 Subject: [PATCH 34/51] Update murmur3 to latest (#4853) ## Description ## Related Issue https://tyktech.atlassian.net/browse/TT-8277 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why Co-authored-by: Tit Petric --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 4f886e82017..8d12987db0d 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/TykTechnologies/goverify v0.0.0-20220808203004-1486f89e7708 github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230214130715-aa076c16772f github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c - github.com/TykTechnologies/murmur3 v0.0.0-20180602122059-1915e687e465 + github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 github.com/TykTechnologies/openid2go v0.1.2 github.com/TykTechnologies/tyk-pump v1.7.0-rc1 github.com/akutz/memconn v0.1.0 diff --git a/go.sum b/go.sum index 86cd993c322..1b89f8dc8cd 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,9 @@ github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230214130715-aa076c16772f github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230214130715-aa076c16772f/go.mod h1:ngqwmltoDadD4ROYMPCTD55Zby0urWj1xVeJmlXGTuc= github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c h1:j6fd0Fz1R4oSWOmcooGjrdahqrML+btQ+PfEJw8SzbA= github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c/go.mod h1:GnHUbsQx+ysI10osPhUdTmsxcE7ef64cVp38Fdyd7e0= -github.com/TykTechnologies/murmur3 v0.0.0-20180602122059-1915e687e465 h1:A2gBjoX8aF0G3GHEpHyj2f0ixuPkCgcGqmPdKHSkW+0= github.com/TykTechnologies/murmur3 v0.0.0-20180602122059-1915e687e465/go.mod h1:sqH/SPFr11m9cahie7ulBuBX9TOhfBX1sp+qf9jh3Vg= +github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 h1:T5NWziFusj8au5nxAqMMh/bZyX9CAyYnBkaMSsfH6BA= +github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632/go.mod h1:UsPYgOFBpNzDXLEti7MKOwHLpVSqdzuNGkVFPspQmnQ= github.com/TykTechnologies/openid2go v0.0.0-20200122120050-1b642583380a/go.mod h1:rGlqNE4CvxZIeiHp0mgrw+/jdGSjJzkZ0n78hhHMdfM= github.com/TykTechnologies/openid2go v0.1.2 h1:WXctksOahA/epTVVvbn9iNUuMXKRr0ksrF4dY9KW8o8= github.com/TykTechnologies/openid2go v0.1.2/go.mod h1:gYfkqeWa+lY3Xz/Z2xYtIzmYXynlgKZaBIbPCqdcdMA= From 3561b9f22474f7df57d103032085dc29374f6d22 Mon Sep 17 00:00:00 2001 From: Asutosh <1187055+asutosh@users.noreply.github.com> Date: Tue, 14 Mar 2023 16:37:50 +0530 Subject: [PATCH 35/51] [TD-1474]: Update releng from the latest templates. (#4860) Test commit for TD-1474, after the token updated with necessary permissions. --- .github/dependabot.yml | 2 +- .github/workflows/del-env.yml | 2 +- .github/workflows/release.yml | 2 +- ci/Dockerfile.std | 2 +- ci/aws/byol.pkr.hcl | 2 +- ci/goreleaser/goreleaser-el7.yml | 2 +- ci/goreleaser/goreleaser.yml | 2 +- ci/install/before_install.sh | 2 +- ci/install/post_install.sh | 2 +- ci/install/post_remove.sh | 2 +- ci/install/post_trans.sh | 2 +- ci/terraform/outputs.tf | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ba8c81f2ded..1731c5ad51f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 version: 2 updates: diff --git a/.github/workflows/del-env.yml b/.github/workflows/del-env.yml index 236a7bb09b9..7cb4bee1167 100644 --- a/.github/workflows/del-env.yml +++ b/.github/workflows/del-env.yml @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 name: Retiring dev env diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 77e14bf6f40..cdc4b2b6a9a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 # Distribution channels covered by this workflow diff --git a/ci/Dockerfile.std b/ci/Dockerfile.std index bbd2250268c..2705ab2fcf3 100644 --- a/ci/Dockerfile.std +++ b/ci/Dockerfile.std @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 FROM debian:bullseye-slim ARG TARGETARCH diff --git a/ci/aws/byol.pkr.hcl b/ci/aws/byol.pkr.hcl index 32cb8060d45..b21fd4f3027 100644 --- a/ci/aws/byol.pkr.hcl +++ b/ci/aws/byol.pkr.hcl @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 packer { required_plugins { diff --git a/ci/goreleaser/goreleaser-el7.yml b/ci/goreleaser/goreleaser-el7.yml index b4d2385bba5..0e6ce652e4c 100644 --- a/ci/goreleaser/goreleaser-el7.yml +++ b/ci/goreleaser/goreleaser-el7.yml @@ -1,5 +1,5 @@ # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 # Check the documentation at http://goreleaser.com # This project needs CGO_ENABLED=1 and the cross-compiler toolchains for diff --git a/ci/goreleaser/goreleaser.yml b/ci/goreleaser/goreleaser.yml index e11d65035b0..cdb973eca42 100644 --- a/ci/goreleaser/goreleaser.yml +++ b/ci/goreleaser/goreleaser.yml @@ -1,5 +1,5 @@ # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 # Check the documentation at http://goreleaser.com # This project needs CGO_ENABLED=1 and the cross-compiler toolchains for diff --git a/ci/install/before_install.sh b/ci/install/before_install.sh index 7ef35697c92..8fa50b5e9c2 100755 --- a/ci/install/before_install.sh +++ b/ci/install/before_install.sh @@ -1,7 +1,7 @@ #!/bin/bash # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 echo "Creating user and group..." GROUPNAME="tyk" diff --git a/ci/install/post_install.sh b/ci/install/post_install.sh index 956244a821e..c1b539bcaf2 100755 --- a/ci/install/post_install.sh +++ b/ci/install/post_install.sh @@ -2,7 +2,7 @@ # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 # If "True" the install directory ownership will be changed to "tyk:tyk" change_ownership="False" diff --git a/ci/install/post_remove.sh b/ci/install/post_remove.sh index 417bc666069..aa18036444e 100755 --- a/ci/install/post_remove.sh +++ b/ci/install/post_remove.sh @@ -1,7 +1,7 @@ #!/bin/sh # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 cleanRemove() { diff --git a/ci/install/post_trans.sh b/ci/install/post_trans.sh index 14421b6c66f..8debb566b70 100644 --- a/ci/install/post_trans.sh +++ b/ci/install/post_trans.sh @@ -1,7 +1,7 @@ #!/bin/sh # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 if command -V systemctl >/dev/null 2>&1; then if [ ! -f /lib/systemd/system/tyk-gateway.service ]; then diff --git a/ci/terraform/outputs.tf b/ci/terraform/outputs.tf index 00fadadeeb9..882e34bfbd0 100644 --- a/ci/terraform/outputs.tf +++ b/ci/terraform/outputs.tf @@ -1,6 +1,6 @@ # Generated by: gromit policy -# Generated on: Thu Mar 2 07:07:43 UTC 2023 +# Generated on: Mon Mar 13 10:17:48 UTC 2023 From 7c3afff7241b586e3dedf260231f34eabfa8bdf3 Mon Sep 17 00:00:00 2001 From: Esteban Ricardo Mirizio Date: Wed, 15 Mar 2023 14:57:15 -0300 Subject: [PATCH 36/51] TD-1590/0-reviewers (#4869) Update/Sync Releng templates for tyk:master :robot: Beep, boop, this PR has been generated by [automation](https://github.com/TykTechnologies/gromit/blob/master/cmd/policy.go) at 2023-03-14 21:06:06.179839 +0000 UTC This PR updates the gpac engineering specific code in the master branch of tyk, and has been generated because some gpac code was changed, and the corresponding templates were updated. These changes should be limited to the `repo-policy/` directory and `.github/workflows/pac.yml` file. This PR should be manuallt merged once the terraform manifest is applied by the SysE team and all branch protection rules are satisfied. For more information about gpac and release branches please go to [Gpac Release Branches](https://tyktech.atlassian.net/wiki/spaces/EN/pages/1907228677/Release+branches#%5CuD83D%5CuDCD8-Instructions) If you think this was made by mistake, or if merging this might cause any instabilities, please bring it up on Slack or log a jira in the TD project. --------- Co-authored-by: Gromit --- .github/CODEOWNERS | 2 ++ .github/workflows/pac.yml | 4 ++-- {ci/repo-policy => repo-policy}/main.tf | 1 + {ci/repo-policy => repo-policy}/modules/github-repos/repo.tf | 0 .../modules/github-repos/variables.tf | 0 5 files changed, 5 insertions(+), 2 deletions(-) rename {ci/repo-policy => repo-policy}/main.tf (99%) rename {ci/repo-policy => repo-policy}/modules/github-repos/repo.tf (100%) rename {ci/repo-policy => repo-policy}/modules/github-repos/variables.tf (100%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 10046521681..8bf7fde1309 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,5 @@ /ci/ @TykTechnologies/devops .github/workflows/release.yml @TykTechnologies/devops .github/workflows/sync-automation.yml @TykTechnologies/devops +.github/workflows/pac.yml @TykTechnologies/devops +/repo-policy/ @TykTechnologies/devops \ No newline at end of file diff --git a/.github/workflows/pac.yml b/.github/workflows/pac.yml index 328a23cd700..e0dfc498675 100644 --- a/.github/workflows/pac.yml +++ b/.github/workflows/pac.yml @@ -3,10 +3,10 @@ name: Policy as Code on: pull_request: paths: - - ci/repo-policy/** + - repo-policy/** env: - TERRAFORM_DIR: "./ci/repo-policy" + TERRAFORM_DIR: "./repo-policy" jobs: terraform: diff --git a/ci/repo-policy/main.tf b/repo-policy/main.tf similarity index 99% rename from ci/repo-policy/main.tf rename to repo-policy/main.tf index 488ce243449..42e0ca8d387 100644 --- a/ci/repo-policy/main.tf +++ b/repo-policy/main.tf @@ -21,6 +21,7 @@ provider "github" { owner = "TykTechnologies" } + module "tyk" { source = "./modules/github-repos" repo = "tyk" diff --git a/ci/repo-policy/modules/github-repos/repo.tf b/repo-policy/modules/github-repos/repo.tf similarity index 100% rename from ci/repo-policy/modules/github-repos/repo.tf rename to repo-policy/modules/github-repos/repo.tf diff --git a/ci/repo-policy/modules/github-repos/variables.tf b/repo-policy/modules/github-repos/variables.tf similarity index 100% rename from ci/repo-policy/modules/github-repos/variables.tf rename to repo-policy/modules/github-repos/variables.tf From 7f7674e68f4b73437e6a9e4d018c15f48075a1ff Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Fri, 17 Mar 2023 15:05:41 +0530 Subject: [PATCH 37/51] [TT-7538]remove unused code block (#4879) ## Description ## Related Issue ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [x] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --- gateway/api_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gateway/api_test.go b/gateway/api_test.go index 6e7b1c3d71e..31a5c35df1b 100644 --- a/gateway/api_test.go +++ b/gateway/api_test.go @@ -646,11 +646,6 @@ func TestKeyHandler_DeleteKeyWithQuota(t *testing.T) { }} }) - withAccess := CreateStandardSession() - withAccess.AccessRights = map[string]user.AccessDefinition{testAPIID: { - APIID: testAPIID, - }} - authHeaders := map[string]string{ "authorization": key, } From c48d31436053c628348f69c588b1dbe2193465c1 Mon Sep 17 00:00:00 2001 From: Tomas Buchaillot Date: Tue, 21 Mar 2023 14:58:46 +0100 Subject: [PATCH 38/51] TT-8265 Update pump and graphql-go-tools dependency (#4876) ## Description Update pump dependency to 1.8.0-rc1 Update graphql-go-tools dependency to latest master. This drops the https://dso.docker.com/cve/CVE-2021-4238 vulnerability. ## Related Issue https://tyktech.atlassian.net/browse/TT-8265 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --- go.mod | 18 ++++++++++++++--- go.sum | 63 ++++++++++++++-------------------------------------------- 2 files changed, 30 insertions(+), 51 deletions(-) diff --git a/go.mod b/go.mod index 8d12987db0d..f0947065041 100644 --- a/go.mod +++ b/go.mod @@ -12,22 +12,25 @@ require ( github.com/TykTechnologies/drl v0.0.0-20221208085827-9bc9b4338f26 github.com/TykTechnologies/goautosocket v0.0.0-20190430121222-97bfa5e7e481 github.com/TykTechnologies/gojsonschema v0.0.0-20170222154038-dcb3e4bb7990 - github.com/TykTechnologies/gorpc v0.0.0-20190515174534-b9c10befc5f4 + github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 github.com/TykTechnologies/goverify v0.0.0-20220808203004-1486f89e7708 - github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230214130715-aa076c16772f + github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230320143102-7a16078ce517 github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 github.com/TykTechnologies/openid2go v0.1.2 - github.com/TykTechnologies/tyk-pump v1.7.0-rc1 + github.com/TykTechnologies/tyk-pump v1.8.0-rc4 github.com/akutz/memconn v0.1.0 github.com/bshuster-repo/logrus-logstash-hook v0.4.1 github.com/buger/jsonparser v1.1.1 github.com/cenk/backoff v2.2.1+incompatible github.com/cenkalti/backoff/v4 v4.0.2 + github.com/certifi/gocertifi v0.0.0-20190905060710-a5e0173ced67 // indirect github.com/clbanning/mxj v1.8.4 github.com/evalphobia/logrus_sentry v0.8.2 + github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/gemnasium/logrus-graylog-hook v2.0.7+incompatible github.com/getkin/kin-openapi v0.89.0 + github.com/getsentry/raven-go v0.2.0 // indirect github.com/go-redis/redis/v8 v8.11.5 github.com/gocraft/health v0.0.0-20170925182251-8675af27fef0 github.com/gofrs/uuid v3.3.0+incompatible @@ -37,6 +40,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/consul/api v1.3.0 + github.com/hashicorp/go-msgpack v0.5.4 // indirect github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.4.0 github.com/hashicorp/vault/api v1.0.4 @@ -45,7 +49,9 @@ require ( github.com/jensneuse/abstractlogger v0.0.4 github.com/justinas/alice v1.2.0 github.com/kelseyhightower/envconfig v1.4.0 + github.com/lonelycode/go-uuid v0.0.0-20141202165402-ed3ca8a15a93 // indirect github.com/lonelycode/osin v0.0.0-20160423095202-da239c9dacb6 + github.com/mavricknz/asn1-ber v0.0.0-20151103223136-b9df1c2f4213 // indirect github.com/mavricknz/ldap v0.0.0-20160227184754-f5a958005e43 github.com/miekg/dns v1.0.14 github.com/mitchellh/copystructure v1.2.0 // indirect @@ -57,6 +63,7 @@ require ( github.com/openzipkin/zipkin-go v0.2.2 github.com/oschwald/maxminddb-golang v1.5.0 github.com/paulbellamy/ratecounter v0.2.0 + github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea // indirect github.com/pires/go-proxyproto v0.0.0-20190615163442-2c19fd512994 github.com/pmylund/go-cache v2.1.0+incompatible github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d @@ -69,14 +76,19 @@ require ( github.com/uber/jaeger-lib v2.4.2-0.20210604143007-135cf5605a6d+incompatible // indirect github.com/valyala/fasthttp v1.43.0 // test github.com/vmihailenco/msgpack v4.0.4+incompatible + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonschema v1.2.0 + github.com/xenolf/lego v0.3.2-0.20170618175828-28ead50ff1ca // indirect golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 + golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/net v0.0.0-20220906165146-f3363e06e74c golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 google.golang.org/grpc v1.36.0 google.golang.org/grpc/examples v0.0.0-20220317213542-f95b001a48df // test gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 + gopkg.in/sourcemap.v1 v1.0.5 // indirect + gopkg.in/square/go-jose.v1 v1.1.2 // indirect gopkg.in/vmihailenco/msgpack.v2 v2.9.1 gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 1b89f8dc8cd..1b80fac9b02 100644 --- a/go.sum +++ b/go.sum @@ -31,7 +31,6 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo= github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= -github.com/Jeffail/tunny v0.0.0-20171107125207-452a8e97d6a3/go.mod h1:BX3q3G70XX0UmIkDWfDHoDRquDS1xFJA5VTbMf+14wM= github.com/Jeffail/tunny v0.1.4 h1:chtpdz+nUtaYQeCKlNBg6GycFF/kGVHOr6A3cmzTJXs= github.com/Jeffail/tunny v0.1.4/go.mod h1:P8xAx4XQl0xsuhjX1DtfaMDCSuavzdb2rwbd0lk+fvo= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -45,7 +44,6 @@ github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZC github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -61,7 +59,6 @@ github.com/TykTechnologies/again v0.0.0-20190805133618-6ad301e7eaed h1:/h52kySW0 github.com/TykTechnologies/again v0.0.0-20190805133618-6ad301e7eaed/go.mod h1:OUrgdjjCoYX2GZY9Vathb4ExCO9WuPtU1piuOpNw19Q= github.com/TykTechnologies/circuitbreaker v2.2.2+incompatible h1:UgJdsV/fBL5Ctx43EGKFxkX0W3MqqeRruadDQ1Kzhn4= github.com/TykTechnologies/circuitbreaker v2.2.2+incompatible/go.mod h1:f2+J36wN08/zLudMnO+QaqaBhTdQuIqemtaeEQbhMEM= -github.com/TykTechnologies/drl v0.0.0-20190905191955-cc541aa8e3e1/go.mod h1:dLW6S3KuurRuyluxy33i57uYuTB1s/u+L8mCT0fqb98= github.com/TykTechnologies/drl v0.0.0-20221208085827-9bc9b4338f26 h1:1vrig7Ce10JAi5oosaHcQaiH6KdxlnbB9yG/0fBrMiA= github.com/TykTechnologies/drl v0.0.0-20221208085827-9bc9b4338f26/go.mod h1:F3Ff6Ih4t1i/etqNJHI2K3jverRhNl759J/ra95/3Hk= github.com/TykTechnologies/goautosocket v0.0.0-20190430121222-97bfa5e7e481 h1:fPcSnu5/IBgyqf73GHA99QuwMDbfWH+L4BEX9EZ5kUo= @@ -70,24 +67,20 @@ github.com/TykTechnologies/gojsonschema v0.0.0-20170222154038-dcb3e4bb7990 h1:CJ github.com/TykTechnologies/gojsonschema v0.0.0-20170222154038-dcb3e4bb7990/go.mod h1:SQT0NBrY4/pMikBgwFIrWCjcHBxg015Y8is0kAnMtug= github.com/TykTechnologies/gorm v1.20.7-0.20210409171139-b5c340f85ed0 h1:Q08OD/xvO1D3rzdtlwCMsPB4XOKeHMVw8FifVWEYHiM= github.com/TykTechnologies/gorm v1.20.7-0.20210409171139-b5c340f85ed0/go.mod h1:l/HFwXrJOl2N+sth1mqa2cd0Gx1Cqb1FRYBLhY1TIJw= -github.com/TykTechnologies/gorpc v0.0.0-20190515174534-b9c10befc5f4 h1:hTjM5Uubg3w9VjNc8WjrDrLiGX14Ih8/ItyXEn2tNUs= -github.com/TykTechnologies/gorpc v0.0.0-20190515174534-b9c10befc5f4/go.mod h1:vqhQRhIHefD4jdFo55j+m0vD5NMjx2liq/ubnshQpaY= -github.com/TykTechnologies/goverify v0.0.0-20160822133757-7ccc57452ade/go.mod h1:mkS8jKcz8otdfEXhJs1QQ/DKoIY1NFFsRPKS0RwQENI= +github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 h1:fbxHiuw/244CQ4TEirzgL/CIMXDUx2szZn8cuuMlCy0= +github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9/go.mod h1:v6v7Mlj08+EmEcXOfpuTxGt2qYU9yhqqtv4QF9Wf50E= github.com/TykTechnologies/goverify v0.0.0-20220808203004-1486f89e7708 h1:cmXjlMzcexhc/Cg+QB/c2CPUVs1ux9xn6162qaf/LC4= github.com/TykTechnologies/goverify v0.0.0-20220808203004-1486f89e7708/go.mod h1:mkS8jKcz8otdfEXhJs1QQ/DKoIY1NFFsRPKS0RwQENI= -github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230214130715-aa076c16772f h1:Kj4P1cimBHYF/DMeUizT/yPBeY/aZLgtmAfMzIXPN/M= -github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230214130715-aa076c16772f/go.mod h1:ngqwmltoDadD4ROYMPCTD55Zby0urWj1xVeJmlXGTuc= +github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230320143102-7a16078ce517 h1:EtNbr8wZPmSBtUKjE2S74bAYeJAJzW5CqJNewSz12sQ= +github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230320143102-7a16078ce517/go.mod h1:ZiFZcrue3+n2mHH+KLHRipbYVULkgy3Myko5S7IIs74= github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c h1:j6fd0Fz1R4oSWOmcooGjrdahqrML+btQ+PfEJw8SzbA= github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c/go.mod h1:GnHUbsQx+ysI10osPhUdTmsxcE7ef64cVp38Fdyd7e0= -github.com/TykTechnologies/murmur3 v0.0.0-20180602122059-1915e687e465/go.mod h1:sqH/SPFr11m9cahie7ulBuBX9TOhfBX1sp+qf9jh3Vg= github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 h1:T5NWziFusj8au5nxAqMMh/bZyX9CAyYnBkaMSsfH6BA= github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632/go.mod h1:UsPYgOFBpNzDXLEti7MKOwHLpVSqdzuNGkVFPspQmnQ= -github.com/TykTechnologies/openid2go v0.0.0-20200122120050-1b642583380a/go.mod h1:rGlqNE4CvxZIeiHp0mgrw+/jdGSjJzkZ0n78hhHMdfM= github.com/TykTechnologies/openid2go v0.1.2 h1:WXctksOahA/epTVVvbn9iNUuMXKRr0ksrF4dY9KW8o8= github.com/TykTechnologies/openid2go v0.1.2/go.mod h1:gYfkqeWa+lY3Xz/Z2xYtIzmYXynlgKZaBIbPCqdcdMA= -github.com/TykTechnologies/tyk v0.0.0-20200207055804-cf1d1ad81206/go.mod h1:+WNQ0t1t4ZCh0Z+mDnnyNAQZc5hVJ490iqLOWKPLIMI= -github.com/TykTechnologies/tyk-pump v1.7.0-rc1 h1:8MPqBmqVe48Uu8wQSm8gdDXUzJMfy01Miu6hCwVDo/8= -github.com/TykTechnologies/tyk-pump v1.7.0-rc1/go.mod h1:4GScGRFI2t5iZj9+LlFKEOmnTY0UZ1e/FO5+4gPbKVI= +github.com/TykTechnologies/tyk-pump v1.8.0-rc4 h1:odhM/skFNYWBdSL+atx49f6/PleNRq6l4E9SyeDfmTg= +github.com/TykTechnologies/tyk-pump v1.8.0-rc4/go.mod h1:dTxcW0leM1oHhMJOxM784iTuWe+D2SnffAEhmHPZOug= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= @@ -111,6 +104,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asyncapi/converter-go v0.0.0-20190802111537-d8459b2bd403 h1:HhobldPK/8Cnhliwl744pXSfyjtF956/vOJLyJ8mrXk= github.com/asyncapi/converter-go v0.0.0-20190802111537-d8459b2bd403/go.mod h1:mpJYWYy+USNiLENQxiyGgRc3qtFPxYSWdSd/eS+R6bo= github.com/asyncapi/parser-go v0.4.2 h1:+PPo+Sk/u9IPH3JklXFrPUQnr9LURwNJU5jfcLBBITc= @@ -121,6 +115,7 @@ github.com/aws/aws-sdk-go v1.29.11/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTg github.com/aws/aws-sdk-go v1.40.32/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v1.10.0/go.mod h1:U/EyyVvKtzmFeQQcca7eBotKdlpcP2zzU6bXBYcf7CE= github.com/aws/aws-sdk-go-v2 v1.11.2/go.mod h1:SQfA+m2ltnu1cA0soUkj4dRSsmITiVQUJvBIZjzfPyQ= +github.com/aws/aws-sdk-go-v2 v1.16.14/go.mod h1:s/G+UV29dECbF5rf+RNj1xhlmvoNurGSr+McVSRj59w= github.com/aws/aws-sdk-go-v2/config v1.9.0/go.mod h1:qhK5NNSgo9/nOSMu3HyE60WHXZTWTHTgd5qtIF44vOQ= github.com/aws/aws-sdk-go-v2/credentials v1.5.0/go.mod h1:kvqTkpzQmzri9PbsiTY+LvwFzM0gY19emlAWwBOJMb0= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.7.0/go.mod h1:KqEkRkxm/+1Pd/rENRNbQpfblDBYeg5HDSqjB6ks8hA= @@ -134,6 +129,7 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.8.0/go.mod h1:dOlm91B439le5y1vtPCk5y github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.9.0/go.mod h1:VN4yDJwgYOO6AzHPE8+QeBwK6wUMOFkSCogZFWifdVc= github.com/aws/smithy-go v1.8.1/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.9.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aws/smithy-go v1.13.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/beeker1121/goque v0.0.0-20170321141813-4044bc29b280/go.mod h1:L6dOWBhDOnxUVQsb0wkLve0VCnt2xJW/MI8pdRX4ANw= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -146,7 +142,6 @@ github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+Wji github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= @@ -175,7 +170,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= @@ -217,7 +211,6 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/emanoelxavier/openid2go v0.0.0-20190718021401-6345b638bfc9/go.mod h1:hahZBazACLtwLVO5XoLT8pPXTGfRt5bK6XddHEy/XUk= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -228,18 +221,14 @@ github.com/evalphobia/logrus_sentry v0.8.2 h1:dotxHq+YLZsT1Bb45bB5UQbfCh3gM/nFFe github.com/evalphobia/logrus_sentry v0.8.2/go.mod h1:pKcp+vriitUqu9KiWj/VRFbRfFNUwz95/UkgG8a6MNc= github.com/evanphx/json-patch/v5 v5.1.0 h1:B0aXl1o/1cP8NbviYiBMkcHBtUjIJ1/Ccg6b+SwCLQg= github.com/evanphx/json-patch/v5 v5.1.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= -github.com/facebookgo/pidfile v0.0.0-20150612191647-f242e2999868/go.mod h1:3Hzo46xzfVpIdv4lJw7YBp9fUJ7HpUgbjH1fFDgy4qM= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/franela/goblin v0.0.0-20181003173013-ead4ad1d2727/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -284,8 +273,6 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-redis/redis v6.15.6+incompatible h1:H9evprGPLI8+ci7fxQx6WNZHJSb7be8FqJQRhdQZ5Sg= -github.com/go-redis/redis v6.15.6+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v8 v8.3.1/go.mod h1:a2xkpBM7NJUN5V5kiF46X5Ltx4WeXJ9757X/ScKUBdE= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= @@ -364,8 +351,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -382,7 +370,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= @@ -447,7 +434,6 @@ github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvh github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/huandu/xstrings v1.2.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= @@ -456,7 +442,6 @@ github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHL github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= @@ -542,6 +527,7 @@ github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -557,7 +543,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/justinas/alice v0.0.0-20171023064455-03f45bd4b7da/go.mod h1:oLH0CmIaxCGXD67VKGR5AacGXZSMznlmeqM8RzPrcY8= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= @@ -620,7 +605,6 @@ github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/V github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -644,8 +628,6 @@ github.com/mavricknz/asn1-ber v0.0.0-20151103223136-b9df1c2f4213 h1:3DongGRjJZvI github.com/mavricknz/asn1-ber v0.0.0-20151103223136-b9df1c2f4213/go.mod h1:v/ZufymxjcI3pnNmQIUQQKxnHLTblrjZ4MNLs5DrZ1o= github.com/mavricknz/ldap v0.0.0-20160227184754-f5a958005e43 h1:x4SDcUPDTMzuFEdWe5lTznj1echpsd0ApTkZOdwtm7g= github.com/mavricknz/ldap v0.0.0-20160227184754-f5a958005e43/go.mod h1:z76yvVwVulPd8FyifHe8UEHeud6XXaSan0ibi2sDy6w= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= @@ -804,6 +786,7 @@ github.com/r3labs/sse/v2 v2.8.1/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEm github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/resurfaceio/logger-go/v3 v3.2.1/go.mod h1:YPcxFUcloW37F1WQA9MUcGWu2JzlvBxlCfFF5+T3GO8= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/robertkowalski/graylog-golang v0.0.0-20151121031040-e5295cfa2827/go.mod h1:jONcYFk83vUF1lv0aERAwaFtDM9wUW4BMGmlnpLJyZY= github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d h1:1VUlQbCfkoSGv7qP7Y+ro3ap1P1pPZxgdGVqiTVy5C4= @@ -821,7 +804,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= @@ -899,11 +881,8 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso github.com/tidwall/sjson v1.0.4 h1:UcdIRXff12Lpnu3OLtZvnc03g4vH2suXDXhBwBqmzYg= github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/uber/jaeger-client-go v2.19.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-client-go v2.30.1-0.20220110192849-8d8e8fcfd04d+incompatible h1:Nriupf3YYKvA5oxiej8Bb+ao/oanuw9lvahjIQc4nEc= github.com/uber/jaeger-client-go v2.30.1-0.20220110192849-8d8e8fcfd04d+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/uber/jaeger-lib v2.4.2-0.20210604143007-135cf5605a6d+incompatible h1:73eb49SfAfRZEhxIKR0tz5MUMu2zjJxJUZlFCHInV34= github.com/uber/jaeger-lib v2.4.2-0.20210604143007-135cf5605a6d+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= @@ -927,8 +906,6 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= -github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/scram v1.0.3/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= @@ -939,7 +916,6 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMc github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20171025060643-212d8a0df7ac/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xenolf/lego v0.3.2-0.20170618175828-28ead50ff1ca h1:HmO0j2gywlGvJEtnSRqupP2pNb2Uoue+Et3efiOLWN8= @@ -948,7 +924,6 @@ github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6Ut github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -989,7 +964,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1032,7 +1006,6 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -1053,12 +1026,10 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -1117,7 +1088,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1156,7 +1126,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1207,7 +1176,6 @@ golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200623185156-456ad74e1464/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= @@ -1264,7 +1232,6 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1286,9 +1253,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/Masterminds/sprig.v2 v2.21.0/go.mod h1:DtHmW+kdrJpYMY6Mk6OHFNi/8EBAnNYVRUffwRCNHgA= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= From 77fefea88e02e371537d428461cfc3c6c1749106 Mon Sep 17 00:00:00 2001 From: itachi sasuke <8012032+Keithwachira@users.noreply.github.com> Date: Wed, 22 Mar 2023 03:41:11 +0300 Subject: [PATCH 39/51] Send an event to the tyk docs repo on config update (#4881) ## Description - Whenever the config.go is updated an event will be sent to the tyk docs repo and this will generate the docs. Previously this was done manually but this pull request will automate it. Issue : https://tyktech.atlassian.net/browse/TT-8253 ## Related Issue https://tyktech.atlassian.net/browse/TT-8253 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [X] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [X ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --- .github/workflows/update-config-docs.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/update-config-docs.yml diff --git a/.github/workflows/update-config-docs.yml b/.github/workflows/update-config-docs.yml new file mode 100644 index 00000000000..816324ba819 --- /dev/null +++ b/.github/workflows/update-config-docs.yml @@ -0,0 +1,23 @@ +# Keep the docs on tyk-docs in sync with this branch's docs + +name: Update Config docs + +on: + push: + branches: + - master + - release-** + paths: + - config/config.go + +jobs: + sync: + name: tyk-config-docs + runs-on: ubuntu-latest + steps: + - uses: peter-evans/repository-dispatch@v1 + with: + token: ${{ secrets.ORG_GH_TOKEN }} + repository: TykTechnologies/tyk-docs + event-type: tyk-config-docs + client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}","repo":"gateway","branch":"${{github.ref_name}}"}' From 970bbe15ec3a93dced4dfca611163d1e50e54b22 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Wed, 22 Mar 2023 15:48:40 +0100 Subject: [PATCH 40/51] [TT-7974] Deprecate LE and remove deprecated package implementation (#4884) ## Description Removes letsencrypt package and related code due to it being obsolete/unused. Deprecates the related config field with a comment change. ## Related Issue https://tyktech.atlassian.net/browse/TT-7974 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --------- Co-authored-by: Tit Petric --- config/config.go | 3 -- gateway/le_helpers.go | 106 --------------------------------------- gateway/redis_signals.go | 3 -- gateway/server.go | 8 --- go.mod | 3 -- go.sum | 6 --- 6 files changed, 129 deletions(-) delete mode 100644 gateway/le_helpers.go diff --git a/config/config.go b/config/config.go index cabb316c7d4..32cc5d80854 100644 --- a/config/config.go +++ b/config/config.go @@ -359,9 +359,6 @@ type HttpServerOptionsConfig struct { // Set to true to enable SSL connections UseSSL bool `json:"use_ssl"` - // Enable Lets-Encrypt support - UseLE_SSL bool `json:"use_ssl_le"` - // Enable HTTP2 protocol handling EnableHttp2 bool `json:"enable_http2"` diff --git a/gateway/le_helpers.go b/gateway/le_helpers.go deleted file mode 100644 index f7fd1db4cf6..00000000000 --- a/gateway/le_helpers.go +++ /dev/null @@ -1,106 +0,0 @@ -package gateway - -import ( - "encoding/json" - - "rsc.io/letsencrypt" - - "github.com/sirupsen/logrus" - - "github.com/TykTechnologies/tyk/storage" -) - -const LEKeyPrefix = "le_ssl:" - -func (gw *Gateway) StoreLEState(m *letsencrypt.Manager) { - log.Debug("Storing SSL backup") - - log.Debug("[SSL] --> Connecting to DB") - - store := storage.RedisCluster{KeyPrefix: LEKeyPrefix, RedisController: gw.RedisController} - connected := store.Connect() - - log.Debug("--> Connected to DB") - - if !connected { - log.Error("[SSL] --> SSL Backup save failed: redis connection failed") - return - } - - state := m.Marshal() - secret := rightPad2Len(gw.GetConfig().Secret, "=", 32) - cryptoText := encrypt([]byte(secret), state) - - if err := store.SetKey("cache", cryptoText, -1); err != nil { - log.Error("[SSL] --> Failed to store SSL backup: ", err) - return - } -} - -func (gw *Gateway) GetLEState(m *letsencrypt.Manager) { - checkKey := "cache" - - store := storage.RedisCluster{KeyPrefix: LEKeyPrefix, RedisController: gw.RedisController} - - connected := store.Connect() - log.Debug("[SSL] --> Connected to DB") - - if !connected { - log.Error("[SSL] --> SSL Backup recovery failed: redis connection failed") - return - } - - cryptoText, err := store.GetKey(checkKey) - if err != nil { - log.Warning("[SSL] --> No SSL backup: ", err) - return - } - - secret := rightPad2Len(gw.GetConfig().Secret, "=", 32) - sslState := decrypt([]byte(secret), cryptoText) - - m.Unmarshal(sslState) -} - -type LE_ServerInfo struct { - HostName string - ID string -} - -func (gw *Gateway) onLESSLStatusReceivedHandler(payload string) { - serverData := LE_ServerInfo{} - if err := json.Unmarshal([]byte(payload), &serverData); err != nil { - log.WithFields(logrus.Fields{ - "prefix": "pub-sub", - }).Error("Failed unmarshal server data: ", err) - return - } - - log.Debug("Received LE data: ", serverData) - - // not great - if serverData.ID != gw.GetNodeID() { - log.Info("Received Redis LE change notification!") - gw.GetLEState(&gw.LE_MANAGER) - } - - log.Info("Received Redis LE change notification from myself, ignoring") - -} - -func (gw *Gateway) StartPeriodicStateBackup(m *letsencrypt.Manager) { - watch := m.Watch() - - for { - select { - case <-gw.ctx.Done(): - return - case <-watch: - if gw.LE_FIRSTRUN { - log.Info("[SSL] State change detected, storing") - gw.StoreLEState(m) - } - gw.LE_FIRSTRUN = true - } - } -} diff --git a/gateway/redis_signals.go b/gateway/redis_signals.go index 227ccf705b5..5ff892e941f 100644 --- a/gateway/redis_signals.go +++ b/gateway/redis_signals.go @@ -32,7 +32,6 @@ const ( NoticeDashboardConfigRequest NotificationCommand = "NoticeDashboardConfigRequest" NoticeGatewayConfigResponse NotificationCommand = "NoticeGatewayConfigResponse" NoticeGatewayDRLNotification NotificationCommand = "NoticeGatewayDRLNotification" - NoticeGatewayLENotification NotificationCommand = "NoticeGatewayLENotification" KeySpaceUpdateNotification NotificationCommand = "KeySpaceUpdateNotification" ) @@ -126,8 +125,6 @@ func (gw *Gateway) handleRedisEvent(v interface{}, handled func(NotificationComm return } gw.onServerStatusReceivedHandler(notif.Payload) - case NoticeGatewayLENotification: - gw.onLESSLStatusReceivedHandler(notif.Payload) case NoticeApiUpdated, NoticeApiRemoved, NoticeApiAdded, NoticePolicyChanged, NoticeGroupReload: pubSubLog.Info("Reloading endpoints") gw.reloadURLStructure(reloaded) diff --git a/gateway/server.go b/gateway/server.go index c40e23cf403..2bf946f41bb 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -36,7 +36,6 @@ import ( "github.com/pmylund/go-cache" "github.com/sirupsen/logrus" logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" - "rsc.io/letsencrypt" "github.com/TykTechnologies/tyk/internal/uuid" @@ -146,9 +145,6 @@ type Gateway struct { consulKVStore kv.Store vaultKVStore kv.Store - LE_MANAGER letsencrypt.Manager - LE_FIRSTRUN bool - NotificationVerifier goverify.Verifier RedisPurgeOnce sync.Once @@ -1257,10 +1253,6 @@ func (gw *Gateway) initialiseSystem() error { gw.InitializeRPCCache() gw.setupInstrumentation() - if gw.GetConfig().HttpServerOptions.UseLE_SSL { - go gw.StartPeriodicStateBackup(&gw.LE_MANAGER) - } - // cleanIdleMemConnProviders checks memconn.Provider (a part of internal API handling) // instances periodically and deletes idle items, closes net.Listener instances to // free resources. diff --git a/go.mod b/go.mod index f0947065041..c07780b2ae7 100644 --- a/go.mod +++ b/go.mod @@ -78,7 +78,6 @@ require ( github.com/vmihailenco/msgpack v4.0.4+incompatible github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonschema v1.2.0 - github.com/xenolf/lego v0.3.2-0.20170618175828-28ead50ff1ca // indirect golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/net v0.0.0-20220906165146-f3363e06e74c @@ -88,11 +87,9 @@ require ( gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 gopkg.in/sourcemap.v1 v1.0.5 // indirect - gopkg.in/square/go-jose.v1 v1.1.2 // indirect gopkg.in/vmihailenco/msgpack.v2 v2.9.1 gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc gopkg.in/yaml.v3 v3.0.1 - rsc.io/letsencrypt v0.0.2 ) replace gorm.io/gorm => github.com/TykTechnologies/gorm v1.20.7-0.20210409171139-b5c340f85ed0 diff --git a/go.sum b/go.sum index 1b80fac9b02..4273895719f 100644 --- a/go.sum +++ b/go.sum @@ -918,8 +918,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xenolf/lego v0.3.2-0.20170618175828-28ead50ff1ca h1:HmO0j2gywlGvJEtnSRqupP2pNb2Uoue+Et3efiOLWN8= -github.com/xenolf/lego v0.3.2-0.20170618175828-28ead50ff1ca/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= @@ -1277,8 +1275,6 @@ gopkg.in/olivere/elastic.v5 v5.0.85/go.mod h1:M3WNlsF+WhYn7api4D87NIflwTV/c0iVs8 gopkg.in/olivere/elastic.v6 v6.2.31/go.mod h1:2cTT8Z+/LcArSWpCgvZqBgt3VOqXiy7v00w12Lz8bd4= gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= -gopkg.in/square/go-jose.v1 v1.1.2 h1:/5jmADZB+RiKtZGr4HxsEFOEfbfsjTKsVnqpThUpE30= -gopkg.in/square/go-jose.v1 v1.1.2/go.mod h1:QpYS+a4WhS+DTlyQIi6Ka7MS3SuR9a055rgXNEe6EiA= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -1311,8 +1307,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/letsencrypt v0.0.2 h1:CWRvaqcmyyWMhhhGes73TvuIjf7O3Crq6F+Xid/cWNI= -rsc.io/letsencrypt v0.0.2/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From 729938e0a9c172dfc10ce073ea3a654213509c1e Mon Sep 17 00:00:00 2001 From: Esteban Ricardo Mirizio Date: Thu, 23 Mar 2023 15:01:41 -0300 Subject: [PATCH 41/51] Fix release workflow (#4896) Issue araised after updating goreleaser image after 3 months --- .github/workflows/release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cdc4b2b6a9a..6836016b033 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -59,6 +59,10 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 1 + + - name: "Add Git safe.directory" + run: git config --global --add safe.directory $GITHUB_WORKSPACE + - uses: docker/setup-qemu-action@v2 - uses: docker/setup-buildx-action@v2 From b04e9a869301650fcf02be7d57da471394209fe6 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 24 Mar 2023 13:10:02 +0100 Subject: [PATCH 42/51] Add new config flag to enable distributed tracing (#4902) Adds `enable_distributed_tracing` (boolean) under the new relic configuration, update usage. https://tyktech.atlassian.net/browse/TT-4209 Co-authored-by: Tit Petric --- cli/linter/schema.json | 3 +++ config/config.go | 2 ++ gateway/newrelic.go | 12 +++++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/cli/linter/schema.json b/cli/linter/schema.json index 605ab4ba949..206c0d455e1 100644 --- a/cli/linter/schema.json +++ b/cli/linter/schema.json @@ -1120,6 +1120,9 @@ }, "license_key": { "type": "string" + }, + "enable_distributed_tracing": { + "type": "boolean" } } }, diff --git a/config/config.go b/config/config.go index 32cc5d80854..5fdf91de866 100644 --- a/config/config.go +++ b/config/config.go @@ -491,6 +491,8 @@ type NewRelicConfig struct { AppName string `json:"app_name"` // New Relic License key LicenseKey string `json:"license_key"` + // Enable distributed tracing + EnableDistributedTracing bool `json:"enable_distributed_tracing"` } type Tracer struct { diff --git a/gateway/newrelic.go b/gateway/newrelic.go index 2d00bbde074..513ddd577a3 100644 --- a/gateway/newrelic.go +++ b/gateway/newrelic.go @@ -13,15 +13,21 @@ import ( // SetupNewRelic creates new newrelic.Application instance func (gw *Gateway) SetupNewRelic() (app newrelic.Application) { - var err error + var ( + err error + gwConfig = gw.GetConfig() + ) + logger := log.WithFields(logrus.Fields{"prefix": "newrelic"}) logger.Info("Initializing NewRelic...") - cfg := newrelic.NewConfig(gw.GetConfig().NewRelic.AppName, gw.GetConfig().NewRelic.LicenseKey) - if gw.GetConfig().NewRelic.AppName != "" { + cfg := newrelic.NewConfig(gwConfig.NewRelic.AppName, gwConfig.NewRelic.LicenseKey) + if gwConfig.NewRelic.AppName != "" { cfg.Enabled = true } + cfg.DistributedTracer.Enabled = gwConfig.NewRelic.EnableDistributedTracing + cfg.Logger = &newRelicLogger{logger} if app, err = newrelic.NewApplication(cfg); err != nil { From 35a6e7476c423118822b42f707bad3ac8914ecc3 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Fri, 24 Mar 2023 18:09:52 +0530 Subject: [PATCH 43/51] [TT-8351] fix jwt_scope_to_policy_mapping backwards compatibility. (#4895) ## Description This PRs fixes broken backward compatibility for `jwt_scope_to_policy_mapping`. - added migration to new fields in `Migrate()` method. - keeping `jwt_scope_to_policy_mapping` to be considered in middlewares when the new fields (`scopes.jwt` / `scopes.oidc`) are not configured. ## Related Issue https://tyktech.atlassian.net/browse/TT-8351 ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [x] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --- apidef/api_definition_test.go | 245 +++++++++++++++++++++++++--------- apidef/api_definitions.go | 42 +++--- apidef/migration.go | 18 +++ apidef/migration_test.go | 43 ++++++ gateway/mw_jwt.go | 6 +- gateway/mw_openid.go | 6 +- 6 files changed, 274 insertions(+), 86 deletions(-) diff --git a/apidef/api_definition_test.go b/apidef/api_definition_test.go index d5f50c4ddb2..d1b168c3d65 100644 --- a/apidef/api_definition_test.go +++ b/apidef/api_definition_test.go @@ -29,37 +29,6 @@ func TestSchema(t *testing.T) { } func TestEncodeForDB(t *testing.T) { - t.Run("update ScopeClaim when Scopes.JWT is not empty and OIDC is not enabled", func(t *testing.T) { - spec := DummyAPI() - defaultScopeName := "scope" - scopeToPolicyMap := map[string]string{ - "user:read": "pID1", - } - spec.Scopes.JWT = ScopeClaim{ - ScopeClaimName: defaultScopeName, - ScopeToPolicy: scopeToPolicyMap, - } - spec.EncodeForDB() - assert.Equal(t, defaultScopeName, spec.JWTScopeClaimName) - assert.Equal(t, scopeToPolicyMap, spec.JWTScopeToPolicyMapping) - }) - - t.Run("update ScopeClaim when Scopes.OIDC is not empty and OpenID is enabled", func(t *testing.T) { - spec := DummyAPI() - defaultScopeName := "scope" - scopeToPolicyMap := map[string]string{ - "user:read": "pID1", - } - spec.Scopes.OIDC = ScopeClaim{ - ScopeClaimName: defaultScopeName, - ScopeToPolicy: scopeToPolicyMap, - } - spec.UseOpenID = true - spec.EncodeForDB() - assert.Equal(t, defaultScopeName, spec.JWTScopeClaimName) - assert.Equal(t, scopeToPolicyMap, spec.JWTScopeToPolicyMapping) - }) - t.Run("EncodeForDB persist schema objects from extended path", func(t *testing.T) { spec := DummyAPI() spec.EncodeForDB() @@ -74,39 +43,6 @@ func TestEncodeForDB(t *testing.T) { } func TestDecodeFromDB(t *testing.T) { - t.Run("update Scopes.JWT when JWTScopeClaimName is not empty", func(t *testing.T) { - spec := DummyAPI() - defaultScopeName := "scope" - spec.JWTScopeClaimName = defaultScopeName - scopeToPolicyMap := map[string]string{ - "user:read": "pID1", - } - spec.JWTScopeToPolicyMapping = scopeToPolicyMap - spec.DecodeFromDB() - expectedJWTScope := ScopeClaim{ - ScopeClaimName: defaultScopeName, - ScopeToPolicy: scopeToPolicyMap, - } - assert.Equal(t, expectedJWTScope, spec.Scopes.JWT) - }) - - t.Run("update Scopes.OIDC when JWTScopeClaimName is not empty and OpenID is enabled", func(t *testing.T) { - spec := DummyAPI() - defaultScopeName := "scope" - spec.JWTScopeClaimName = defaultScopeName - scopeToPolicyMap := map[string]string{ - "user:read": "pID1", - } - spec.UseOpenID = true - spec.JWTScopeToPolicyMapping = scopeToPolicyMap - spec.DecodeFromDB() - expectedOICScope := ScopeClaim{ - ScopeClaimName: defaultScopeName, - ScopeToPolicy: scopeToPolicyMap, - } - assert.Equal(t, expectedOICScope, spec.Scopes.OIDC) - }) - t.Run("json schema validation middleware", func(t *testing.T) { apiDef := DummyAPI() var ( @@ -185,3 +121,184 @@ func TestAPIDefinition_GenerateAPIID(t *testing.T) { a.GenerateAPIID() assert.NotEmpty(t, a.APIID) } + +func TestAPIDefinition_GetScopeClaimName(t *testing.T) { + var ( + scopeName = "scope" + oidcScopeName = "oidc_scope" + newScopeName = "new_scope" + newOIDCScopeName = "new_oidc_scope" + ) + + getAPIDef := func(deprecatedScopeName, jwtScopeName, oidcScopeName string, useOIDC bool) APIDefinition { + return APIDefinition{ + UseOpenID: useOIDC, + JWTScopeClaimName: deprecatedScopeName, + Scopes: Scopes{ + JWT: ScopeClaim{ + ScopeClaimName: jwtScopeName, + }, + OIDC: ScopeClaim{ + ScopeClaimName: oidcScopeName, + }, + }, + } + } + + testCases := []struct { + name string + deprecatedScopeName string + jwtScopeName string + oidcScopeName string + useOIDC bool + expectedScopeName string + }{ + { + name: "jwt: only deprecated fields", + deprecatedScopeName: scopeName, + expectedScopeName: scopeName, + }, + { + name: "jwt: only scopes.jwt", + jwtScopeName: newScopeName, + expectedScopeName: newScopeName, + }, + { + name: "jwt: both scopes.jwt and scopes.oidc", + jwtScopeName: newScopeName, + oidcScopeName: newOIDCScopeName, + expectedScopeName: newScopeName, + }, + { + name: "jwt: deprecated field and jwt.scopes", + deprecatedScopeName: scopeName, + jwtScopeName: newScopeName, + expectedScopeName: newScopeName, + }, + + { + name: "oidc: only deprecated fields", + deprecatedScopeName: oidcScopeName, + expectedScopeName: oidcScopeName, + useOIDC: true, + }, + { + name: "oidc: only scopes.oidc", + oidcScopeName: newOIDCScopeName, + expectedScopeName: newOIDCScopeName, + useOIDC: true, + }, + { + name: "oidc: both scopes.jwt and scopes.oidc", + jwtScopeName: newScopeName, + oidcScopeName: newOIDCScopeName, + expectedScopeName: newOIDCScopeName, + useOIDC: true, + }, + { + name: "oidc: deprecated field and oidc.scopes", + deprecatedScopeName: oidcScopeName, + oidcScopeName: newOIDCScopeName, + expectedScopeName: newOIDCScopeName, + useOIDC: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + apiDef := getAPIDef(tc.deprecatedScopeName, tc.jwtScopeName, tc.oidcScopeName, tc.useOIDC) + assert.Equal(t, tc.expectedScopeName, apiDef.GetScopeClaimName()) + }) + } +} + +func TestAPIDefinition_GetScopeToPolicyMapping(t *testing.T) { + var ( + scopeToPolicyMapping = map[string]string{"jwtClaim": "pol1"} + newScopeToPolicyMapping = map[string]string{"jwtClaim1": "pol1"} + oidcScopeToPolicyMapping = map[string]string{"oidcClaim": "pol1"} + newOIDCScopeToPolicyMapping = map[string]string{"oidcClaim1": "pol1"} + ) + + getAPIDef := func(deprecatedScopeToPolicy, jwtScopeToPolicy, oidcScopeToPolicy map[string]string, useOIDC bool) APIDefinition { + return APIDefinition{ + UseOpenID: useOIDC, + JWTScopeToPolicyMapping: deprecatedScopeToPolicy, + Scopes: Scopes{ + JWT: ScopeClaim{ + ScopeToPolicy: jwtScopeToPolicy, + }, + OIDC: ScopeClaim{ + ScopeToPolicy: oidcScopeToPolicy, + }, + }, + } + } + + testCases := []struct { + name string + deprecatedScopeToPolicy map[string]string + jwtScopeToPolicy map[string]string + oidcScopeToPolicy map[string]string + useOIDC bool + expectedScopeToPolicy map[string]string + }{ + { + name: "jwt: only deprecated fields", + deprecatedScopeToPolicy: scopeToPolicyMapping, + expectedScopeToPolicy: scopeToPolicyMapping, + }, + { + name: "jwt: only scopes.jwt", + jwtScopeToPolicy: scopeToPolicyMapping, + expectedScopeToPolicy: scopeToPolicyMapping, + }, + { + name: "jwt: both scopes.jwt and scopes.oidc", + jwtScopeToPolicy: scopeToPolicyMapping, + oidcScopeToPolicy: oidcScopeToPolicyMapping, + expectedScopeToPolicy: scopeToPolicyMapping, + }, + { + name: "jwt: deprecated field and jwt.scopes", + deprecatedScopeToPolicy: scopeToPolicyMapping, + jwtScopeToPolicy: newScopeToPolicyMapping, + expectedScopeToPolicy: newScopeToPolicyMapping, + }, + + { + name: "oidc: only deprecated fields", + deprecatedScopeToPolicy: oidcScopeToPolicyMapping, + expectedScopeToPolicy: oidcScopeToPolicyMapping, + useOIDC: true, + }, + { + name: "oidc: only scopes.oidc", + oidcScopeToPolicy: newOIDCScopeToPolicyMapping, + expectedScopeToPolicy: newOIDCScopeToPolicyMapping, + useOIDC: true, + }, + { + name: "oidc: both scopes.jwt and scopes.oidc", + jwtScopeToPolicy: scopeToPolicyMapping, + oidcScopeToPolicy: oidcScopeToPolicyMapping, + expectedScopeToPolicy: oidcScopeToPolicyMapping, + useOIDC: true, + }, + { + name: "oidc: deprecated field and oidc.scopes", + deprecatedScopeToPolicy: oidcScopeToPolicyMapping, + oidcScopeToPolicy: newOIDCScopeToPolicyMapping, + expectedScopeToPolicy: newOIDCScopeToPolicyMapping, + useOIDC: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + apiDef := getAPIDef(tc.deprecatedScopeToPolicy, tc.jwtScopeToPolicy, tc.oidcScopeToPolicy, tc.useOIDC) + assert.Equal(t, tc.expectedScopeToPolicy, apiDef.GetScopeToPolicyMapping()) + }) + } + +} diff --git a/apidef/api_definitions.go b/apidef/api_definitions.go index 5cdcb31ddac..e9de51c5446 100644 --- a/apidef/api_definitions.go +++ b/apidef/api_definitions.go @@ -15,6 +15,8 @@ import ( "github.com/lonelycode/osin" "gopkg.in/mgo.v2/bson" + "github.com/TykTechnologies/tyk/internal/reflect" + "github.com/TykTechnologies/graphql-go-tools/pkg/engine/datasource/kafka_datasource" "github.com/TykTechnologies/graphql-go-tools/pkg/execution/datasource" @@ -965,14 +967,6 @@ func (a *APIDefinition) EncodeForDB() { if a.Auth.AuthHeaderName == "" { a.Auth = a.AuthConfigs["authToken"] } - // JWTScopeToPolicyMapping and JWTScopeClaimName are deprecated and following code ensures backward compatibility - if !a.UseOpenID && a.Scopes.JWT.ScopeClaimName != "" { - a.JWTScopeToPolicyMapping = a.Scopes.JWT.ScopeToPolicy - a.JWTScopeClaimName = a.Scopes.JWT.ScopeClaimName - } else if a.UseOpenID && a.Scopes.OIDC.ScopeClaimName != "" { - a.JWTScopeToPolicyMapping = a.Scopes.OIDC.ScopeToPolicy - a.JWTScopeClaimName = a.Scopes.OIDC.ScopeClaimName - } } func (a *APIDefinition) DecodeFromDB() { @@ -1039,14 +1033,6 @@ func (a *APIDefinition) DecodeFromDB() { makeCompatible("authToken", a.UseStandardAuth) makeCompatible("jwt", a.EnableJWT) - // JWTScopeToPolicyMapping and JWTScopeClaimName are deprecated and following code ensures backward compatibility - if !a.UseOpenID && a.JWTScopeClaimName != "" && a.Scopes.JWT.ScopeClaimName == "" { - a.Scopes.JWT.ScopeToPolicy = a.JWTScopeToPolicyMapping - a.Scopes.JWT.ScopeClaimName = a.JWTScopeClaimName - } else if a.UseOpenID && a.JWTScopeClaimName != "" && a.Scopes.OIDC.ScopeClaimName == "" { - a.Scopes.OIDC.ScopeToPolicy = a.JWTScopeToPolicyMapping - a.Scopes.OIDC.ScopeClaimName = a.JWTScopeClaimName - } } // Expired returns true if this Version has expired @@ -1291,6 +1277,30 @@ func DummyAPI() APIDefinition { } } +func (a *APIDefinition) GetScopeClaimName() string { + if reflect.IsEmpty(a.Scopes) { + return a.JWTScopeClaimName + } + + if a.UseOpenID { + return a.Scopes.OIDC.ScopeClaimName + } + + return a.Scopes.JWT.ScopeClaimName +} + +func (a *APIDefinition) GetScopeToPolicyMapping() map[string]string { + if reflect.IsEmpty(a.Scopes) { + return a.JWTScopeToPolicyMapping + } + + if a.UseOpenID { + return a.Scopes.OIDC.ScopeToPolicy + } + + return a.Scopes.JWT.ScopeToPolicy +} + var Template = template.New("").Funcs(map[string]interface{}{ "jsonMarshal": func(v interface{}) (string, error) { bs, err := json.Marshal(v) diff --git a/apidef/migration.go b/apidef/migration.go index e4423350b72..bb321238848 100644 --- a/apidef/migration.go +++ b/apidef/migration.go @@ -232,6 +232,7 @@ func (a *APIDefinition) Migrate() (versions []APIDefinition, err error) { a.migrateAuthenticationPlugin() a.migrateIDExtractor() a.migrateCustomDomain() + a.migrateScopeToPolicy() versions, err = a.MigrateVersioning() if err != nil { @@ -423,3 +424,20 @@ func (a *APIDefinition) SetDisabledFlags() { } } } + +func (a *APIDefinition) migrateScopeToPolicy() { + scopeClaim := ScopeClaim{ + ScopeClaimName: a.JWTScopeClaimName, + ScopeToPolicy: a.JWTScopeToPolicyMapping, + } + + a.JWTScopeToPolicyMapping = nil + a.JWTScopeClaimName = "" + + if a.UseOpenID { + a.Scopes.OIDC = scopeClaim + return + } + + a.Scopes.JWT = scopeClaim +} diff --git a/apidef/migration_test.go b/apidef/migration_test.go index 0101b2f993c..8740f29d7b0 100644 --- a/apidef/migration_test.go +++ b/apidef/migration_test.go @@ -676,3 +676,46 @@ func TestAPIDefinition_migratePluginConfigData(t *testing.T) { assert.True(t, base.ConfigDataDisabled) } + +func TestAPIDefinition_migrateScopeToPolicy(t *testing.T) { + var ( + scopeName = "scope" + scopeToPolicyMapping = map[string]string{"claim1": "pol1"} + ) + + expectedScopeClaim := ScopeClaim{ + ScopeClaimName: scopeName, + ScopeToPolicy: scopeToPolicyMapping, + } + + check := func(t *testing.T, jwtScopeClaimName string, jwtScopeToPolicyMapping map[string]string, scopeClaim ScopeClaim) { + t.Helper() + assert.Equal(t, expectedScopeClaim, scopeClaim) + assert.Empty(t, jwtScopeClaimName) + assert.Nil(t, jwtScopeToPolicyMapping) + } + + t.Run("jwt", func(t *testing.T) { + apiDef := APIDefinition{ + JWTScopeClaimName: scopeName, + JWTScopeToPolicyMapping: scopeToPolicyMapping, + } + + _, err := apiDef.Migrate() + assert.NoError(t, err) + check(t, apiDef.JWTScopeClaimName, apiDef.JWTScopeToPolicyMapping, apiDef.Scopes.JWT) + }) + + t.Run("oidc", func(t *testing.T) { + apiDef := APIDefinition{ + UseOpenID: true, + JWTScopeClaimName: scopeName, + JWTScopeToPolicyMapping: scopeToPolicyMapping, + } + + _, err := apiDef.Migrate() + assert.NoError(t, err) + check(t, apiDef.JWTScopeClaimName, apiDef.JWTScopeToPolicyMapping, apiDef.Scopes.OIDC) + }) + +} diff --git a/gateway/mw_jwt.go b/gateway/mw_jwt.go index a1cc119b671..c9f9aeb8fda 100644 --- a/gateway/mw_jwt.go +++ b/gateway/mw_jwt.go @@ -521,8 +521,8 @@ func (k *JWTMiddleware) processCentralisedJWT(r *http.Request, token *jwt.Token) } // apply policies from scope if scope-to-policy mapping is specified for this API - if len(k.Spec.Scopes.JWT.ScopeToPolicy) != 0 { - scopeClaimName := k.Spec.Scopes.JWT.ScopeClaimName + if len(k.Spec.GetScopeToPolicyMapping()) != 0 { + scopeClaimName := k.Spec.GetScopeClaimName() if scopeClaimName == "" { scopeClaimName = "scope" } @@ -538,7 +538,7 @@ func (k *JWTMiddleware) processCentralisedJWT(r *http.Request, token *jwt.Token) } // add all policies matched from scope-policy mapping - mappedPolIDs := mapScopeToPolicies(k.Spec.Scopes.JWT.ScopeToPolicy, scope) + mappedPolIDs := mapScopeToPolicies(k.Spec.GetScopeToPolicyMapping(), scope) if len(mappedPolIDs) > 0 { k.Logger().Debugf("Identified policy(s) to apply to this token from scope claim: %s", scopeClaimName) } else { diff --git a/gateway/mw_openid.go b/gateway/mw_openid.go index 40b3cd309e9..84e183dab2e 100644 --- a/gateway/mw_openid.go +++ b/gateway/mw_openid.go @@ -127,7 +127,7 @@ func (k *OpenIDMW) ProcessRequest(w http.ResponseWriter, r *http.Request, _ inte } // decide if we use policy ID from provider client settings or list of policies from scope-policy mapping - useScope := len(k.Spec.Scopes.OIDC.ScopeToPolicy) != 0 + useScope := len(k.Spec.GetScopeToPolicyMapping()) != 0 k.lock.RLock() clientSet, foundIssuer := k.provider_client_policymap[iss.(string)] @@ -181,14 +181,14 @@ func (k *OpenIDMW) ProcessRequest(w http.ResponseWriter, r *http.Request, _ inte if !useScope { policiesToApply = append(policiesToApply, policyID) } else { - scopeClaimName := k.Spec.Scopes.OIDC.ScopeClaimName + scopeClaimName := k.Spec.GetScopeClaimName() if scopeClaimName == "" { scopeClaimName = "scope" } if scope := getScopeFromClaim(token.Claims.(jwt.MapClaims), scopeClaimName); scope != nil { // add all policies matched from scope-policy mapping - policiesToApply = mapScopeToPolicies(k.Spec.Scopes.OIDC.ScopeToPolicy, scope) + policiesToApply = mapScopeToPolicies(k.Spec.GetScopeToPolicyMapping(), scope) } } From e6761d56d53a1deb38b61988cb22611b9a602129 Mon Sep 17 00:00:00 2001 From: Tomas Buchaillot Date: Fri, 24 Mar 2023 15:31:31 +0100 Subject: [PATCH 44/51] TT-8308 Fix/el7 CVE (#4901) ## Description https://tyktech.atlassian.net/browse/TT-8308 Fixes: [Choose the Most Secure Images | Docker Image Vulnerability Database](https://dso.docker.com/cve/CVE-2018-13347) [Choose the Most Secure Images | Docker Image Vulnerability Database](https://dso.docker.com/cve/CVE-2018-17983) [Choose the Most Secure Images | Docker Image Vulnerability Database](https://dso.docker.com/cve/CVE-2018-1000132) [Choose the Most Secure Images | Docker Image Vulnerability Database](https://dso.docker.com/cve/CVE-2017-17458) [Choose the Most Secure Images | Docker Image Vulnerability Database](https://dso.docker.com/cve/CVE-2017-1000116) ## Related Issue ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --- ci/images/plugin-compiler/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci/images/plugin-compiler/Dockerfile b/ci/images/plugin-compiler/Dockerfile index 51375cc7dbc..f91325bc16f 100644 --- a/ci/images/plugin-compiler/Dockerfile +++ b/ci/images/plugin-compiler/Dockerfile @@ -16,6 +16,10 @@ RUN mkdir -p $TYK_GW_PATH $PLUGIN_SOURCE_PATH COPY ci/images/plugin-compiler/data/build.sh /build.sh RUN chmod +x /build.sh +RUN apt-get remove -y --allow-remove-essential --auto-remove mercurial \ + && rm /usr/bin/passwd && rm /usr/sbin/adduser + + COPY . $TYK_GW_PATH RUN cd $TYK_GW_PATH && go mod vendor From f1af19f2f8888e3fdf1e8db1af62e9253c68aa95 Mon Sep 17 00:00:00 2001 From: Furkan Senharputlu Date: Tue, 28 Mar 2023 13:45:15 +0300 Subject: [PATCH 45/51] [TT-8290] Fix policy acl-ratelimit partition combination (#4913) The rate and acl(access rights) partitioned policies merging for a key doesn't work correctly. This PR fixes it and adds a test case for it. --- gateway/middleware.go | 9 ++++++--- gateway/policy_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/gateway/middleware.go b/gateway/middleware.go index 50f4a07a9e8..0168e9c7159 100644 --- a/gateway/middleware.go +++ b/gateway/middleware.go @@ -530,9 +530,7 @@ func (t BaseMiddleware) ApplyPolicies(session *user.SessionState) error { ar.Limit.QuotaRenews = r.Limit.QuotaRenews } - if !usePartitions || policy.Partitions.Acl { - rights[k] = ar - } + rights[k] = ar } // Master policy case @@ -608,6 +606,11 @@ func (t BaseMiddleware) ApplyPolicies(session *user.SessionState) error { // If some APIs had only ACL partitions, inherit rest from session level for k, v := range rights { + if !didACL[k] { + delete(rights, k) + continue + } + if !didRateLimit[k] { v.Limit.Rate = session.Rate v.Limit.Per = session.Per diff --git a/gateway/policy_test.go b/gateway/policy_test.go index 7f2b28c7255..ecf45eb4393 100644 --- a/gateway/policy_test.go +++ b/gateway/policy_test.go @@ -134,6 +134,21 @@ func (s *Test) TestPrepareApplyPolicies() (*BaseMiddleware, []testApplyPoliciesD Rate: 4, Per: 4, }, + "rate-for-a": { + Partitions: user.PolicyPartitions{RateLimit: true}, + AccessRights: map[string]user.AccessDefinition{"a": {}}, + Rate: 4, + }, + "rate-for-b": { + Partitions: user.PolicyPartitions{RateLimit: true}, + AccessRights: map[string]user.AccessDefinition{"b": {}}, + Rate: 2, + }, + "rate-for-a-b": { + Partitions: user.PolicyPartitions{RateLimit: true}, + AccessRights: map[string]user.AccessDefinition{"a": {}, "b": {}}, + Rate: 4, + }, "acl1": { Partitions: user.PolicyPartitions{Acl: true}, AccessRights: map[string]user.AccessDefinition{"a": {}}, @@ -145,6 +160,10 @@ func (s *Test) TestPrepareApplyPolicies() (*BaseMiddleware, []testApplyPoliciesD "acl3": { AccessRights: map[string]user.AccessDefinition{"c": {}}, }, + "acl-for-a-b": { + Partitions: user.PolicyPartitions{Acl: true}, + AccessRights: map[string]user.AccessDefinition{"a": {}, "b": {}}, + }, "unlimitedComplexity": { Partitions: user.PolicyPartitions{Complexity: true}, AccessRights: map[string]user.AccessDefinition{"a": {}}, @@ -572,6 +591,23 @@ func (s *Test) TestPrepareApplyPolicies() (*BaseMiddleware, []testApplyPoliciesD assert.Equal(t, want, s.AccessRights) }, nil, }, + { + "Acl for a and rate for a,b", []string{"acl1", "rate-for-a-b"}, + "", func(t *testing.T, s *user.SessionState) { + want := map[string]user.AccessDefinition{"a": {Limit: user.APILimit{Rate: 4}}} + assert.Equal(t, want, s.AccessRights) + }, nil, + }, + { + "Acl for a,b and individual rate for a,b", []string{"acl-for-a-b", "rate-for-a", "rate-for-b"}, + "", func(t *testing.T, s *user.SessionState) { + want := map[string]user.AccessDefinition{ + "a": {Limit: user.APILimit{Rate: 4}}, + "b": {Limit: user.APILimit{Rate: 2}}, + } + assert.Equal(t, want, s.AccessRights) + }, nil, + }, { "RightsUpdate", []string{"acl3"}, "", func(t *testing.T, ses *user.SessionState) { From 5cb869add2eae4c17e26a541a318e1de7eabd177 Mon Sep 17 00:00:00 2001 From: Laurentiu Ghiur Date: Wed, 29 Mar 2023 13:50:30 +0300 Subject: [PATCH 46/51] [TT-8457] Update swagger.yml (#4916) ## Description Update Tyk OAS to v5 ## Related Issue ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why --- swagger.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swagger.yml b/swagger.yml index 2ad91c30527..179d14f2807 100644 --- a/swagger.yml +++ b/swagger.yml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Tyk Gateway API - version: 4.3.0 + version: 5.0.0 description: |- The Tyk Gateway API is the primary means for integrating your application with the Tyk API Gateway system. This API is very small, and has no granular permissions system. It is intended to be used purely for internal automation and integration. From 8c42e9d35f3d693119b33c9f173a267ce0826671 Mon Sep 17 00:00:00 2001 From: Furkan Senharputlu Date: Thu, 30 Mar 2023 16:29:03 +0300 Subject: [PATCH 47/51] [TT-6024] Cover GoPluginMiddleware.EnabledForSpec() with test (#4921) This PR covers `GoPluginMiddleware.EnabledForSpec()` with a test. --- gateway/mw_go_plugin_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/gateway/mw_go_plugin_test.go b/gateway/mw_go_plugin_test.go index bf6cd1d5aa4..e3b27d862ca 100644 --- a/gateway/mw_go_plugin_test.go +++ b/gateway/mw_go_plugin_test.go @@ -3,6 +3,8 @@ package gateway import ( "testing" + "github.com/TykTechnologies/tyk/apidef" + "github.com/stretchr/testify/assert" ) @@ -16,3 +18,29 @@ func TestLoadPlugin(t *testing.T) { pluginLoaded := plugin.loadPlugin() assert.Equal(t, false, pluginLoaded) } + +func TestGoPluginMiddleware_EnabledForSpec(t *testing.T) { + gpm := GoPluginMiddleware{} + apiSpec := &APISpec{APIDefinition: &apidef.APIDefinition{}} + gpm.Spec = apiSpec + + assert.False(t, gpm.EnabledForSpec()) + + t.Run("global go plugin", func(t *testing.T) { + gpm.Path = "plugin.so" + gpm.SymbolName = "name" + + assert.True(t, gpm.EnabledForSpec()) + + gpm.Path = "" + gpm.SymbolName = "" + }) + + t.Run("per path go plugin", func(t *testing.T) { + apiSpec.VersionData.Versions = map[string]apidef.VersionInfo{"v1": { + ExtendedPaths: apidef.ExtendedPathsSet{GoPlugin: make([]apidef.GoPluginMeta, 1)}, + }} + + assert.True(t, gpm.EnabledForSpec()) + }) +} From f72e5388afa53c90ae0881023e6ef8cdf4cf844a Mon Sep 17 00:00:00 2001 From: Furkan Senharputlu Date: Thu, 30 Mar 2023 21:02:48 +0300 Subject: [PATCH 48/51] [TT-6024] Fix GoPlugin enable case (#4925) `GoPluginMiddleware` in path level is enabled if there is one `disabled: false` in the paths section. --- gateway/mw_go_plugin.go | 6 ++++-- gateway/mw_go_plugin_test.go | 9 ++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/gateway/mw_go_plugin.go b/gateway/mw_go_plugin.go index aca98745893..faf172bc262 100644 --- a/gateway/mw_go_plugin.go +++ b/gateway/mw_go_plugin.go @@ -109,8 +109,10 @@ func (m *GoPluginMiddleware) EnabledForSpec() bool { // per path go plugins for _, version := range m.Spec.VersionData.Versions { - if len(version.ExtendedPaths.GoPlugin) > 0 { - return true + for _, p := range version.ExtendedPaths.GoPlugin { + if !p.Disabled { + return true + } } } diff --git a/gateway/mw_go_plugin_test.go b/gateway/mw_go_plugin_test.go index e3b27d862ca..e3065dc3983 100644 --- a/gateway/mw_go_plugin_test.go +++ b/gateway/mw_go_plugin_test.go @@ -37,10 +37,17 @@ func TestGoPluginMiddleware_EnabledForSpec(t *testing.T) { }) t.Run("per path go plugin", func(t *testing.T) { + ep := apidef.ExtendedPathsSet{GoPlugin: make([]apidef.GoPluginMeta, 1)} apiSpec.VersionData.Versions = map[string]apidef.VersionInfo{"v1": { - ExtendedPaths: apidef.ExtendedPathsSet{GoPlugin: make([]apidef.GoPluginMeta, 1)}, + ExtendedPaths: ep, }} assert.True(t, gpm.EnabledForSpec()) + + t.Run("disabled", func(t *testing.T) { + ep.GoPlugin[0].Disabled = true + + assert.False(t, gpm.EnabledForSpec()) + }) }) } From bb6b882b8439a2ca063fa043e5d0190b952d585f Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 31 Mar 2023 10:46:15 +0200 Subject: [PATCH 49/51] [TT-4028] Fix race condition in httpdashboardhandler (#4844) ## Description Dashboardhandler has a race condition, PR fixes it using atomic operations. It tentatively fixes a bug where StopBeating would stop the beat loop, but would immediately reset it to started. Moved the "STARTED" operation higher up. ## Related Issue ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [x] I would like a code coverage CI quality gate exception and have explained why --------- Co-authored-by: Tit Petric Co-authored-by: jeff --- gateway/dashboard_register.go | 28 +++++++++++++++++++++++----- gateway/dashboard_register_test.go | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/gateway/dashboard_register.go b/gateway/dashboard_register.go index c6af1359e25..8d204fc062c 100644 --- a/gateway/dashboard_register.go +++ b/gateway/dashboard_register.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "net/http" + "sync/atomic" "time" "github.com/TykTechnologies/tyk/header" @@ -30,6 +31,18 @@ type DashboardServiceSender interface { NotifyDashboardOfEvent(interface{}) error } +// Constants for heartBeatStopSentinel indicators. +// +// Go 1.17 adds atomic.Value.Swap which is great, but 1.19 +// adds atomic.Bool and other types. This is a go <1.13 cludge. +const ( + // HeartBeatStarted Zero value - the handlers started + HeartBeatStarted = 0 + + // HeartBeatStopped value - the handlers invoked shutdown + HeartBeatStopped = 1 +) + type HTTPDashboardHandler struct { RegistrationEndpoint string DeRegistrationEndpoint string @@ -38,8 +51,9 @@ type HTTPDashboardHandler struct { Secret string - heartBeatStopSentinel bool - Gw *Gateway `json:"-"` + heartBeatStopSentinel int32 + + Gw *Gateway `json:"-"` } var dashClient *http.Client @@ -213,13 +227,18 @@ func (h *HTTPDashboardHandler) Ping() error { h.Gw.initialiseClient()) } +func (h *HTTPDashboardHandler) isHeartBeatStopped() bool { + return atomic.LoadInt32(&h.heartBeatStopSentinel) == HeartBeatStopped +} + func (h *HTTPDashboardHandler) StartBeating() error { + atomic.SwapInt32(&h.heartBeatStopSentinel, HeartBeatStarted) req := h.newRequest(http.MethodGet, h.HeartBeatEndpoint) client := h.Gw.initialiseClient() - for !h.heartBeatStopSentinel { + for !h.isHeartBeatStopped() { if err := h.sendHeartBeat(req, client); err != nil { dashLog.Warning(err) } @@ -227,12 +246,11 @@ func (h *HTTPDashboardHandler) StartBeating() error { } dashLog.Info("Stopped Heartbeat") - h.heartBeatStopSentinel = false return nil } func (h *HTTPDashboardHandler) StopBeating() { - h.heartBeatStopSentinel = true + atomic.SwapInt32(&h.heartBeatStopSentinel, HeartBeatStopped) } func (h *HTTPDashboardHandler) newRequest(method, endpoint string) *http.Request { diff --git a/gateway/dashboard_register_test.go b/gateway/dashboard_register_test.go index 10d4d62531e..9fa628aeacf 100644 --- a/gateway/dashboard_register_test.go +++ b/gateway/dashboard_register_test.go @@ -28,3 +28,25 @@ func Test_BuildDashboardConnStr(t *testing.T) { assert.Equal(t, connStr, "http://localhost/test") } + +func Test_DashboardLifecycle(t *testing.T) { + var handler HTTPDashboardHandler + + handler = HTTPDashboardHandler{ + heartBeatStopSentinel: HeartBeatStarted, + } + assert.False(t, handler.isHeartBeatStopped()) + + handler = HTTPDashboardHandler{ + heartBeatStopSentinel: HeartBeatStopped, + } + + assert.True(t, handler.isHeartBeatStopped()) + + handler = HTTPDashboardHandler{ + heartBeatStopSentinel: HeartBeatStarted, + } + + handler.StopBeating() + assert.True(t, handler.isHeartBeatStopped()) +} From 96543f2c900703832b335da7b4433dde183a6cc0 Mon Sep 17 00:00:00 2001 From: Furkan Senharputlu Date: Fri, 31 Mar 2023 13:12:46 +0300 Subject: [PATCH 50/51] [TT-6024] Return error when GoPlugin handler is nil (#4922) When GoPlugin load fails during API load, its handler is set as `nil`. The gateway should stop and return error when the API is called instead of skipping to other middlewares. Fixes https://github.com/TykTechnologies/tyk/issues/4469 --- gateway/middleware.go | 6 +---- gateway/mw_go_plugin.go | 6 +++++ goplugin/mw_go_plugin_test.go | 43 +++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/gateway/middleware.go b/gateway/middleware.go index 0168e9c7159..84f48b4c339 100644 --- a/gateway/middleware.go +++ b/gateway/middleware.go @@ -133,12 +133,8 @@ func (gw *Gateway) createMiddleware(actualMW TykMiddleware) func(http.Handler) h err, errCode := mw.ProcessRequest(w, r, mwConf) if err != nil { - // GoPluginMiddleware are expected to send response in case of error - // but we still want to record error - _, isGoPlugin := actualMW.(*GoPluginMiddleware) - handler := ErrorHandler{*mw.Base()} - handler.HandleError(w, r, err.Error(), errCode, !isGoPlugin) + handler.HandleError(w, r, err.Error(), errCode, true) meta["error"] = err.Error() diff --git a/gateway/mw_go_plugin.go b/gateway/mw_go_plugin.go index faf172bc262..05f06e34b0d 100644 --- a/gateway/mw_go_plugin.go +++ b/gateway/mw_go_plugin.go @@ -2,6 +2,7 @@ package gateway import ( "bytes" + "errors" "fmt" "io/ioutil" "net/http" @@ -181,9 +182,14 @@ func (m *GoPluginMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Reque if pluginMw, found := m.goPluginFromRequest(r); found { logger = pluginMw.logger handler = pluginMw.handler + } else { + return nil, http.StatusOK // next middleware } } + if handler == nil { + respCode = http.StatusInternalServerError + err = errors.New(http.StatusText(respCode)) return } diff --git a/goplugin/mw_go_plugin_test.go b/goplugin/mw_go_plugin_test.go index 3e40de6b185..c46ecd42d7c 100644 --- a/goplugin/mw_go_plugin_test.go +++ b/goplugin/mw_go_plugin_test.go @@ -452,3 +452,46 @@ func TestGoPluginAPIandPerPath(t *testing.T) { }...) }) } + +func TestGoPluginMiddleware_ProcessRequest_ShouldFailWhenNotLoaded(t *testing.T) { + ts := gateway.StartTest(nil) + defer ts.Close() + + api := ts.Gw.BuildAndLoadAPI(func(spec *gateway.APISpec) { + spec.Proxy.ListenPath = "/" + spec.UseKeylessAccess = false + spec.CustomPluginAuthEnabled = true + spec.CustomMiddleware.Driver = apidef.GoPluginDriver + spec.CustomMiddleware.AuthCheck.Name = "my-auth" + spec.CustomMiddleware.AuthCheck.Path = "auth.so" + })[0] + + _, _ = ts.Run(t, test.TestCase{ + Path: "/get", Code: http.StatusInternalServerError, BodyMatch: http.StatusText(http.StatusInternalServerError), + }) + + t.Run("path level", func(t *testing.T) { + api.CustomPluginAuthEnabled = false + api.UseKeylessAccess = true + + v := api.VersionData.Versions["v1"] + v.UseExtendedPaths = true + v.ExtendedPaths = apidef.ExtendedPathsSet{ + GoPlugin: []apidef.GoPluginMeta{ + apidef.GoPluginMeta{ + Path: "/my-plugin", + Method: http.MethodGet, + PluginPath: "non-existing.so", + SymbolName: "NonExistingPlugin", + }, + }, + } + api.VersionData.Versions["v1"] = v + ts.Gw.LoadAPI(api) + + _, _ = ts.Run(t, []test.TestCase{ + {Path: "/get", Code: http.StatusOK}, + {Path: "/my-plugin", Code: http.StatusInternalServerError, BodyMatch: http.StatusText(http.StatusInternalServerError)}, + }...) + }) +} From fbfaa7ab1fd7c4c34c931e8dc12bcf24e0e39d2b Mon Sep 17 00:00:00 2001 From: Kofo Okesola Date: Fri, 31 Mar 2023 14:25:52 +0100 Subject: [PATCH 51/51] [TT-8166]: fix graphql persist enabled middleware (#4917) ## Description This ticket fixes an issue where the persist graphql middleware is always run for tyk apis. The expected behavior is the middleware should only run if there are versions that support it. [TT-8166](https://tyktech.atlassian.net/browse/TT-8166) ## Related Issue ## Motivation and Context ## How This Has Been Tested ## Screenshots (if appropriate) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why [TT-8166]: https://tyktech.atlassian.net/browse/TT-8166?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --- gateway/mw_persist_graphql_operation.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gateway/mw_persist_graphql_operation.go b/gateway/mw_persist_graphql_operation.go index 05afc6b47c5..b8cbfebe31f 100644 --- a/gateway/mw_persist_graphql_operation.go +++ b/gateway/mw_persist_graphql_operation.go @@ -22,7 +22,13 @@ func (i *PersistGraphQLOperationMiddleware) Name() string { } func (i *PersistGraphQLOperationMiddleware) EnabledForSpec() bool { - return true + for _, v := range i.Spec.VersionData.Versions { + if len(v.ExtendedPaths.PersistGraphQL) > 0 { + return true + } + } + + return false } type GraphQLRequest struct {