1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package validate
16
17 import (
18 "encoding/json"
19 "fmt"
20 "reflect"
21
22 "github.com/go-openapi/swag"
23 "k8s.io/kube-openapi/pkg/validation/errors"
24 "k8s.io/kube-openapi/pkg/validation/spec"
25 "k8s.io/kube-openapi/pkg/validation/strfmt"
26 )
27
28 var (
29 specSchemaType = reflect.TypeOf(&spec.Schema{})
30
31 )
32
33
34 type SchemaValidator struct {
35 Path string
36 in string
37 Schema *spec.Schema
38 validators []ValueValidator
39 Root interface{}
40 KnownFormats strfmt.Registry
41 Options SchemaValidatorOptions
42 }
43
44
45
46
47 func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registry, options ...Option) error {
48 res := NewSchemaValidator(schema, nil, "", formats, options...).Validate(data)
49 if res.HasErrors() {
50 return errors.CompositeValidationError(res.Errors...)
51 }
52 return nil
53 }
54
55
56
57
58 func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...Option) *SchemaValidator {
59 if schema == nil {
60 return nil
61 }
62
63 if rootSchema == nil {
64 rootSchema = schema
65 }
66
67 if ref := schema.Ref.String(); ref != "" {
68 panic(fmt.Sprintf("schema references not supported: %s", ref))
69 }
70
71 s := SchemaValidator{
72 Path: root,
73 in: "body",
74 Schema: schema,
75 Root: rootSchema,
76 KnownFormats: formats,
77 Options: SchemaValidatorOptions{}}
78 for _, o := range options {
79 o(&s.Options)
80 }
81
82 if s.Options.NewValidatorForIndex == nil {
83 s.Options.NewValidatorForIndex = s.NewValidatorForIndex
84 }
85 if s.Options.NewValidatorForField == nil {
86 s.Options.NewValidatorForField = s.NewValidatorForField
87 }
88
89 s.validators = []ValueValidator{
90 s.typeValidator(),
91 s.schemaPropsValidator(),
92 s.stringValidator(),
93 s.formatValidator(),
94 s.numberValidator(),
95 s.sliceValidator(),
96 s.commonValidator(),
97 s.objectValidator(),
98 }
99 return &s
100 }
101
102 func (s *SchemaValidator) NewValidatorForField(field string, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator {
103 return NewSchemaValidator(schema, rootSchema, root, formats, opts...)
104 }
105
106 func (s *SchemaValidator) NewValidatorForIndex(index int, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator {
107 return NewSchemaValidator(schema, rootSchema, root, formats, opts...)
108 }
109
110
111 func (s *SchemaValidator) SetPath(path string) {
112 s.Path = path
113 for _, v := range s.validators {
114 v.SetPath(path)
115 }
116 }
117
118
119 func (s *SchemaValidator) Applies(source interface{}, kind reflect.Kind) bool {
120 _, ok := source.(*spec.Schema)
121 return ok
122 }
123
124
125 func (s *SchemaValidator) Validate(data interface{}) *Result {
126 result := new(Result)
127 if s == nil {
128 return result
129 }
130
131 if data == nil {
132 result.Merge(s.validators[0].Validate(data))
133 result.Merge(s.validators[6].Validate(data))
134 return result
135 }
136
137 tpe := reflect.TypeOf(data)
138 kind := tpe.Kind()
139 for kind == reflect.Ptr {
140 tpe = tpe.Elem()
141 kind = tpe.Kind()
142 }
143 d := data
144
145 if kind == reflect.Struct {
146
147
148
149
150 d = swag.ToDynamicJSON(data)
151 }
152
153
154
155 isnumber := s.Schema.Type.Contains(numberType) || s.Schema.Type.Contains(integerType)
156 if num, ok := data.(json.Number); ok && isnumber {
157 if s.Schema.Type.Contains(integerType) {
158 in, erri := num.Int64()
159 if erri != nil {
160 result.AddErrors(invalidTypeConversionMsg(s.Path, erri))
161 result.Inc()
162 return result
163 }
164 d = in
165 } else {
166 nf, errf := num.Float64()
167 if errf != nil {
168 result.AddErrors(invalidTypeConversionMsg(s.Path, errf))
169 result.Inc()
170 return result
171 }
172 d = nf
173 }
174
175 tpe = reflect.TypeOf(d)
176 kind = tpe.Kind()
177 }
178
179 for _, v := range s.validators {
180 if !v.Applies(s.Schema, kind) {
181 debugLog("%T does not apply for %v", v, kind)
182 continue
183 }
184
185 err := v.Validate(d)
186 result.Merge(err)
187 result.Inc()
188 }
189 result.Inc()
190 return result
191 }
192
193 func (s *SchemaValidator) typeValidator() ValueValidator {
194 return &typeValidator{Type: s.Schema.Type, Nullable: s.Schema.Nullable, Format: s.Schema.Format, In: s.in, Path: s.Path}
195 }
196
197 func (s *SchemaValidator) commonValidator() ValueValidator {
198 return &basicCommonValidator{
199 Path: s.Path,
200 In: s.in,
201 Enum: s.Schema.Enum,
202 }
203 }
204
205 func (s *SchemaValidator) sliceValidator() ValueValidator {
206 return &schemaSliceValidator{
207 Path: s.Path,
208 In: s.in,
209 MaxItems: s.Schema.MaxItems,
210 MinItems: s.Schema.MinItems,
211 UniqueItems: s.Schema.UniqueItems,
212 AdditionalItems: s.Schema.AdditionalItems,
213 Items: s.Schema.Items,
214 Root: s.Root,
215 KnownFormats: s.KnownFormats,
216 Options: s.Options,
217 }
218 }
219
220 func (s *SchemaValidator) numberValidator() ValueValidator {
221 return &numberValidator{
222 Path: s.Path,
223 In: s.in,
224 Default: s.Schema.Default,
225 MultipleOf: s.Schema.MultipleOf,
226 Maximum: s.Schema.Maximum,
227 ExclusiveMaximum: s.Schema.ExclusiveMaximum,
228 Minimum: s.Schema.Minimum,
229 ExclusiveMinimum: s.Schema.ExclusiveMinimum,
230 }
231 }
232
233 func (s *SchemaValidator) stringValidator() ValueValidator {
234 return &stringValidator{
235 Path: s.Path,
236 In: s.in,
237 MaxLength: s.Schema.MaxLength,
238 MinLength: s.Schema.MinLength,
239 Pattern: s.Schema.Pattern,
240 }
241 }
242
243 func (s *SchemaValidator) formatValidator() ValueValidator {
244 return &formatValidator{
245 Path: s.Path,
246 In: s.in,
247 Format: s.Schema.Format,
248 KnownFormats: s.KnownFormats,
249 }
250 }
251
252 func (s *SchemaValidator) schemaPropsValidator() ValueValidator {
253 sch := s.Schema
254 return newSchemaPropsValidator(s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats, s.Options.Options()...)
255 }
256
257 func (s *SchemaValidator) objectValidator() ValueValidator {
258 return &objectValidator{
259 Path: s.Path,
260 In: s.in,
261 MaxProperties: s.Schema.MaxProperties,
262 MinProperties: s.Schema.MinProperties,
263 Required: s.Schema.Required,
264 Properties: s.Schema.Properties,
265 AdditionalProperties: s.Schema.AdditionalProperties,
266 PatternProperties: s.Schema.PatternProperties,
267 Root: s.Root,
268 KnownFormats: s.KnownFormats,
269 Options: s.Options,
270 }
271 }
272
View as plain text