...

Source file src/github.com/go-openapi/validate/values.go

Documentation: github.com/go-openapi/validate

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

View as plain text