From e1f01305d8fcd93504db4fecbbbcbec0c8f7e970 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sun, 11 Jun 2017 02:59:49 -0400 Subject: [PATCH] vendor: update github.com/go-macaron/binding (#4428) --- .../github.com/go-macaron/binding/README.md | 2 +- .../github.com/go-macaron/binding/binding.go | 136 +++++++++++++++--- vendor/vendor.json | 6 +- 3 files changed, 118 insertions(+), 26 deletions(-) diff --git a/vendor/github.com/go-macaron/binding/README.md b/vendor/github.com/go-macaron/binding/README.md index a6748b57a..aeaa57193 100644 --- a/vendor/github.com/go-macaron/binding/README.md +++ b/vendor/github.com/go-macaron/binding/README.md @@ -1,4 +1,4 @@ -# binding [![Build Status](https://travis-ci.org/go-macaron/binding.svg?branch=master)](https://travis-ci.org/go-macaron/binding) [![](http://gocover.io/_badge/github.com/go-macaron/binding)](http://gocover.io/github.com/go-macaron/binding) +# binding [![Build Status](https://travis-ci.org/go-macaron/binding.svg?branch=master)](https://travis-ci.org/go-macaron/binding) [![Sourcegraph](https://sourcegraph.com/github.com/go-macaron/binding/-/badge.svg)](https://sourcegraph.com/github.com/go-macaron/binding?badge) Middleware binding provides request data binding and validation for [Macaron](https://github.com/go-macaron/macaron). diff --git a/vendor/github.com/go-macaron/binding/binding.go b/vendor/github.com/go-macaron/binding/binding.go index ace08d6cf..5d178b7e1 100644 --- a/vendor/github.com/go-macaron/binding/binding.go +++ b/vendor/github.com/go-macaron/binding/binding.go @@ -22,6 +22,7 @@ import ( "io" "mime/multipart" "net/http" + "net/url" "reflect" "regexp" "strconv" @@ -32,7 +33,7 @@ import ( "gopkg.in/macaron.v1" ) -const _VERSION = "0.3.2" +const _VERSION = "0.6.0" func Version() string { return _VERSION @@ -145,7 +146,7 @@ func Form(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler { if parseErr != nil { errors.Add([]string{}, ERR_DESERIALIZATION, parseErr.Error()) } - mapForm(formStruct, ctx.Req.Form, nil, errors) + errors = mapForm(formStruct, ctx.Req.Form, nil, errors) validateAndMap(formStruct, ctx, errors, ifacePtr...) } } @@ -185,7 +186,7 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) macaron.Hand ctx.Req.MultipartForm = form } } - mapForm(formStruct, ctx.Req.MultipartForm.Value, ctx.Req.MultipartForm.File, errors) + errors = mapForm(formStruct, ctx.Req.MultipartForm.Value, ctx.Req.MultipartForm.File, errors) validateAndMap(formStruct, ctx, errors, ifacePtr...) } } @@ -211,13 +212,35 @@ func Json(jsonStruct interface{}, ifacePtr ...interface{}) macaron.Handler { } } +// RawValidate is same as Validate but does not require a HTTP context, +// and can be used independently just for validation. +// This function does not support Validator interface. +func RawValidate(obj interface{}) Errors { + var errs Errors + v := reflect.ValueOf(obj) + k := v.Kind() + if k == reflect.Interface || k == reflect.Ptr { + v = v.Elem() + k = v.Kind() + } + if k == reflect.Slice || k == reflect.Array { + for i := 0; i < v.Len(); i++ { + e := v.Index(i).Interface() + errs = validateStruct(errs, e) + } + } else { + errs = validateStruct(errs, obj) + } + return errs +} + // Validate is middleware to enforce required fields. If the struct // passed in implements Validator, then the user-defined Validate method // is executed, and its errors are mapped to the context. This middleware // performs no error handling: it merely detects errors and maps them. func Validate(obj interface{}) macaron.Handler { return func(ctx *macaron.Context) { - var errors Errors + var errs Errors v := reflect.ValueOf(obj) k := v.Kind() if k == reflect.Interface || k == reflect.Ptr { @@ -227,18 +250,18 @@ func Validate(obj interface{}) macaron.Handler { if k == reflect.Slice || k == reflect.Array { for i := 0; i < v.Len(); i++ { e := v.Index(i).Interface() - errors = validateStruct(errors, e) + errs = validateStruct(errs, e) if validator, ok := e.(Validator); ok { - errors = validator.Validate(ctx, errors) + errs = validator.Validate(ctx, errs) } } } else { - errors = validateStruct(errors, obj) + errs = validateStruct(errs, obj) if validator, ok := obj.(Validator); ok { - errors = validator.Validate(ctx, errors) + errs = validator.Validate(ctx, errs) } } - ctx.Map(errors) + ctx.Map(errs) } } @@ -246,9 +269,42 @@ var ( AlphaDashPattern = regexp.MustCompile("[^\\d\\w-_]") AlphaDashDotPattern = regexp.MustCompile("[^\\d\\w-_\\.]") EmailPattern = regexp.MustCompile("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?") - URLPattern = regexp.MustCompile(`(http|https):\/\/(?:\\S+(?::\\S*)?@)?[\w\-_]+(\.[\w\-_]+)*([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?`) ) +// Copied from github.com/asaskevich/govalidator. +const _MAX_URL_RUNE_COUNT = 2083 +const _MIN_URL_RUNE_COUNT = 3 + +var ( + urlSchemaRx = `((ftp|tcp|udp|wss?|https?):\/\/)` + urlUsernameRx = `(\S+(:\S*)?@)` + urlIPRx = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))` + ipRx = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` + urlSubdomainRx = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))` + urlPortRx = `(:(\d{1,5}))` + urlPathRx = `((\/|\?|#)[^\s]*)` + URLPattern = regexp.MustCompile(`^` + urlSchemaRx + `?` + urlUsernameRx + `?` + `((` + urlIPRx + `|(\[` + ipRx + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + urlSubdomainRx + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + urlPortRx + `?` + urlPathRx + `?$`) +) + +// IsURL check if the string is an URL. +func isURL(str string) bool { + if str == "" || utf8.RuneCountInString(str) >= _MAX_URL_RUNE_COUNT || len(str) <= _MIN_URL_RUNE_COUNT || strings.HasPrefix(str, ".") { + return false + } + u, err := url.Parse(str) + if err != nil { + return false + } + if strings.HasPrefix(u.Host, ".") { + return false + } + if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { + return false + } + return URLPattern.MatchString(str) + +} + type ( // Rule represents a validation rule. Rule struct { @@ -257,18 +313,34 @@ type ( // IsValid applies validation rule to condition. IsValid func(Errors, string, interface{}) (bool, Errors) } - // RuleMapper represents a validation rule mapper, + + // ParamRule does same thing as Rule but passes rule itself to IsValid method. + ParamRule struct { + // IsMatch checks if rule matches. + IsMatch func(string) bool + // IsValid applies validation rule to condition. + IsValid func(Errors, string, string, interface{}) (bool, Errors) + } + + // RuleMapper and ParamRuleMapper represent validation rule mappers, // it allwos users to add custom validation rules. - RuleMapper []*Rule + RuleMapper []*Rule + ParamRuleMapper []*ParamRule ) var ruleMapper RuleMapper +var paramRuleMapper ParamRuleMapper // AddRule adds new validation rule. func AddRule(r *Rule) { ruleMapper = append(ruleMapper, r) } +// AddParamRule adds new validation rule. +func AddParamRule(r *ParamRule) { + paramRuleMapper = append(paramRuleMapper, r) +} + func in(fieldValue interface{}, arr string) bool { val := fmt.Sprintf("%v", fieldValue) vals := strings.Split(arr, ",") @@ -356,6 +428,16 @@ VALIDATE_RULES: break VALIDATE_RULES } case rule == "Required": + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice { + if v.Len() == 0 { + errors.Add([]string{field.Name}, ERR_REQUIRED, "Required") + break VALIDATE_RULES + } + + continue + } + if reflect.DeepEqual(zero, fieldValue) { errors.Add([]string{field.Name}, ERR_REQUIRED, "Required") break VALIDATE_RULES @@ -422,7 +504,7 @@ VALIDATE_RULES: str := fmt.Sprintf("%v", fieldValue) if len(str) == 0 { continue - } else if !URLPattern.MatchString(str) { + } else if !isURL(str) { errors.Add([]string{field.Name}, ERR_URL, "Url") break VALIDATE_RULES } @@ -449,14 +531,14 @@ VALIDATE_RULES: case strings.HasPrefix(rule, "Default("): if reflect.DeepEqual(zero, fieldValue) { if fieldVal.CanAddr() { - setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors) + errors = setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors) } else { errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default") break VALIDATE_RULES } } default: - // Apply custom validation rules. + // Apply custom validation rules var isValid bool for i := range ruleMapper { if ruleMapper[i].IsMatch(rule) { @@ -466,6 +548,14 @@ VALIDATE_RULES: } } } + for i := range paramRuleMapper { + if paramRuleMapper[i].IsMatch(rule) { + isValid, errors = paramRuleMapper[i].IsValid(errors, rule, field.Name, fieldValue) + if !isValid { + break VALIDATE_RULES + } + } + } } } return errors @@ -497,7 +587,7 @@ func SetNameMapper(nm NameMapper) { // Takes values from the form data and puts them into a struct func mapForm(formStruct reflect.Value, form map[string][]string, - formfile map[string][]*multipart.FileHeader, errors Errors) { + formfile map[string][]*multipart.FileHeader, errors Errors) Errors { if formStruct.Kind() == reflect.Ptr { formStruct = formStruct.Elem() @@ -510,12 +600,12 @@ func mapForm(formStruct reflect.Value, form map[string][]string, if typeField.Type.Kind() == reflect.Ptr && typeField.Anonymous { structField.Set(reflect.New(typeField.Type.Elem())) - mapForm(structField.Elem(), form, formfile, errors) + errors = mapForm(structField.Elem(), form, formfile, errors) if reflect.DeepEqual(structField.Elem().Interface(), reflect.Zero(structField.Elem().Type()).Interface()) { structField.Set(reflect.Zero(structField.Type())) } } else if typeField.Type.Kind() == reflect.Struct { - mapForm(structField, form, formfile, errors) + errors = mapForm(structField, form, formfile, errors) } inputFieldName := parseFormName(typeField.Name, typeField.Tag.Get("form")) @@ -530,11 +620,11 @@ func mapForm(formStruct reflect.Value, form map[string][]string, sliceOf := structField.Type().Elem().Kind() slice := reflect.MakeSlice(structField.Type(), numElems, numElems) for i := 0; i < numElems; i++ { - setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors) + errors = setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors) } formStruct.Field(i).Set(slice) } else { - setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors) + errors = setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors) } continue } @@ -555,13 +645,14 @@ func mapForm(formStruct reflect.Value, form map[string][]string, structField.Set(reflect.ValueOf(inputFile[0])) } } + return errors } // This sets the value in a struct of an indeterminate type to the // matching value from the request (via Form middleware) in the // same type, so that not all deserialized values have to be strings. // Supported types are string, int, float, and bool. -func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value, nameInTag string, errors Errors) { +func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value, nameInTag string, errors Errors) Errors { switch valueKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if val == "" { @@ -586,7 +677,7 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V case reflect.Bool: if val == "on" { structField.SetBool(true) - return + break } if val == "" { @@ -621,6 +712,7 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V case reflect.String: structField.SetString(val) } + return errors } // Don't pass in pointers to bind to. Can lead to bugs. diff --git a/vendor/vendor.json b/vendor/vendor.json index f10f8c9f3..4485a824a 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -75,10 +75,10 @@ "revisionTime": "2017-01-13T15:16:12Z" }, { - "checksumSHA1": "qM/kf31cT2cxjtHxdzbu8q8jPq0=", + "checksumSHA1": "J0tcl2i76AQvMio9MWNQaucepYA=", "path": "github.com/go-macaron/binding", - "revision": "9440f336b443056c90d7d448a0a55ad8c7599880", - "revisionTime": "2016-07-11T22:56:54Z" + "revision": "ac54ee249c27dca7e76fad851a4a04b73bd1b183", + "revisionTime": "2017-06-11T06:58:19Z" }, { "checksumSHA1": "6pSblXcT+pZJWhPCEyLZONiQUHg=",