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/errors"
23	"github.com/go-openapi/strfmt"
24	"github.com/go-openapi/swag"
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)
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)
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)
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)
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// RequiredString validates a string for requiredness
125func RequiredString(path, in, data string) *errors.Validation {
126	if data == "" {
127		return errors.Required(path, in)
128	}
129	return nil
130}
131
132// RequiredNumber validates a number for requiredness
133func RequiredNumber(path, in string, data float64) *errors.Validation {
134	if data == 0 {
135		return errors.Required(path, in)
136	}
137	return nil
138}
139
140// Pattern validates a string against a regular expression
141func Pattern(path, in, data, pattern string) *errors.Validation {
142	re, err := compileRegexp(pattern)
143	if err != nil {
144		return errors.FailedPattern(path, in, fmt.Sprintf("%s, but pattern is invalid: %s", pattern, err.Error()))
145	}
146	if !re.MatchString(data) {
147		return errors.FailedPattern(path, in, pattern)
148	}
149	return nil
150}
151
152// MaximumInt validates if a number is smaller than a given maximum
153func MaximumInt(path, in string, data, max int64, exclusive bool) *errors.Validation {
154	if (!exclusive && data > max) || (exclusive && data >= max) {
155		return errors.ExceedsMaximumInt(path, in, max, exclusive)
156	}
157	return nil
158}
159
160// MaximumUint validates if a number is smaller than a given maximum
161func MaximumUint(path, in string, data, max uint64, exclusive bool) *errors.Validation {
162	if (!exclusive && data > max) || (exclusive && data >= max) {
163		return errors.ExceedsMaximumUint(path, in, max, exclusive)
164	}
165	return nil
166}
167
168// Maximum validates if a number is smaller than a given maximum
169func Maximum(path, in string, data, max float64, exclusive bool) *errors.Validation {
170	if (!exclusive && data > max) || (exclusive && data >= max) {
171		return errors.ExceedsMaximum(path, in, max, exclusive)
172	}
173	return nil
174}
175
176// Minimum validates if a number is smaller than a given minimum
177func Minimum(path, in string, data, min float64, exclusive bool) *errors.Validation {
178	if (!exclusive && data < min) || (exclusive && data <= min) {
179		return errors.ExceedsMinimum(path, in, min, exclusive)
180	}
181	return nil
182}
183
184// MinimumInt validates if a number is smaller than a given minimum
185func MinimumInt(path, in string, data, min int64, exclusive bool) *errors.Validation {
186	if (!exclusive && data < min) || (exclusive && data <= min) {
187		return errors.ExceedsMinimumInt(path, in, min, exclusive)
188	}
189	return nil
190}
191
192// MinimumUint validates if a number is smaller than a given minimum
193func MinimumUint(path, in string, data, min uint64, exclusive bool) *errors.Validation {
194	if (!exclusive && data < min) || (exclusive && data <= min) {
195		return errors.ExceedsMinimumUint(path, in, min, exclusive)
196	}
197	return nil
198}
199
200// MultipleOf validates if the provided number is a multiple of the factor
201func MultipleOf(path, in string, data, factor float64) *errors.Validation {
202	// multipleOf factor must be positive
203	if factor < 0 {
204		return errors.MultipleOfMustBePositive(path, in, factor)
205	}
206	var mult float64
207	if factor < 1 {
208		mult = 1 / factor * data
209	} else {
210		mult = data / factor
211	}
212	if !swag.IsFloat64AJSONInteger(mult) {
213		return errors.NotMultipleOf(path, in, factor)
214	}
215	return nil
216}
217
218// MultipleOfInt validates if the provided integer is a multiple of the factor
219func MultipleOfInt(path, in string, data int64, factor int64) *errors.Validation {
220	// multipleOf factor must be positive
221	if factor < 0 {
222		return errors.MultipleOfMustBePositive(path, in, factor)
223	}
224	mult := data / factor
225	if mult*factor != data {
226		return errors.NotMultipleOf(path, in, factor)
227	}
228	return nil
229}
230
231// MultipleOfUint validates if the provided unsigned integer is a multiple of the factor
232func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation {
233	mult := data / factor
234	if mult*factor != data {
235		return errors.NotMultipleOf(path, in, factor)
236	}
237	return nil
238}
239
240// FormatOf validates if a string matches a format in the format registry
241func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation {
242	if registry == nil {
243		registry = strfmt.Default
244	}
245	if ok := registry.ContainsName(format); !ok {
246		return errors.InvalidTypeName(format)
247	}
248	if ok := registry.Validates(format, data); !ok {
249		return errors.InvalidType(path, in, format, data)
250	}
251	return nil
252}
253
254// MaximumNativeType provides native type constraint validation as a facade
255// to various numeric types versions of Maximum constraint check.
256//
257// Assumes that any possible loss conversion during conversion has been
258// checked beforehand.
259//
260// NOTE: currently, the max value is marshalled as a float64, no matter what,
261// which means there may be a loss during conversions (e.g. for very large integers)
262//
263// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
264func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation {
265	kind := reflect.ValueOf(val).Type().Kind()
266	switch kind {
267	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
268		value := valueHelp.asInt64(val)
269		return MaximumInt(path, in, value, int64(max), exclusive)
270	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
271		value := valueHelp.asUint64(val)
272		if max < 0 {
273			return errors.ExceedsMaximum(path, in, max, exclusive)
274		}
275		return MaximumUint(path, in, value, uint64(max), exclusive)
276	case reflect.Float32, reflect.Float64:
277		fallthrough
278	default:
279		value := valueHelp.asFloat64(val)
280		return Maximum(path, in, value, max, exclusive)
281	}
282}
283
284// MinimumNativeType provides native type constraint validation as a facade
285// to various numeric types versions of Minimum constraint check.
286//
287// Assumes that any possible loss conversion during conversion has been
288// checked beforehand.
289//
290// NOTE: currently, the min value is marshalled as a float64, no matter what,
291// which means there may be a loss during conversions (e.g. for very large integers)
292//
293// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
294func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation {
295	kind := reflect.ValueOf(val).Type().Kind()
296	switch kind {
297	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
298		value := valueHelp.asInt64(val)
299		return MinimumInt(path, in, value, int64(min), exclusive)
300	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
301		value := valueHelp.asUint64(val)
302		if min < 0 {
303			return nil
304		}
305		return MinimumUint(path, in, value, uint64(min), exclusive)
306	case reflect.Float32, reflect.Float64:
307		fallthrough
308	default:
309		value := valueHelp.asFloat64(val)
310		return Minimum(path, in, value, min, exclusive)
311	}
312}
313
314// MultipleOfNativeType provides native type constraint validation as a facade
315// to various numeric types version of MultipleOf constraint check.
316//
317// Assumes that any possible loss conversion during conversion has been
318// checked beforehand.
319//
320// NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what,
321// which means there may be a loss during conversions (e.g. for very large integers)
322//
323// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
324func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation {
325	kind := reflect.ValueOf(val).Type().Kind()
326	switch kind {
327	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
328		value := valueHelp.asInt64(val)
329		return MultipleOfInt(path, in, value, int64(multipleOf))
330	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
331		value := valueHelp.asUint64(val)
332		return MultipleOfUint(path, in, value, uint64(multipleOf))
333	case reflect.Float32, reflect.Float64:
334		fallthrough
335	default:
336		value := valueHelp.asFloat64(val)
337		return MultipleOf(path, in, value, multipleOf)
338	}
339}
340
341// IsValueValidAgainstRange checks that a numeric value is compatible with
342// the range defined by Type and Format, that is, may be converted without loss.
343//
344// NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L
345func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error {
346	kind := reflect.ValueOf(val).Type().Kind()
347
348	// What is the string representation of val
349	stringRep := ""
350	switch kind {
351	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
352		stringRep = swag.FormatUint64(valueHelp.asUint64(val))
353	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
354		stringRep = swag.FormatInt64(valueHelp.asInt64(val))
355	case reflect.Float32, reflect.Float64:
356		stringRep = swag.FormatFloat64(valueHelp.asFloat64(val))
357	default:
358		return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path)
359	}
360
361	var errVal error
362
363	switch typeName {
364	case "integer":
365		switch format {
366		case "int32":
367			_, errVal = swag.ConvertInt32(stringRep)
368		case "uint32":
369			_, errVal = swag.ConvertUint32(stringRep)
370		case "uint64":
371			_, errVal = swag.ConvertUint64(stringRep)
372		case "int64":
373			fallthrough
374		default:
375			_, errVal = swag.ConvertInt64(stringRep)
376		}
377	case "number":
378		fallthrough
379	default:
380		switch format {
381		case "float", "float32":
382			_, errVal = swag.ConvertFloat32(stringRep)
383		case "double", "float64":
384			fallthrough
385		default:
386			// No check can be performed here since
387			// no number beyond float64 is supported
388		}
389	}
390	if errVal != nil { // We don't report the actual errVal from strconv
391		if format != "" {
392			errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path)
393		} else {
394			errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path)
395		}
396	}
397	return errVal
398}
399