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 "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 28func 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 60func 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 68func 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 76func 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 95func 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 104func 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 113func 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 125func 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 137func 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 145func 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 153func 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 161func 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 169func 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 177func 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 185func 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 203func 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 216func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation { 217 mult := data / factor 218 if mult*factor != data { 219 return errors.NotMultipleOf(path, in, factor, data) 220 } 221 return nil 222} 223 224// FormatOf validates if a string matches a format in the format registry 225func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation { 226 if registry == nil { 227 registry = strfmt.Default 228 } 229 if ok := registry.ContainsName(format); !ok { 230 return errors.InvalidTypeName(format) 231 } 232 if ok := registry.Validates(format, data); !ok { 233 return errors.InvalidType(path, in, format, data) 234 } 235 return nil 236} 237 238// MaximumNativeType provides native type constraint validation as a facade 239// to various numeric types versions of Maximum constraint check. 240// 241// Assumes that any possible loss conversion during conversion has been 242// checked beforehand. 243// 244// NOTE: currently, the max value is marshalled as a float64, no matter what, 245// which means there may be a loss during conversions (e.g. for very large integers) 246// 247// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free 248func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation { 249 kind := reflect.ValueOf(val).Type().Kind() 250 switch kind { 251 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 252 value := valueHelp.asInt64(val) 253 return MaximumInt(path, in, value, int64(max), exclusive) 254 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 255 value := valueHelp.asUint64(val) 256 if max < 0 { 257 return errors.ExceedsMaximum(path, in, max, exclusive, val) 258 } 259 return MaximumUint(path, in, value, uint64(max), exclusive) 260 case reflect.Float32, reflect.Float64: 261 fallthrough 262 default: 263 value := valueHelp.asFloat64(val) 264 return Maximum(path, in, value, max, exclusive) 265 } 266} 267 268// MinimumNativeType provides native type constraint validation as a facade 269// to various numeric types versions of Minimum constraint check. 270// 271// Assumes that any possible loss conversion during conversion has been 272// checked beforehand. 273// 274// NOTE: currently, the min value is marshalled as a float64, no matter what, 275// which means there may be a loss during conversions (e.g. for very large integers) 276// 277// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free 278func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation { 279 kind := reflect.ValueOf(val).Type().Kind() 280 switch kind { 281 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 282 value := valueHelp.asInt64(val) 283 return MinimumInt(path, in, value, int64(min), exclusive) 284 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 285 value := valueHelp.asUint64(val) 286 if min < 0 { 287 return nil 288 } 289 return MinimumUint(path, in, value, uint64(min), exclusive) 290 case reflect.Float32, reflect.Float64: 291 fallthrough 292 default: 293 value := valueHelp.asFloat64(val) 294 return Minimum(path, in, value, min, exclusive) 295 } 296} 297 298// MultipleOfNativeType provides native type constraint validation as a facade 299// to various numeric types version of MultipleOf constraint check. 300// 301// Assumes that any possible loss conversion during conversion has been 302// checked beforehand. 303// 304// NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what, 305// which means there may be a loss during conversions (e.g. for very large integers) 306// 307// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free 308func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation { 309 kind := reflect.ValueOf(val).Type().Kind() 310 switch kind { 311 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 312 value := valueHelp.asInt64(val) 313 return MultipleOfInt(path, in, value, int64(multipleOf)) 314 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 315 value := valueHelp.asUint64(val) 316 return MultipleOfUint(path, in, value, uint64(multipleOf)) 317 case reflect.Float32, reflect.Float64: 318 fallthrough 319 default: 320 value := valueHelp.asFloat64(val) 321 return MultipleOf(path, in, value, multipleOf) 322 } 323} 324 325// IsValueValidAgainstRange checks that a numeric value is compatible with 326// the range defined by Type and Format, that is, may be converted without loss. 327// 328// NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L 329func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error { 330 kind := reflect.ValueOf(val).Type().Kind() 331 332 // What is the string representation of val 333 stringRep := "" 334 switch kind { 335 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 336 stringRep = swag.FormatUint64(valueHelp.asUint64(val)) 337 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 338 stringRep = swag.FormatInt64(valueHelp.asInt64(val)) 339 case reflect.Float32, reflect.Float64: 340 stringRep = swag.FormatFloat64(valueHelp.asFloat64(val)) 341 default: 342 return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path) 343 } 344 345 var errVal error 346 347 switch typeName { 348 case integerType: 349 switch format { 350 case integerFormatInt32: 351 _, errVal = swag.ConvertInt32(stringRep) 352 case integerFormatUInt32: 353 _, errVal = swag.ConvertUint32(stringRep) 354 case integerFormatUInt64: 355 _, errVal = swag.ConvertUint64(stringRep) 356 case integerFormatInt64: 357 fallthrough 358 default: 359 _, errVal = swag.ConvertInt64(stringRep) 360 } 361 case numberType: 362 fallthrough 363 default: 364 switch format { 365 case numberFormatFloat, numberFormatFloat32: 366 _, errVal = swag.ConvertFloat32(stringRep) 367 case numberFormatDouble, numberFormatFloat64: 368 fallthrough 369 default: 370 // No check can be performed here since 371 // no number beyond float64 is supported 372 } 373 } 374 if errVal != nil { // We don't report the actual errVal from strconv 375 if format != "" { 376 errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path) 377 } else { 378 errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path) 379 } 380 } 381 return errVal 382} 383