blob: 98c9f546d199d672e5720235b72da18ee917a167 [file] [log] [blame]
Serge Bazanskicc25bdf2018-10-25 14:02:58 +02001// 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 "fmt"
19 "reflect"
20
21 "github.com/go-openapi/errors"
22 "github.com/go-openapi/spec"
23 "github.com/go-openapi/strfmt"
24)
25
26// An EntityValidator is an interface for things that can validate entities
27type EntityValidator interface {
28 Validate(interface{}) *Result
29}
30
31type valueValidator interface {
32 SetPath(path string)
33 Applies(interface{}, reflect.Kind) bool
34 Validate(interface{}) *Result
35}
36
37type itemsValidator struct {
38 items *spec.Items
39 root interface{}
40 path string
41 in string
42 validators []valueValidator
43 KnownFormats strfmt.Registry
44}
45
46func newItemsValidator(path, in string, items *spec.Items, root interface{}, formats strfmt.Registry) *itemsValidator {
47 iv := &itemsValidator{path: path, in: in, items: items, root: root, KnownFormats: formats}
48 iv.validators = []valueValidator{
49 &typeValidator{
50 Type: spec.StringOrArray([]string{items.Type}),
51 Format: items.Format,
52 In: in,
53 Path: path,
54 },
55 iv.stringValidator(),
56 iv.formatValidator(),
57 iv.numberValidator(),
58 iv.sliceValidator(),
59 iv.commonValidator(),
60 }
61 return iv
62}
63
64func (i *itemsValidator) Validate(index int, data interface{}) *Result {
65 tpe := reflect.TypeOf(data)
66 kind := tpe.Kind()
67 mainResult := new(Result)
68 path := fmt.Sprintf("%s.%d", i.path, index)
69
70 for _, validator := range i.validators {
71 validator.SetPath(path)
72 if validator.Applies(i.root, kind) {
73 result := validator.Validate(data)
74 mainResult.Merge(result)
75 mainResult.Inc()
76 if result != nil && result.HasErrors() {
77 return mainResult
78 }
79 }
80 }
81 return mainResult
82}
83
84func (i *itemsValidator) commonValidator() valueValidator {
85 return &basicCommonValidator{
86 In: i.in,
87 Default: i.items.Default,
88 Enum: i.items.Enum,
89 }
90}
91
92func (i *itemsValidator) sliceValidator() valueValidator {
93 return &basicSliceValidator{
94 In: i.in,
95 Default: i.items.Default,
96 MaxItems: i.items.MaxItems,
97 MinItems: i.items.MinItems,
98 UniqueItems: i.items.UniqueItems,
99 Source: i.root,
100 Items: i.items.Items,
101 KnownFormats: i.KnownFormats,
102 }
103}
104
105func (i *itemsValidator) numberValidator() valueValidator {
106 return &numberValidator{
107 In: i.in,
108 Default: i.items.Default,
109 MultipleOf: i.items.MultipleOf,
110 Maximum: i.items.Maximum,
111 ExclusiveMaximum: i.items.ExclusiveMaximum,
112 Minimum: i.items.Minimum,
113 ExclusiveMinimum: i.items.ExclusiveMinimum,
114 Type: i.items.Type,
115 Format: i.items.Format,
116 }
117}
118
119func (i *itemsValidator) stringValidator() valueValidator {
120 return &stringValidator{
121 In: i.in,
122 Default: i.items.Default,
123 MaxLength: i.items.MaxLength,
124 MinLength: i.items.MinLength,
125 Pattern: i.items.Pattern,
126 AllowEmptyValue: false,
127 }
128}
129
130func (i *itemsValidator) formatValidator() valueValidator {
131 return &formatValidator{
132 In: i.in,
133 //Default: i.items.Default,
134 Format: i.items.Format,
135 KnownFormats: i.KnownFormats,
136 }
137}
138
139type basicCommonValidator struct {
140 Path string
141 In string
142 Default interface{}
143 Enum []interface{}
144}
145
146func (b *basicCommonValidator) SetPath(path string) {
147 b.Path = path
148}
149
150func (b *basicCommonValidator) Applies(source interface{}, kind reflect.Kind) bool {
151 switch source.(type) {
152 case *spec.Parameter, *spec.Schema, *spec.Header:
153 return true
154 }
155 return false
156}
157
158func (b *basicCommonValidator) Validate(data interface{}) (res *Result) {
159 if len(b.Enum) > 0 {
160 for _, enumValue := range b.Enum {
161 actualType := reflect.TypeOf(enumValue)
162 if actualType != nil { // Safeguard
163 expectedValue := reflect.ValueOf(data)
164 if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
165 if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
166 return nil
167 }
168 }
169 }
170 }
171 return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum))
172 }
173 return nil
174}
175
176// A HeaderValidator has very limited subset of validations to apply
177type HeaderValidator struct {
178 name string
179 header *spec.Header
180 validators []valueValidator
181 KnownFormats strfmt.Registry
182}
183
184// NewHeaderValidator creates a new header validator object
185func NewHeaderValidator(name string, header *spec.Header, formats strfmt.Registry) *HeaderValidator {
186 p := &HeaderValidator{name: name, header: header, KnownFormats: formats}
187 p.validators = []valueValidator{
188 &typeValidator{
189 Type: spec.StringOrArray([]string{header.Type}),
190 Format: header.Format,
191 In: "header",
192 Path: name,
193 },
194 p.stringValidator(),
195 p.formatValidator(),
196 p.numberValidator(),
197 p.sliceValidator(),
198 p.commonValidator(),
199 }
200 return p
201}
202
203// Validate the value of the header against its schema
204func (p *HeaderValidator) Validate(data interface{}) *Result {
205 result := new(Result)
206 tpe := reflect.TypeOf(data)
207 kind := tpe.Kind()
208
209 for _, validator := range p.validators {
210 if validator.Applies(p.header, kind) {
211 if err := validator.Validate(data); err != nil {
212 result.Merge(err)
213 if err.HasErrors() {
214 return result
215 }
216 }
217 }
218 }
219 return nil
220}
221
222func (p *HeaderValidator) commonValidator() valueValidator {
223 return &basicCommonValidator{
224 Path: p.name,
225 In: "response",
226 Default: p.header.Default,
227 Enum: p.header.Enum,
228 }
229}
230
231func (p *HeaderValidator) sliceValidator() valueValidator {
232 return &basicSliceValidator{
233 Path: p.name,
234 In: "response",
235 Default: p.header.Default,
236 MaxItems: p.header.MaxItems,
237 MinItems: p.header.MinItems,
238 UniqueItems: p.header.UniqueItems,
239 Items: p.header.Items,
240 Source: p.header,
241 KnownFormats: p.KnownFormats,
242 }
243}
244
245func (p *HeaderValidator) numberValidator() valueValidator {
246 return &numberValidator{
247 Path: p.name,
248 In: "response",
249 Default: p.header.Default,
250 MultipleOf: p.header.MultipleOf,
251 Maximum: p.header.Maximum,
252 ExclusiveMaximum: p.header.ExclusiveMaximum,
253 Minimum: p.header.Minimum,
254 ExclusiveMinimum: p.header.ExclusiveMinimum,
255 Type: p.header.Type,
256 Format: p.header.Format,
257 }
258}
259
260func (p *HeaderValidator) stringValidator() valueValidator {
261 return &stringValidator{
262 Path: p.name,
263 In: "response",
264 Default: p.header.Default,
265 Required: true,
266 MaxLength: p.header.MaxLength,
267 MinLength: p.header.MinLength,
268 Pattern: p.header.Pattern,
269 AllowEmptyValue: false,
270 }
271}
272
273func (p *HeaderValidator) formatValidator() valueValidator {
274 return &formatValidator{
275 Path: p.name,
276 In: "response",
277 //Default: p.header.Default,
278 Format: p.header.Format,
279 KnownFormats: p.KnownFormats,
280 }
281}
282
283// A ParamValidator has very limited subset of validations to apply
284type ParamValidator struct {
285 param *spec.Parameter
286 validators []valueValidator
287 KnownFormats strfmt.Registry
288}
289
290// NewParamValidator creates a new param validator object
291func NewParamValidator(param *spec.Parameter, formats strfmt.Registry) *ParamValidator {
292 p := &ParamValidator{param: param, KnownFormats: formats}
293 p.validators = []valueValidator{
294 &typeValidator{
295 Type: spec.StringOrArray([]string{param.Type}),
296 Format: param.Format,
297 In: param.In,
298 Path: param.Name,
299 },
300 p.stringValidator(),
301 p.formatValidator(),
302 p.numberValidator(),
303 p.sliceValidator(),
304 p.commonValidator(),
305 }
306 return p
307}
308
309// Validate the data against the description of the parameter
310func (p *ParamValidator) Validate(data interface{}) *Result {
311 result := new(Result)
312 tpe := reflect.TypeOf(data)
313 kind := tpe.Kind()
314
315 // TODO: validate type
316 for _, validator := range p.validators {
317 if validator.Applies(p.param, kind) {
318 if err := validator.Validate(data); err != nil {
319 result.Merge(err)
320 if err.HasErrors() {
321 return result
322 }
323 }
324 }
325 }
326 return nil
327}
328
329func (p *ParamValidator) commonValidator() valueValidator {
330 return &basicCommonValidator{
331 Path: p.param.Name,
332 In: p.param.In,
333 Default: p.param.Default,
334 Enum: p.param.Enum,
335 }
336}
337
338func (p *ParamValidator) sliceValidator() valueValidator {
339 return &basicSliceValidator{
340 Path: p.param.Name,
341 In: p.param.In,
342 Default: p.param.Default,
343 MaxItems: p.param.MaxItems,
344 MinItems: p.param.MinItems,
345 UniqueItems: p.param.UniqueItems,
346 Items: p.param.Items,
347 Source: p.param,
348 KnownFormats: p.KnownFormats,
349 }
350}
351
352func (p *ParamValidator) numberValidator() valueValidator {
353 return &numberValidator{
354 Path: p.param.Name,
355 In: p.param.In,
356 Default: p.param.Default,
357 MultipleOf: p.param.MultipleOf,
358 Maximum: p.param.Maximum,
359 ExclusiveMaximum: p.param.ExclusiveMaximum,
360 Minimum: p.param.Minimum,
361 ExclusiveMinimum: p.param.ExclusiveMinimum,
362 Type: p.param.Type,
363 Format: p.param.Format,
364 }
365}
366
367func (p *ParamValidator) stringValidator() valueValidator {
368 return &stringValidator{
369 Path: p.param.Name,
370 In: p.param.In,
371 Default: p.param.Default,
372 AllowEmptyValue: p.param.AllowEmptyValue,
373 Required: p.param.Required,
374 MaxLength: p.param.MaxLength,
375 MinLength: p.param.MinLength,
376 Pattern: p.param.Pattern,
377 }
378}
379
380func (p *ParamValidator) formatValidator() valueValidator {
381 return &formatValidator{
382 Path: p.param.Name,
383 In: p.param.In,
384 //Default: p.param.Default,
385 Format: p.param.Format,
386 KnownFormats: p.KnownFormats,
387 }
388}
389
390type basicSliceValidator struct {
391 Path string
392 In string
393 Default interface{}
394 MaxItems *int64
395 MinItems *int64
396 UniqueItems bool
397 Items *spec.Items
398 Source interface{}
399 itemsValidator *itemsValidator
400 KnownFormats strfmt.Registry
401}
402
403func (s *basicSliceValidator) SetPath(path string) {
404 s.Path = path
405}
406
407func (s *basicSliceValidator) Applies(source interface{}, kind reflect.Kind) bool {
408 switch source.(type) {
409 case *spec.Parameter, *spec.Items, *spec.Header:
410 return kind == reflect.Slice
411 }
412 return false
413}
414
415func (s *basicSliceValidator) Validate(data interface{}) *Result {
416 val := reflect.ValueOf(data)
417
418 size := int64(val.Len())
419 if s.MinItems != nil {
420 if err := MinItems(s.Path, s.In, size, *s.MinItems); err != nil {
421 return errorHelp.sErr(err)
422 }
423 }
424
425 if s.MaxItems != nil {
426 if err := MaxItems(s.Path, s.In, size, *s.MaxItems); err != nil {
427 return errorHelp.sErr(err)
428 }
429 }
430
431 if s.UniqueItems {
432 if err := UniqueItems(s.Path, s.In, data); err != nil {
433 return errorHelp.sErr(err)
434 }
435 }
436
437 if s.itemsValidator == nil && s.Items != nil {
438 s.itemsValidator = newItemsValidator(s.Path, s.In, s.Items, s.Source, s.KnownFormats)
439 }
440
441 if s.itemsValidator != nil {
442 for i := 0; i < int(size); i++ {
443 ele := val.Index(i)
444 if err := s.itemsValidator.Validate(i, ele.Interface()); err != nil && err.HasErrors() {
445 return err
446 }
447 }
448 }
449 return nil
450}
451
452func (s *basicSliceValidator) hasDuplicates(value reflect.Value, size int) bool {
453 dict := make(map[interface{}]struct{})
454 for i := 0; i < size; i++ {
455 ele := value.Index(i)
456 if _, ok := dict[ele.Interface()]; ok {
457 return true
458 }
459 dict[ele.Interface()] = struct{}{}
460 }
461 return false
462}
463
464type numberValidator struct {
465 Path string
466 In string
467 Default interface{}
468 MultipleOf *float64
469 Maximum *float64
470 ExclusiveMaximum bool
471 Minimum *float64
472 ExclusiveMinimum bool
473 // Allows for more accurate behavior regarding integers
474 Type string
475 Format string
476}
477
478func (n *numberValidator) SetPath(path string) {
479 n.Path = path
480}
481
482func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool {
483 switch source.(type) {
484 case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header:
485 isInt := kind >= reflect.Int && kind <= reflect.Uint64
486 isFloat := kind == reflect.Float32 || kind == reflect.Float64
487 r := isInt || isFloat
488 debugLog("schema props validator for %q applies %t for %T (kind: %v) isInt=%t, isFloat=%t\n", n.Path, r, source, kind, isInt, isFloat)
489 return r
490 }
491 debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", n.Path, false, source, kind)
492 return false
493}
494
495// Validate provides a validator for generic JSON numbers,
496//
497// By default, numbers are internally represented as float64.
498// Formats float, or float32 may alter this behavior by mapping to float32.
499// A special validation process is followed for integers, with optional "format":
500// this is an attempt to provide a validation with native types.
501//
502// NOTE: since the constraint specified (boundary, multipleOf) is unmarshalled
503// as float64, loss of information remains possible (e.g. on very large integers).
504//
505// Since this value directly comes from the unmarshalling, it is not possible
506// at this stage of processing to check further and guarantee the correctness of such values.
507//
508// Normally, the JSON Number.MAX_SAFE_INTEGER (resp. Number.MIN_SAFE_INTEGER)
509// would check we do not get such a loss.
510//
511// If this is the case, replace AddErrors() by AddWarnings() and IsValid() by !HasWarnings().
512//
513// TODO: consider replacing boundary check errors by simple warnings.
514//
515// TODO: default boundaries with MAX_SAFE_INTEGER are not checked (specific to json.Number?)
516func (n *numberValidator) Validate(val interface{}) *Result {
517 res := new(Result)
518
519 resMultiple := new(Result)
520 resMinimum := new(Result)
521 resMaximum := new(Result)
522
523 // Used only to attempt to validate constraint on value,
524 // even though value or constraint specified do not match type and format
525 data := valueHelp.asFloat64(val)
526
527 // Is the provided value within the range of the specified numeric type and format?
528 res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path))
529
530 if n.MultipleOf != nil {
531 // Is the constraint specifier within the range of the specific numeric type and format?
532 resMultiple.AddErrors(IsValueValidAgainstRange(*n.MultipleOf, n.Type, n.Format, "MultipleOf", n.Path))
533 if resMultiple.IsValid() {
534 // Constraint validated with compatible types
535 if err := MultipleOfNativeType(n.Path, n.In, val, *n.MultipleOf); err != nil {
536 resMultiple.Merge(errorHelp.sErr(err))
537 }
538 } else {
539 // Constraint nevertheless validated, converted as general number
540 if err := MultipleOf(n.Path, n.In, data, *n.MultipleOf); err != nil {
541 resMultiple.Merge(errorHelp.sErr(err))
542 }
543 }
544 }
545
546 if n.Maximum != nil {
547 // Is the constraint specifier within the range of the specific numeric type and format?
548 resMaximum.AddErrors(IsValueValidAgainstRange(*n.Maximum, n.Type, n.Format, "Maximum boundary", n.Path))
549 if resMaximum.IsValid() {
550 // Constraint validated with compatible types
551 if err := MaximumNativeType(n.Path, n.In, val, *n.Maximum, n.ExclusiveMaximum); err != nil {
552 resMaximum.Merge(errorHelp.sErr(err))
553 }
554 } else {
555 // Constraint nevertheless validated, converted as general number
556 if err := Maximum(n.Path, n.In, data, *n.Maximum, n.ExclusiveMaximum); err != nil {
557 resMaximum.Merge(errorHelp.sErr(err))
558 }
559 }
560 }
561
562 if n.Minimum != nil {
563 // Is the constraint specifier within the range of the specific numeric type and format?
564 resMinimum.AddErrors(IsValueValidAgainstRange(*n.Minimum, n.Type, n.Format, "Minimum boundary", n.Path))
565 if resMinimum.IsValid() {
566 // Constraint validated with compatible types
567 if err := MinimumNativeType(n.Path, n.In, val, *n.Minimum, n.ExclusiveMinimum); err != nil {
568 resMinimum.Merge(errorHelp.sErr(err))
569 }
570 } else {
571 // Constraint nevertheless validated, converted as general number
572 if err := Minimum(n.Path, n.In, data, *n.Minimum, n.ExclusiveMinimum); err != nil {
573 resMinimum.Merge(errorHelp.sErr(err))
574 }
575 }
576 }
577 res.Merge(resMultiple, resMinimum, resMaximum)
578 res.Inc()
579 return res
580}
581
582type stringValidator struct {
583 Default interface{}
584 Required bool
585 AllowEmptyValue bool
586 MaxLength *int64
587 MinLength *int64
588 Pattern string
589 Path string
590 In string
591}
592
593func (s *stringValidator) SetPath(path string) {
594 s.Path = path
595}
596
597func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool {
598 switch source.(type) {
599 case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header:
600 r := kind == reflect.String
601 debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind)
602 return r
603 }
604 debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, false, source, kind)
605 return false
606}
607
608func (s *stringValidator) Validate(val interface{}) *Result {
609 data, ok := val.(string)
610 if !ok {
611 return errorHelp.sErr(errors.InvalidType(s.Path, s.In, "string", val))
612 }
613
614 if s.Required && !s.AllowEmptyValue && (s.Default == nil || s.Default == "") {
615 if err := RequiredString(s.Path, s.In, data); err != nil {
616 return errorHelp.sErr(err)
617 }
618 }
619
620 if s.MaxLength != nil {
621 if err := MaxLength(s.Path, s.In, data, *s.MaxLength); err != nil {
622 return errorHelp.sErr(err)
623 }
624 }
625
626 if s.MinLength != nil {
627 if err := MinLength(s.Path, s.In, data, *s.MinLength); err != nil {
628 return errorHelp.sErr(err)
629 }
630 }
631
632 if s.Pattern != "" {
633 if err := Pattern(s.Path, s.In, data, s.Pattern); err != nil {
634 return errorHelp.sErr(err)
635 }
636 }
637 return nil
638}