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/errors" 23 "github.com/go-openapi/strfmt" 24 "github.com/go-openapi/swag" 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) 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) 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) 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) 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// RequiredString validates a string for requiredness 125func RequiredString(path, in, data string) *errors.Validation { 126 if data == "" { 127 return errors.Required(path, in) 128 } 129 return nil 130} 131 132// RequiredNumber validates a number for requiredness 133func RequiredNumber(path, in string, data float64) *errors.Validation { 134 if data == 0 { 135 return errors.Required(path, in) 136 } 137 return nil 138} 139 140// Pattern validates a string against a regular expression 141func Pattern(path, in, data, pattern string) *errors.Validation { 142 re, err := compileRegexp(pattern) 143 if err != nil { 144 return errors.FailedPattern(path, in, fmt.Sprintf("%s, but pattern is invalid: %s", pattern, err.Error())) 145 } 146 if !re.MatchString(data) { 147 return errors.FailedPattern(path, in, pattern) 148 } 149 return nil 150} 151 152// MaximumInt validates if a number is smaller than a given maximum 153func MaximumInt(path, in string, data, max int64, exclusive bool) *errors.Validation { 154 if (!exclusive && data > max) || (exclusive && data >= max) { 155 return errors.ExceedsMaximumInt(path, in, max, exclusive) 156 } 157 return nil 158} 159 160// MaximumUint validates if a number is smaller than a given maximum 161func MaximumUint(path, in string, data, max uint64, exclusive bool) *errors.Validation { 162 if (!exclusive && data > max) || (exclusive && data >= max) { 163 return errors.ExceedsMaximumUint(path, in, max, exclusive) 164 } 165 return nil 166} 167 168// Maximum validates if a number is smaller than a given maximum 169func Maximum(path, in string, data, max float64, exclusive bool) *errors.Validation { 170 if (!exclusive && data > max) || (exclusive && data >= max) { 171 return errors.ExceedsMaximum(path, in, max, exclusive) 172 } 173 return nil 174} 175 176// Minimum validates if a number is smaller than a given minimum 177func Minimum(path, in string, data, min float64, exclusive bool) *errors.Validation { 178 if (!exclusive && data < min) || (exclusive && data <= min) { 179 return errors.ExceedsMinimum(path, in, min, exclusive) 180 } 181 return nil 182} 183 184// MinimumInt validates if a number is smaller than a given minimum 185func MinimumInt(path, in string, data, min int64, exclusive bool) *errors.Validation { 186 if (!exclusive && data < min) || (exclusive && data <= min) { 187 return errors.ExceedsMinimumInt(path, in, min, exclusive) 188 } 189 return nil 190} 191 192// MinimumUint validates if a number is smaller than a given minimum 193func MinimumUint(path, in string, data, min uint64, exclusive bool) *errors.Validation { 194 if (!exclusive && data < min) || (exclusive && data <= min) { 195 return errors.ExceedsMinimumUint(path, in, min, exclusive) 196 } 197 return nil 198} 199 200// MultipleOf validates if the provided number is a multiple of the factor 201func MultipleOf(path, in string, data, factor float64) *errors.Validation { 202 // multipleOf factor must be positive 203 if factor < 0 { 204 return errors.MultipleOfMustBePositive(path, in, factor) 205 } 206 var mult float64 207 if factor < 1 { 208 mult = 1 / factor * data 209 } else { 210 mult = data / factor 211 } 212 if !swag.IsFloat64AJSONInteger(mult) { 213 return errors.NotMultipleOf(path, in, factor) 214 } 215 return nil 216} 217 218// MultipleOfInt validates if the provided integer is a multiple of the factor 219func MultipleOfInt(path, in string, data int64, factor int64) *errors.Validation { 220 // multipleOf factor must be positive 221 if factor < 0 { 222 return errors.MultipleOfMustBePositive(path, in, factor) 223 } 224 mult := data / factor 225 if mult*factor != data { 226 return errors.NotMultipleOf(path, in, factor) 227 } 228 return nil 229} 230 231// MultipleOfUint validates if the provided unsigned integer is a multiple of the factor 232func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation { 233 mult := data / factor 234 if mult*factor != data { 235 return errors.NotMultipleOf(path, in, factor) 236 } 237 return nil 238} 239 240// FormatOf validates if a string matches a format in the format registry 241func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation { 242 if registry == nil { 243 registry = strfmt.Default 244 } 245 if ok := registry.ContainsName(format); !ok { 246 return errors.InvalidTypeName(format) 247 } 248 if ok := registry.Validates(format, data); !ok { 249 return errors.InvalidType(path, in, format, data) 250 } 251 return nil 252} 253 254// MaximumNativeType provides native type constraint validation as a facade 255// to various numeric types versions of Maximum constraint check. 256// 257// Assumes that any possible loss conversion during conversion has been 258// checked beforehand. 259// 260// NOTE: currently, the max value is marshalled as a float64, no matter what, 261// which means there may be a loss during conversions (e.g. for very large integers) 262// 263// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free 264func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation { 265 kind := reflect.ValueOf(val).Type().Kind() 266 switch kind { 267 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 268 value := valueHelp.asInt64(val) 269 return MaximumInt(path, in, value, int64(max), exclusive) 270 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 271 value := valueHelp.asUint64(val) 272 if max < 0 { 273 return errors.ExceedsMaximum(path, in, max, exclusive) 274 } 275 return MaximumUint(path, in, value, uint64(max), exclusive) 276 case reflect.Float32, reflect.Float64: 277 fallthrough 278 default: 279 value := valueHelp.asFloat64(val) 280 return Maximum(path, in, value, max, exclusive) 281 } 282} 283 284// MinimumNativeType provides native type constraint validation as a facade 285// to various numeric types versions of Minimum constraint check. 286// 287// Assumes that any possible loss conversion during conversion has been 288// checked beforehand. 289// 290// NOTE: currently, the min value is marshalled as a float64, no matter what, 291// which means there may be a loss during conversions (e.g. for very large integers) 292// 293// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free 294func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation { 295 kind := reflect.ValueOf(val).Type().Kind() 296 switch kind { 297 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 298 value := valueHelp.asInt64(val) 299 return MinimumInt(path, in, value, int64(min), exclusive) 300 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 301 value := valueHelp.asUint64(val) 302 if min < 0 { 303 return nil 304 } 305 return MinimumUint(path, in, value, uint64(min), exclusive) 306 case reflect.Float32, reflect.Float64: 307 fallthrough 308 default: 309 value := valueHelp.asFloat64(val) 310 return Minimum(path, in, value, min, exclusive) 311 } 312} 313 314// MultipleOfNativeType provides native type constraint validation as a facade 315// to various numeric types version of MultipleOf constraint check. 316// 317// Assumes that any possible loss conversion during conversion has been 318// checked beforehand. 319// 320// NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what, 321// which means there may be a loss during conversions (e.g. for very large integers) 322// 323// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free 324func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation { 325 kind := reflect.ValueOf(val).Type().Kind() 326 switch kind { 327 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 328 value := valueHelp.asInt64(val) 329 return MultipleOfInt(path, in, value, int64(multipleOf)) 330 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 331 value := valueHelp.asUint64(val) 332 return MultipleOfUint(path, in, value, uint64(multipleOf)) 333 case reflect.Float32, reflect.Float64: 334 fallthrough 335 default: 336 value := valueHelp.asFloat64(val) 337 return MultipleOf(path, in, value, multipleOf) 338 } 339} 340 341// IsValueValidAgainstRange checks that a numeric value is compatible with 342// the range defined by Type and Format, that is, may be converted without loss. 343// 344// NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L 345func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error { 346 kind := reflect.ValueOf(val).Type().Kind() 347 348 // What is the string representation of val 349 stringRep := "" 350 switch kind { 351 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 352 stringRep = swag.FormatUint64(valueHelp.asUint64(val)) 353 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 354 stringRep = swag.FormatInt64(valueHelp.asInt64(val)) 355 case reflect.Float32, reflect.Float64: 356 stringRep = swag.FormatFloat64(valueHelp.asFloat64(val)) 357 default: 358 return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path) 359 } 360 361 var errVal error 362 363 switch typeName { 364 case "integer": 365 switch format { 366 case "int32": 367 _, errVal = swag.ConvertInt32(stringRep) 368 case "uint32": 369 _, errVal = swag.ConvertUint32(stringRep) 370 case "uint64": 371 _, errVal = swag.ConvertUint64(stringRep) 372 case "int64": 373 fallthrough 374 default: 375 _, errVal = swag.ConvertInt64(stringRep) 376 } 377 case "number": 378 fallthrough 379 default: 380 switch format { 381 case "float", "float32": 382 _, errVal = swag.ConvertFloat32(stringRep) 383 case "double", "float64": 384 fallthrough 385 default: 386 // No check can be performed here since 387 // no number beyond float64 is supported 388 } 389 } 390 if errVal != nil { // We don't report the actual errVal from strconv 391 if format != "" { 392 errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path) 393 } else { 394 errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path) 395 } 396 } 397 return errVal 398} 399