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 "reflect" 19 20 "k8s.io/kube-openapi/pkg/validation/errors" 21 "k8s.io/kube-openapi/pkg/validation/spec" 22) 23 24// An EntityValidator is an interface for things that can validate entities 25type EntityValidator interface { 26 Validate(interface{}) *Result 27} 28 29type valueValidator interface { 30 SetPath(path string) 31 Applies(interface{}, reflect.Kind) bool 32 Validate(interface{}) *Result 33} 34 35type basicCommonValidator struct { 36 Path string 37 In string 38 Default interface{} 39 Enum []interface{} 40} 41 42func (b *basicCommonValidator) SetPath(path string) { 43 b.Path = path 44} 45 46func (b *basicCommonValidator) Applies(source interface{}, kind reflect.Kind) bool { 47 switch source.(type) { 48 case *spec.Schema: 49 return true 50 } 51 return false 52} 53 54func (b *basicCommonValidator) Validate(data interface{}) (res *Result) { 55 if len(b.Enum) > 0 { 56 for _, enumValue := range b.Enum { 57 actualType := reflect.TypeOf(enumValue) 58 if actualType != nil { // Safeguard 59 expectedValue := reflect.ValueOf(data) 60 if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { 61 if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) { 62 return nil 63 } 64 } 65 } 66 } 67 return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum)) 68 } 69 return nil 70} 71 72type numberValidator struct { 73 Path string 74 In string 75 Default interface{} 76 MultipleOf *float64 77 Maximum *float64 78 ExclusiveMaximum bool 79 Minimum *float64 80 ExclusiveMinimum bool 81 // Allows for more accurate behavior regarding integers 82 Type string 83 Format string 84} 85 86func (n *numberValidator) SetPath(path string) { 87 n.Path = path 88} 89 90func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool { 91 switch source.(type) { 92 case *spec.Schema: 93 isInt := kind >= reflect.Int && kind <= reflect.Uint64 94 isFloat := kind == reflect.Float32 || kind == reflect.Float64 95 r := isInt || isFloat 96 debugLog("schema props validator for %q applies %t for %T (kind: %v) isInt=%t, isFloat=%t\n", n.Path, r, source, kind, isInt, isFloat) 97 return r 98 } 99 debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", n.Path, false, source, kind) 100 return false 101} 102 103// Validate provides a validator for generic JSON numbers, 104// 105// By default, numbers are internally represented as float64. 106// Formats float, or float32 may alter this behavior by mapping to float32. 107// A special validation process is followed for integers, with optional "format": 108// this is an attempt to provide a validation with native types. 109// 110// NOTE: since the constraint specified (boundary, multipleOf) is unmarshalled 111// as float64, loss of information remains possible (e.g. on very large integers). 112// 113// Since this value directly comes from the unmarshalling, it is not possible 114// at this stage of processing to check further and guarantee the correctness of such values. 115// 116// Normally, the JSON Number.MAX_SAFE_INTEGER (resp. Number.MIN_SAFE_INTEGER) 117// would check we do not get such a loss. 118// 119// If this is the case, replace AddErrors() by AddWarnings() and IsValid() by !HasWarnings(). 120// 121// TODO: consider replacing boundary check errors by simple warnings. 122// 123// TODO: default boundaries with MAX_SAFE_INTEGER are not checked (specific to json.Number?) 124func (n *numberValidator) Validate(val interface{}) *Result { 125 res := new(Result) 126 127 resMultiple := new(Result) 128 resMinimum := new(Result) 129 resMaximum := new(Result) 130 131 // Used only to attempt to validate constraint on value, 132 // even though value or constraint specified do not match type and format 133 data := valueHelp.asFloat64(val) 134 135 // Is the provided value within the range of the specified numeric type and format? 136 res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path)) 137 138 // nolint: dupl 139 if n.MultipleOf != nil { 140 // Is the constraint specifier within the range of the specific numeric type and format? 141 resMultiple.AddErrors(IsValueValidAgainstRange(*n.MultipleOf, n.Type, n.Format, "MultipleOf", n.Path)) 142 if resMultiple.IsValid() { 143 // Constraint validated with compatible types 144 if err := MultipleOfNativeType(n.Path, n.In, val, *n.MultipleOf); err != nil { 145 resMultiple.Merge(errorHelp.sErr(err)) 146 } 147 } else { 148 // Constraint nevertheless validated, converted as general number 149 if err := MultipleOf(n.Path, n.In, data, *n.MultipleOf); err != nil { 150 resMultiple.Merge(errorHelp.sErr(err)) 151 } 152 } 153 } 154 155 // nolint: dupl 156 if n.Maximum != nil { 157 // Is the constraint specifier within the range of the specific numeric type and format? 158 resMaximum.AddErrors(IsValueValidAgainstRange(*n.Maximum, n.Type, n.Format, "Maximum boundary", n.Path)) 159 if resMaximum.IsValid() { 160 // Constraint validated with compatible types 161 if err := MaximumNativeType(n.Path, n.In, val, *n.Maximum, n.ExclusiveMaximum); err != nil { 162 resMaximum.Merge(errorHelp.sErr(err)) 163 } 164 } else { 165 // Constraint nevertheless validated, converted as general number 166 if err := Maximum(n.Path, n.In, data, *n.Maximum, n.ExclusiveMaximum); err != nil { 167 resMaximum.Merge(errorHelp.sErr(err)) 168 } 169 } 170 } 171 172 // nolint: dupl 173 if n.Minimum != nil { 174 // Is the constraint specifier within the range of the specific numeric type and format? 175 resMinimum.AddErrors(IsValueValidAgainstRange(*n.Minimum, n.Type, n.Format, "Minimum boundary", n.Path)) 176 if resMinimum.IsValid() { 177 // Constraint validated with compatible types 178 if err := MinimumNativeType(n.Path, n.In, val, *n.Minimum, n.ExclusiveMinimum); err != nil { 179 resMinimum.Merge(errorHelp.sErr(err)) 180 } 181 } else { 182 // Constraint nevertheless validated, converted as general number 183 if err := Minimum(n.Path, n.In, data, *n.Minimum, n.ExclusiveMinimum); err != nil { 184 resMinimum.Merge(errorHelp.sErr(err)) 185 } 186 } 187 } 188 res.Merge(resMultiple, resMinimum, resMaximum) 189 res.Inc() 190 return res 191} 192 193type stringValidator struct { 194 MaxLength *int64 195 MinLength *int64 196 Pattern string 197 Path string 198 In string 199} 200 201func (s *stringValidator) SetPath(path string) { 202 s.Path = path 203} 204 205func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool { 206 switch source.(type) { 207 case *spec.Schema: 208 r := kind == reflect.String 209 debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind) 210 return r 211 } 212 debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, false, source, kind) 213 return false 214} 215 216func (s *stringValidator) Validate(val interface{}) *Result { 217 data, ok := val.(string) 218 if !ok { 219 return errorHelp.sErr(errors.InvalidType(s.Path, s.In, stringType, val)) 220 } 221 222 if s.MaxLength != nil { 223 if err := MaxLength(s.Path, s.In, data, *s.MaxLength); err != nil { 224 return errorHelp.sErr(err) 225 } 226 } 227 228 if s.MinLength != nil { 229 if err := MinLength(s.Path, s.In, data, *s.MinLength); err != nil { 230 return errorHelp.sErr(err) 231 } 232 } 233 234 if s.Pattern != "" { 235 if err := Pattern(s.Path, s.In, data, s.Pattern); err != nil { 236 return errorHelp.sErr(err) 237 } 238 } 239 return nil 240} 241