mirror of https://github.com/k3s-io/k3s
241 lines
7.5 KiB
Go
241 lines
7.5 KiB
Go
// 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 (
|
|
"reflect"
|
|
|
|
"k8s.io/kube-openapi/pkg/validation/errors"
|
|
"k8s.io/kube-openapi/pkg/validation/spec"
|
|
)
|
|
|
|
// 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 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.Schema:
|
|
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
|
|
}
|
|
|
|
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.Schema:
|
|
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))
|
|
|
|
// nolint: dupl
|
|
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 {
|
|
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.Schema:
|
|
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.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
|
|
}
|