summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-openapi/validate/validator.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-openapi/validate/validator.go')
-rw-r--r--vendor/github.com/go-openapi/validate/validator.go645
1 files changed, 645 insertions, 0 deletions
diff --git a/vendor/github.com/go-openapi/validate/validator.go b/vendor/github.com/go-openapi/validate/validator.go
new file mode 100644
index 000000000..38cdb9bb6
--- /dev/null
+++ b/vendor/github.com/go-openapi/validate/validator.go
@@ -0,0 +1,645 @@
+// 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 (
+ "fmt"
+ "reflect"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/spec"
+ "github.com/go-openapi/strfmt"
+)
+
+// An EntityValidator is an interface for things that can validate entities
+type EntityValidator interface {
+ Validate(interface{}) *Result
+}
+
+type valueValidator interface {
+ SetPath(path string)
+ Applies(interface{}, reflect.Kind) bool
+ Validate(interface{}) *Result
+}
+
+type itemsValidator struct {
+ items *spec.Items
+ root interface{}
+ path string
+ in string
+ validators []valueValidator
+ KnownFormats strfmt.Registry
+}
+
+func newItemsValidator(path, in string, items *spec.Items, root interface{}, formats strfmt.Registry) *itemsValidator {
+ iv := &itemsValidator{path: path, in: in, items: items, root: root, KnownFormats: formats}
+ iv.validators = []valueValidator{
+ &typeValidator{
+ Type: spec.StringOrArray([]string{items.Type}),
+ Nullable: items.Nullable,
+ Format: items.Format,
+ In: in,
+ Path: path,
+ },
+ iv.stringValidator(),
+ iv.formatValidator(),
+ iv.numberValidator(),
+ iv.sliceValidator(),
+ iv.commonValidator(),
+ }
+ return iv
+}
+
+func (i *itemsValidator) Validate(index int, data interface{}) *Result {
+ tpe := reflect.TypeOf(data)
+ kind := tpe.Kind()
+ mainResult := new(Result)
+ path := fmt.Sprintf("%s.%d", i.path, index)
+
+ for _, validator := range i.validators {
+ validator.SetPath(path)
+ if validator.Applies(i.root, kind) {
+ result := validator.Validate(data)
+ mainResult.Merge(result)
+ mainResult.Inc()
+ if result != nil && result.HasErrors() {
+ return mainResult
+ }
+ }
+ }
+ return mainResult
+}
+
+func (i *itemsValidator) commonValidator() valueValidator {
+ return &basicCommonValidator{
+ In: i.in,
+ Default: i.items.Default,
+ Enum: i.items.Enum,
+ }
+}
+
+func (i *itemsValidator) sliceValidator() valueValidator {
+ return &basicSliceValidator{
+ In: i.in,
+ Default: i.items.Default,
+ MaxItems: i.items.MaxItems,
+ MinItems: i.items.MinItems,
+ UniqueItems: i.items.UniqueItems,
+ Source: i.root,
+ Items: i.items.Items,
+ KnownFormats: i.KnownFormats,
+ }
+}
+
+func (i *itemsValidator) numberValidator() valueValidator {
+ return &numberValidator{
+ In: i.in,
+ Default: i.items.Default,
+ MultipleOf: i.items.MultipleOf,
+ Maximum: i.items.Maximum,
+ ExclusiveMaximum: i.items.ExclusiveMaximum,
+ Minimum: i.items.Minimum,
+ ExclusiveMinimum: i.items.ExclusiveMinimum,
+ Type: i.items.Type,
+ Format: i.items.Format,
+ }
+}
+
+func (i *itemsValidator) stringValidator() valueValidator {
+ return &stringValidator{
+ In: i.in,
+ Default: i.items.Default,
+ MaxLength: i.items.MaxLength,
+ MinLength: i.items.MinLength,
+ Pattern: i.items.Pattern,
+ AllowEmptyValue: false,
+ }
+}
+
+func (i *itemsValidator) formatValidator() valueValidator {
+ return &formatValidator{
+ In: i.in,
+ //Default: i.items.Default,
+ Format: i.items.Format,
+ KnownFormats: i.KnownFormats,
+ }
+}
+
+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.Parameter, *spec.Schema, *spec.Header:
+ 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
+}
+
+// A HeaderValidator has very limited subset of validations to apply
+type HeaderValidator struct {
+ name string
+ header *spec.Header
+ validators []valueValidator
+ KnownFormats strfmt.Registry
+}
+
+// NewHeaderValidator creates a new header validator object
+func NewHeaderValidator(name string, header *spec.Header, formats strfmt.Registry) *HeaderValidator {
+ p := &HeaderValidator{name: name, header: header, KnownFormats: formats}
+ p.validators = []valueValidator{
+ &typeValidator{
+ Type: spec.StringOrArray([]string{header.Type}),
+ Nullable: header.Nullable,
+ Format: header.Format,
+ In: "header",
+ Path: name,
+ },
+ p.stringValidator(),
+ p.formatValidator(),
+ p.numberValidator(),
+ p.sliceValidator(),
+ p.commonValidator(),
+ }
+ return p
+}
+
+// Validate the value of the header against its schema
+func (p *HeaderValidator) Validate(data interface{}) *Result {
+ result := new(Result)
+ tpe := reflect.TypeOf(data)
+ kind := tpe.Kind()
+
+ for _, validator := range p.validators {
+ if validator.Applies(p.header, kind) {
+ if err := validator.Validate(data); err != nil {
+ result.Merge(err)
+ if err.HasErrors() {
+ return result
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func (p *HeaderValidator) commonValidator() valueValidator {
+ return &basicCommonValidator{
+ Path: p.name,
+ In: "response",
+ Default: p.header.Default,
+ Enum: p.header.Enum,
+ }
+}
+
+func (p *HeaderValidator) sliceValidator() valueValidator {
+ return &basicSliceValidator{
+ Path: p.name,
+ In: "response",
+ Default: p.header.Default,
+ MaxItems: p.header.MaxItems,
+ MinItems: p.header.MinItems,
+ UniqueItems: p.header.UniqueItems,
+ Items: p.header.Items,
+ Source: p.header,
+ KnownFormats: p.KnownFormats,
+ }
+}
+
+func (p *HeaderValidator) numberValidator() valueValidator {
+ return &numberValidator{
+ Path: p.name,
+ In: "response",
+ Default: p.header.Default,
+ MultipleOf: p.header.MultipleOf,
+ Maximum: p.header.Maximum,
+ ExclusiveMaximum: p.header.ExclusiveMaximum,
+ Minimum: p.header.Minimum,
+ ExclusiveMinimum: p.header.ExclusiveMinimum,
+ Type: p.header.Type,
+ Format: p.header.Format,
+ }
+}
+
+func (p *HeaderValidator) stringValidator() valueValidator {
+ return &stringValidator{
+ Path: p.name,
+ In: "response",
+ Default: p.header.Default,
+ Required: true,
+ MaxLength: p.header.MaxLength,
+ MinLength: p.header.MinLength,
+ Pattern: p.header.Pattern,
+ AllowEmptyValue: false,
+ }
+}
+
+func (p *HeaderValidator) formatValidator() valueValidator {
+ return &formatValidator{
+ Path: p.name,
+ In: "response",
+ //Default: p.header.Default,
+ Format: p.header.Format,
+ KnownFormats: p.KnownFormats,
+ }
+}
+
+// A ParamValidator has very limited subset of validations to apply
+type ParamValidator struct {
+ param *spec.Parameter
+ validators []valueValidator
+ KnownFormats strfmt.Registry
+}
+
+// NewParamValidator creates a new param validator object
+func NewParamValidator(param *spec.Parameter, formats strfmt.Registry) *ParamValidator {
+ p := &ParamValidator{param: param, KnownFormats: formats}
+ p.validators = []valueValidator{
+ &typeValidator{
+ Type: spec.StringOrArray([]string{param.Type}),
+ Nullable: param.Nullable,
+ Format: param.Format,
+ In: param.In,
+ Path: param.Name,
+ },
+ p.stringValidator(),
+ p.formatValidator(),
+ p.numberValidator(),
+ p.sliceValidator(),
+ p.commonValidator(),
+ }
+ return p
+}
+
+// Validate the data against the description of the parameter
+func (p *ParamValidator) Validate(data interface{}) *Result {
+ result := new(Result)
+ tpe := reflect.TypeOf(data)
+ kind := tpe.Kind()
+
+ // TODO: validate type
+ for _, validator := range p.validators {
+ if validator.Applies(p.param, kind) {
+ if err := validator.Validate(data); err != nil {
+ result.Merge(err)
+ if err.HasErrors() {
+ return result
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func (p *ParamValidator) commonValidator() valueValidator {
+ return &basicCommonValidator{
+ Path: p.param.Name,
+ In: p.param.In,
+ Default: p.param.Default,
+ Enum: p.param.Enum,
+ }
+}
+
+func (p *ParamValidator) sliceValidator() valueValidator {
+ return &basicSliceValidator{
+ Path: p.param.Name,
+ In: p.param.In,
+ Default: p.param.Default,
+ MaxItems: p.param.MaxItems,
+ MinItems: p.param.MinItems,
+ UniqueItems: p.param.UniqueItems,
+ Items: p.param.Items,
+ Source: p.param,
+ KnownFormats: p.KnownFormats,
+ }
+}
+
+func (p *ParamValidator) numberValidator() valueValidator {
+ return &numberValidator{
+ Path: p.param.Name,
+ In: p.param.In,
+ Default: p.param.Default,
+ MultipleOf: p.param.MultipleOf,
+ Maximum: p.param.Maximum,
+ ExclusiveMaximum: p.param.ExclusiveMaximum,
+ Minimum: p.param.Minimum,
+ ExclusiveMinimum: p.param.ExclusiveMinimum,
+ Type: p.param.Type,
+ Format: p.param.Format,
+ }
+}
+
+func (p *ParamValidator) stringValidator() valueValidator {
+ return &stringValidator{
+ Path: p.param.Name,
+ In: p.param.In,
+ Default: p.param.Default,
+ AllowEmptyValue: p.param.AllowEmptyValue,
+ Required: p.param.Required,
+ MaxLength: p.param.MaxLength,
+ MinLength: p.param.MinLength,
+ Pattern: p.param.Pattern,
+ }
+}
+
+func (p *ParamValidator) formatValidator() valueValidator {
+ return &formatValidator{
+ Path: p.param.Name,
+ In: p.param.In,
+ //Default: p.param.Default,
+ Format: p.param.Format,
+ KnownFormats: p.KnownFormats,
+ }
+}
+
+type basicSliceValidator struct {
+ Path string
+ In string
+ Default interface{}
+ MaxItems *int64
+ MinItems *int64
+ UniqueItems bool
+ Items *spec.Items
+ Source interface{}
+ itemsValidator *itemsValidator
+ KnownFormats strfmt.Registry
+}
+
+func (s *basicSliceValidator) SetPath(path string) {
+ s.Path = path
+}
+
+func (s *basicSliceValidator) Applies(source interface{}, kind reflect.Kind) bool {
+ switch source.(type) {
+ case *spec.Parameter, *spec.Items, *spec.Header:
+ return kind == reflect.Slice
+ }
+ return false
+}
+
+func (s *basicSliceValidator) Validate(data interface{}) *Result {
+ val := reflect.ValueOf(data)
+
+ size := int64(val.Len())
+ if s.MinItems != nil {
+ if err := MinItems(s.Path, s.In, size, *s.MinItems); err != nil {
+ return errorHelp.sErr(err)
+ }
+ }
+
+ if s.MaxItems != nil {
+ if err := MaxItems(s.Path, s.In, size, *s.MaxItems); err != nil {
+ return errorHelp.sErr(err)
+ }
+ }
+
+ if s.UniqueItems {
+ if err := UniqueItems(s.Path, s.In, data); err != nil {
+ return errorHelp.sErr(err)
+ }
+ }
+
+ if s.itemsValidator == nil && s.Items != nil {
+ s.itemsValidator = newItemsValidator(s.Path, s.In, s.Items, s.Source, s.KnownFormats)
+ }
+
+ if s.itemsValidator != nil {
+ for i := 0; i < int(size); i++ {
+ ele := val.Index(i)
+ if err := s.itemsValidator.Validate(i, ele.Interface()); err != nil && err.HasErrors() {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+/* unused
+func (s *basicSliceValidator) hasDuplicates(value reflect.Value, size int) bool {
+ dict := make(map[interface{}]struct{})
+ for i := 0; i < size; i++ {
+ ele := value.Index(i)
+ if _, ok := dict[ele.Interface()]; ok {
+ return true
+ }
+ dict[ele.Interface()] = struct{}{}
+ }
+ return false
+}
+*/
+
+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.Parameter, *spec.Schema, *spec.Items, *spec.Header:
+ 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))
+
+ 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 {
+ Default interface{}
+ Required bool
+ AllowEmptyValue bool
+ 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.Parameter, *spec.Schema, *spec.Items, *spec.Header:
+ 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.Required && !s.AllowEmptyValue && (s.Default == nil || s.Default == "") {
+ if err := RequiredString(s.Path, s.In, data); err != nil {
+ return errorHelp.sErr(err)
+ }
+ }
+
+ 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
+}