mirror of
https://github.com/containers/podman.git
synced 2025-06-01 01:00:22 +08:00
update c/{buildah,common,image,storage} to latest main
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
480
vendor/github.com/go-openapi/validate/object_validator.go
generated
vendored
480
vendor/github.com/go-openapi/validate/object_validator.go
generated
vendored
@ -15,8 +15,8 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
@ -35,62 +35,116 @@ type objectValidator struct {
|
||||
PatternProperties map[string]spec.Schema
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
Options *SchemaValidatorOptions
|
||||
splitPath []string
|
||||
}
|
||||
|
||||
func newObjectValidator(path, in string,
|
||||
maxProperties, minProperties *int64, required []string, properties spec.SchemaProperties,
|
||||
additionalProperties *spec.SchemaOrBool, patternProperties spec.SchemaProperties,
|
||||
root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *objectValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
var v *objectValidator
|
||||
if opts.recycleValidators {
|
||||
v = pools.poolOfObjectValidators.BorrowValidator()
|
||||
} else {
|
||||
v = new(objectValidator)
|
||||
}
|
||||
|
||||
v.Path = path
|
||||
v.In = in
|
||||
v.MaxProperties = maxProperties
|
||||
v.MinProperties = minProperties
|
||||
v.Required = required
|
||||
v.Properties = properties
|
||||
v.AdditionalProperties = additionalProperties
|
||||
v.PatternProperties = patternProperties
|
||||
v.Root = root
|
||||
v.KnownFormats = formats
|
||||
v.Options = opts
|
||||
v.splitPath = strings.Split(v.Path, ".")
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (o *objectValidator) SetPath(path string) {
|
||||
o.Path = path
|
||||
o.splitPath = strings.Split(path, ".")
|
||||
}
|
||||
|
||||
func (o *objectValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
// TODO: this should also work for structs
|
||||
// there is a problem in the type validator where it will be unhappy about null values
|
||||
// so that requires more testing
|
||||
r := reflect.TypeOf(source) == specSchemaType && (kind == reflect.Map || kind == reflect.Struct)
|
||||
debugLog("object validator for %q applies %t for %T (kind: %v)\n", o.Path, r, source, kind)
|
||||
return r
|
||||
_, isSchema := source.(*spec.Schema)
|
||||
return isSchema && (kind == reflect.Map || kind == reflect.Struct)
|
||||
}
|
||||
|
||||
func (o *objectValidator) isProperties() bool {
|
||||
p := strings.Split(o.Path, ".")
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && p[len(p)-1] == jsonProperties && p[len(p)-2] != jsonProperties
|
||||
}
|
||||
|
||||
func (o *objectValidator) isDefault() bool {
|
||||
p := strings.Split(o.Path, ".")
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && p[len(p)-1] == jsonDefault && p[len(p)-2] != jsonDefault
|
||||
}
|
||||
|
||||
func (o *objectValidator) isExample() bool {
|
||||
p := strings.Split(o.Path, ".")
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && (p[len(p)-1] == swaggerExample || p[len(p)-1] == swaggerExamples) && p[len(p)-2] != swaggerExample
|
||||
}
|
||||
|
||||
func (o *objectValidator) checkArrayMustHaveItems(res *Result, val map[string]interface{}) {
|
||||
// for swagger 2.0 schemas, there is an additional constraint to have array items defined explicitly.
|
||||
// with pure jsonschema draft 4, one may have arrays with undefined items (i.e. any type).
|
||||
if t, typeFound := val[jsonType]; typeFound {
|
||||
if tpe, ok := t.(string); ok && tpe == arrayType {
|
||||
if item, itemsKeyFound := val[jsonItems]; !itemsKeyFound {
|
||||
res.AddErrors(errors.Required(jsonItems, o.Path, item))
|
||||
}
|
||||
}
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
|
||||
t, typeFound := val[jsonType]
|
||||
if !typeFound {
|
||||
return
|
||||
}
|
||||
|
||||
tpe, isString := t.(string)
|
||||
if !isString || tpe != arrayType {
|
||||
return
|
||||
}
|
||||
|
||||
item, itemsKeyFound := val[jsonItems]
|
||||
if itemsKeyFound {
|
||||
return
|
||||
}
|
||||
|
||||
res.AddErrors(errors.Required(jsonItems, o.Path, item))
|
||||
}
|
||||
|
||||
func (o *objectValidator) checkItemsMustBeTypeArray(res *Result, val map[string]interface{}) {
|
||||
if !o.isProperties() && !o.isDefault() && !o.isExample() {
|
||||
if _, itemsKeyFound := val[jsonItems]; itemsKeyFound {
|
||||
t, typeFound := val[jsonType]
|
||||
if typeFound {
|
||||
if tpe, ok := t.(string); !ok || tpe != arrayType {
|
||||
res.AddErrors(errors.InvalidType(o.Path, o.In, arrayType, nil))
|
||||
}
|
||||
} else {
|
||||
// there is no type
|
||||
res.AddErrors(errors.Required(jsonType, o.Path, t))
|
||||
}
|
||||
}
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if o.isProperties() || o.isDefault() || o.isExample() {
|
||||
return
|
||||
}
|
||||
|
||||
_, itemsKeyFound := val[jsonItems]
|
||||
if !itemsKeyFound {
|
||||
return
|
||||
}
|
||||
|
||||
t, typeFound := val[jsonType]
|
||||
if !typeFound {
|
||||
// there is no type
|
||||
res.AddErrors(errors.Required(jsonType, o.Path, t))
|
||||
}
|
||||
|
||||
if tpe, isString := t.(string); !isString || tpe != arrayType {
|
||||
res.AddErrors(errors.InvalidType(o.Path, o.In, arrayType, nil))
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,176 +158,274 @@ func (o *objectValidator) precheck(res *Result, val map[string]interface{}) {
|
||||
}
|
||||
|
||||
func (o *objectValidator) Validate(data interface{}) *Result {
|
||||
val := data.(map[string]interface{})
|
||||
// TODO: guard against nil data
|
||||
if o.Options.recycleValidators {
|
||||
defer func() {
|
||||
o.redeem()
|
||||
}()
|
||||
}
|
||||
|
||||
var val map[string]interface{}
|
||||
if data != nil {
|
||||
var ok bool
|
||||
val, ok = data.(map[string]interface{})
|
||||
if !ok {
|
||||
return errorHelp.sErr(invalidObjectMsg(o.Path, o.In), o.Options.recycleResult)
|
||||
}
|
||||
}
|
||||
numKeys := int64(len(val))
|
||||
|
||||
if o.MinProperties != nil && numKeys < *o.MinProperties {
|
||||
return errorHelp.sErr(errors.TooFewProperties(o.Path, o.In, *o.MinProperties))
|
||||
return errorHelp.sErr(errors.TooFewProperties(o.Path, o.In, *o.MinProperties), o.Options.recycleResult)
|
||||
}
|
||||
if o.MaxProperties != nil && numKeys > *o.MaxProperties {
|
||||
return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties))
|
||||
return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties), o.Options.recycleResult)
|
||||
}
|
||||
|
||||
res := new(Result)
|
||||
var res *Result
|
||||
if o.Options.recycleResult {
|
||||
res = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
res = new(Result)
|
||||
}
|
||||
|
||||
o.precheck(res, val)
|
||||
|
||||
// check validity of field names
|
||||
if o.AdditionalProperties != nil && !o.AdditionalProperties.Allows {
|
||||
// Case: additionalProperties: false
|
||||
for k := range val {
|
||||
_, regularProperty := o.Properties[k]
|
||||
matched := false
|
||||
|
||||
for pk := range o.PatternProperties {
|
||||
if matches, _ := regexp.MatchString(pk, k); matches {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !regularProperty && k != "$schema" && k != "id" && !matched {
|
||||
// Special properties "$schema" and "id" are ignored
|
||||
res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
|
||||
|
||||
// BUG(fredbi): This section should move to a part dedicated to spec validation as
|
||||
// it will conflict with regular schemas where a property "headers" is defined.
|
||||
|
||||
//
|
||||
// Croaks a more explicit message on top of the standard one
|
||||
// on some recognized cases.
|
||||
//
|
||||
// NOTE: edge cases with invalid type assertion are simply ignored here.
|
||||
// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
|
||||
// by higher level callers (the IMPORTANT! tag will be eventually
|
||||
// removed).
|
||||
if k == "headers" && val[k] != nil {
|
||||
// $ref is forbidden in header
|
||||
if headers, mapOk := val[k].(map[string]interface{}); mapOk {
|
||||
for headerKey, headerBody := range headers {
|
||||
if headerBody != nil {
|
||||
if headerSchema, mapOfMapOk := headerBody.(map[string]interface{}); mapOfMapOk {
|
||||
if _, found := headerSchema["$ref"]; found {
|
||||
var msg string
|
||||
if refString, stringOk := headerSchema["$ref"].(string); stringOk {
|
||||
msg = strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "")
|
||||
}
|
||||
res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
case "$ref":
|
||||
if val[k] != nil {
|
||||
// TODO: check context of that ref: warn about siblings, check against invalid context
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
o.validateNoAdditionalProperties(val, res)
|
||||
} else {
|
||||
// Cases: no additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
|
||||
// Validates property against "patternProperties" if applicable
|
||||
// BUG(fredbi): succeededOnce is always false
|
||||
|
||||
// NOTE: how about regular properties which do not match patternProperties?
|
||||
matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
|
||||
|
||||
if !(regularProperty || matched || succeededOnce) {
|
||||
|
||||
// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
|
||||
if o.AdditionalProperties != nil && o.AdditionalProperties.Schema != nil {
|
||||
// AdditionalProperties as Schema
|
||||
r := NewSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
|
||||
res.mergeForField(data.(map[string]interface{}), key, r)
|
||||
} else if regularProperty && !(matched || succeededOnce) {
|
||||
// TODO: this is dead code since regularProperty=false here
|
||||
res.AddErrors(errors.FailedAllPatternProperties(o.Path, o.In, key))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Valid cases: additionalProperties: true or undefined
|
||||
// Cases: empty additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
|
||||
o.validateAdditionalProperties(val, res)
|
||||
}
|
||||
|
||||
createdFromDefaults := map[string]bool{}
|
||||
|
||||
// Property types:
|
||||
// - regular Property
|
||||
for pName := range o.Properties {
|
||||
pSchema := o.Properties[pName] // one instance per iteration
|
||||
rName := pName
|
||||
if o.Path != "" {
|
||||
rName = o.Path + "." + pName
|
||||
}
|
||||
|
||||
// Recursively validates each property against its schema
|
||||
if v, ok := val[pName]; ok {
|
||||
r := NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats, o.Options.Options()...).Validate(v)
|
||||
res.mergeForField(data.(map[string]interface{}), pName, r)
|
||||
} else if pSchema.Default != nil {
|
||||
// If a default value is defined, creates the property from defaults
|
||||
// NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does.
|
||||
createdFromDefaults[pName] = true
|
||||
res.addPropertySchemata(data.(map[string]interface{}), pName, &pSchema)
|
||||
}
|
||||
}
|
||||
|
||||
// Check required properties
|
||||
if len(o.Required) > 0 {
|
||||
for _, k := range o.Required {
|
||||
if v, ok := val[k]; !ok && !createdFromDefaults[k] {
|
||||
res.AddErrors(errors.Required(o.Path+"."+k, o.In, v))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
o.validatePropertiesSchema(val, res)
|
||||
|
||||
// Check patternProperties
|
||||
// TODO: it looks like we have done that twice in many cases
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
matched, _ /*succeededOnce*/, patterns := o.validatePatternProperty(key, value, res)
|
||||
if !regularProperty && (matched /*|| succeededOnce*/) {
|
||||
for _, pName := range patterns {
|
||||
if v, ok := o.PatternProperties[pName]; ok {
|
||||
r := NewSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
|
||||
res.mergeForField(data.(map[string]interface{}), key, r)
|
||||
}
|
||||
matched, _, patterns := o.validatePatternProperty(key, value, res) // applies to regular properties as well
|
||||
if regularProperty || !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, pName := range patterns {
|
||||
if v, ok := o.PatternProperties[pName]; ok {
|
||||
r := newSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options).Validate(value)
|
||||
res.mergeForField(data.(map[string]interface{}), key, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (o *objectValidator) validateNoAdditionalProperties(val map[string]interface{}, res *Result) {
|
||||
for k := range val {
|
||||
if k == "$schema" || k == "id" {
|
||||
// special properties "$schema" and "id" are ignored
|
||||
continue
|
||||
}
|
||||
|
||||
_, regularProperty := o.Properties[k]
|
||||
if regularProperty {
|
||||
continue
|
||||
}
|
||||
|
||||
matched := false
|
||||
for pk := range o.PatternProperties {
|
||||
re, err := compileRegexp(pk)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if matches := re.MatchString(k); matches {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if matched {
|
||||
continue
|
||||
}
|
||||
|
||||
res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
|
||||
|
||||
// BUG(fredbi): This section should move to a part dedicated to spec validation as
|
||||
// it will conflict with regular schemas where a property "headers" is defined.
|
||||
|
||||
//
|
||||
// Croaks a more explicit message on top of the standard one
|
||||
// on some recognized cases.
|
||||
//
|
||||
// NOTE: edge cases with invalid type assertion are simply ignored here.
|
||||
// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
|
||||
// by higher level callers (the IMPORTANT! tag will be eventually
|
||||
// removed).
|
||||
if k != "headers" || val[k] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// $ref is forbidden in header
|
||||
headers, mapOk := val[k].(map[string]interface{})
|
||||
if !mapOk {
|
||||
continue
|
||||
}
|
||||
|
||||
for headerKey, headerBody := range headers {
|
||||
if headerBody == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
headerSchema, mapOfMapOk := headerBody.(map[string]interface{})
|
||||
if !mapOfMapOk {
|
||||
continue
|
||||
}
|
||||
|
||||
_, found := headerSchema["$ref"]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
refString, stringOk := headerSchema["$ref"].(string)
|
||||
if !stringOk {
|
||||
continue
|
||||
}
|
||||
|
||||
msg := strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "")
|
||||
res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg))
|
||||
/*
|
||||
case "$ref":
|
||||
if val[k] != nil {
|
||||
// TODO: check context of that ref: warn about siblings, check against invalid context
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *objectValidator) validateAdditionalProperties(val map[string]interface{}, res *Result) {
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
if regularProperty {
|
||||
continue
|
||||
}
|
||||
|
||||
// Validates property against "patternProperties" if applicable
|
||||
// BUG(fredbi): succeededOnce is always false
|
||||
|
||||
// NOTE: how about regular properties which do not match patternProperties?
|
||||
matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
|
||||
if matched || succeededOnce {
|
||||
continue
|
||||
}
|
||||
|
||||
if o.AdditionalProperties == nil || o.AdditionalProperties.Schema == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
|
||||
// AdditionalProperties as Schema
|
||||
r := newSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options).Validate(value)
|
||||
res.mergeForField(val, key, r)
|
||||
}
|
||||
// Valid cases: additionalProperties: true or undefined
|
||||
}
|
||||
|
||||
func (o *objectValidator) validatePropertiesSchema(val map[string]interface{}, res *Result) {
|
||||
createdFromDefaults := map[string]struct{}{}
|
||||
|
||||
// Property types:
|
||||
// - regular Property
|
||||
pSchema := pools.poolOfSchemas.BorrowSchema() // recycle a spec.Schema object which lifespan extends only to the validation of properties
|
||||
defer func() {
|
||||
pools.poolOfSchemas.RedeemSchema(pSchema)
|
||||
}()
|
||||
|
||||
for pName := range o.Properties {
|
||||
*pSchema = o.Properties[pName]
|
||||
var rName string
|
||||
if o.Path == "" {
|
||||
rName = pName
|
||||
} else {
|
||||
rName = o.Path + "." + pName
|
||||
}
|
||||
|
||||
// Recursively validates each property against its schema
|
||||
v, ok := val[pName]
|
||||
if ok {
|
||||
r := newSchemaValidator(pSchema, o.Root, rName, o.KnownFormats, o.Options).Validate(v)
|
||||
res.mergeForField(val, pName, r)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if pSchema.Default != nil {
|
||||
// if a default value is defined, creates the property from defaults
|
||||
// NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does.
|
||||
createdFromDefaults[pName] = struct{}{}
|
||||
if !o.Options.skipSchemataResult {
|
||||
res.addPropertySchemata(val, pName, pSchema) // this shallow-clones the content of the pSchema pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(o.Required) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Check required properties
|
||||
for _, k := range o.Required {
|
||||
v, ok := val[k]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
_, isCreatedFromDefaults := createdFromDefaults[k]
|
||||
if isCreatedFromDefaults {
|
||||
continue
|
||||
}
|
||||
|
||||
res.AddErrors(errors.Required(fmt.Sprintf("%s.%s", o.Path, k), o.In, v))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: succeededOnce is not used anywhere
|
||||
func (o *objectValidator) validatePatternProperty(key string, value interface{}, result *Result) (bool, bool, []string) {
|
||||
matched := false
|
||||
succeededOnce := false
|
||||
var patterns []string
|
||||
|
||||
for k, schema := range o.PatternProperties {
|
||||
sch := schema
|
||||
if match, _ := regexp.MatchString(k, key); match {
|
||||
patterns = append(patterns, k)
|
||||
matched = true
|
||||
validator := NewSchemaValidator(&sch, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...)
|
||||
|
||||
res := validator.Validate(value)
|
||||
result.Merge(res)
|
||||
}
|
||||
if len(o.PatternProperties) == 0 {
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
// BUG(fredbi): can't get to here. Should remove dead code (commented out).
|
||||
matched := false
|
||||
succeededOnce := false
|
||||
patterns := make([]string, 0, len(o.PatternProperties))
|
||||
|
||||
// if succeededOnce {
|
||||
// result.Inc()
|
||||
// }
|
||||
schema := pools.poolOfSchemas.BorrowSchema()
|
||||
defer func() {
|
||||
pools.poolOfSchemas.RedeemSchema(schema)
|
||||
}()
|
||||
|
||||
for k := range o.PatternProperties {
|
||||
re, err := compileRegexp(k)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
match := re.MatchString(key)
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
|
||||
*schema = o.PatternProperties[k]
|
||||
patterns = append(patterns, k)
|
||||
matched = true
|
||||
validator := newSchemaValidator(schema, o.Root, fmt.Sprintf("%s.%s", o.Path, key), o.KnownFormats, o.Options)
|
||||
|
||||
res := validator.Validate(value)
|
||||
result.Merge(res)
|
||||
}
|
||||
|
||||
return matched, succeededOnce, patterns
|
||||
}
|
||||
|
||||
func (o *objectValidator) redeem() {
|
||||
pools.poolOfObjectValidators.RedeemValidator(o)
|
||||
}
|
||||
|
Reference in New Issue
Block a user