podman
450 строк · 14.5 Кб
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
15package validate16
17import (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
30func Enum(path, in string, data interface{}, enum interface{}) *errors.Validation {31return 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
35func EnumCase(path, in string, data interface{}, enum interface{}, caseSensitive bool) *errors.Validation {36val := reflect.ValueOf(enum)37if val.Kind() != reflect.Slice {38return nil39}40
41dataString := convertEnumCaseStringKind(data, caseSensitive)42var values []interface{}43for i := 0; i < val.Len(); i++ {44ele := val.Index(i)45enumValue := ele.Interface()46if data != nil {47if reflect.DeepEqual(data, enumValue) {48return nil49}50enumString := convertEnumCaseStringKind(enumValue, caseSensitive)51if dataString != nil && enumString != nil && strings.EqualFold(*dataString, *enumString) {52return nil53}54actualType := reflect.TypeOf(enumValue)55if actualType == nil { // Safeguard. Frankly, I don't know how we may get a nil56continue57}58expectedValue := reflect.ValueOf(data)59if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {60// Attempt comparison after type conversion61if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {62return nil63}64}65}66values = append(values, enumValue)67}68return errors.EnumFail(path, in, data, values)69}
70
71// convertEnumCaseStringKind converts interface if it is kind of string and case insensitivity is set
72func convertEnumCaseStringKind(value interface{}, caseSensitive bool) *string {73if caseSensitive {74return nil75}76
77val := reflect.ValueOf(value)78if val.Kind() != reflect.String {79return nil80}81
82str := fmt.Sprintf("%v", value)83return &str84}
85
86// MinItems validates that there are at least n items in a slice
87func MinItems(path, in string, size, min int64) *errors.Validation {88if size < min {89return errors.TooFewItems(path, in, min, size)90}91return nil92}
93
94// MaxItems validates that there are at most n items in a slice
95func MaxItems(path, in string, size, max int64) *errors.Validation {96if size > max {97return errors.TooManyItems(path, in, max, size)98}99return nil100}
101
102// UniqueItems validates that the provided slice has unique elements
103func UniqueItems(path, in string, data interface{}) *errors.Validation {104val := reflect.ValueOf(data)105if val.Kind() != reflect.Slice {106return nil107}108var unique []interface{}109for i := 0; i < val.Len(); i++ {110v := val.Index(i).Interface()111for _, u := range unique {112if reflect.DeepEqual(v, u) {113return errors.DuplicateItems(path, in)114}115}116unique = append(unique, v)117}118return nil119}
120
121// MinLength validates a string for minimum length
122func MinLength(path, in, data string, minLength int64) *errors.Validation {123strLen := int64(utf8.RuneCount([]byte(data)))124if strLen < minLength {125return errors.TooShort(path, in, minLength, data)126}127return nil128}
129
130// MaxLength validates a string for maximum length
131func MaxLength(path, in, data string, maxLength int64) *errors.Validation {132strLen := int64(utf8.RuneCount([]byte(data)))133if strLen > maxLength {134return errors.TooLong(path, in, maxLength, data)135}136return nil137}
138
139// ReadOnly validates an interface for readonly
140func ReadOnly(ctx context.Context, path, in string, data interface{}) *errors.Validation {141
142// read only is only validated when operationType is request143if op := extractOperationType(ctx); op != request {144return nil145}146
147// data must be of zero value of its type148val := reflect.ValueOf(data)149if val.IsValid() {150if reflect.DeepEqual(reflect.Zero(val.Type()).Interface(), val.Interface()) {151return nil152}153} else {154return nil155}156
157return errors.ReadOnly(path, in, data)158}
159
160// Required validates an interface for requiredness
161func Required(path, in string, data interface{}) *errors.Validation {162val := reflect.ValueOf(data)163if val.IsValid() {164if reflect.DeepEqual(reflect.Zero(val.Type()).Interface(), val.Interface()) {165return errors.Required(path, in, data)166}167return nil168}169return errors.Required(path, in, data)170}
171
172// RequiredString validates a string for requiredness
173func RequiredString(path, in, data string) *errors.Validation {174if data == "" {175return errors.Required(path, in, data)176}177return nil178}
179
180// RequiredNumber validates a number for requiredness
181func RequiredNumber(path, in string, data float64) *errors.Validation {182if data == 0 {183return errors.Required(path, in, data)184}185return nil186}
187
188// Pattern validates a string against a regular expression
189func Pattern(path, in, data, pattern string) *errors.Validation {190re, err := compileRegexp(pattern)191if err != nil {192return errors.FailedPattern(path, in, fmt.Sprintf("%s, but pattern is invalid: %s", pattern, err.Error()), data)193}194if !re.MatchString(data) {195return errors.FailedPattern(path, in, pattern, data)196}197return nil198}
199
200// MaximumInt validates if a number is smaller than a given maximum
201func MaximumInt(path, in string, data, max int64, exclusive bool) *errors.Validation {202if (!exclusive && data > max) || (exclusive && data >= max) {203return errors.ExceedsMaximumInt(path, in, max, exclusive, data)204}205return nil206}
207
208// MaximumUint validates if a number is smaller than a given maximum
209func MaximumUint(path, in string, data, max uint64, exclusive bool) *errors.Validation {210if (!exclusive && data > max) || (exclusive && data >= max) {211return errors.ExceedsMaximumUint(path, in, max, exclusive, data)212}213return nil214}
215
216// Maximum validates if a number is smaller than a given maximum
217func Maximum(path, in string, data, max float64, exclusive bool) *errors.Validation {218if (!exclusive && data > max) || (exclusive && data >= max) {219return errors.ExceedsMaximum(path, in, max, exclusive, data)220}221return nil222}
223
224// Minimum validates if a number is smaller than a given minimum
225func Minimum(path, in string, data, min float64, exclusive bool) *errors.Validation {226if (!exclusive && data < min) || (exclusive && data <= min) {227return errors.ExceedsMinimum(path, in, min, exclusive, data)228}229return nil230}
231
232// MinimumInt validates if a number is smaller than a given minimum
233func MinimumInt(path, in string, data, min int64, exclusive bool) *errors.Validation {234if (!exclusive && data < min) || (exclusive && data <= min) {235return errors.ExceedsMinimumInt(path, in, min, exclusive, data)236}237return nil238}
239
240// MinimumUint validates if a number is smaller than a given minimum
241func MinimumUint(path, in string, data, min uint64, exclusive bool) *errors.Validation {242if (!exclusive && data < min) || (exclusive && data <= min) {243return errors.ExceedsMinimumUint(path, in, min, exclusive, data)244}245return nil246}
247
248// MultipleOf validates if the provided number is a multiple of the factor
249func MultipleOf(path, in string, data, factor float64) *errors.Validation {250// multipleOf factor must be positive251if factor <= 0 {252return errors.MultipleOfMustBePositive(path, in, factor)253}254var mult float64255if factor < 1 {256mult = 1 / factor * data257} else {258mult = data / factor259}260if !swag.IsFloat64AJSONInteger(mult) {261return errors.NotMultipleOf(path, in, factor, data)262}263return nil264}
265
266// MultipleOfInt validates if the provided integer is a multiple of the factor
267func MultipleOfInt(path, in string, data int64, factor int64) *errors.Validation {268// multipleOf factor must be positive269if factor <= 0 {270return errors.MultipleOfMustBePositive(path, in, factor)271}272mult := data / factor273if mult*factor != data {274return errors.NotMultipleOf(path, in, factor, data)275}276return nil277}
278
279// MultipleOfUint validates if the provided unsigned integer is a multiple of the factor
280func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation {281// multipleOf factor must be positive282if factor == 0 {283return errors.MultipleOfMustBePositive(path, in, factor)284}285mult := data / factor286if mult*factor != data {287return errors.NotMultipleOf(path, in, factor, data)288}289return nil290}
291
292// FormatOf validates if a string matches a format in the format registry
293func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation {294if registry == nil {295registry = strfmt.Default296}297if ok := registry.ContainsName(format); !ok {298return errors.InvalidTypeName(format)299}300if ok := registry.Validates(format, data); !ok {301return errors.InvalidType(path, in, format, data)302}303return nil304}
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
316func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation {317kind := reflect.ValueOf(val).Type().Kind()318switch kind {319case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:320value := valueHelp.asInt64(val)321return MaximumInt(path, in, value, int64(max), exclusive)322case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:323value := valueHelp.asUint64(val)324if max < 0 {325return errors.ExceedsMaximum(path, in, max, exclusive, val)326}327return MaximumUint(path, in, value, uint64(max), exclusive)328case reflect.Float32, reflect.Float64:329fallthrough330default:331value := valueHelp.asFloat64(val)332return 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
346func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation {347kind := reflect.ValueOf(val).Type().Kind()348switch kind {349case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:350value := valueHelp.asInt64(val)351return MinimumInt(path, in, value, int64(min), exclusive)352case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:353value := valueHelp.asUint64(val)354if min < 0 {355return nil356}357return MinimumUint(path, in, value, uint64(min), exclusive)358case reflect.Float32, reflect.Float64:359fallthrough360default:361value := valueHelp.asFloat64(val)362return 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
376func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation {377kind := reflect.ValueOf(val).Type().Kind()378switch kind {379case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:380value := valueHelp.asInt64(val)381return MultipleOfInt(path, in, value, int64(multipleOf))382case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:383value := valueHelp.asUint64(val)384return MultipleOfUint(path, in, value, uint64(multipleOf))385case reflect.Float32, reflect.Float64:386fallthrough387default:388value := valueHelp.asFloat64(val)389return 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
397func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error {398kind := reflect.ValueOf(val).Type().Kind()399
400// What is the string representation of val401var stringRep string402switch kind {403case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:404stringRep = swag.FormatUint64(valueHelp.asUint64(val))405case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:406stringRep = swag.FormatInt64(valueHelp.asInt64(val))407case reflect.Float32, reflect.Float64:408stringRep = swag.FormatFloat64(valueHelp.asFloat64(val))409default:410return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path)411}412
413var errVal error414
415switch typeName {416case integerType:417switch format {418case integerFormatInt32:419_, errVal = swag.ConvertInt32(stringRep)420case integerFormatUInt32:421_, errVal = swag.ConvertUint32(stringRep)422case integerFormatUInt64:423_, errVal = swag.ConvertUint64(stringRep)424case integerFormatInt64:425fallthrough426default:427_, errVal = swag.ConvertInt64(stringRep)428}429case numberType:430fallthrough431default:432switch format {433case numberFormatFloat, numberFormatFloat32:434_, errVal = swag.ConvertFloat32(stringRep)435case numberFormatDouble, numberFormatFloat64:436fallthrough437default:438// No check can be performed here since439// no number beyond float64 is supported440}441}442if errVal != nil { // We don't report the actual errVal from strconv443if format != "" {444errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path)445} else {446errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path)447}448}449return errVal450}
451