1/* 2Package validation provides methods for validating parameter value using reflection. 3*/ 4package validation 5 6// Copyright 2017 Microsoft Corporation 7// 8// Licensed under the Apache License, Version 2.0 (the "License"); 9// you may not use this file except in compliance with the License. 10// You may obtain a copy of the License at 11// 12// http://www.apache.org/licenses/LICENSE-2.0 13// 14// Unless required by applicable law or agreed to in writing, software 15// distributed under the License is distributed on an "AS IS" BASIS, 16// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17// See the License for the specific language governing permissions and 18// limitations under the License. 19 20import ( 21 "fmt" 22 "reflect" 23 "regexp" 24 "strings" 25) 26 27// Disabled controls if parameter validation should be globally disabled. The default is false. 28var Disabled bool 29 30// Constraint stores constraint name, target field name 31// Rule and chain validations. 32type Constraint struct { 33 34 // Target field name for validation. 35 Target string 36 37 // Constraint name e.g. minLength, MaxLength, Pattern, etc. 38 Name string 39 40 // Rule for constraint e.g. greater than 10, less than 5 etc. 41 Rule interface{} 42 43 // Chain Validations for struct type 44 Chain []Constraint 45} 46 47// Validation stores parameter-wise validation. 48type Validation struct { 49 TargetValue interface{} 50 Constraints []Constraint 51} 52 53// Constraint list 54const ( 55 Empty = "Empty" 56 Null = "Null" 57 ReadOnly = "ReadOnly" 58 Pattern = "Pattern" 59 MaxLength = "MaxLength" 60 MinLength = "MinLength" 61 MaxItems = "MaxItems" 62 MinItems = "MinItems" 63 MultipleOf = "MultipleOf" 64 UniqueItems = "UniqueItems" 65 InclusiveMaximum = "InclusiveMaximum" 66 ExclusiveMaximum = "ExclusiveMaximum" 67 ExclusiveMinimum = "ExclusiveMinimum" 68 InclusiveMinimum = "InclusiveMinimum" 69) 70 71// Validate method validates constraints on parameter 72// passed in validation array. 73func Validate(m []Validation) error { 74 if Disabled { 75 return nil 76 } 77 for _, item := range m { 78 v := reflect.ValueOf(item.TargetValue) 79 for _, constraint := range item.Constraints { 80 var err error 81 switch v.Kind() { 82 case reflect.Ptr: 83 err = validatePtr(v, constraint) 84 case reflect.String: 85 err = validateString(v, constraint) 86 case reflect.Struct: 87 err = validateStruct(v, constraint) 88 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 89 err = validateInt(v, constraint) 90 case reflect.Float32, reflect.Float64: 91 err = validateFloat(v, constraint) 92 case reflect.Array, reflect.Slice, reflect.Map: 93 err = validateArrayMap(v, constraint) 94 default: 95 err = createError(v, constraint, fmt.Sprintf("unknown type %v", v.Kind())) 96 } 97 98 if err != nil { 99 return err 100 } 101 } 102 } 103 return nil 104} 105 106func validateStruct(x reflect.Value, v Constraint, name ...string) error { 107 //Get field name from target name which is in format a.b.c 108 s := strings.Split(v.Target, ".") 109 f := x.FieldByName(s[len(s)-1]) 110 if isZero(f) { 111 return createError(x, v, fmt.Sprintf("field %q doesn't exist", v.Target)) 112 } 113 114 return Validate([]Validation{ 115 { 116 TargetValue: getInterfaceValue(f), 117 Constraints: []Constraint{v}, 118 }, 119 }) 120} 121 122func validatePtr(x reflect.Value, v Constraint) error { 123 if v.Name == ReadOnly { 124 if !x.IsNil() { 125 return createError(x.Elem(), v, "readonly parameter; must send as nil or empty in request") 126 } 127 return nil 128 } 129 if x.IsNil() { 130 return checkNil(x, v) 131 } 132 if v.Chain != nil { 133 return Validate([]Validation{ 134 { 135 TargetValue: getInterfaceValue(x.Elem()), 136 Constraints: v.Chain, 137 }, 138 }) 139 } 140 return nil 141} 142 143func validateInt(x reflect.Value, v Constraint) error { 144 i := x.Int() 145 r, ok := toInt64(v.Rule) 146 if !ok { 147 return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule)) 148 } 149 switch v.Name { 150 case MultipleOf: 151 if i%r != 0 { 152 return createError(x, v, fmt.Sprintf("value must be a multiple of %v", r)) 153 } 154 case ExclusiveMinimum: 155 if i <= r { 156 return createError(x, v, fmt.Sprintf("value must be greater than %v", r)) 157 } 158 case ExclusiveMaximum: 159 if i >= r { 160 return createError(x, v, fmt.Sprintf("value must be less than %v", r)) 161 } 162 case InclusiveMinimum: 163 if i < r { 164 return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r)) 165 } 166 case InclusiveMaximum: 167 if i > r { 168 return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r)) 169 } 170 default: 171 return createError(x, v, fmt.Sprintf("constraint %v is not applicable for type integer", v.Name)) 172 } 173 return nil 174} 175 176func validateFloat(x reflect.Value, v Constraint) error { 177 f := x.Float() 178 r, ok := v.Rule.(float64) 179 if !ok { 180 return createError(x, v, fmt.Sprintf("rule must be float value for %v constraint; got: %v", v.Name, v.Rule)) 181 } 182 switch v.Name { 183 case ExclusiveMinimum: 184 if f <= r { 185 return createError(x, v, fmt.Sprintf("value must be greater than %v", r)) 186 } 187 case ExclusiveMaximum: 188 if f >= r { 189 return createError(x, v, fmt.Sprintf("value must be less than %v", r)) 190 } 191 case InclusiveMinimum: 192 if f < r { 193 return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r)) 194 } 195 case InclusiveMaximum: 196 if f > r { 197 return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r)) 198 } 199 default: 200 return createError(x, v, fmt.Sprintf("constraint %s is not applicable for type float", v.Name)) 201 } 202 return nil 203} 204 205func validateString(x reflect.Value, v Constraint) error { 206 s := x.String() 207 switch v.Name { 208 case Empty: 209 if len(s) == 0 { 210 return checkEmpty(x, v) 211 } 212 case Pattern: 213 reg, err := regexp.Compile(v.Rule.(string)) 214 if err != nil { 215 return createError(x, v, err.Error()) 216 } 217 if !reg.MatchString(s) { 218 return createError(x, v, fmt.Sprintf("value doesn't match pattern %v", v.Rule)) 219 } 220 case MaxLength: 221 if _, ok := v.Rule.(int); !ok { 222 return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule)) 223 } 224 if len(s) > v.Rule.(int) { 225 return createError(x, v, fmt.Sprintf("value length must be less than or equal to %v", v.Rule)) 226 } 227 case MinLength: 228 if _, ok := v.Rule.(int); !ok { 229 return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule)) 230 } 231 if len(s) < v.Rule.(int) { 232 return createError(x, v, fmt.Sprintf("value length must be greater than or equal to %v", v.Rule)) 233 } 234 case ReadOnly: 235 if len(s) > 0 { 236 return createError(reflect.ValueOf(s), v, "readonly parameter; must send as nil or empty in request") 237 } 238 default: 239 return createError(x, v, fmt.Sprintf("constraint %s is not applicable to string type", v.Name)) 240 } 241 242 if v.Chain != nil { 243 return Validate([]Validation{ 244 { 245 TargetValue: getInterfaceValue(x), 246 Constraints: v.Chain, 247 }, 248 }) 249 } 250 return nil 251} 252 253func validateArrayMap(x reflect.Value, v Constraint) error { 254 switch v.Name { 255 case Null: 256 if x.IsNil() { 257 return checkNil(x, v) 258 } 259 case Empty: 260 if x.IsNil() || x.Len() == 0 { 261 return checkEmpty(x, v) 262 } 263 case MaxItems: 264 if _, ok := v.Rule.(int); !ok { 265 return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule)) 266 } 267 if x.Len() > v.Rule.(int) { 268 return createError(x, v, fmt.Sprintf("maximum item limit is %v; got: %v", v.Rule, x.Len())) 269 } 270 case MinItems: 271 if _, ok := v.Rule.(int); !ok { 272 return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule)) 273 } 274 if x.Len() < v.Rule.(int) { 275 return createError(x, v, fmt.Sprintf("minimum item limit is %v; got: %v", v.Rule, x.Len())) 276 } 277 case UniqueItems: 278 if x.Kind() == reflect.Array || x.Kind() == reflect.Slice { 279 if !checkForUniqueInArray(x) { 280 return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x)) 281 } 282 } else if x.Kind() == reflect.Map { 283 if !checkForUniqueInMap(x) { 284 return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x)) 285 } 286 } else { 287 return createError(x, v, fmt.Sprintf("type must be array, slice or map for constraint %v; got: %v", v.Name, x.Kind())) 288 } 289 case ReadOnly: 290 if x.Len() != 0 { 291 return createError(x, v, "readonly parameter; must send as nil or empty in request") 292 } 293 case Pattern: 294 reg, err := regexp.Compile(v.Rule.(string)) 295 if err != nil { 296 return createError(x, v, err.Error()) 297 } 298 keys := x.MapKeys() 299 for _, k := range keys { 300 if !reg.MatchString(k.String()) { 301 return createError(k, v, fmt.Sprintf("map key doesn't match pattern %v", v.Rule)) 302 } 303 } 304 default: 305 return createError(x, v, fmt.Sprintf("constraint %v is not applicable to array, slice and map type", v.Name)) 306 } 307 308 if v.Chain != nil { 309 return Validate([]Validation{ 310 { 311 TargetValue: getInterfaceValue(x), 312 Constraints: v.Chain, 313 }, 314 }) 315 } 316 return nil 317} 318 319func checkNil(x reflect.Value, v Constraint) error { 320 if _, ok := v.Rule.(bool); !ok { 321 return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule)) 322 } 323 if v.Rule.(bool) { 324 return createError(x, v, "value can not be null; required parameter") 325 } 326 return nil 327} 328 329func checkEmpty(x reflect.Value, v Constraint) error { 330 if _, ok := v.Rule.(bool); !ok { 331 return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule)) 332 } 333 334 if v.Rule.(bool) { 335 return createError(x, v, "value can not be null or empty; required parameter") 336 } 337 return nil 338} 339 340func checkForUniqueInArray(x reflect.Value) bool { 341 if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 { 342 return false 343 } 344 arrOfInterface := make([]interface{}, x.Len()) 345 346 for i := 0; i < x.Len(); i++ { 347 arrOfInterface[i] = x.Index(i).Interface() 348 } 349 350 m := make(map[interface{}]bool) 351 for _, val := range arrOfInterface { 352 if m[val] { 353 return false 354 } 355 m[val] = true 356 } 357 return true 358} 359 360func checkForUniqueInMap(x reflect.Value) bool { 361 if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 { 362 return false 363 } 364 mapOfInterface := make(map[interface{}]interface{}, x.Len()) 365 366 keys := x.MapKeys() 367 for _, k := range keys { 368 mapOfInterface[k.Interface()] = x.MapIndex(k).Interface() 369 } 370 371 m := make(map[interface{}]bool) 372 for _, val := range mapOfInterface { 373 if m[val] { 374 return false 375 } 376 m[val] = true 377 } 378 return true 379} 380 381func getInterfaceValue(x reflect.Value) interface{} { 382 if x.Kind() == reflect.Invalid { 383 return nil 384 } 385 return x.Interface() 386} 387 388func isZero(x interface{}) bool { 389 return x == reflect.Zero(reflect.TypeOf(x)).Interface() 390} 391 392func createError(x reflect.Value, v Constraint, err string) error { 393 return fmt.Errorf("autorest/validation: validation failed: parameter=%s constraint=%s value=%#v details: %s", 394 v.Target, v.Name, getInterfaceValue(x), err) 395} 396 397func toInt64(v interface{}) (int64, bool) { 398 if i64, ok := v.(int64); ok { 399 return i64, true 400 } 401 // older generators emit max constants as int, so if int64 fails fall back to int 402 if i32, ok := v.(int); ok { 403 return int64(i32), true 404 } 405 return 0, false 406} 407