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 validate
16
17import (
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
28func 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
60func 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
68func 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
76func 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
95func 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
104func 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
113func 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
125func 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
137func 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
145func 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
153func 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
161func 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
169func 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
177func 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
185func 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
203func 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
216func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation {
217	mult := data / factor
218	if mult*factor != data {
219		return errors.NotMultipleOf(path, in, factor, data)
220	}
221	return nil
222}
223
224// FormatOf validates if a string matches a format in the format registry
225func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation {
226	if registry == nil {
227		registry = strfmt.Default
228	}
229	if ok := registry.ContainsName(format); !ok {
230		return errors.InvalidTypeName(format)
231	}
232	if ok := registry.Validates(format, data); !ok {
233		return errors.InvalidType(path, in, format, data)
234	}
235	return nil
236}
237
238// MaximumNativeType provides native type constraint validation as a facade
239// to various numeric types versions of Maximum constraint check.
240//
241// Assumes that any possible loss conversion during conversion has been
242// checked beforehand.
243//
244// NOTE: currently, the max value is marshalled as a float64, no matter what,
245// which means there may be a loss during conversions (e.g. for very large integers)
246//
247// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
248func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation {
249	kind := reflect.ValueOf(val).Type().Kind()
250	switch kind {
251	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
252		value := valueHelp.asInt64(val)
253		return MaximumInt(path, in, value, int64(max), exclusive)
254	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
255		value := valueHelp.asUint64(val)
256		if max < 0 {
257			return errors.ExceedsMaximum(path, in, max, exclusive, val)
258		}
259		return MaximumUint(path, in, value, uint64(max), exclusive)
260	case reflect.Float32, reflect.Float64:
261		fallthrough
262	default:
263		value := valueHelp.asFloat64(val)
264		return Maximum(path, in, value, max, exclusive)
265	}
266}
267
268// MinimumNativeType provides native type constraint validation as a facade
269// to various numeric types versions of Minimum constraint check.
270//
271// Assumes that any possible loss conversion during conversion has been
272// checked beforehand.
273//
274// NOTE: currently, the min value is marshalled as a float64, no matter what,
275// which means there may be a loss during conversions (e.g. for very large integers)
276//
277// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
278func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation {
279	kind := reflect.ValueOf(val).Type().Kind()
280	switch kind {
281	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
282		value := valueHelp.asInt64(val)
283		return MinimumInt(path, in, value, int64(min), exclusive)
284	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
285		value := valueHelp.asUint64(val)
286		if min < 0 {
287			return nil
288		}
289		return MinimumUint(path, in, value, uint64(min), exclusive)
290	case reflect.Float32, reflect.Float64:
291		fallthrough
292	default:
293		value := valueHelp.asFloat64(val)
294		return Minimum(path, in, value, min, exclusive)
295	}
296}
297
298// MultipleOfNativeType provides native type constraint validation as a facade
299// to various numeric types version of MultipleOf constraint check.
300//
301// Assumes that any possible loss conversion during conversion has been
302// checked beforehand.
303//
304// NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what,
305// which means there may be a loss during conversions (e.g. for very large integers)
306//
307// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
308func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation {
309	kind := reflect.ValueOf(val).Type().Kind()
310	switch kind {
311	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
312		value := valueHelp.asInt64(val)
313		return MultipleOfInt(path, in, value, int64(multipleOf))
314	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
315		value := valueHelp.asUint64(val)
316		return MultipleOfUint(path, in, value, uint64(multipleOf))
317	case reflect.Float32, reflect.Float64:
318		fallthrough
319	default:
320		value := valueHelp.asFloat64(val)
321		return MultipleOf(path, in, value, multipleOf)
322	}
323}
324
325// IsValueValidAgainstRange checks that a numeric value is compatible with
326// the range defined by Type and Format, that is, may be converted without loss.
327//
328// NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L
329func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error {
330	kind := reflect.ValueOf(val).Type().Kind()
331
332	// What is the string representation of val
333	stringRep := ""
334	switch kind {
335	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
336		stringRep = swag.FormatUint64(valueHelp.asUint64(val))
337	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
338		stringRep = swag.FormatInt64(valueHelp.asInt64(val))
339	case reflect.Float32, reflect.Float64:
340		stringRep = swag.FormatFloat64(valueHelp.asFloat64(val))
341	default:
342		return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path)
343	}
344
345	var errVal error
346
347	switch typeName {
348	case integerType:
349		switch format {
350		case integerFormatInt32:
351			_, errVal = swag.ConvertInt32(stringRep)
352		case integerFormatUInt32:
353			_, errVal = swag.ConvertUint32(stringRep)
354		case integerFormatUInt64:
355			_, errVal = swag.ConvertUint64(stringRep)
356		case integerFormatInt64:
357			fallthrough
358		default:
359			_, errVal = swag.ConvertInt64(stringRep)
360		}
361	case numberType:
362		fallthrough
363	default:
364		switch format {
365		case numberFormatFloat, numberFormatFloat32:
366			_, errVal = swag.ConvertFloat32(stringRep)
367		case numberFormatDouble, numberFormatFloat64:
368			fallthrough
369		default:
370			// No check can be performed here since
371			// no number beyond float64 is supported
372		}
373	}
374	if errVal != nil { // We don't report the actual errVal from strconv
375		if format != "" {
376			errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path)
377		} else {
378			errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path)
379		}
380	}
381	return errVal
382}
383