1// Copyright 2015 go-swagger maintainers 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package validate 16 17import ( 18 "fmt" 19 "reflect" 20 21 "k8s.io/kube-openapi/pkg/validation/spec" 22 "k8s.io/kube-openapi/pkg/validation/strfmt" 23) 24 25type schemaPropsValidator struct { 26 Path string 27 In string 28 AllOf []spec.Schema 29 OneOf []spec.Schema 30 AnyOf []spec.Schema 31 Not *spec.Schema 32 Dependencies spec.Dependencies 33 anyOfValidators []SchemaValidator 34 allOfValidators []SchemaValidator 35 oneOfValidators []SchemaValidator 36 notValidator *SchemaValidator 37 Root interface{} 38 KnownFormats strfmt.Registry 39 Options SchemaValidatorOptions 40} 41 42func (s *schemaPropsValidator) SetPath(path string) { 43 s.Path = path 44} 45 46func newSchemaPropsValidator(path string, in string, allOf, oneOf, anyOf []spec.Schema, not *spec.Schema, deps spec.Dependencies, root interface{}, formats strfmt.Registry, options ...Option) *schemaPropsValidator { 47 var anyValidators []SchemaValidator 48 for _, v := range anyOf { 49 v := v 50 anyValidators = append(anyValidators, *NewSchemaValidator(&v, root, path, formats, options...)) 51 } 52 var allValidators []SchemaValidator 53 for _, v := range allOf { 54 v := v 55 allValidators = append(allValidators, *NewSchemaValidator(&v, root, path, formats, options...)) 56 } 57 var oneValidators []SchemaValidator 58 for _, v := range oneOf { 59 v := v 60 oneValidators = append(oneValidators, *NewSchemaValidator(&v, root, path, formats, options...)) 61 } 62 63 var notValidator *SchemaValidator 64 if not != nil { 65 notValidator = NewSchemaValidator(not, root, path, formats, options...) 66 } 67 68 schOptions := &SchemaValidatorOptions{} 69 for _, o := range options { 70 o(schOptions) 71 } 72 return &schemaPropsValidator{ 73 Path: path, 74 In: in, 75 AllOf: allOf, 76 OneOf: oneOf, 77 AnyOf: anyOf, 78 Not: not, 79 Dependencies: deps, 80 anyOfValidators: anyValidators, 81 allOfValidators: allValidators, 82 oneOfValidators: oneValidators, 83 notValidator: notValidator, 84 Root: root, 85 KnownFormats: formats, 86 Options: *schOptions, 87 } 88} 89 90func (s *schemaPropsValidator) Applies(source interface{}, kind reflect.Kind) bool { 91 r := reflect.TypeOf(source) == specSchemaType 92 debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind) 93 return r 94} 95 96func (s *schemaPropsValidator) Validate(data interface{}) *Result { 97 mainResult := new(Result) 98 99 // Intermediary error results 100 101 // IMPORTANT! messages from underlying validators 102 keepResultAnyOf := new(Result) 103 keepResultOneOf := new(Result) 104 keepResultAllOf := new(Result) 105 106 // Validates at least one in anyOf schemas 107 var firstSuccess *Result 108 if len(s.anyOfValidators) > 0 { 109 var bestFailures *Result 110 succeededOnce := false 111 for _, anyOfSchema := range s.anyOfValidators { 112 result := anyOfSchema.Validate(data) 113 // We keep inner IMPORTANT! errors no matter what MatchCount tells us 114 keepResultAnyOf.Merge(result.keepRelevantErrors()) 115 if result.IsValid() { 116 bestFailures = nil 117 succeededOnce = true 118 if firstSuccess == nil { 119 firstSuccess = result 120 } 121 keepResultAnyOf = new(Result) 122 break 123 } 124 // MatchCount is used to select errors from the schema with most positive checks 125 if bestFailures == nil || result.MatchCount > bestFailures.MatchCount { 126 bestFailures = result 127 } 128 } 129 130 if !succeededOnce { 131 mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path)) 132 } 133 if bestFailures != nil { 134 mainResult.Merge(bestFailures) 135 } else if firstSuccess != nil { 136 mainResult.Merge(firstSuccess) 137 } 138 } 139 140 // Validates exactly one in oneOf schemas 141 if len(s.oneOfValidators) > 0 { 142 var bestFailures *Result 143 var firstSuccess *Result 144 validated := 0 145 146 for _, oneOfSchema := range s.oneOfValidators { 147 result := oneOfSchema.Validate(data) 148 // We keep inner IMPORTANT! errors no matter what MatchCount tells us 149 keepResultOneOf.Merge(result.keepRelevantErrors()) 150 if result.IsValid() { 151 validated++ 152 bestFailures = nil 153 if firstSuccess == nil { 154 firstSuccess = result 155 } 156 keepResultOneOf = new(Result) 157 continue 158 } 159 // MatchCount is used to select errors from the schema with most positive checks 160 if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) { 161 bestFailures = result 162 } 163 } 164 165 if validated != 1 { 166 additionalMsg := "" 167 if validated == 0 { 168 additionalMsg = "Found none valid" 169 } else { 170 additionalMsg = fmt.Sprintf("Found %d valid alternatives", validated) 171 } 172 173 mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, additionalMsg)) 174 if bestFailures != nil { 175 mainResult.Merge(bestFailures) 176 } 177 } else if firstSuccess != nil { 178 mainResult.Merge(firstSuccess) 179 } 180 } 181 182 // Validates all of allOf schemas 183 if len(s.allOfValidators) > 0 { 184 validated := 0 185 186 for _, allOfSchema := range s.allOfValidators { 187 result := allOfSchema.Validate(data) 188 // We keep inner IMPORTANT! errors no matter what MatchCount tells us 189 keepResultAllOf.Merge(result.keepRelevantErrors()) 190 //keepResultAllOf.Merge(result) 191 if result.IsValid() { 192 validated++ 193 } 194 mainResult.Merge(result) 195 } 196 197 if validated != len(s.allOfValidators) { 198 additionalMsg := "" 199 if validated == 0 { 200 additionalMsg = ". None validated" 201 } 202 203 mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, additionalMsg)) 204 } 205 } 206 207 if s.notValidator != nil { 208 result := s.notValidator.Validate(data) 209 // We keep inner IMPORTANT! errors no matter what MatchCount tells us 210 if result.IsValid() { 211 mainResult.AddErrors(mustNotValidatechemaMsg(s.Path)) 212 } 213 } 214 215 if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map { 216 val := data.(map[string]interface{}) 217 for key := range val { 218 if dep, ok := s.Dependencies[key]; ok { 219 220 if dep.Schema != nil { 221 mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options.Options()...).Validate(data)) 222 continue 223 } 224 225 if len(dep.Property) > 0 { 226 for _, depKey := range dep.Property { 227 if _, ok := val[depKey]; !ok { 228 mainResult.AddErrors(hasADependencyMsg(s.Path, depKey)) 229 } 230 } 231 } 232 } 233 } 234 } 235 236 mainResult.Inc() 237 // In the end we retain best failures for schema validation 238 // plus, if any, composite errors which may explain special cases (tagged as IMPORTANT!). 239 return mainResult.Merge(keepResultAllOf, keepResultOneOf, keepResultAnyOf) 240} 241