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