podman

Форк
0
279 строк · 9.4 Кб
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

15
package validate
16

17
import (
18
	"reflect"
19
	"regexp"
20
	"strings"
21

22
	"github.com/go-openapi/errors"
23
	"github.com/go-openapi/spec"
24
	"github.com/go-openapi/strfmt"
25
)
26

27
type objectValidator struct {
28
	Path                 string
29
	In                   string
30
	MaxProperties        *int64
31
	MinProperties        *int64
32
	Required             []string
33
	Properties           map[string]spec.Schema
34
	AdditionalProperties *spec.SchemaOrBool
35
	PatternProperties    map[string]spec.Schema
36
	Root                 interface{}
37
	KnownFormats         strfmt.Registry
38
	Options              SchemaValidatorOptions
39
}
40

41
func (o *objectValidator) SetPath(path string) {
42
	o.Path = path
43
}
44

45
func (o *objectValidator) Applies(source interface{}, kind reflect.Kind) bool {
46
	// TODO: this should also work for structs
47
	// there is a problem in the type validator where it will be unhappy about null values
48
	// so that requires more testing
49
	r := reflect.TypeOf(source) == specSchemaType && (kind == reflect.Map || kind == reflect.Struct)
50
	debugLog("object validator for %q applies %t for %T (kind: %v)\n", o.Path, r, source, kind)
51
	return r
52
}
53

54
func (o *objectValidator) isProperties() bool {
55
	p := strings.Split(o.Path, ".")
56
	return len(p) > 1 && p[len(p)-1] == jsonProperties && p[len(p)-2] != jsonProperties
57
}
58

59
func (o *objectValidator) isDefault() bool {
60
	p := strings.Split(o.Path, ".")
61
	return len(p) > 1 && p[len(p)-1] == jsonDefault && p[len(p)-2] != jsonDefault
62
}
63

64
func (o *objectValidator) isExample() bool {
65
	p := strings.Split(o.Path, ".")
66
	return len(p) > 1 && (p[len(p)-1] == swaggerExample || p[len(p)-1] == swaggerExamples) && p[len(p)-2] != swaggerExample
67
}
68

69
func (o *objectValidator) checkArrayMustHaveItems(res *Result, val map[string]interface{}) {
70
	// for swagger 2.0 schemas, there is an additional constraint to have array items defined explicitly.
71
	// with pure jsonschema draft 4, one may have arrays with undefined items (i.e. any type).
72
	if t, typeFound := val[jsonType]; typeFound {
73
		if tpe, ok := t.(string); ok && tpe == arrayType {
74
			if item, itemsKeyFound := val[jsonItems]; !itemsKeyFound {
75
				res.AddErrors(errors.Required(jsonItems, o.Path, item))
76
			}
77
		}
78
	}
79
}
80

81
func (o *objectValidator) checkItemsMustBeTypeArray(res *Result, val map[string]interface{}) {
82
	if !o.isProperties() && !o.isDefault() && !o.isExample() {
83
		if _, itemsKeyFound := val[jsonItems]; itemsKeyFound {
84
			t, typeFound := val[jsonType]
85
			if typeFound {
86
				if tpe, ok := t.(string); !ok || tpe != arrayType {
87
					res.AddErrors(errors.InvalidType(o.Path, o.In, arrayType, nil))
88
				}
89
			} else {
90
				// there is no type
91
				res.AddErrors(errors.Required(jsonType, o.Path, t))
92
			}
93
		}
94
	}
95
}
96

97
func (o *objectValidator) precheck(res *Result, val map[string]interface{}) {
98
	if o.Options.EnableArrayMustHaveItemsCheck {
99
		o.checkArrayMustHaveItems(res, val)
100
	}
101
	if o.Options.EnableObjectArrayTypeCheck {
102
		o.checkItemsMustBeTypeArray(res, val)
103
	}
104
}
105

106
func (o *objectValidator) Validate(data interface{}) *Result {
107
	val := data.(map[string]interface{})
108
	// TODO: guard against nil data
109
	numKeys := int64(len(val))
110

111
	if o.MinProperties != nil && numKeys < *o.MinProperties {
112
		return errorHelp.sErr(errors.TooFewProperties(o.Path, o.In, *o.MinProperties))
113
	}
114
	if o.MaxProperties != nil && numKeys > *o.MaxProperties {
115
		return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties))
116
	}
117

118
	res := new(Result)
119

120
	o.precheck(res, val)
121

122
	// check validity of field names
123
	if o.AdditionalProperties != nil && !o.AdditionalProperties.Allows {
124
		// Case: additionalProperties: false
125
		for k := range val {
126
			_, regularProperty := o.Properties[k]
127
			matched := false
128

129
			for pk := range o.PatternProperties {
130
				if matches, _ := regexp.MatchString(pk, k); matches {
131
					matched = true
132
					break
133
				}
134
			}
135

136
			if !regularProperty && k != "$schema" && k != "id" && !matched {
137
				// Special properties "$schema" and "id" are ignored
138
				res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
139

140
				// BUG(fredbi): This section should move to a part dedicated to spec validation as
141
				// it will conflict with regular schemas where a property "headers" is defined.
142

143
				//
144
				// Croaks a more explicit message on top of the standard one
145
				// on some recognized cases.
146
				//
147
				// NOTE: edge cases with invalid type assertion are simply ignored here.
148
				// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
149
				// by higher level callers (the IMPORTANT! tag will be eventually
150
				// removed).
151
				if k == "headers" && val[k] != nil {
152
					// $ref is forbidden in header
153
					if headers, mapOk := val[k].(map[string]interface{}); mapOk {
154
						for headerKey, headerBody := range headers {
155
							if headerBody != nil {
156
								if headerSchema, mapOfMapOk := headerBody.(map[string]interface{}); mapOfMapOk {
157
									if _, found := headerSchema["$ref"]; found {
158
										var msg string
159
										if refString, stringOk := headerSchema["$ref"].(string); stringOk {
160
											msg = strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "")
161
										}
162
										res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg))
163
									}
164
								}
165
							}
166
						}
167
					}
168
					/*
169
						case "$ref":
170
							if val[k] != nil {
171
								// TODO: check context of that ref: warn about siblings, check against invalid context
172
							}
173
					*/
174
				}
175
			}
176
		}
177
	} else {
178
		// Cases: no additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
179
		for key, value := range val {
180
			_, regularProperty := o.Properties[key]
181

182
			// Validates property against "patternProperties" if applicable
183
			// BUG(fredbi): succeededOnce is always false
184

185
			// NOTE: how about regular properties which do not match patternProperties?
186
			matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
187

188
			if !(regularProperty || matched || succeededOnce) {
189

190
				// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
191
				if o.AdditionalProperties != nil && o.AdditionalProperties.Schema != nil {
192
					// AdditionalProperties as Schema
193
					r := NewSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
194
					res.mergeForField(data.(map[string]interface{}), key, r)
195
				} else if regularProperty && !(matched || succeededOnce) {
196
					// TODO: this is dead code since regularProperty=false here
197
					res.AddErrors(errors.FailedAllPatternProperties(o.Path, o.In, key))
198
				}
199
			}
200
		}
201
		// Valid cases: additionalProperties: true or undefined
202
	}
203

204
	createdFromDefaults := map[string]bool{}
205

206
	// Property types:
207
	// - regular Property
208
	for pName := range o.Properties {
209
		pSchema := o.Properties[pName] // one instance per iteration
210
		rName := pName
211
		if o.Path != "" {
212
			rName = o.Path + "." + pName
213
		}
214

215
		// Recursively validates each property against its schema
216
		if v, ok := val[pName]; ok {
217
			r := NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats, o.Options.Options()...).Validate(v)
218
			res.mergeForField(data.(map[string]interface{}), pName, r)
219
		} else if pSchema.Default != nil {
220
			// If a default value is defined, creates the property from defaults
221
			// NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does.
222
			createdFromDefaults[pName] = true
223
			res.addPropertySchemata(data.(map[string]interface{}), pName, &pSchema)
224
		}
225
	}
226

227
	// Check required properties
228
	if len(o.Required) > 0 {
229
		for _, k := range o.Required {
230
			if v, ok := val[k]; !ok && !createdFromDefaults[k] {
231
				res.AddErrors(errors.Required(o.Path+"."+k, o.In, v))
232
				continue
233
			}
234
		}
235
	}
236

237
	// Check patternProperties
238
	// TODO: it looks like we have done that twice in many cases
239
	for key, value := range val {
240
		_, regularProperty := o.Properties[key]
241
		matched, _ /*succeededOnce*/, patterns := o.validatePatternProperty(key, value, res)
242
		if !regularProperty && (matched /*|| succeededOnce*/) {
243
			for _, pName := range patterns {
244
				if v, ok := o.PatternProperties[pName]; ok {
245
					r := NewSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
246
					res.mergeForField(data.(map[string]interface{}), key, r)
247
				}
248
			}
249
		}
250
	}
251
	return res
252
}
253

254
// TODO: succeededOnce is not used anywhere
255
func (o *objectValidator) validatePatternProperty(key string, value interface{}, result *Result) (bool, bool, []string) {
256
	matched := false
257
	succeededOnce := false
258
	var patterns []string
259

260
	for k, schema := range o.PatternProperties {
261
		sch := schema
262
		if match, _ := regexp.MatchString(k, key); match {
263
			patterns = append(patterns, k)
264
			matched = true
265
			validator := NewSchemaValidator(&sch, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...)
266

267
			res := validator.Validate(value)
268
			result.Merge(res)
269
		}
270
	}
271

272
	// BUG(fredbi): can't get to here. Should remove dead code (commented out).
273

274
	// if succeededOnce {
275
	//	result.Inc()
276
	// }
277

278
	return matched, succeededOnce, patterns
279
}
280

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.