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	"reflect"
19
20	"k8s.io/kube-openapi/pkg/validation/errors"
21	"k8s.io/kube-openapi/pkg/validation/spec"
22)
23
24// An EntityValidator is an interface for things that can validate entities
25type EntityValidator interface {
26	Validate(interface{}) *Result
27}
28
29type valueValidator interface {
30	SetPath(path string)
31	Applies(interface{}, reflect.Kind) bool
32	Validate(interface{}) *Result
33}
34
35type basicCommonValidator struct {
36	Path    string
37	In      string
38	Default interface{}
39	Enum    []interface{}
40}
41
42func (b *basicCommonValidator) SetPath(path string) {
43	b.Path = path
44}
45
46func (b *basicCommonValidator) Applies(source interface{}, kind reflect.Kind) bool {
47	switch source.(type) {
48	case *spec.Schema:
49		return true
50	}
51	return false
52}
53
54func (b *basicCommonValidator) Validate(data interface{}) (res *Result) {
55	if len(b.Enum) > 0 {
56		for _, enumValue := range b.Enum {
57			actualType := reflect.TypeOf(enumValue)
58			if actualType != nil { // Safeguard
59				expectedValue := reflect.ValueOf(data)
60				if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
61					if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
62						return nil
63					}
64				}
65			}
66		}
67		return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum))
68	}
69	return nil
70}
71
72type numberValidator struct {
73	Path             string
74	In               string
75	Default          interface{}
76	MultipleOf       *float64
77	Maximum          *float64
78	ExclusiveMaximum bool
79	Minimum          *float64
80	ExclusiveMinimum bool
81	// Allows for more accurate behavior regarding integers
82	Type   string
83	Format string
84}
85
86func (n *numberValidator) SetPath(path string) {
87	n.Path = path
88}
89
90func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool {
91	switch source.(type) {
92	case *spec.Schema:
93		isInt := kind >= reflect.Int && kind <= reflect.Uint64
94		isFloat := kind == reflect.Float32 || kind == reflect.Float64
95		r := isInt || isFloat
96		debugLog("schema props validator for %q applies %t for %T (kind: %v) isInt=%t, isFloat=%t\n", n.Path, r, source, kind, isInt, isFloat)
97		return r
98	}
99	debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", n.Path, false, source, kind)
100	return false
101}
102
103// Validate provides a validator for generic JSON numbers,
104//
105// By default, numbers are internally represented as float64.
106// Formats float, or float32 may alter this behavior by mapping to float32.
107// A special validation process is followed for integers, with optional "format":
108// this is an attempt to provide a validation with native types.
109//
110// NOTE: since the constraint specified (boundary, multipleOf) is unmarshalled
111// as float64, loss of information remains possible (e.g. on very large integers).
112//
113// Since this value directly comes from the unmarshalling, it is not possible
114// at this stage of processing to check further and guarantee the correctness of such values.
115//
116// Normally, the JSON Number.MAX_SAFE_INTEGER (resp. Number.MIN_SAFE_INTEGER)
117// would check we do not get such a loss.
118//
119// If this is the case, replace AddErrors() by AddWarnings() and IsValid() by !HasWarnings().
120//
121// TODO: consider replacing boundary check errors by simple warnings.
122//
123// TODO: default boundaries with MAX_SAFE_INTEGER are not checked (specific to json.Number?)
124func (n *numberValidator) Validate(val interface{}) *Result {
125	res := new(Result)
126
127	resMultiple := new(Result)
128	resMinimum := new(Result)
129	resMaximum := new(Result)
130
131	// Used only to attempt to validate constraint on value,
132	// even though value or constraint specified do not match type and format
133	data := valueHelp.asFloat64(val)
134
135	// Is the provided value within the range of the specified numeric type and format?
136	res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path))
137
138	// nolint: dupl
139	if n.MultipleOf != nil {
140		// Is the constraint specifier within the range of the specific numeric type and format?
141		resMultiple.AddErrors(IsValueValidAgainstRange(*n.MultipleOf, n.Type, n.Format, "MultipleOf", n.Path))
142		if resMultiple.IsValid() {
143			// Constraint validated with compatible types
144			if err := MultipleOfNativeType(n.Path, n.In, val, *n.MultipleOf); err != nil {
145				resMultiple.Merge(errorHelp.sErr(err))
146			}
147		} else {
148			// Constraint nevertheless validated, converted as general number
149			if err := MultipleOf(n.Path, n.In, data, *n.MultipleOf); err != nil {
150				resMultiple.Merge(errorHelp.sErr(err))
151			}
152		}
153	}
154
155	// nolint: dupl
156	if n.Maximum != nil {
157		// Is the constraint specifier within the range of the specific numeric type and format?
158		resMaximum.AddErrors(IsValueValidAgainstRange(*n.Maximum, n.Type, n.Format, "Maximum boundary", n.Path))
159		if resMaximum.IsValid() {
160			// Constraint validated with compatible types
161			if err := MaximumNativeType(n.Path, n.In, val, *n.Maximum, n.ExclusiveMaximum); err != nil {
162				resMaximum.Merge(errorHelp.sErr(err))
163			}
164		} else {
165			// Constraint nevertheless validated, converted as general number
166			if err := Maximum(n.Path, n.In, data, *n.Maximum, n.ExclusiveMaximum); err != nil {
167				resMaximum.Merge(errorHelp.sErr(err))
168			}
169		}
170	}
171
172	// nolint: dupl
173	if n.Minimum != nil {
174		// Is the constraint specifier within the range of the specific numeric type and format?
175		resMinimum.AddErrors(IsValueValidAgainstRange(*n.Minimum, n.Type, n.Format, "Minimum boundary", n.Path))
176		if resMinimum.IsValid() {
177			// Constraint validated with compatible types
178			if err := MinimumNativeType(n.Path, n.In, val, *n.Minimum, n.ExclusiveMinimum); err != nil {
179				resMinimum.Merge(errorHelp.sErr(err))
180			}
181		} else {
182			// Constraint nevertheless validated, converted as general number
183			if err := Minimum(n.Path, n.In, data, *n.Minimum, n.ExclusiveMinimum); err != nil {
184				resMinimum.Merge(errorHelp.sErr(err))
185			}
186		}
187	}
188	res.Merge(resMultiple, resMinimum, resMaximum)
189	res.Inc()
190	return res
191}
192
193type stringValidator struct {
194	MaxLength *int64
195	MinLength *int64
196	Pattern   string
197	Path      string
198	In        string
199}
200
201func (s *stringValidator) SetPath(path string) {
202	s.Path = path
203}
204
205func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool {
206	switch source.(type) {
207	case *spec.Schema:
208		r := kind == reflect.String
209		debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind)
210		return r
211	}
212	debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, false, source, kind)
213	return false
214}
215
216func (s *stringValidator) Validate(val interface{}) *Result {
217	data, ok := val.(string)
218	if !ok {
219		return errorHelp.sErr(errors.InvalidType(s.Path, s.In, stringType, val))
220	}
221
222	if s.MaxLength != nil {
223		if err := MaxLength(s.Path, s.In, data, *s.MaxLength); err != nil {
224			return errorHelp.sErr(err)
225		}
226	}
227
228	if s.MinLength != nil {
229		if err := MinLength(s.Path, s.In, data, *s.MinLength); err != nil {
230			return errorHelp.sErr(err)
231		}
232	}
233
234	if s.Pattern != "" {
235		if err := Pattern(s.Path, s.In, data, s.Pattern); err != nil {
236			return errorHelp.sErr(err)
237		}
238	}
239	return nil
240}
241