diff --git a/examples/custom-resources/api-key/api-key-policy-2.yaml b/examples/custom-resources/api-key/api-key-policy-2.yaml index 7ecf2452fc..2870eb1716 100644 --- a/examples/custom-resources/api-key/api-key-policy-2.yaml +++ b/examples/custom-resources/api-key/api-key-policy-2.yaml @@ -9,7 +9,4 @@ spec: - "X-header-name" query: - "queryName" - rejectCodes: # optional - notSupplied: 408 - noMatch: 410 clientSecret: api-key-client-secret-2 diff --git a/examples/custom-resources/api-key/api-key-policy.yaml b/examples/custom-resources/api-key/api-key-policy.yaml index 2c12b38923..30c45bbd3b 100644 --- a/examples/custom-resources/api-key/api-key-policy.yaml +++ b/examples/custom-resources/api-key/api-key-policy.yaml @@ -11,7 +11,4 @@ spec: - "some-other-header" query: - "queryName" -# rejectCodes: # optional -# notSupplied: 401 -# noMatch: 403 clientSecret: api-key-client-secret-1 diff --git a/examples/custom-resources/api-key/api-key-secret-1.yaml b/examples/custom-resources/api-key/api-key-secret-1.yaml index 318529da6a..bf6954f4b0 100644 --- a/examples/custom-resources/api-key/api-key-secret-1.yaml +++ b/examples/custom-resources/api-key/api-key-secret-1.yaml @@ -5,4 +5,4 @@ metadata: type: nginx.org/apikey data: client1: cGFzc3dvcmQ= - client2: N2ViNDMwOGItY2Q1Yi00NDEzLWI0NTUtYjMyZmQ4OTg2MmZk + client2: N2ViNDMwOGItY2Q1Yi00NDEzLWI0NTUtYjMyZmQ4OTg2MmZk \ No newline at end of file diff --git a/examples/custom-resources/api-key/cafe-virtual-server-2.yaml b/examples/custom-resources/api-key/cafe-virtual-server-2.yaml index db55ebb5fc..7cb56276f1 100644 --- a/examples/custom-resources/api-key/cafe-virtual-server-2.yaml +++ b/examples/custom-resources/api-key/cafe-virtual-server-2.yaml @@ -24,3 +24,9 @@ spec: pass: coffee policies: - name: api-key-policy-2 + - path: /coffee2 + action: + pass: coffee + policies: + - name: api-key-policy + diff --git a/examples/custom-resources/api-key/coffee-virtual-server-route.yaml b/examples/custom-resources/api-key/coffee-virtual-server-route.yaml index d24eca3e1a..5b921011c7 100644 --- a/examples/custom-resources/api-key/coffee-virtual-server-route.yaml +++ b/examples/custom-resources/api-key/coffee-virtual-server-route.yaml @@ -14,3 +14,8 @@ spec: pass: coffee policies: - name: api-key-policy-2 + - path: /coffee2 + action: + pass: coffee + policies: + - name: api-key-policy \ No newline at end of file diff --git a/internal/configs/configurator.go b/internal/configs/configurator.go index 69ce005fbc..9d99c245c2 100644 --- a/internal/configs/configurator.go +++ b/internal/configs/configurator.go @@ -1991,9 +1991,7 @@ func (cnf *Configurator) AddOrUpdateSecret(secret *api_v1.Secret) string { // OIDC ClientSecret is not required on the filesystem, it is written directly to the config file. return "" case secrets.SecretTypeAPIKey: - glog.Infof("adding apikey secret: %s", secret.Name) - // OIDC ClientSecret is not required on the filesystem, it is written directly to the config file. - + // APIKey ClientSecret is not required on the filesystem, it is written directly to the config file. return "" default: return cnf.addOrUpdateTLSSecret(secret) diff --git a/internal/configs/version1/nginx.tmpl b/internal/configs/version1/nginx.tmpl index fc298fd814..9d9db599d6 100644 --- a/internal/configs/version1/nginx.tmpl +++ b/internal/configs/version1/nginx.tmpl @@ -1,5 +1,4 @@ {{- /*gotype: github.com/nginxinc/kubernetes-ingress/internal/configs/version1.MainConfig*/ -}} - worker_processes {{.WorkerProcesses}}; {{- if .WorkerRlimitNofile}} worker_rlimit_nofile {{.WorkerRlimitNofile}};{{end}} diff --git a/internal/configs/version2/http.go b/internal/configs/version2/http.go index 18e8338d5b..bd72ae6a9f 100644 --- a/internal/configs/version2/http.go +++ b/internal/configs/version2/http.go @@ -139,17 +139,9 @@ type OIDC struct { } type APIKey struct { - Header []string - Query []string - RejectCodeNotSupplied int - RejectCodeNoMatch int - Clients []Client - MapName string -} - -type Client struct { - ClientID string - EncryptedKey string + Header []string + Query []string + MapName string } // WAF defines WAF configuration. diff --git a/internal/configs/version2/nginx-plus.virtualserver.tmpl b/internal/configs/version2/nginx-plus.virtualserver.tmpl index 7cd2da4a20..bbf5bb76f7 100644 --- a/internal/configs/version2/nginx-plus.virtualserver.tmpl +++ b/internal/configs/version2/nginx-plus.virtualserver.tmpl @@ -1,6 +1,4 @@ {{- /*gotype: github.com/nginxinc/kubernetes-ingress/internal/configs/version2.VirtualServerConfig*/ -}} - - {{ range $u := .Upstreams }} upstream {{ $u.Name }} { zone {{ $u.Name }} {{ if ne $u.UpstreamZoneSize "0" }}{{ $u.UpstreamZoneSize }}{{ else }}512k{{ end }}; diff --git a/internal/configs/virtualserver.go b/internal/configs/virtualserver.go index 6c06c54678..ba79a4b5a7 100644 --- a/internal/configs/virtualserver.go +++ b/internal/configs/virtualserver.go @@ -416,18 +416,18 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( vsNamespace: vsEx.VirtualServer.Namespace, vsName: vsEx.VirtualServer.Name, } - policiesCfg, mapsFromPolicies := vsc.generatePolicies(ownerDetails, vsEx.VirtualServer.Spec.Policies, vsEx.Policies, specContext, policyOpts) - - maps = append(maps, mapsFromPolicies...) + policiesCfg := vsc.generatePolicies(ownerDetails, vsEx.VirtualServer.Spec.Policies, vsEx.Policies, specContext, policyOpts) if policiesCfg.JWKSAuthEnabled { jwtAuthKey := policiesCfg.JWTAuth.Key policiesCfg.JWTAuthList = make(map[string]*version2.JWTAuth) policiesCfg.JWTAuthList[jwtAuthKey] = policiesCfg.JWTAuth } - // - //if policiesCfg.APIKey { - // apiKey := policiesCfg.APIKey.MapName - //} + + if policiesCfg.APIKeyEnabled { + apiMapName := policiesCfg.APIKey.MapName + policiesCfg.APIKeyClientMap = make(map[string][]APIKeyClient) + policiesCfg.APIKeyClientMap[apiMapName] = policiesCfg.APIKeyClients + } dosCfg := generateDosCfg(dosResources[""]) @@ -571,9 +571,7 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( vsNamespace: vsEx.VirtualServer.Namespace, vsName: vsEx.VirtualServer.Name, } - routePoliciesCfg, mapsFromPolicies := vsc.generatePolicies(ownerDetails, r.Policies, vsEx.Policies, routeContext, policyOpts) - maps = append(maps, mapsFromPolicies...) - + routePoliciesCfg := vsc.generatePolicies(ownerDetails, r.Policies, vsEx.Policies, routeContext, policyOpts) if policiesCfg.OIDC { routePoliciesCfg.OIDC = policiesCfg.OIDC } @@ -589,6 +587,16 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( policiesCfg.JWTAuthList[jwtAuthKey] = routePoliciesCfg.JWTAuth } } + if routePoliciesCfg.APIKeyEnabled { + policiesCfg.APIKeyEnabled = routePoliciesCfg.APIKeyEnabled + apiMapName := routePoliciesCfg.APIKey.MapName + if policiesCfg.APIKeyClientMap == nil { + policiesCfg.APIKeyClientMap = make(map[string][]APIKeyClient) + } + if _, exists := policiesCfg.APIKeyClientMap[apiMapName]; !exists { + policiesCfg.APIKeyClientMap[apiMapName] = routePoliciesCfg.APIKeyClients + } + } limitReqZones = append(limitReqZones, routePoliciesCfg.LimitReqZones...) dosRouteCfg := generateDosCfg(dosResources[r.Path]) @@ -703,8 +711,7 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( policyRefs = r.Policies context = subRouteContext } - routePoliciesCfg, mapsFromPolicies := vsc.generatePolicies(ownerDetails, policyRefs, vsEx.Policies, context, policyOpts) - maps = append(maps, mapsFromPolicies...) + routePoliciesCfg := vsc.generatePolicies(ownerDetails, policyRefs, vsEx.Policies, context, policyOpts) if policiesCfg.OIDC { routePoliciesCfg.OIDC = policiesCfg.OIDC } @@ -720,6 +727,17 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( policiesCfg.JWTAuthList[jwtAuthKey] = routePoliciesCfg.JWTAuth } } + if routePoliciesCfg.APIKeyEnabled { + policiesCfg.APIKeyEnabled = routePoliciesCfg.APIKeyEnabled + apiMapName := routePoliciesCfg.APIKey.MapName + if policiesCfg.APIKeyClientMap == nil { + policiesCfg.APIKeyClientMap = make(map[string][]APIKeyClient) + } + if _, exists := policiesCfg.APIKeyClientMap[apiMapName]; !exists { + policiesCfg.APIKeyClientMap[apiMapName] = routePoliciesCfg.APIKeyClients + } + } + limitReqZones = append(limitReqZones, routePoliciesCfg.LimitReqZones...) dosRouteCfg := generateDosCfg(dosResources[r.Path]) @@ -787,6 +805,10 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( } } + for mapName, apiKeyClients := range policiesCfg.APIKeyClientMap { + maps = append(maps, *generateAPIKeyClientMap(mapName, apiKeyClients)) + } + httpSnippets := generateSnippets(vsc.enableSnippets, vsEx.VirtualServer.Spec.HTTPSnippets, []string{}) serverSnippets := generateSnippets( vsc.enableSnippets, @@ -870,8 +892,10 @@ type policiesCfg struct { IngressMTLS *version2.IngressMTLS EgressMTLS *version2.EgressMTLS OIDC bool + APIKeyEnabled bool APIKey *version2.APIKey - APIKeyList map[string]*version2.APIKey + APIKeyClients []APIKeyClient + APIKeyClientMap map[string][]APIKeyClient WAF *version2.WAF ErrorReturn *version2.Return BundleValidator bundleValidator @@ -886,6 +910,11 @@ type internalBundleValidator struct { bundlePath string } +type APIKeyClient struct { + ClientID string + HashedKey string +} + func (i internalBundleValidator) validate(bundle string) (string, error) { bundle = path.Join(i.bundlePath, bundle) _, err := os.Stat(bundle) @@ -1308,19 +1337,17 @@ func (p *policiesCfg) addAPIKeyConfig( polNamespace string, secretRefs map[string]*secrets.SecretReference, context string, -) (*validationResults, *version2.Map) { +) *validationResults { res := newValidationResults() - var clients []version2.Client if p.APIKey != nil { res.addWarningf( "Multiple APIKey policies in the same context is not valid. APIKey policy %s will be ignored", polKey, ) res.isError = true - return res, nil + return res } - // if apiKey.ClientSecret != "" { secretKey := fmt.Sprintf("%v/%v", polNamespace, apiKey.ClientSecret) glog.Infof("secretKey: %v", secretKey) secretRef := secretRefs[secretKey] @@ -1333,57 +1360,65 @@ func (p *policiesCfg) addAPIKeyConfig( if secretType != "" && secretType != secrets.SecretTypeAPIKey { res.addWarningf("API Key policy %s references a secret %s of a wrong type '%s', must be '%s'", polKey, secretKey, secretType, secrets.SecretTypeAPIKey) res.isError = true - return res, nil + return res } else if secretRef.Error != nil { res.addWarningf("API Key %s references an invalid secret %s: %v", polKey, secretKey, secretRef.Error) res.isError = true - return res, nil + return res + } + + p.APIKeyClients = generateAPIKeyClients(secretRef.Secret.Data) + + mapName := fmt.Sprintf("apikey_auth_client_name_%s", strings.Split(strings.Replace(polKey, "-", "_", -1), "/")[1]) + p.APIKey = &version2.APIKey{ + Header: apiKey.SuppliedIn.Header, + Query: apiKey.SuppliedIn.Query, + MapName: mapName, } + p.APIKeyEnabled = true + return res +} - for clientID, clientSecret := range secretRef.Secret.Data { +func generateAPIKeyClients(secretData map[string][]byte) []APIKeyClient { + var clients []APIKeyClient + for clientID, apiKey := range secretData { h := sha256.New() - h.Write([]byte(clientSecret)) + h.Write([]byte(apiKey)) sha256Hash := hex.EncodeToString(h.Sum(nil)) base64Str := base64.URLEncoding.EncodeToString(h.Sum(nil)) - glog.Infof("clientSecret %s", clientSecret) + glog.Infof("apiKey %s", apiKey) glog.Infof("sha %s", sha256Hash) glog.Infof("base64Str %s", base64Str) - clients = append(clients, version2.Client{ClientID: clientID, EncryptedKey: sha256Hash}) // + clients = append(clients, APIKeyClient{ClientID: clientID, HashedKey: sha256Hash}) // } + return clients +} - default_parameter := version2.Parameter{ +func generateAPIKeyClientMap(mapName string, apiKeyClients []APIKeyClient) *version2.Map { + glog.Infof("mapName: %v, apiKeyClients: %v", mapName, apiKeyClients) + + defaultParam := version2.Parameter{ Value: "default", Result: "\"\"", } - params := []version2.Parameter{default_parameter} - for _, client := range clients { + params := []version2.Parameter{defaultParam} + for _, client := range apiKeyClients { params = append(params, version2.Parameter{ - Value: fmt.Sprintf("\"%s\"", client.EncryptedKey), + Value: fmt.Sprintf("\"%s\"", client.HashedKey), Result: fmt.Sprintf("\"%s\"", client.ClientID), }) } sourceName := "$apikey_auth_token" - mapName := fmt.Sprintf("apikey_auth_client_name_%s", strings.Split(strings.Replace(polKey, "-", "_", -1), "/")[1]) - shaToClient := &version2.Map{ + return &version2.Map{ Source: sourceName, Variable: fmt.Sprintf("$%s", mapName), Parameters: params, } - - p.APIKey = &version2.APIKey{ - Header: apiKey.SuppliedIn.Header, - Query: apiKey.SuppliedIn.Query, - RejectCodeNotSupplied: generateIntFromPointer(apiKey.RejectCodes.NotSupplied, 401), - RejectCodeNoMatch: generateIntFromPointer(apiKey.RejectCodes.NoMatch, 403), - Clients: clients, - MapName: mapName, - } - return res, shaToClient } func (p *policiesCfg) addWAFConfig( @@ -1474,9 +1509,8 @@ func (vsc *virtualServerConfigurator) generatePolicies( policies map[string]*conf_v1.Policy, context string, policyOpts policyOptions, -) (policiesCfg, []version2.Map) { +) policiesCfg { config := newPoliciesConfig(vsc.bundleValidator) - maps := []version2.Map{} for _, p := range policyRefs { polNamespace := p.Namespace @@ -1518,44 +1552,27 @@ func (vsc *virtualServerConfigurator) generatePolicies( case pol.Spec.OIDC != nil: res = config.addOIDCConfig(pol.Spec.OIDC, key, polNamespace, policyOpts.secretRefs, vsc.oidcPolCfg) case pol.Spec.APIKey != nil: - res, shaToClientMap := config.addAPIKeyConfig(pol.Spec.APIKey, key, polNamespace, policyOpts.secretRefs, context) - // TODO: refactor - if res != nil && len(res.warnings) > 0 { - vsc.addWarnings(ownerDetails.owner, res.warnings) - } - - if res != nil && res.isError { - return policiesCfg{ - ErrorReturn: &version2.Return{Code: 500}, - }, maps - } - if res != nil && !res.isError && shaToClientMap != nil { - maps = append(maps, *shaToClientMap) - } - + res = config.addAPIKeyConfig(pol.Spec.APIKey, key, polNamespace, policyOpts.secretRefs, context) case pol.Spec.WAF != nil: res = config.addWAFConfig(pol.Spec.WAF, key, polNamespace, policyOpts.apResources) default: res = newValidationResults() } - if res != nil && len(res.warnings) > 0 { - vsc.addWarnings(ownerDetails.owner, res.warnings) - } - - if res != nil && res.isError { + vsc.addWarnings(ownerDetails.owner, res.warnings) + if res.isError { return policiesCfg{ ErrorReturn: &version2.Return{Code: 500}, - }, maps + } } } else { vsc.addWarningf(ownerDetails.owner, "Policy %s is missing or invalid", key) return policiesCfg{ ErrorReturn: &version2.Return{Code: 500}, - }, maps + } } } - return *config, maps + return *config } func generateLimitReq(zoneName string, rateLimitPol *conf_v1.RateLimit) version2.LimitReq { diff --git a/internal/configs/virtualserver_test.go b/internal/configs/virtualserver_test.go index b073f0e271..b74c5b7569 100644 --- a/internal/configs/virtualserver_test.go +++ b/internal/configs/virtualserver_test.go @@ -6284,9 +6284,9 @@ func TestGeneratePolicies(t *testing.T) { vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}, false, &fakeBV) for _, test := range tests { - result, maps := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, policyOpts) + result := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, policyOpts) // TODO test maps - println(maps) // temporary print to use the variable + // println(maps) // temporary print to use the variable result.BundleValidator = nil if diff := cmp.Diff(test.expected, result); diff != "" { t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff) @@ -6379,9 +6379,9 @@ func TestGeneratePolicies_GeneratesWAFPolicyOnValidApBundle(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}, false, &fakeBV) - res, maps := vsc.generatePolicies(ownerDetails, tc.policyRefs, tc.policies, tc.context, policyOptions{apResources: &appProtectResourcesForVS{}}) + res := vsc.generatePolicies(ownerDetails, tc.policyRefs, tc.policies, tc.context, policyOptions{apResources: &appProtectResourcesForVS{}}) // TODO test maps - println(maps) // temporary print to use the variable + // println(maps) // temporary print to use the variable res.BundleValidator = nil if !cmp.Equal(tc.want, res) { t.Error(cmp.Diff(tc.want, res)) @@ -7726,9 +7726,9 @@ func TestGeneratePoliciesFails(t *testing.T) { vsc.oidcPolCfg = test.oidcPolCfg } - result, maps := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, test.policyOpts) + result := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, test.policyOpts) // TODO test maps - println(maps) // Temporty print to use the variable + // println(maps) // Temporty print to use the variable result.BundleValidator = nil if diff := cmp.Diff(test.expected, result); diff != "" { t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff) diff --git a/internal/k8s/secrets/validation.go b/internal/k8s/secrets/validation.go index 53e69b46f0..2392e463b7 100644 --- a/internal/k8s/secrets/validation.go +++ b/internal/k8s/secrets/validation.go @@ -116,10 +116,15 @@ func ValidateAPIKeySecret(secret *api_v1.Secret) error { if secret.Type != SecretTypeAPIKey { return fmt.Errorf("APIKey secret must be of the type %v", SecretTypeAPIKey) } - //clientSecret, exists := secret.Data[ClientSecretKey] - //if !exists { - // return fmt.Errorf("OIDC secret must have the data field %v", ClientSecretKey) - //} + + uniqueKeys := make(map[string]bool) + for _, key := range secret.Data { + if uniqueKeys[string(key)] { + return fmt.Errorf("API Keys cannot be repeated") + } + uniqueKeys[string(key)] = true + } + return nil } @@ -147,7 +152,6 @@ func IsSupportedSecretType(secretType api_v1.SecretType) bool { secretType == SecretTypeOIDC || secretType == SecretTypeHtpasswd || secretType == SecretTypeAPIKey - // secretType == api_v1.SecretTypeOpaque } // ValidateSecret validates the secret. If it is valid, the function returns nil. diff --git a/internal/k8s/secrets/validation_test.go b/internal/k8s/secrets/validation_test.go index 4539ec4cc6..4297c9788f 100644 --- a/internal/k8s/secrets/validation_test.go +++ b/internal/k8s/secrets/validation_test.go @@ -65,6 +65,71 @@ func TestValidateJWKSecretFails(t *testing.T) { } } +func TestValidateValidateAPIKeySecret(t *testing.T) { + t.Parallel() + secret := &v1.Secret{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "api-key-secret", + Namespace: "default", + }, + Type: SecretTypeAPIKey, + Data: map[string][]byte{ + "client1": []byte("cGFzc3dvcmQ="), + "client2": []byte("N2ViNDMwOGItY2Q1Yi00NDEzLWI0NTUtYjMyZmQ4OTg2MmZk"), + }, + } + + err := ValidateAPIKeySecret(secret) + if err != nil { + t.Errorf("ValidateAPIKeySecret() returned error %v", err) + } +} + +func TestValidateValidateAPIKeyFails(t *testing.T) { + t.Parallel() + tests := []struct { + secret *v1.Secret + msg string + }{ + { + secret: &v1.Secret{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "api-key-secret", + Namespace: "default", + }, + Type: "some-type", + Data: map[string][]byte{ + "client": nil, + }, + }, + msg: "Incorrect type for API Key secret", + }, + { + secret: &v1.Secret{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "api-key-secret", + Namespace: "default", + }, + Type: SecretTypeAPIKey, + Data: map[string][]byte{ + "client1": []byte("cGFzc3dvcmQ="), + "client2": []byte("N2ViNDMwOGItY2Q1Yi00NDEzLWI0NTUtYjMyZmQ4OTg2MmZk"), + "client3": []byte("N2ViNDMwOGItY2Q1Yi00NDEzLWI0NTUtYjMyZmQ4OTg2MmZk"), + }, + }, + msg: "repeated API Keys for API Key secret", + }, + } + + for _, test := range tests { + err := ValidateAPIKeySecret(test.secret) + t.Logf("ValidateAPIKeySecret() returned error %v", err) + if err == nil { + t.Errorf("ValidateAPIKeySecret() returned no error for the case of %s", test.msg) + } + } +} + func TestValidateHtpasswdSecret(t *testing.T) { t.Parallel() secret := &v1.Secret{ diff --git a/pkg/apis/configuration/validation/policy.go b/pkg/apis/configuration/validation/policy.go index 8d845d42d2..5b28c7b375 100644 --- a/pkg/apis/configuration/validation/policy.go +++ b/pkg/apis/configuration/validation/policy.go @@ -283,11 +283,31 @@ func validateOIDC(oidc *v1.OIDC, fieldPath *field.Path) field.ErrorList { } func validateAPIKey(apiKey *v1.APIKey, fieldPath *field.Path) field.ErrorList { - // if api + allErrs := field.ErrorList{} + if apiKey.SuppliedIn.Query == nil && apiKey.SuppliedIn.Header == nil { + msg := "at least one query or header name must be provided" + allErrs = append(allErrs, field.Required(fieldPath.Child("SuppliedIn"), msg)) + } + + if apiKey.SuppliedIn.Header != nil { + for _, header := range apiKey.SuppliedIn.Header { + for _, msg := range validation.IsHTTPHeaderName(header) { + allErrs = append(allErrs, field.Invalid(fieldPath.Child("suppliedIn.header"), header, msg)) + } + } + } + + if apiKey.SuppliedIn.Query != nil { + for _, query := range apiKey.SuppliedIn.Query { + if err := ValidateEscapedString(query); err != nil { + allErrs = append(allErrs, field.Invalid(fieldPath.Child("suppliedIn.query"), query, err.Error())) + } + } + } + if apiKey.ClientSecret == "" { - return field.ErrorList{field.Required(fieldPath.Child("clientSecret"), "")} + allErrs = append(allErrs, field.Required(fieldPath.Child("clientSecret"), "")) } - allErrs := field.ErrorList{} allErrs = append(allErrs, validateSecretName(apiKey.ClientSecret, fieldPath.Child("clientSecret"))...)