k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/validate/validator.go (about)

     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  
    15  package validate
    16  
    17  import (
    18  	"reflect"
    19  
    20  	"k8s.io/kube-openapi/pkg/validation/errors"
    21  	"k8s.io/kube-openapi/pkg/validation/spec"
    22  )
    23  
    24  // ValueValidator validates the values it applies to.
    25  type ValueValidator interface {
    26  	// SetPath sets the exact path of the validator prior to calling Validate.
    27  	// The exact path contains the map keys and array indices to locate the
    28  	// value to be validated from the root data element.
    29  	SetPath(path string)
    30  	// Applies returns true if the validator applies to the valueKind
    31  	// from source. Validate will be called if and only if Applies returns true.
    32  	Applies(source interface{}, valueKind reflect.Kind) bool
    33  	// Validate validates the value.
    34  	Validate(value interface{}) *Result
    35  }
    36  
    37  type basicCommonValidator struct {
    38  	Path    string
    39  	In      string
    40  	Default interface{}
    41  	Enum    []interface{}
    42  }
    43  
    44  func (b *basicCommonValidator) SetPath(path string) {
    45  	b.Path = path
    46  }
    47  
    48  func (b *basicCommonValidator) Applies(source interface{}, kind reflect.Kind) bool {
    49  	switch source.(type) {
    50  	case *spec.Schema:
    51  		return true
    52  	}
    53  	return false
    54  }
    55  
    56  func (b *basicCommonValidator) Validate(data interface{}) (res *Result) {
    57  	if len(b.Enum) > 0 {
    58  		for _, enumValue := range b.Enum {
    59  			actualType := reflect.TypeOf(enumValue)
    60  			if actualType != nil { // Safeguard
    61  				expectedValue := reflect.ValueOf(data)
    62  				if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
    63  					if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
    64  						return nil
    65  					}
    66  				}
    67  			}
    68  		}
    69  		return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum))
    70  	}
    71  	return nil
    72  }
    73  
    74  type numberValidator struct {
    75  	Path             string
    76  	In               string
    77  	Default          interface{}
    78  	MultipleOf       *float64
    79  	Maximum          *float64
    80  	ExclusiveMaximum bool
    81  	Minimum          *float64
    82  	ExclusiveMinimum bool
    83  	// Allows for more accurate behavior regarding integers
    84  	Type   string
    85  	Format string
    86  }
    87  
    88  func (n *numberValidator) SetPath(path string) {
    89  	n.Path = path
    90  }
    91  
    92  func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool {
    93  	switch source.(type) {
    94  	case *spec.Schema:
    95  		isInt := kind >= reflect.Int && kind <= reflect.Uint64
    96  		isFloat := kind == reflect.Float32 || kind == reflect.Float64
    97  		r := isInt || isFloat
    98  		debugLog("schema props validator for %q applies %t for %T (kind: %v) isInt=%t, isFloat=%t\n", n.Path, r, source, kind, isInt, isFloat)
    99  		return r
   100  	}
   101  	debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", n.Path, false, source, kind)
   102  	return false
   103  }
   104  
   105  // Validate provides a validator for generic JSON numbers,
   106  //
   107  // By default, numbers are internally represented as float64.
   108  // Formats float, or float32 may alter this behavior by mapping to float32.
   109  // A special validation process is followed for integers, with optional "format":
   110  // this is an attempt to provide a validation with native types.
   111  //
   112  // NOTE: since the constraint specified (boundary, multipleOf) is unmarshalled
   113  // as float64, loss of information remains possible (e.g. on very large integers).
   114  //
   115  // Since this value directly comes from the unmarshalling, it is not possible
   116  // at this stage of processing to check further and guarantee the correctness of such values.
   117  //
   118  // Normally, the JSON Number.MAX_SAFE_INTEGER (resp. Number.MIN_SAFE_INTEGER)
   119  // would check we do not get such a loss.
   120  //
   121  // If this is the case, replace AddErrors() by AddWarnings() and IsValid() by !HasWarnings().
   122  //
   123  // TODO: consider replacing boundary check errors by simple warnings.
   124  //
   125  // TODO: default boundaries with MAX_SAFE_INTEGER are not checked (specific to json.Number?)
   126  func (n *numberValidator) Validate(val interface{}) *Result {
   127  	res := new(Result)
   128  
   129  	resMultiple := new(Result)
   130  	resMinimum := new(Result)
   131  	resMaximum := new(Result)
   132  
   133  	// Used only to attempt to validate constraint on value,
   134  	// even though value or constraint specified do not match type and format
   135  	data := valueHelp.asFloat64(val)
   136  
   137  	// Is the provided value within the range of the specified numeric type and format?
   138  	res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path))
   139  
   140  	// nolint: dupl
   141  	if n.MultipleOf != nil {
   142  		// Is the constraint specifier within the range of the specific numeric type and format?
   143  		resMultiple.AddErrors(IsValueValidAgainstRange(*n.MultipleOf, n.Type, n.Format, "MultipleOf", n.Path))
   144  		if resMultiple.IsValid() {
   145  			// Constraint validated with compatible types
   146  			if err := MultipleOfNativeType(n.Path, n.In, val, *n.MultipleOf); err != nil {
   147  				resMultiple.Merge(errorHelp.sErr(err))
   148  			}
   149  		} else {
   150  			// Constraint nevertheless validated, converted as general number
   151  			if err := MultipleOf(n.Path, n.In, data, *n.MultipleOf); err != nil {
   152  				resMultiple.Merge(errorHelp.sErr(err))
   153  			}
   154  		}
   155  	}
   156  
   157  	// nolint: dupl
   158  	if n.Maximum != nil {
   159  		// Is the constraint specifier within the range of the specific numeric type and format?
   160  		resMaximum.AddErrors(IsValueValidAgainstRange(*n.Maximum, n.Type, n.Format, "Maximum boundary", n.Path))
   161  		if resMaximum.IsValid() {
   162  			// Constraint validated with compatible types
   163  			if err := MaximumNativeType(n.Path, n.In, val, *n.Maximum, n.ExclusiveMaximum); err != nil {
   164  				resMaximum.Merge(errorHelp.sErr(err))
   165  			}
   166  		} else {
   167  			// Constraint nevertheless validated, converted as general number
   168  			if err := Maximum(n.Path, n.In, data, *n.Maximum, n.ExclusiveMaximum); err != nil {
   169  				resMaximum.Merge(errorHelp.sErr(err))
   170  			}
   171  		}
   172  	}
   173  
   174  	// nolint: dupl
   175  	if n.Minimum != nil {
   176  		// Is the constraint specifier within the range of the specific numeric type and format?
   177  		resMinimum.AddErrors(IsValueValidAgainstRange(*n.Minimum, n.Type, n.Format, "Minimum boundary", n.Path))
   178  		if resMinimum.IsValid() {
   179  			// Constraint validated with compatible types
   180  			if err := MinimumNativeType(n.Path, n.In, val, *n.Minimum, n.ExclusiveMinimum); err != nil {
   181  				resMinimum.Merge(errorHelp.sErr(err))
   182  			}
   183  		} else {
   184  			// Constraint nevertheless validated, converted as general number
   185  			if err := Minimum(n.Path, n.In, data, *n.Minimum, n.ExclusiveMinimum); err != nil {
   186  				resMinimum.Merge(errorHelp.sErr(err))
   187  			}
   188  		}
   189  	}
   190  	res.Merge(resMultiple, resMinimum, resMaximum)
   191  	res.Inc()
   192  	return res
   193  }
   194  
   195  type stringValidator struct {
   196  	MaxLength *int64
   197  	MinLength *int64
   198  	Pattern   string
   199  	Path      string
   200  	In        string
   201  }
   202  
   203  func (s *stringValidator) SetPath(path string) {
   204  	s.Path = path
   205  }
   206  
   207  func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool {
   208  	switch source.(type) {
   209  	case *spec.Schema:
   210  		r := kind == reflect.String
   211  		debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind)
   212  		return r
   213  	}
   214  	debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, false, source, kind)
   215  	return false
   216  }
   217  
   218  func (s *stringValidator) Validate(val interface{}) *Result {
   219  	data, ok := val.(string)
   220  	if !ok {
   221  		return errorHelp.sErr(errors.InvalidType(s.Path, s.In, stringType, val))
   222  	}
   223  
   224  	if s.MaxLength != nil {
   225  		if err := MaxLength(s.Path, s.In, data, *s.MaxLength); err != nil {
   226  			return errorHelp.sErr(err)
   227  		}
   228  	}
   229  
   230  	if s.MinLength != nil {
   231  		if err := MinLength(s.Path, s.In, data, *s.MinLength); err != nil {
   232  			return errorHelp.sErr(err)
   233  		}
   234  	}
   235  
   236  	if s.Pattern != "" {
   237  		if err := Pattern(s.Path, s.In, data, s.Pattern); err != nil {
   238  			return errorHelp.sErr(err)
   239  		}
   240  	}
   241  	return nil
   242  }