|
- // 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
- }
-
- 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))
- }
- }
- }
-
- 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))
- }
- }
- }
-
- 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, "string", 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
- }
|