1/*
2Package validation provides methods for validating parameter value using reflection.
3*/
4package validation
5
6// Copyright 2017 Microsoft Corporation
7//
8//  Licensed under the Apache License, Version 2.0 (the "License");
9//  you may not use this file except in compliance with the License.
10//  You may obtain a copy of the License at
11//
12//      http://www.apache.org/licenses/LICENSE-2.0
13//
14//  Unless required by applicable law or agreed to in writing, software
15//  distributed under the License is distributed on an "AS IS" BASIS,
16//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17//  See the License for the specific language governing permissions and
18//  limitations under the License.
19
20import (
21	"fmt"
22	"reflect"
23	"regexp"
24	"strings"
25)
26
27// Disabled controls if parameter validation should be globally disabled.  The default is false.
28var Disabled bool
29
30// Constraint stores constraint name, target field name
31// Rule and chain validations.
32type Constraint struct {
33
34	// Target field name for validation.
35	Target string
36
37	// Constraint name e.g. minLength, MaxLength, Pattern, etc.
38	Name string
39
40	// Rule for constraint e.g. greater than 10, less than 5 etc.
41	Rule interface{}
42
43	// Chain Validations for struct type
44	Chain []Constraint
45}
46
47// Validation stores parameter-wise validation.
48type Validation struct {
49	TargetValue interface{}
50	Constraints []Constraint
51}
52
53// Constraint list
54const (
55	Empty            = "Empty"
56	Null             = "Null"
57	ReadOnly         = "ReadOnly"
58	Pattern          = "Pattern"
59	MaxLength        = "MaxLength"
60	MinLength        = "MinLength"
61	MaxItems         = "MaxItems"
62	MinItems         = "MinItems"
63	MultipleOf       = "MultipleOf"
64	UniqueItems      = "UniqueItems"
65	InclusiveMaximum = "InclusiveMaximum"
66	ExclusiveMaximum = "ExclusiveMaximum"
67	ExclusiveMinimum = "ExclusiveMinimum"
68	InclusiveMinimum = "InclusiveMinimum"
69)
70
71// Validate method validates constraints on parameter
72// passed in validation array.
73func Validate(m []Validation) error {
74	if Disabled {
75		return nil
76	}
77	for _, item := range m {
78		v := reflect.ValueOf(item.TargetValue)
79		for _, constraint := range item.Constraints {
80			var err error
81			switch v.Kind() {
82			case reflect.Ptr:
83				err = validatePtr(v, constraint)
84			case reflect.String:
85				err = validateString(v, constraint)
86			case reflect.Struct:
87				err = validateStruct(v, constraint)
88			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
89				err = validateInt(v, constraint)
90			case reflect.Float32, reflect.Float64:
91				err = validateFloat(v, constraint)
92			case reflect.Array, reflect.Slice, reflect.Map:
93				err = validateArrayMap(v, constraint)
94			default:
95				err = createError(v, constraint, fmt.Sprintf("unknown type %v", v.Kind()))
96			}
97
98			if err != nil {
99				return err
100			}
101		}
102	}
103	return nil
104}
105
106func validateStruct(x reflect.Value, v Constraint, name ...string) error {
107	//Get field name from target name which is in format a.b.c
108	s := strings.Split(v.Target, ".")
109	f := x.FieldByName(s[len(s)-1])
110	if isZero(f) {
111		return createError(x, v, fmt.Sprintf("field %q doesn't exist", v.Target))
112	}
113
114	return Validate([]Validation{
115		{
116			TargetValue: getInterfaceValue(f),
117			Constraints: []Constraint{v},
118		},
119	})
120}
121
122func validatePtr(x reflect.Value, v Constraint) error {
123	if v.Name == ReadOnly {
124		if !x.IsNil() {
125			return createError(x.Elem(), v, "readonly parameter; must send as nil or empty in request")
126		}
127		return nil
128	}
129	if x.IsNil() {
130		return checkNil(x, v)
131	}
132	if v.Chain != nil {
133		return Validate([]Validation{
134			{
135				TargetValue: getInterfaceValue(x.Elem()),
136				Constraints: v.Chain,
137			},
138		})
139	}
140	return nil
141}
142
143func validateInt(x reflect.Value, v Constraint) error {
144	i := x.Int()
145	r, ok := toInt64(v.Rule)
146	if !ok {
147		return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
148	}
149	switch v.Name {
150	case MultipleOf:
151		if i%r != 0 {
152			return createError(x, v, fmt.Sprintf("value must be a multiple of %v", r))
153		}
154	case ExclusiveMinimum:
155		if i <= r {
156			return createError(x, v, fmt.Sprintf("value must be greater than %v", r))
157		}
158	case ExclusiveMaximum:
159		if i >= r {
160			return createError(x, v, fmt.Sprintf("value must be less than %v", r))
161		}
162	case InclusiveMinimum:
163		if i < r {
164			return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r))
165		}
166	case InclusiveMaximum:
167		if i > r {
168			return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r))
169		}
170	default:
171		return createError(x, v, fmt.Sprintf("constraint %v is not applicable for type integer", v.Name))
172	}
173	return nil
174}
175
176func validateFloat(x reflect.Value, v Constraint) error {
177	f := x.Float()
178	r, ok := v.Rule.(float64)
179	if !ok {
180		return createError(x, v, fmt.Sprintf("rule must be float value for %v constraint; got: %v", v.Name, v.Rule))
181	}
182	switch v.Name {
183	case ExclusiveMinimum:
184		if f <= r {
185			return createError(x, v, fmt.Sprintf("value must be greater than %v", r))
186		}
187	case ExclusiveMaximum:
188		if f >= r {
189			return createError(x, v, fmt.Sprintf("value must be less than %v", r))
190		}
191	case InclusiveMinimum:
192		if f < r {
193			return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r))
194		}
195	case InclusiveMaximum:
196		if f > r {
197			return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r))
198		}
199	default:
200		return createError(x, v, fmt.Sprintf("constraint %s is not applicable for type float", v.Name))
201	}
202	return nil
203}
204
205func validateString(x reflect.Value, v Constraint) error {
206	s := x.String()
207	switch v.Name {
208	case Empty:
209		if len(s) == 0 {
210			return checkEmpty(x, v)
211		}
212	case Pattern:
213		reg, err := regexp.Compile(v.Rule.(string))
214		if err != nil {
215			return createError(x, v, err.Error())
216		}
217		if !reg.MatchString(s) {
218			return createError(x, v, fmt.Sprintf("value doesn't match pattern %v", v.Rule))
219		}
220	case MaxLength:
221		if _, ok := v.Rule.(int); !ok {
222			return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
223		}
224		if len(s) > v.Rule.(int) {
225			return createError(x, v, fmt.Sprintf("value length must be less than or equal to %v", v.Rule))
226		}
227	case MinLength:
228		if _, ok := v.Rule.(int); !ok {
229			return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
230		}
231		if len(s) < v.Rule.(int) {
232			return createError(x, v, fmt.Sprintf("value length must be greater than or equal to %v", v.Rule))
233		}
234	case ReadOnly:
235		if len(s) > 0 {
236			return createError(reflect.ValueOf(s), v, "readonly parameter; must send as nil or empty in request")
237		}
238	default:
239		return createError(x, v, fmt.Sprintf("constraint %s is not applicable to string type", v.Name))
240	}
241
242	if v.Chain != nil {
243		return Validate([]Validation{
244			{
245				TargetValue: getInterfaceValue(x),
246				Constraints: v.Chain,
247			},
248		})
249	}
250	return nil
251}
252
253func validateArrayMap(x reflect.Value, v Constraint) error {
254	switch v.Name {
255	case Null:
256		if x.IsNil() {
257			return checkNil(x, v)
258		}
259	case Empty:
260		if x.IsNil() || x.Len() == 0 {
261			return checkEmpty(x, v)
262		}
263	case MaxItems:
264		if _, ok := v.Rule.(int); !ok {
265			return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule))
266		}
267		if x.Len() > v.Rule.(int) {
268			return createError(x, v, fmt.Sprintf("maximum item limit is %v; got: %v", v.Rule, x.Len()))
269		}
270	case MinItems:
271		if _, ok := v.Rule.(int); !ok {
272			return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule))
273		}
274		if x.Len() < v.Rule.(int) {
275			return createError(x, v, fmt.Sprintf("minimum item limit is %v; got: %v", v.Rule, x.Len()))
276		}
277	case UniqueItems:
278		if x.Kind() == reflect.Array || x.Kind() == reflect.Slice {
279			if !checkForUniqueInArray(x) {
280				return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x))
281			}
282		} else if x.Kind() == reflect.Map {
283			if !checkForUniqueInMap(x) {
284				return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x))
285			}
286		} else {
287			return createError(x, v, fmt.Sprintf("type must be array, slice or map for constraint %v; got: %v", v.Name, x.Kind()))
288		}
289	case ReadOnly:
290		if x.Len() != 0 {
291			return createError(x, v, "readonly parameter; must send as nil or empty in request")
292		}
293	case Pattern:
294		reg, err := regexp.Compile(v.Rule.(string))
295		if err != nil {
296			return createError(x, v, err.Error())
297		}
298		keys := x.MapKeys()
299		for _, k := range keys {
300			if !reg.MatchString(k.String()) {
301				return createError(k, v, fmt.Sprintf("map key doesn't match pattern %v", v.Rule))
302			}
303		}
304	default:
305		return createError(x, v, fmt.Sprintf("constraint %v is not applicable to array, slice and map type", v.Name))
306	}
307
308	if v.Chain != nil {
309		return Validate([]Validation{
310			{
311				TargetValue: getInterfaceValue(x),
312				Constraints: v.Chain,
313			},
314		})
315	}
316	return nil
317}
318
319func checkNil(x reflect.Value, v Constraint) error {
320	if _, ok := v.Rule.(bool); !ok {
321		return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule))
322	}
323	if v.Rule.(bool) {
324		return createError(x, v, "value can not be null; required parameter")
325	}
326	return nil
327}
328
329func checkEmpty(x reflect.Value, v Constraint) error {
330	if _, ok := v.Rule.(bool); !ok {
331		return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule))
332	}
333
334	if v.Rule.(bool) {
335		return createError(x, v, "value can not be null or empty; required parameter")
336	}
337	return nil
338}
339
340func checkForUniqueInArray(x reflect.Value) bool {
341	if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 {
342		return false
343	}
344	arrOfInterface := make([]interface{}, x.Len())
345
346	for i := 0; i < x.Len(); i++ {
347		arrOfInterface[i] = x.Index(i).Interface()
348	}
349
350	m := make(map[interface{}]bool)
351	for _, val := range arrOfInterface {
352		if m[val] {
353			return false
354		}
355		m[val] = true
356	}
357	return true
358}
359
360func checkForUniqueInMap(x reflect.Value) bool {
361	if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 {
362		return false
363	}
364	mapOfInterface := make(map[interface{}]interface{}, x.Len())
365
366	keys := x.MapKeys()
367	for _, k := range keys {
368		mapOfInterface[k.Interface()] = x.MapIndex(k).Interface()
369	}
370
371	m := make(map[interface{}]bool)
372	for _, val := range mapOfInterface {
373		if m[val] {
374			return false
375		}
376		m[val] = true
377	}
378	return true
379}
380
381func getInterfaceValue(x reflect.Value) interface{} {
382	if x.Kind() == reflect.Invalid {
383		return nil
384	}
385	return x.Interface()
386}
387
388func isZero(x interface{}) bool {
389	return x == reflect.Zero(reflect.TypeOf(x)).Interface()
390}
391
392func createError(x reflect.Value, v Constraint, err string) error {
393	return fmt.Errorf("autorest/validation: validation failed: parameter=%s constraint=%s value=%#v details: %s",
394		v.Target, v.Name, getInterfaceValue(x), err)
395}
396
397func toInt64(v interface{}) (int64, bool) {
398	if i64, ok := v.(int64); ok {
399		return i64, true
400	}
401	// older generators emit max constants as int, so if int64 fails fall back to int
402	if i32, ok := v.(int); ok {
403		return int64(i32), true
404	}
405	return 0, false
406}
407