k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/validate/values.go (about) 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 15 package validate 16 17 import ( 18 "fmt" 19 "reflect" 20 "unicode/utf8" 21 22 "github.com/go-openapi/swag" 23 "k8s.io/kube-openapi/pkg/validation/errors" 24 "k8s.io/kube-openapi/pkg/validation/strfmt" 25 ) 26 27 // Enum validates if the data is a member of the enum 28 func Enum(path, in string, data interface{}, enum interface{}) *errors.Validation { 29 val := reflect.ValueOf(enum) 30 if val.Kind() != reflect.Slice { 31 return nil 32 } 33 34 var values []interface{} 35 for i := 0; i < val.Len(); i++ { 36 ele := val.Index(i) 37 enumValue := ele.Interface() 38 if data != nil { 39 if reflect.DeepEqual(data, enumValue) { 40 return nil 41 } 42 actualType := reflect.TypeOf(enumValue) 43 if actualType == nil { // Safeguard. Frankly, I don't know how we may get a nil 44 continue 45 } 46 expectedValue := reflect.ValueOf(data) 47 if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { 48 // Attempt comparison after type conversion 49 if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) { 50 return nil 51 } 52 } 53 } 54 values = append(values, enumValue) 55 } 56 return errors.EnumFail(path, in, data, values) 57 } 58 59 // MinItems validates that there are at least n items in a slice 60 func MinItems(path, in string, size, min int64) *errors.Validation { 61 if size < min { 62 return errors.TooFewItems(path, in, min, size) 63 } 64 return nil 65 } 66 67 // MaxItems validates that there are at most n items in a slice 68 func MaxItems(path, in string, size, max int64) *errors.Validation { 69 if size > max { 70 return errors.TooManyItems(path, in, max, size) 71 } 72 return nil 73 } 74 75 // UniqueItems validates that the provided slice has unique elements 76 func UniqueItems(path, in string, data interface{}) *errors.Validation { 77 val := reflect.ValueOf(data) 78 if val.Kind() != reflect.Slice { 79 return nil 80 } 81 var unique []interface{} 82 for i := 0; i < val.Len(); i++ { 83 v := val.Index(i).Interface() 84 for _, u := range unique { 85 if reflect.DeepEqual(v, u) { 86 return errors.DuplicateItems(path, in) 87 } 88 } 89 unique = append(unique, v) 90 } 91 return nil 92 } 93 94 // MinLength validates a string for minimum length 95 func MinLength(path, in, data string, minLength int64) *errors.Validation { 96 strLen := int64(utf8.RuneCount([]byte(data))) 97 if strLen < minLength { 98 return errors.TooShort(path, in, minLength, data) 99 } 100 return nil 101 } 102 103 // MaxLength validates a string for maximum length 104 func MaxLength(path, in, data string, maxLength int64) *errors.Validation { 105 strLen := int64(utf8.RuneCount([]byte(data))) 106 if strLen > maxLength { 107 return errors.TooLong(path, in, maxLength, data) 108 } 109 return nil 110 } 111 112 // Required validates an interface for requiredness 113 func Required(path, in string, data interface{}) *errors.Validation { 114 val := reflect.ValueOf(data) 115 if val.IsValid() { 116 if reflect.DeepEqual(reflect.Zero(val.Type()).Interface(), val.Interface()) { 117 return errors.Required(path, in) 118 } 119 return nil 120 } 121 return errors.Required(path, in) 122 } 123 124 // Pattern validates a string against a regular expression 125 func Pattern(path, in, data, pattern string) *errors.Validation { 126 re, err := compileRegexp(pattern) 127 if err != nil { 128 return errors.FailedPattern(path, in, fmt.Sprintf("%s, but pattern is invalid: %s", pattern, err.Error()), data) 129 } 130 if !re.MatchString(data) { 131 return errors.FailedPattern(path, in, pattern, data) 132 } 133 return nil 134 } 135 136 // MaximumInt validates if a number is smaller than a given maximum 137 func MaximumInt(path, in string, data, max int64, exclusive bool) *errors.Validation { 138 if (!exclusive && data > max) || (exclusive && data >= max) { 139 return errors.ExceedsMaximumInt(path, in, max, exclusive, data) 140 } 141 return nil 142 } 143 144 // MaximumUint validates if a number is smaller than a given maximum 145 func MaximumUint(path, in string, data, max uint64, exclusive bool) *errors.Validation { 146 if (!exclusive && data > max) || (exclusive && data >= max) { 147 return errors.ExceedsMaximumUint(path, in, max, exclusive, data) 148 } 149 return nil 150 } 151 152 // Maximum validates if a number is smaller than a given maximum 153 func Maximum(path, in string, data, max float64, exclusive bool) *errors.Validation { 154 if (!exclusive && data > max) || (exclusive && data >= max) { 155 return errors.ExceedsMaximum(path, in, max, exclusive, data) 156 } 157 return nil 158 } 159 160 // Minimum validates if a number is smaller than a given minimum 161 func Minimum(path, in string, data, min float64, exclusive bool) *errors.Validation { 162 if (!exclusive && data < min) || (exclusive && data <= min) { 163 return errors.ExceedsMinimum(path, in, min, exclusive, data) 164 } 165 return nil 166 } 167 168 // MinimumInt validates if a number is smaller than a given minimum 169 func MinimumInt(path, in string, data, min int64, exclusive bool) *errors.Validation { 170 if (!exclusive && data < min) || (exclusive && data <= min) { 171 return errors.ExceedsMinimumInt(path, in, min, exclusive, data) 172 } 173 return nil 174 } 175 176 // MinimumUint validates if a number is smaller than a given minimum 177 func MinimumUint(path, in string, data, min uint64, exclusive bool) *errors.Validation { 178 if (!exclusive && data < min) || (exclusive && data <= min) { 179 return errors.ExceedsMinimumUint(path, in, min, exclusive, data) 180 } 181 return nil 182 } 183 184 // MultipleOf validates if the provided number is a multiple of the factor 185 func MultipleOf(path, in string, data, factor float64) *errors.Validation { 186 // multipleOf factor must be positive 187 if factor <= 0 { 188 return errors.MultipleOfMustBePositive(path, in, factor) 189 } 190 var mult float64 191 if factor < 1 { 192 mult = 1 / factor * data 193 } else { 194 mult = data / factor 195 } 196 if !swag.IsFloat64AJSONInteger(mult) { 197 return errors.NotMultipleOf(path, in, factor, data) 198 } 199 return nil 200 } 201 202 // MultipleOfInt validates if the provided integer is a multiple of the factor 203 func MultipleOfInt(path, in string, data int64, factor int64) *errors.Validation { 204 // multipleOf factor must be positive 205 if factor <= 0 { 206 return errors.MultipleOfMustBePositive(path, in, factor) 207 } 208 mult := data / factor 209 if mult*factor != data { 210 return errors.NotMultipleOf(path, in, factor, data) 211 } 212 return nil 213 } 214 215 // MultipleOfUint validates if the provided unsigned integer is a multiple of the factor 216 func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation { 217 // multipleOf factor must be positive 218 if factor == 0 { 219 return errors.MultipleOfMustBePositive(path, in, factor) 220 } 221 mult := data / factor 222 if mult*factor != data { 223 return errors.NotMultipleOf(path, in, factor, data) 224 } 225 return nil 226 } 227 228 // FormatOf validates if a string matches a format in the format registry 229 func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation { 230 if registry == nil { 231 registry = strfmt.Default 232 } 233 if ok := registry.ContainsName(format); !ok { 234 return errors.InvalidTypeName(format) 235 } 236 if ok := registry.Validates(format, data); !ok { 237 return errors.InvalidType(path, in, format, data) 238 } 239 return nil 240 } 241 242 // MaximumNativeType provides native type constraint validation as a facade 243 // to various numeric types versions of Maximum constraint check. 244 // 245 // Assumes that any possible loss conversion during conversion has been 246 // checked beforehand. 247 // 248 // NOTE: currently, the max value is marshalled as a float64, no matter what, 249 // which means there may be a loss during conversions (e.g. for very large integers) 250 // 251 // TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free 252 func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation { 253 kind := reflect.ValueOf(val).Type().Kind() 254 switch kind { 255 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 256 value := valueHelp.asInt64(val) 257 return MaximumInt(path, in, value, int64(max), exclusive) 258 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 259 value := valueHelp.asUint64(val) 260 if max < 0 { 261 return errors.ExceedsMaximum(path, in, max, exclusive, val) 262 } 263 return MaximumUint(path, in, value, uint64(max), exclusive) 264 case reflect.Float32, reflect.Float64: 265 fallthrough 266 default: 267 value := valueHelp.asFloat64(val) 268 return Maximum(path, in, value, max, exclusive) 269 } 270 } 271 272 // MinimumNativeType provides native type constraint validation as a facade 273 // to various numeric types versions of Minimum constraint check. 274 // 275 // Assumes that any possible loss conversion during conversion has been 276 // checked beforehand. 277 // 278 // NOTE: currently, the min value is marshalled as a float64, no matter what, 279 // which means there may be a loss during conversions (e.g. for very large integers) 280 // 281 // TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free 282 func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation { 283 kind := reflect.ValueOf(val).Type().Kind() 284 switch kind { 285 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 286 value := valueHelp.asInt64(val) 287 return MinimumInt(path, in, value, int64(min), exclusive) 288 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 289 value := valueHelp.asUint64(val) 290 if min < 0 { 291 return nil 292 } 293 return MinimumUint(path, in, value, uint64(min), exclusive) 294 case reflect.Float32, reflect.Float64: 295 fallthrough 296 default: 297 value := valueHelp.asFloat64(val) 298 return Minimum(path, in, value, min, exclusive) 299 } 300 } 301 302 // MultipleOfNativeType provides native type constraint validation as a facade 303 // to various numeric types version of MultipleOf constraint check. 304 // 305 // Assumes that any possible loss conversion during conversion has been 306 // checked beforehand. 307 // 308 // NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what, 309 // which means there may be a loss during conversions (e.g. for very large integers) 310 // 311 // TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free 312 func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation { 313 kind := reflect.ValueOf(val).Type().Kind() 314 switch kind { 315 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 316 value := valueHelp.asInt64(val) 317 return MultipleOfInt(path, in, value, int64(multipleOf)) 318 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 319 value := valueHelp.asUint64(val) 320 return MultipleOfUint(path, in, value, uint64(multipleOf)) 321 case reflect.Float32, reflect.Float64: 322 fallthrough 323 default: 324 value := valueHelp.asFloat64(val) 325 return MultipleOf(path, in, value, multipleOf) 326 } 327 } 328 329 // IsValueValidAgainstRange checks that a numeric value is compatible with 330 // the range defined by Type and Format, that is, may be converted without loss. 331 // 332 // NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L 333 func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error { 334 kind := reflect.ValueOf(val).Type().Kind() 335 336 // What is the string representation of val 337 stringRep := "" 338 switch kind { 339 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 340 stringRep = swag.FormatUint64(valueHelp.asUint64(val)) 341 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 342 stringRep = swag.FormatInt64(valueHelp.asInt64(val)) 343 case reflect.Float32, reflect.Float64: 344 stringRep = swag.FormatFloat64(valueHelp.asFloat64(val)) 345 default: 346 return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path) 347 } 348 349 var errVal error 350 351 switch typeName { 352 case integerType: 353 switch format { 354 case integerFormatInt32: 355 _, errVal = swag.ConvertInt32(stringRep) 356 case integerFormatUInt32: 357 _, errVal = swag.ConvertUint32(stringRep) 358 case integerFormatUInt64: 359 _, errVal = swag.ConvertUint64(stringRep) 360 case integerFormatInt64: 361 fallthrough 362 default: 363 _, errVal = swag.ConvertInt64(stringRep) 364 } 365 case numberType: 366 fallthrough 367 default: 368 switch format { 369 case numberFormatFloat, numberFormatFloat32: 370 _, errVal = swag.ConvertFloat32(stringRep) 371 case numberFormatDouble, numberFormatFloat64: 372 fallthrough 373 default: 374 // No check can be performed here since 375 // no number beyond float64 is supported 376 } 377 } 378 if errVal != nil { // We don't report the actual errVal from strconv 379 if format != "" { 380 errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path) 381 } else { 382 errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path) 383 } 384 } 385 return errVal 386 }