From aa7ce3398cc7feceba4c1e0d8e4f1122c49c7c38 Mon Sep 17 00:00:00 2001 From: Leonid Bugaev Date: Tue, 22 Aug 2023 00:06:20 +0300 Subject: [PATCH] Merging to release-5-lts: [TT-9628] Reset fields inside classic API def but removed from OAS (#5419) [TT-9628] Reset fields inside classic API def but removed from OAS (#5419)
TT-9628
Summary Accept CRUD operations of OAS APIs with DB stored API definition
Type Story Story
Status In Code Review
Points N/A
Labels Carnival, customer_request
--- This PR introduces the ability to reset fields in the classic API definition that have been populated when OAS removes corresponding fields. --- apidef/oas/authentication.go | 1 + apidef/oas/middleware.go | 90 +++++++++++---- apidef/oas/oas.go | 22 ++-- apidef/oas/oas_test.go | 212 +++++++++++++++++++++++++++++++++++ apidef/oas/operation.go | 12 ++ apidef/oas/root.go | 30 +++-- apidef/oas/root_test.go | 68 +++++++++++ apidef/oas/security.go | 69 +++++++++++- apidef/oas/security_test.go | 6 +- apidef/oas/server.go | 28 ++++- apidef/oas/upstream.go | 52 ++++++--- 11 files changed, 523 insertions(+), 67 deletions(-) diff --git a/apidef/oas/authentication.go b/apidef/oas/authentication.go index 2e0d84aa284..08d2c28c840 100644 --- a/apidef/oas/authentication.go +++ b/apidef/oas/authentication.go @@ -546,6 +546,7 @@ func (o *OIDC) ExtractTo(api *apidef.APIDefinition) { api.AuthConfigs["oidc"] = authConfig api.OpenIDOptions.SegregateByClient = o.SegregateByClientId + api.OpenIDOptions.Providers = []apidef.OIDProviderConfig{} for _, p := range o.Providers { clientIDs := make(map[string]string) diff --git a/apidef/oas/middleware.go b/apidef/oas/middleware.go index 27f60a4801d..aadebf809d1 100644 --- a/apidef/oas/middleware.go +++ b/apidef/oas/middleware.go @@ -31,9 +31,14 @@ func (m *Middleware) Fill(api apidef.APIDefinition) { // ExtractTo extracts *Middleware into *apidef.APIDefinition. func (m *Middleware) ExtractTo(api *apidef.APIDefinition) { - if m.Global != nil { - m.Global.ExtractTo(api) + if m.Global == nil { + m.Global = &Global{} + defer func() { + m.Global = nil + }() } + + m.Global.ExtractTo(api) } // Global holds configuration applies globally: CORS and caching. @@ -134,33 +139,68 @@ func (g *Global) Fill(api apidef.APIDefinition) { // ExtractTo extracts *Global into *apidef.APIDefinition. func (g *Global) ExtractTo(api *apidef.APIDefinition) { - if g.PluginConfig != nil { - g.PluginConfig.ExtractTo(api) + if g.PluginConfig == nil { + g.PluginConfig = &PluginConfig{} + defer func() { + g.PluginConfig = nil + }() } - if g.CORS != nil { - g.CORS.ExtractTo(&api.CORS) + g.PluginConfig.ExtractTo(api) + + if g.CORS == nil { + g.CORS = &CORS{} + defer func() { + g.CORS = nil + }() } - if g.PrePlugin != nil { - g.PrePlugin.ExtractTo(api) + g.CORS.ExtractTo(&api.CORS) + + if g.PrePlugin == nil { + g.PrePlugin = &PrePlugin{} + defer func() { + g.PrePlugin = nil + }() } - if g.PostAuthenticationPlugin != nil { - g.PostAuthenticationPlugin.ExtractTo(api) + g.PrePlugin.ExtractTo(api) + + if g.PostAuthenticationPlugin == nil { + g.PostAuthenticationPlugin = &PostAuthenticationPlugin{} + defer func() { + g.PostAuthenticationPlugin = nil + }() } - if g.PostPlugin != nil { - g.PostPlugin.ExtractTo(api) + g.PostAuthenticationPlugin.ExtractTo(api) + + if g.PostPlugin == nil { + g.PostPlugin = &PostPlugin{} + defer func() { + g.PostPlugin = nil + }() } - if g.Cache != nil { - g.Cache.ExtractTo(&api.CacheOptions) + g.PostPlugin.ExtractTo(api) + + if g.Cache == nil { + g.Cache = &Cache{} + defer func() { + g.Cache = nil + }() } - if g.ResponsePlugin != nil { - g.ResponsePlugin.ExtractTo(api) + g.Cache.ExtractTo(&api.CacheOptions) + + if g.ResponsePlugin == nil { + g.ResponsePlugin = &ResponsePlugin{} + defer func() { + g.ResponsePlugin = nil + }() } + + g.ResponsePlugin.ExtractTo(api) } // PluginConfigData configures config data for custom plugins. @@ -232,13 +272,23 @@ func (p *PluginConfig) Fill(api apidef.APIDefinition) { func (p *PluginConfig) ExtractTo(api *apidef.APIDefinition) { api.CustomMiddleware.Driver = p.Driver - if p.Bundle != nil { - p.Bundle.ExtractTo(api) + if p.Bundle == nil { + p.Bundle = &PluginBundle{} + defer func() { + p.Bundle = nil + }() } - if p.Data != nil { - p.Data.ExtractTo(api) + p.Bundle.ExtractTo(api) + + if p.Data == nil { + p.Data = &PluginConfigData{} + defer func() { + p.Data = nil + }() } + + p.Data.ExtractTo(api) } // PluginBundle holds configuration for custom plugins. diff --git a/apidef/oas/oas.go b/apidef/oas/oas.go index 479bf3efa6c..0c2ff9f8ccb 100644 --- a/apidef/oas/oas.go +++ b/apidef/oas/oas.go @@ -87,21 +87,21 @@ func (s *OAS) Fill(api apidef.APIDefinition) { // ExtractTo extracts *OAS into *apidef.APIDefinition. func (s *OAS) ExtractTo(api *apidef.APIDefinition) { - if s.GetTykExtension() != nil { - s.GetTykExtension().ExtractTo(api) + if s.GetTykExtension() == nil { + s.SetTykExtension(&XTykAPIGateway{}) + defer func() { + delete(s.Extensions, ExtensionTykAPIGateway) + }() } - s.extractSecurityTo(api) + s.GetTykExtension().ExtractTo(api) - ep := api.VersionData.Versions[Main].ExtendedPaths - s.extractPathsAndOperations(&ep) + s.extractSecurityTo(api) - api.VersionData.Versions = map[string]apidef.VersionInfo{ - Main: { - UseExtendedPaths: true, - ExtendedPaths: ep, - }, - } + vInfo := api.VersionData.Versions[Main] + vInfo.UseExtendedPaths = true + s.extractPathsAndOperations(&vInfo.ExtendedPaths) + api.VersionData.Versions[Main] = vInfo } // SetTykExtension populates our OAS schema extension inside *OAS. diff --git a/apidef/oas/oas_test.go b/apidef/oas/oas_test.go index 3cfe430175d..455668ba025 100644 --- a/apidef/oas/oas_test.go +++ b/apidef/oas/oas_test.go @@ -124,6 +124,218 @@ func TestOAS_ExtractTo_DontTouchExistingClassicFields(t *testing.T) { assert.Len(t, api.VersionData.Versions[Main].ExtendedPaths.TransformHeader, 1) } +func TestOAS_ExtractTo_ResetAPIDefinition(t *testing.T) { + var a apidef.APIDefinition + Fill(t, &a, 0) + + var vInfo apidef.VersionInfo + Fill(t, &vInfo, 0) + a.VersionData.Versions = map[string]apidef.VersionInfo{ + Main: vInfo, + } + + var s OAS + s.ExtractTo(&a) + + a.UseKeylessAccess = false + a.UpstreamCertificatesDisabled = false + a.CertificatePinningDisabled = false + a.Proxy.ServiceDiscovery.CacheDisabled = false + a.CustomMiddlewareBundleDisabled = false + a.DomainDisabled = false + a.ConfigDataDisabled = false + a.TagsDisabled = false + a.IsOAS = false + + // deprecated fields + a.Auth = apidef.AuthConfig{} + a.VersionDefinition.StripPath = false + a.UseGoPluginAuth = false + a.EnableCoProcessAuth = false + a.JWTScopeToPolicyMapping = nil + a.JWTScopeClaimName = "" + a.VersionData.NotVersioned = false + vInfo = a.VersionData.Versions[""] + vInfo.Name = "" + vInfo.Expires = "" + vInfo.Paths.Ignored = nil + vInfo.Paths.WhiteList = nil + vInfo.Paths.BlackList = nil + vInfo.OverrideTarget = "" + vInfo.UseExtendedPaths = false + vInfo.ExtendedPaths.MockResponse = nil + vInfo.ExtendedPaths.Cached = nil + vInfo.ExtendedPaths.ValidateJSON = nil + a.VersionData.Versions[""] = vInfo + + assert.Empty(t, a.Name) + + noOASSupportFields := getNonEmptyFields(a, "APIDefinition") + + expectedFields := []string{ + "APIDefinition.ListenPort", + "APIDefinition.Protocol", + "APIDefinition.EnableProxyProtocol", + "APIDefinition.RequestSigning.IsEnabled", + "APIDefinition.RequestSigning.Secret", + "APIDefinition.RequestSigning.KeyId", + "APIDefinition.RequestSigning.Algorithm", + "APIDefinition.RequestSigning.HeaderList[0]", + "APIDefinition.RequestSigning.CertificateId", + "APIDefinition.RequestSigning.SignatureHeader", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformJQ[0].Filter", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformJQ[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformJQ[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformJQResponse[0].Filter", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformJQResponse[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformJQResponse[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformHeader[0].DeleteHeaders[0]", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformHeader[0].AddHeaders[0]", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformHeader[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformHeader[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformHeader[0].ActOnResponse", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformResponseHeader[0].DeleteHeaders[0]", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformResponseHeader[0].AddHeaders[0]", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformResponseHeader[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformResponseHeader[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TransformResponseHeader[0].ActOnResponse", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.CircuitBreaker[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.CircuitBreaker[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.CircuitBreaker[0].ThresholdPercent", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.CircuitBreaker[0].Samples", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.CircuitBreaker[0].ReturnToServiceAfter", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.CircuitBreaker[0].DisableHalfOpenState", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].MatchPattern", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].RewriteTo", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].On", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.HeaderMatches[0].MatchPattern", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.HeaderMatches[0].Reverse", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.QueryValMatches[0].MatchPattern", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.QueryValMatches[0].Reverse", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.PathPartMatches[0].MatchPattern", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.PathPartMatches[0].Reverse", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.SessionMetaMatches[0].MatchPattern", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.SessionMetaMatches[0].Reverse", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.RequestContextMatches[0].MatchPattern", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.RequestContextMatches[0].Reverse", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.PayloadMatches.MatchPattern", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].Options.PayloadMatches.Reverse", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.URLRewrite[0].Triggers[0].RewriteTo", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.SizeLimit[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.SizeLimit[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.SizeLimit[0].SizeLimit", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TrackEndpoints[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.TrackEndpoints[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.DoNotTrackEndpoints[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.DoNotTrackEndpoints[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.Internal[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.Internal[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.PersistGraphQL[0].Path", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.PersistGraphQL[0].Method", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.PersistGraphQL[0].Operation", + "APIDefinition.VersionData.Versions[0].ExtendedPaths.PersistGraphQL[0].Variables[0]", + "APIDefinition.VersionData.Versions[0].GlobalHeaders[0]", + "APIDefinition.VersionData.Versions[0].GlobalHeadersRemove[0]", + "APIDefinition.VersionData.Versions[0].GlobalResponseHeaders[0]", + "APIDefinition.VersionData.Versions[0].GlobalResponseHeadersRemove[0]", + "APIDefinition.VersionData.Versions[0].IgnoreEndpointCase", + "APIDefinition.VersionData.Versions[0].GlobalSizeLimit", + "APIDefinition.UptimeTests.CheckList[0].CheckURL", + "APIDefinition.UptimeTests.CheckList[0].Protocol", + "APIDefinition.UptimeTests.CheckList[0].Timeout", + "APIDefinition.UptimeTests.CheckList[0].EnableProxyProtocol", + "APIDefinition.UptimeTests.CheckList[0].Commands[0].Name", + "APIDefinition.UptimeTests.CheckList[0].Commands[0].Message", + "APIDefinition.UptimeTests.CheckList[0].Method", + "APIDefinition.UptimeTests.CheckList[0].Headers[0]", + "APIDefinition.UptimeTests.CheckList[0].Body", + "APIDefinition.UptimeTests.Config.ExpireUptimeAnalyticsAfter", + "APIDefinition.UptimeTests.Config.ServiceDiscovery.CacheDisabled", + "APIDefinition.UptimeTests.Config.RecheckWait", + "APIDefinition.Proxy.PreserveHostHeader", + "APIDefinition.Proxy.DisableStripSlash", + "APIDefinition.Proxy.EnableLoadBalancing", + "APIDefinition.Proxy.Targets[0]", + "APIDefinition.Proxy.CheckHostAgainstUptimeTests", + "APIDefinition.Proxy.Transport.SSLInsecureSkipVerify", + "APIDefinition.Proxy.Transport.SSLCipherSuites[0]", + "APIDefinition.Proxy.Transport.SSLMinVersion", + "APIDefinition.Proxy.Transport.SSLMaxVersion", + "APIDefinition.Proxy.Transport.SSLForceCommonNameCheck", + "APIDefinition.Proxy.Transport.ProxyURL", + "APIDefinition.DisableRateLimit", + "APIDefinition.DisableQuota", + "APIDefinition.SessionLifetimeRespectsKeyExpiration", + "APIDefinition.SessionLifetime", + "APIDefinition.AuthProvider.Name", + "APIDefinition.AuthProvider.StorageEngine", + "APIDefinition.AuthProvider.Meta[0]", + "APIDefinition.SessionProvider.Name", + "APIDefinition.SessionProvider.StorageEngine", + "APIDefinition.SessionProvider.Meta[0]", + "APIDefinition.EventHandlers.Events[0]", + "APIDefinition.EnableBatchRequestSupport", + "APIDefinition.EnableIpWhiteListing", + "APIDefinition.AllowedIPs[0]", + "APIDefinition.EnableIpBlacklisting", + "APIDefinition.BlacklistedIPs[0]", + "APIDefinition.DontSetQuotasOnCreate", + "APIDefinition.ExpireAnalyticsAfter", + "APIDefinition.ResponseProcessors[0].Name", + "APIDefinition.ResponseProcessors[0].Options", + "APIDefinition.Certificates[0]", + "APIDefinition.DoNotTrack", + "APIDefinition.EnableContextVars", + "APIDefinition.TagHeaders[0]", + "APIDefinition.GlobalRateLimit.Rate", + "APIDefinition.GlobalRateLimit.Per", + "APIDefinition.EnableDetailedRecording", + "APIDefinition.GraphQL.Enabled", + "APIDefinition.GraphQL.ExecutionMode", + "APIDefinition.GraphQL.Version", + "APIDefinition.GraphQL.Schema", + "APIDefinition.GraphQL.TypeFieldConfigurations[0].TypeName", + "APIDefinition.GraphQL.TypeFieldConfigurations[0].FieldName", + "APIDefinition.GraphQL.TypeFieldConfigurations[0].Mapping.Disabled", + "APIDefinition.GraphQL.TypeFieldConfigurations[0].Mapping.Path", + "APIDefinition.GraphQL.TypeFieldConfigurations[0].DataSource.Name", + "APIDefinition.GraphQL.TypeFieldConfigurations[0].DataSource.Config[0]", + "APIDefinition.GraphQL.GraphQLPlayground.Enabled", + "APIDefinition.GraphQL.GraphQLPlayground.Path", + "APIDefinition.GraphQL.Engine.FieldConfigs[0].TypeName", + "APIDefinition.GraphQL.Engine.FieldConfigs[0].FieldName", + "APIDefinition.GraphQL.Engine.FieldConfigs[0].DisableDefaultMapping", + "APIDefinition.GraphQL.Engine.FieldConfigs[0].Path[0]", + "APIDefinition.GraphQL.Engine.DataSources[0].Kind", + "APIDefinition.GraphQL.Engine.DataSources[0].Name", + "APIDefinition.GraphQL.Engine.DataSources[0].Internal", + "APIDefinition.GraphQL.Engine.DataSources[0].RootFields[0].Type", + "APIDefinition.GraphQL.Engine.DataSources[0].RootFields[0].Fields[0]", + "APIDefinition.GraphQL.Engine.DataSources[0].Config[0]", + "APIDefinition.GraphQL.Proxy.AuthHeaders[0]", + "APIDefinition.GraphQL.Proxy.SubscriptionType", + "APIDefinition.GraphQL.Proxy.RequestHeaders[0]", + "APIDefinition.GraphQL.Proxy.UseResponseExtensions.OnErrorForwarding", + "APIDefinition.GraphQL.Subgraph.SDL", + "APIDefinition.GraphQL.Supergraph.Subgraphs[0].APIID", + "APIDefinition.GraphQL.Supergraph.Subgraphs[0].Name", + "APIDefinition.GraphQL.Supergraph.Subgraphs[0].URL", + "APIDefinition.GraphQL.Supergraph.Subgraphs[0].SDL", + "APIDefinition.GraphQL.Supergraph.Subgraphs[0].Headers[0]", + "APIDefinition.GraphQL.Supergraph.Subgraphs[0].SubscriptionType", + "APIDefinition.GraphQL.Supergraph.MergedSDL", + "APIDefinition.GraphQL.Supergraph.GlobalHeaders[0]", + "APIDefinition.GraphQL.Supergraph.DisableQueryBatching", + "APIDefinition.AnalyticsPlugin.Enabled", + "APIDefinition.AnalyticsPlugin.PluginPath", + "APIDefinition.AnalyticsPlugin.FuncName", + } + + assert.Equal(t, expectedFields, noOASSupportFields) +} + func TestOAS_AddServers(t *testing.T) { t.Parallel() type fields struct { diff --git a/apidef/oas/operation.go b/apidef/oas/operation.go index deab0e32e63..fc342fdc1a2 100644 --- a/apidef/oas/operation.go +++ b/apidef/oas/operation.go @@ -124,6 +124,18 @@ func (s *OAS) fillPathsAndOperations(ep apidef.ExtendedPathsSet) { } func (s *OAS) extractPathsAndOperations(ep *apidef.ExtendedPathsSet) { + ep.Ignored = nil + ep.WhiteList = nil + ep.BlackList = nil + ep.AdvanceCacheConfig = nil + ep.Transform = nil + ep.TransformResponse = nil + ep.HardTimeouts = nil + ep.Virtual = nil + ep.MethodTransforms = nil + ep.ValidateRequest = nil + ep.GoPlugin = nil + tykOperations := s.getTykOperations() if len(tykOperations) == 0 { return diff --git a/apidef/oas/root.go b/apidef/oas/root.go index 4d69d31338e..d849610cf5f 100644 --- a/apidef/oas/root.go +++ b/apidef/oas/root.go @@ -44,9 +44,14 @@ func (x *XTykAPIGateway) ExtractTo(api *apidef.APIDefinition) { x.Upstream.ExtractTo(api) x.Server.ExtractTo(api) - if x.Middleware != nil { - x.Middleware.ExtractTo(api) + if x.Middleware == nil { + x.Middleware = &Middleware{} + defer func() { + x.Middleware = nil + }() } + + x.Middleware.ExtractTo(api) } // Info contains the main metadata about the API definition. @@ -99,10 +104,15 @@ func (i *Info) ExtractTo(api *apidef.APIDefinition) { api.Expiration = i.Expiration i.State.ExtractTo(api) - if i.Versioning != nil { - i.Versioning.ExtractTo(api) + if i.Versioning == nil { + i.Versioning = &Versioning{} + defer func() { + i.Versioning = nil + }() } + i.Versioning.ExtractTo(api) + // everytime api.VersionData.NotVersioned = true api.VersionData.DefaultVersion = "" @@ -189,12 +199,14 @@ func (v *Versioning) ExtractTo(api *apidef.APIDefinition) { api.VersionDefinition.Default = v.Default api.VersionDefinition.Location = v.Location api.VersionDefinition.Key = v.Key - if api.VersionDefinition.Versions == nil { - api.VersionDefinition.Versions = make(map[string]string) - } - for _, val := range v.Versions { - api.VersionDefinition.Versions[val.Name] = val.ID + if len(v.Versions) > 0 { + api.VersionDefinition.Versions = make(map[string]string) + for _, val := range v.Versions { + api.VersionDefinition.Versions[val.Name] = val.ID + } + } else { + api.VersionDefinition.Versions = nil } api.VersionDefinition.StripVersioningData = v.StripVersioningData diff --git a/apidef/oas/root_test.go b/apidef/oas/root_test.go index e2670eeb2f1..b014a056f73 100644 --- a/apidef/oas/root_test.go +++ b/apidef/oas/root_test.go @@ -185,6 +185,74 @@ func Fill(t *testing.T, input interface{}, index int) { } } +// getNonEmptyFields returns non-empty fields inside a struct. +func getNonEmptyFields(data interface{}, prefix string) (fields []string) { + val := reflect.ValueOf(data) + if val.Kind() != reflect.Struct { + fields = append(fields, prefix) + return + } + + for i := 0; i < val.NumField(); i++ { + field := val.Field(i) + fieldType := val.Type().Field(i) + + fieldName := fieldType.Name + fullName := prefix + "." + fieldName + + switch field.Kind() { + case reflect.String: + if field.String() != "" { + fields = append(fields, fullName) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if field.Int() != 0 { + fields = append(fields, fullName) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if field.Uint() != 0 { + fields = append(fields, fullName) + } + case reflect.Float32, reflect.Float64: + if field.Float() != 0 { + fields = append(fields, fullName) + } + case reflect.Bool: + if field.Bool() { + fields = append(fields, fullName) + } + case reflect.Struct: + fields = append(fields, getNonEmptyFields(field.Interface(), fullName)...) + case reflect.Array, reflect.Slice: + for j := 0; j < field.Len(); j++ { + elemFullName := fullName + fmt.Sprintf("[%d]", j) + fields = append(fields, getNonEmptyFields(field.Index(j).Interface(), elemFullName)...) + break + } + + case reflect.Map: + for _, key := range field.MapKeys() { + elemFullName := fullName + "[0]" + fields = append(fields, getNonEmptyFields(field.MapIndex(key).Interface(), elemFullName)...) + break + } + case reflect.Interface: + if !field.IsNil() { + fields = append(fields, getNonEmptyFields(field.Elem().Interface(), fullName)...) + } + case reflect.Ptr: + if !field.IsNil() { + fields = append(fields, getNonEmptyFields(field.Elem().Interface(), fullName)...) + } + default: + panic(fmt.Sprintf("unsupported kind: %v", field.Kind())) + } + + } + + return fields +} + func FillTestAuthConfigs(t *testing.T, index int) map[string]apidef.AuthConfig { authConfigs := make(map[string]apidef.AuthConfig) diff --git a/apidef/oas/security.go b/apidef/oas/security.go index 9926d23511b..d5bb56ac414 100644 --- a/apidef/oas/security.go +++ b/apidef/oas/security.go @@ -629,13 +629,17 @@ func (s *OAS) fillSecurity(api apidef.APIDefinition) { } func (s *OAS) extractSecurityTo(api *apidef.APIDefinition) { - if tykAuthentication := s.getTykAuthentication(); tykAuthentication != nil { - tykAuthentication.ExtractTo(api) - } else { - api.UseKeylessAccess = true - return + if s.getTykAuthentication() == nil { + s.GetTykExtension().Server.Authentication = &Authentication{} + defer func() { + s.GetTykExtension().Server.Authentication = nil + }() } + resetSecuritySchemes(api) + + s.getTykAuthentication().ExtractTo(api) + if api.AuthConfigs == nil { api.AuthConfigs = make(map[string]apidef.AuthConfig) } @@ -677,6 +681,61 @@ func (s *OAS) extractSecurityTo(api *apidef.APIDefinition) { } } +func resetSecuritySchemes(api *apidef.APIDefinition) { + api.AuthConfigs = nil + + // OAuth2 + api.UseOauth2 = false + api.Oauth2Meta.AllowedAccessTypes = nil + api.Oauth2Meta.AllowedAuthorizeTypes = nil + api.Oauth2Meta.AuthorizeLoginRedirect = "" + api.NotificationsDetails = apidef.NotificationsManager{} + + // External OAuth + api.ExternalOAuth = apidef.ExternalOAuth{} + + // OIDC + api.UseOpenID = false + api.Scopes.OIDC = apidef.ScopeClaim{} + api.OpenIDOptions = apidef.OpenIDOptions{} + + // Basic + api.UseBasicAuth = false + api.BasicAuth.DisableCaching = false + api.BasicAuth.CacheTTL = 0 + api.BasicAuth.ExtractFromBody = false + api.BasicAuth.BodyUserRegexp = "" + api.BasicAuth.BodyPasswordRegexp = "" + delete(api.AuthConfigs, "basic") + + // HMAC + api.EnableSignatureChecking = false + api.HmacAllowedClockSkew = 0 + api.HmacAllowedAlgorithms = nil + + // JWT + api.EnableJWT = false + api.JWTSource = "" + api.JWTSigningMethod = "" + api.JWTIdentityBaseField = "" + api.JWTSkipKid = false + api.JWTPolicyFieldName = "" + api.JWTClientIDBaseField = "" + api.Scopes.JWT = apidef.ScopeClaim{} + api.JWTDefaultPolicies = nil + api.JWTIssuedAtValidationSkew = 0 + api.JWTExpiresAtValidationSkew = 0 + api.JWTNotBeforeValidationSkew = 0 + + // Auth Token + api.UseStandardAuth = false + + // Custom + api.CustomPluginAuthEnabled = false + api.CustomMiddleware.AuthCheck = apidef.MiddlewareDefinition{} + api.CustomMiddleware.IdExtractor = apidef.MiddlewareIdExtractor{} +} + func (s *OAS) fillAPIKeyScheme(ac *apidef.AuthConfig) { ss := s.Components.SecuritySchemes if ss == nil { diff --git a/apidef/oas/security_test.go b/apidef/oas/security_test.go index 738f4b4bfad..e55bdd6786b 100644 --- a/apidef/oas/security_test.go +++ b/apidef/oas/security_test.go @@ -15,16 +15,16 @@ func TestOAS_Security(t *testing.T) { Fill(t, &auth, 0) auth.DisableHeader = false - var api apidef.APIDefinition + var api apidef.APIDefinition // bundle enabled api.AuthConfigs = map[string]apidef.AuthConfig{ apidef.AuthTokenType: auth, } - var oas OAS + var oas OAS // bundle enabled true oas.SetTykExtension(&XTykAPIGateway{}) oas.fillSecurity(api) - var convertedAPI apidef.APIDefinition + var convertedAPI apidef.APIDefinition // bundle enabled oas.extractSecurityTo(&convertedAPI) assert.Equal(t, api, convertedAPI) diff --git a/apidef/oas/server.go b/apidef/oas/server.go index 8a4468ce2f6..34cf105e537 100644 --- a/apidef/oas/server.go +++ b/apidef/oas/server.go @@ -66,16 +66,32 @@ func (s *Server) ExtractTo(api *apidef.APIDefinition) { s.ListenPath.ExtractTo(api) api.Slug = s.Slug - if s.ClientCertificates != nil { - s.ClientCertificates.ExtractTo(api) + if s.ClientCertificates == nil { + s.ClientCertificates = &ClientCertificates{} + defer func() { + s.ClientCertificates = nil + }() } - if s.GatewayTags != nil { - s.GatewayTags.ExtractTo(api) + + s.ClientCertificates.ExtractTo(api) + + if s.GatewayTags == nil { + s.GatewayTags = &GatewayTags{} + defer func() { + s.GatewayTags = nil + }() } - if s.CustomDomain != nil { - s.CustomDomain.ExtractTo(api) + s.GatewayTags.ExtractTo(api) + + if s.CustomDomain == nil { + s.CustomDomain = &Domain{} + defer func() { + s.CustomDomain = nil + }() } + + s.CustomDomain.ExtractTo(api) } // ListenPath represents the path the server should listen on. diff --git a/apidef/oas/upstream.go b/apidef/oas/upstream.go index d0a8645e580..0e419030491 100644 --- a/apidef/oas/upstream.go +++ b/apidef/oas/upstream.go @@ -72,21 +72,41 @@ func (u *Upstream) Fill(api apidef.APIDefinition) { func (u *Upstream) ExtractTo(api *apidef.APIDefinition) { api.Proxy.TargetURL = u.URL - if u.ServiceDiscovery != nil { - u.ServiceDiscovery.ExtractTo(&api.Proxy.ServiceDiscovery) + if u.ServiceDiscovery == nil { + u.ServiceDiscovery = &ServiceDiscovery{} + defer func() { + u.ServiceDiscovery = nil + }() } - if u.Test != nil { - u.Test.ExtractTo(&api.UptimeTests) + u.ServiceDiscovery.ExtractTo(&api.Proxy.ServiceDiscovery) + + if u.Test == nil { + u.Test = &Test{} + defer func() { + u.Test = nil + }() } - if u.MutualTLS != nil { - u.MutualTLS.ExtractTo(api) + u.Test.ExtractTo(&api.UptimeTests) + + if u.MutualTLS == nil { + u.MutualTLS = &MutualTLS{} + defer func() { + u.MutualTLS = nil + }() } - if u.CertificatePinning != nil { - u.CertificatePinning.ExtractTo(api) + u.MutualTLS.ExtractTo(api) + + if u.CertificatePinning == nil { + u.CertificatePinning = &CertificatePinning{} + defer func() { + u.CertificatePinning = nil + }() } + + u.CertificatePinning.ExtractTo(api) } // ServiceDiscovery holds configuration required for service discovery. @@ -241,11 +261,6 @@ func (sd *ServiceDiscovery) Fill(serviceDiscovery apidef.ServiceDiscoveryConfigu // ExtractTo extracts *ServiceDiscovery into *apidef.ServiceDiscoveryConfiguration. func (sd *ServiceDiscovery) ExtractTo(serviceDiscovery *apidef.ServiceDiscoveryConfiguration) { - if sd == nil { - serviceDiscovery.CacheDisabled = true - return - } - serviceDiscovery.UseDiscoveryService = sd.Enabled serviceDiscovery.EndpointReturnsList = sd.EndpointReturnsList serviceDiscovery.ParentDataPath = sd.ParentDataPath @@ -282,6 +297,13 @@ func (t *Test) Fill(uptimeTests apidef.UptimeTests) { // ExtractTo extracts *Test into *apidef.UptimeTests. func (t *Test) ExtractTo(uptimeTests *apidef.UptimeTests) { + if t.ServiceDiscovery == nil { + t.ServiceDiscovery = &ServiceDiscovery{} + defer func() { + t.ServiceDiscovery = nil + }() + } + t.ServiceDiscovery.ExtractTo(&uptimeTests.Config.ServiceDiscovery) } @@ -327,6 +349,8 @@ func (m *MutualTLS) ExtractTo(api *apidef.APIDefinition) { if len(m.DomainToCertificates) > 0 { api.UpstreamCertificates = make(map[string]string) + } else { + api.UpstreamCertificates = nil } for _, domainToCert := range m.DomainToCertificates { @@ -409,5 +433,7 @@ func (cp *CertificatePinning) ExtractTo(api *apidef.APIDefinition) { if len(cp.DomainToPublicKeysMapping) > 0 { api.PinnedPublicKeys = make(map[string]string) cp.DomainToPublicKeysMapping.ExtractTo(api.PinnedPublicKeys) + } else { + api.PinnedPublicKeys = nil } }