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