k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/validate/values.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  	"fmt"
    19  	"reflect"
    20  	"unicode/utf8"
    21  
    22  	"github.com/go-openapi/swag"
    23  	"k8s.io/kube-openapi/pkg/validation/errors"
    24  	"k8s.io/kube-openapi/pkg/validation/strfmt"
    25  )
    26  
    27  // Enum validates if the data is a member of the enum
    28  func Enum(path, in string, data interface{}, enum interface{}) *errors.Validation {
    29  	val := reflect.ValueOf(enum)
    30  	if val.Kind() != reflect.Slice {
    31  		return nil
    32  	}
    33  
    34  	var values []interface{}
    35  	for i := 0; i < val.Len(); i++ {
    36  		ele := val.Index(i)
    37  		enumValue := ele.Interface()
    38  		if data != nil {
    39  			if reflect.DeepEqual(data, enumValue) {
    40  				return nil
    41  			}
    42  			actualType := reflect.TypeOf(enumValue)
    43  			if actualType == nil { // Safeguard. Frankly, I don't know how we may get a nil
    44  				continue
    45  			}
    46  			expectedValue := reflect.ValueOf(data)
    47  			if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
    48  				// Attempt comparison after type conversion
    49  				if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
    50  					return nil
    51  				}
    52  			}
    53  		}
    54  		values = append(values, enumValue)
    55  	}
    56  	return errors.EnumFail(path, in, data, values)
    57  }
    58  
    59  // MinItems validates that there are at least n items in a slice
    60  func MinItems(path, in string, size, min int64) *errors.Validation {
    61  	if size < min {
    62  		return errors.TooFewItems(path, in, min, size)
    63  	}
    64  	return nil
    65  }
    66  
    67  // MaxItems validates that there are at most n items in a slice
    68  func MaxItems(path, in string, size, max int64) *errors.Validation {
    69  	if size > max {
    70  		return errors.TooManyItems(path, in, max, size)
    71  	}
    72  	return nil
    73  }
    74  
    75  // UniqueItems validates that the provided slice has unique elements
    76  func UniqueItems(path, in string, data interface{}) *errors.Validation {
    77  	val := reflect.ValueOf(data)
    78  	if val.Kind() != reflect.Slice {
    79  		return nil
    80  	}
    81  	var unique []interface{}
    82  	for i := 0; i < val.Len(); i++ {
    83  		v := val.Index(i).Interface()
    84  		for _, u := range unique {
    85  			if reflect.DeepEqual(v, u) {
    86  				return errors.DuplicateItems(path, in)
    87  			}
    88  		}
    89  		unique = append(unique, v)
    90  	}
    91  	return nil
    92  }
    93  
    94  // MinLength validates a string for minimum length
    95  func MinLength(path, in, data string, minLength int64) *errors.Validation {
    96  	strLen := int64(utf8.RuneCount([]byte(data)))
    97  	if strLen < minLength {
    98  		return errors.TooShort(path, in, minLength, data)
    99  	}
   100  	return nil
   101  }
   102  
   103  // MaxLength validates a string for maximum length
   104  func MaxLength(path, in, data string, maxLength int64) *errors.Validation {
   105  	strLen := int64(utf8.RuneCount([]byte(data)))
   106  	if strLen > maxLength {
   107  		return errors.TooLong(path, in, maxLength, data)
   108  	}
   109  	return nil
   110  }
   111  
   112  // Required validates an interface for requiredness
   113  func Required(path, in string, data interface{}) *errors.Validation {
   114  	val := reflect.ValueOf(data)
   115  	if val.IsValid() {
   116  		if reflect.DeepEqual(reflect.Zero(val.Type()).Interface(), val.Interface()) {
   117  			return errors.Required(path, in)
   118  		}
   119  		return nil
   120  	}
   121  	return errors.Required(path, in)
   122  }
   123  
   124  // Pattern validates a string against a regular expression
   125  func Pattern(path, in, data, pattern string) *errors.Validation {
   126  	re, err := compileRegexp(pattern)
   127  	if err != nil {
   128  		return errors.FailedPattern(path, in, fmt.Sprintf("%s, but pattern is invalid: %s", pattern, err.Error()), data)
   129  	}
   130  	if !re.MatchString(data) {
   131  		return errors.FailedPattern(path, in, pattern, data)
   132  	}
   133  	return nil
   134  }
   135  
   136  // MaximumInt validates if a number is smaller than a given maximum
   137  func MaximumInt(path, in string, data, max int64, exclusive bool) *errors.Validation {
   138  	if (!exclusive && data > max) || (exclusive && data >= max) {
   139  		return errors.ExceedsMaximumInt(path, in, max, exclusive, data)
   140  	}
   141  	return nil
   142  }
   143  
   144  // MaximumUint validates if a number is smaller than a given maximum
   145  func MaximumUint(path, in string, data, max uint64, exclusive bool) *errors.Validation {
   146  	if (!exclusive && data > max) || (exclusive && data >= max) {
   147  		return errors.ExceedsMaximumUint(path, in, max, exclusive, data)
   148  	}
   149  	return nil
   150  }
   151  
   152  // Maximum validates if a number is smaller than a given maximum
   153  func Maximum(path, in string, data, max float64, exclusive bool) *errors.Validation {
   154  	if (!exclusive && data > max) || (exclusive && data >= max) {
   155  		return errors.ExceedsMaximum(path, in, max, exclusive, data)
   156  	}
   157  	return nil
   158  }
   159  
   160  // Minimum validates if a number is smaller than a given minimum
   161  func Minimum(path, in string, data, min float64, exclusive bool) *errors.Validation {
   162  	if (!exclusive && data < min) || (exclusive && data <= min) {
   163  		return errors.ExceedsMinimum(path, in, min, exclusive, data)
   164  	}
   165  	return nil
   166  }
   167  
   168  // MinimumInt validates if a number is smaller than a given minimum
   169  func MinimumInt(path, in string, data, min int64, exclusive bool) *errors.Validation {
   170  	if (!exclusive && data < min) || (exclusive && data <= min) {
   171  		return errors.ExceedsMinimumInt(path, in, min, exclusive, data)
   172  	}
   173  	return nil
   174  }
   175  
   176  // MinimumUint validates if a number is smaller than a given minimum
   177  func MinimumUint(path, in string, data, min uint64, exclusive bool) *errors.Validation {
   178  	if (!exclusive && data < min) || (exclusive && data <= min) {
   179  		return errors.ExceedsMinimumUint(path, in, min, exclusive, data)
   180  	}
   181  	return nil
   182  }
   183  
   184  // MultipleOf validates if the provided number is a multiple of the factor
   185  func MultipleOf(path, in string, data, factor float64) *errors.Validation {
   186  	// multipleOf factor must be positive
   187  	if factor <= 0 {
   188  		return errors.MultipleOfMustBePositive(path, in, factor)
   189  	}
   190  	var mult float64
   191  	if factor < 1 {
   192  		mult = 1 / factor * data
   193  	} else {
   194  		mult = data / factor
   195  	}
   196  	if !swag.IsFloat64AJSONInteger(mult) {
   197  		return errors.NotMultipleOf(path, in, factor, data)
   198  	}
   199  	return nil
   200  }
   201  
   202  // MultipleOfInt validates if the provided integer is a multiple of the factor
   203  func MultipleOfInt(path, in string, data int64, factor int64) *errors.Validation {
   204  	// multipleOf factor must be positive
   205  	if factor <= 0 {
   206  		return errors.MultipleOfMustBePositive(path, in, factor)
   207  	}
   208  	mult := data / factor
   209  	if mult*factor != data {
   210  		return errors.NotMultipleOf(path, in, factor, data)
   211  	}
   212  	return nil
   213  }
   214  
   215  // MultipleOfUint validates if the provided unsigned integer is a multiple of the factor
   216  func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation {
   217  	// multipleOf factor must be positive
   218  	if factor == 0 {
   219  		return errors.MultipleOfMustBePositive(path, in, factor)
   220  	}
   221  	mult := data / factor
   222  	if mult*factor != data {
   223  		return errors.NotMultipleOf(path, in, factor, data)
   224  	}
   225  	return nil
   226  }
   227  
   228  // FormatOf validates if a string matches a format in the format registry
   229  func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation {
   230  	if registry == nil {
   231  		registry = strfmt.Default
   232  	}
   233  	if ok := registry.ContainsName(format); !ok {
   234  		return errors.InvalidTypeName(format)
   235  	}
   236  	if ok := registry.Validates(format, data); !ok {
   237  		return errors.InvalidType(path, in, format, data)
   238  	}
   239  	return nil
   240  }
   241  
   242  // MaximumNativeType provides native type constraint validation as a facade
   243  // to various numeric types versions of Maximum constraint check.
   244  //
   245  // Assumes that any possible loss conversion during conversion has been
   246  // checked beforehand.
   247  //
   248  // NOTE: currently, the max value is marshalled as a float64, no matter what,
   249  // which means there may be a loss during conversions (e.g. for very large integers)
   250  //
   251  // TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
   252  func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation {
   253  	kind := reflect.ValueOf(val).Type().Kind()
   254  	switch kind {
   255  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   256  		value := valueHelp.asInt64(val)
   257  		return MaximumInt(path, in, value, int64(max), exclusive)
   258  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   259  		value := valueHelp.asUint64(val)
   260  		if max < 0 {
   261  			return errors.ExceedsMaximum(path, in, max, exclusive, val)
   262  		}
   263  		return MaximumUint(path, in, value, uint64(max), exclusive)
   264  	case reflect.Float32, reflect.Float64:
   265  		fallthrough
   266  	default:
   267  		value := valueHelp.asFloat64(val)
   268  		return Maximum(path, in, value, max, exclusive)
   269  	}
   270  }
   271  
   272  // MinimumNativeType provides native type constraint validation as a facade
   273  // to various numeric types versions of Minimum constraint check.
   274  //
   275  // Assumes that any possible loss conversion during conversion has been
   276  // checked beforehand.
   277  //
   278  // NOTE: currently, the min value is marshalled as a float64, no matter what,
   279  // which means there may be a loss during conversions (e.g. for very large integers)
   280  //
   281  // TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
   282  func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation {
   283  	kind := reflect.ValueOf(val).Type().Kind()
   284  	switch kind {
   285  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   286  		value := valueHelp.asInt64(val)
   287  		return MinimumInt(path, in, value, int64(min), exclusive)
   288  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   289  		value := valueHelp.asUint64(val)
   290  		if min < 0 {
   291  			return nil
   292  		}
   293  		return MinimumUint(path, in, value, uint64(min), exclusive)
   294  	case reflect.Float32, reflect.Float64:
   295  		fallthrough
   296  	default:
   297  		value := valueHelp.asFloat64(val)
   298  		return Minimum(path, in, value, min, exclusive)
   299  	}
   300  }
   301  
   302  // MultipleOfNativeType provides native type constraint validation as a facade
   303  // to various numeric types version of MultipleOf constraint check.
   304  //
   305  // Assumes that any possible loss conversion during conversion has been
   306  // checked beforehand.
   307  //
   308  // NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what,
   309  // which means there may be a loss during conversions (e.g. for very large integers)
   310  //
   311  // TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
   312  func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation {
   313  	kind := reflect.ValueOf(val).Type().Kind()
   314  	switch kind {
   315  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   316  		value := valueHelp.asInt64(val)
   317  		return MultipleOfInt(path, in, value, int64(multipleOf))
   318  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   319  		value := valueHelp.asUint64(val)
   320  		return MultipleOfUint(path, in, value, uint64(multipleOf))
   321  	case reflect.Float32, reflect.Float64:
   322  		fallthrough
   323  	default:
   324  		value := valueHelp.asFloat64(val)
   325  		return MultipleOf(path, in, value, multipleOf)
   326  	}
   327  }
   328  
   329  // IsValueValidAgainstRange checks that a numeric value is compatible with
   330  // the range defined by Type and Format, that is, may be converted without loss.
   331  //
   332  // NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L
   333  func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error {
   334  	kind := reflect.ValueOf(val).Type().Kind()
   335  
   336  	// What is the string representation of val
   337  	stringRep := ""
   338  	switch kind {
   339  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   340  		stringRep = swag.FormatUint64(valueHelp.asUint64(val))
   341  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   342  		stringRep = swag.FormatInt64(valueHelp.asInt64(val))
   343  	case reflect.Float32, reflect.Float64:
   344  		stringRep = swag.FormatFloat64(valueHelp.asFloat64(val))
   345  	default:
   346  		return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path)
   347  	}
   348  
   349  	var errVal error
   350  
   351  	switch typeName {
   352  	case integerType:
   353  		switch format {
   354  		case integerFormatInt32:
   355  			_, errVal = swag.ConvertInt32(stringRep)
   356  		case integerFormatUInt32:
   357  			_, errVal = swag.ConvertUint32(stringRep)
   358  		case integerFormatUInt64:
   359  			_, errVal = swag.ConvertUint64(stringRep)
   360  		case integerFormatInt64:
   361  			fallthrough
   362  		default:
   363  			_, errVal = swag.ConvertInt64(stringRep)
   364  		}
   365  	case numberType:
   366  		fallthrough
   367  	default:
   368  		switch format {
   369  		case numberFormatFloat, numberFormatFloat32:
   370  			_, errVal = swag.ConvertFloat32(stringRep)
   371  		case numberFormatDouble, numberFormatFloat64:
   372  			fallthrough
   373  		default:
   374  			// No check can be performed here since
   375  			// no number beyond float64 is supported
   376  		}
   377  	}
   378  	if errVal != nil { // We don't report the actual errVal from strconv
   379  		if format != "" {
   380  			errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path)
   381  		} else {
   382  			errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path)
   383  		}
   384  	}
   385  	return errVal
   386  }