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