k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/validate/type.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 "strings" 20 21 "github.com/go-openapi/swag" 22 "k8s.io/kube-openapi/pkg/validation/errors" 23 "k8s.io/kube-openapi/pkg/validation/spec" 24 "k8s.io/kube-openapi/pkg/validation/strfmt" 25 ) 26 27 type typeValidator struct { 28 Type spec.StringOrArray 29 Nullable bool 30 Format string 31 In string 32 Path string 33 } 34 35 func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) { 36 // internal type to JSON type with swagger 2.0 format (with go-openapi/strfmt extensions), 37 // see https://github.com/go-openapi/strfmt/blob/master/README.md 38 // TODO: this switch really is some sort of reverse lookup for formats. It should be provided by strfmt. 39 switch data.(type) { 40 case []byte, strfmt.Base64, *strfmt.Base64: 41 return stringType, stringFormatByte 42 case strfmt.CreditCard, *strfmt.CreditCard: 43 return stringType, stringFormatCreditCard 44 case strfmt.Date, *strfmt.Date: 45 return stringType, stringFormatDate 46 case strfmt.DateTime, *strfmt.DateTime: 47 return stringType, stringFormatDateTime 48 case strfmt.Duration, *strfmt.Duration: 49 return stringType, stringFormatDuration 50 case strfmt.Email, *strfmt.Email: 51 return stringType, stringFormatEmail 52 case strfmt.HexColor, *strfmt.HexColor: 53 return stringType, stringFormatHexColor 54 case strfmt.Hostname, *strfmt.Hostname: 55 return stringType, stringFormatHostname 56 case strfmt.IPv4, *strfmt.IPv4: 57 return stringType, stringFormatIPv4 58 case strfmt.IPv6, *strfmt.IPv6: 59 return stringType, stringFormatIPv6 60 case strfmt.ISBN, *strfmt.ISBN: 61 return stringType, stringFormatISBN 62 case strfmt.ISBN10, *strfmt.ISBN10: 63 return stringType, stringFormatISBN10 64 case strfmt.ISBN13, *strfmt.ISBN13: 65 return stringType, stringFormatISBN13 66 case strfmt.MAC, *strfmt.MAC: 67 return stringType, stringFormatMAC 68 case strfmt.Password, *strfmt.Password: 69 return stringType, stringFormatPassword 70 case strfmt.RGBColor, *strfmt.RGBColor: 71 return stringType, stringFormatRGBColor 72 case strfmt.SSN, *strfmt.SSN: 73 return stringType, stringFormatSSN 74 case strfmt.URI, *strfmt.URI: 75 return stringType, stringFormatURI 76 case strfmt.UUID, *strfmt.UUID: 77 return stringType, stringFormatUUID 78 case strfmt.UUID3, *strfmt.UUID3: 79 return stringType, stringFormatUUID3 80 case strfmt.UUID4, *strfmt.UUID4: 81 return stringType, stringFormatUUID4 82 case strfmt.UUID5, *strfmt.UUID5: 83 return stringType, stringFormatUUID5 84 // TODO: missing binary (io.ReadCloser) 85 // TODO: missing json.Number 86 default: 87 val := reflect.ValueOf(data) 88 tpe := val.Type() 89 switch tpe.Kind() { 90 case reflect.Bool: 91 return booleanType, "" 92 case reflect.String: 93 return stringType, "" 94 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32: 95 // NOTE: that is the spec. With go-openapi, is that not uint32 for unsigned integers? 96 return integerType, integerFormatInt32 97 case reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64: 98 return integerType, integerFormatInt64 99 case reflect.Float32: 100 // NOTE: is that not numberFormatFloat? 101 return numberType, numberFormatFloat32 102 case reflect.Float64: 103 // NOTE: is that not "double"? 104 return numberType, numberFormatFloat64 105 // NOTE: go arrays (reflect.Array) are not supported (fixed length) 106 case reflect.Slice: 107 return arrayType, "" 108 case reflect.Map, reflect.Struct: 109 return objectType, "" 110 case reflect.Interface: 111 // What to do here? 112 panic("dunno what to do here") 113 case reflect.Ptr: 114 return t.schemaInfoForType(reflect.Indirect(val).Interface()) 115 } 116 } 117 return "", "" 118 } 119 120 func (t *typeValidator) SetPath(path string) { 121 t.Path = path 122 } 123 124 func (t *typeValidator) Applies(source interface{}, kind reflect.Kind) bool { 125 // typeValidator applies to Schema, Parameter and Header objects 126 stpe := reflect.TypeOf(source) 127 r := (len(t.Type) > 0 || t.Format != "") && stpe == specSchemaType 128 debugLog("type validator for %q applies %t for %T (kind: %v)\n", t.Path, r, source, kind) 129 return r 130 } 131 132 func (t *typeValidator) Validate(data interface{}) *Result { 133 result := new(Result) 134 result.Inc() 135 if data == nil { 136 // nil or zero value for the passed structure require Type: null 137 if len(t.Type) > 0 && !t.Type.Contains(nullType) && !t.Nullable { // TODO: if a property is not required it also passes this 138 return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), nullType)) 139 } 140 return result 141 } 142 143 // check if the type matches, should be used in every validator chain as first item 144 val := reflect.Indirect(reflect.ValueOf(data)) 145 kind := val.Kind() 146 147 // infer schema type (JSON) and format from passed data type 148 schType, format := t.schemaInfoForType(data) 149 150 debugLog("path: %s, schType: %s, format: %s, expType: %s, expFmt: %s, kind: %s", t.Path, schType, format, t.Type, t.Format, val.Kind().String()) 151 152 // check numerical types 153 // TODO: check unsigned ints 154 // TODO: check json.Number (see schema.go) 155 isLowerInt := t.Format == integerFormatInt64 && format == integerFormatInt32 156 isLowerFloat := t.Format == numberFormatFloat64 && format == numberFormatFloat32 157 isFloatInt := schType == numberType && swag.IsFloat64AJSONInteger(val.Float()) && t.Type.Contains(integerType) 158 isIntFloat := schType == integerType && t.Type.Contains(numberType) 159 160 if kind != reflect.String && kind != reflect.Slice && t.Format != "" && !(t.Type.Contains(schType) || format == t.Format || isFloatInt || isIntFloat || isLowerInt || isLowerFloat) { 161 // TODO: test case 162 return errorHelp.sErr(errors.InvalidType(t.Path, t.In, t.Format, format)) 163 } 164 165 if !(t.Type.Contains(numberType) || t.Type.Contains(integerType)) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) { 166 return result 167 } 168 169 if !(t.Type.Contains(schType) || isFloatInt || isIntFloat) { 170 return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType)) 171 } 172 return result 173 }