diff options
Diffstat (limited to 'vendor/github.com/labstack/echo/v4')
17 files changed, 339 insertions, 199 deletions
diff --git a/vendor/github.com/labstack/echo/v4/CHANGELOG.md b/vendor/github.com/labstack/echo/v4/CHANGELOG.md index c1be77a9..f4a74760 100644 --- a/vendor/github.com/labstack/echo/v4/CHANGELOG.md +++ b/vendor/github.com/labstack/echo/v4/CHANGELOG.md @@ -1,6 +1,30 @@ # Changelog -## v4.2.2 - 2020-04-07 +## v4.3.0 - 2021-05-08 + +**Important notes** + +* Route matching has improvements for following cases: + 1. Correctly match routes with parameter part as last part of route (with trailing backslash) + 2. Considering handlers when resolving routes and search for matching http method handler +* Echo minimal Go version is now 1.13. + +**Fixes** + +* When url ends with slash first param route is the match [#1804](https://github.com/labstack/echo/pull/1812) +* Router should check if node is suitable as matching route by path+method and if not then continue search in tree [#1808](https://github.com/labstack/echo/issues/1808) +* Fix timeout middleware not writing response correctly when handler panics [#1864](https://github.com/labstack/echo/pull/1864) +* Fix binder not working with embedded pointer structs [#1861](https://github.com/labstack/echo/pull/1861) +* Add Go 1.16 to CI and drop 1.12 specific code [#1850](https://github.com/labstack/echo/pull/1850) + +**Enhancements** + +* Make KeyFunc public in JWT middleware [#1756](https://github.com/labstack/echo/pull/1756) +* Add support for optional filesystem to the static middleware [#1797](https://github.com/labstack/echo/pull/1797) +* Add a custom error handler to key-auth middleware [#1847](https://github.com/labstack/echo/pull/1847) +* Allow JWT token to be looked up from multiple sources [#1845](https://github.com/labstack/echo/pull/1845) + +## v4.2.2 - 2021-04-07 **Fixes** @@ -10,7 +34,7 @@ * Fix panic in redirect middleware on short host name (#1813) * Fix timeout middleware docs (#1836) -## v4.2.1 - 2020-03-08 +## v4.2.1 - 2021-03-08 **Important notes** @@ -32,7 +56,7 @@ A performance regression has been fixed, even bringing better performance than b This release was made possible by our **contributors**: aldas, clwluvw, lammel, Le0tk0k, maciej-jezierski, rkilingr, stffabi, withshubh -## v4.2.0 - 2020-02-11 +## v4.2.0 - 2021-02-11 **Important notes** diff --git a/vendor/github.com/labstack/echo/v4/LICENSE b/vendor/github.com/labstack/echo/v4/LICENSE index b5b006b4..c46d0105 100644 --- a/vendor/github.com/labstack/echo/v4/LICENSE +++ b/vendor/github.com/labstack/echo/v4/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2017 LabStack +Copyright (c) 2021 LabStack Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/labstack/echo/v4/bind.go b/vendor/github.com/labstack/echo/v4/bind.go index 08d39891..dfdf82d0 100644 --- a/vendor/github.com/labstack/echo/v4/bind.go +++ b/vendor/github.com/labstack/echo/v4/bind.go @@ -144,11 +144,20 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri for i := 0; i < typ.NumField(); i++ { typeField := typ.Field(i) structField := val.Field(i) + if typeField.Anonymous { + if structField.Kind() == reflect.Ptr { + structField = structField.Elem() + } + } if !structField.CanSet() { continue } structFieldKind := structField.Kind() inputFieldName := typeField.Tag.Get(tag) + if typeField.Anonymous && structField.Kind() == reflect.Struct && inputFieldName != "" { + // if anonymous struct with query/param/form tags, report an error + return errors.New("query/param/form tags are not allowed with anonymous struct field") + } if inputFieldName == "" { // If tag is nil, we inspect if the field is a not BindUnmarshaler struct and try to bind data into it (might contains fields with tags). diff --git a/vendor/github.com/labstack/echo/v4/echo.go b/vendor/github.com/labstack/echo/v4/echo.go index a24e3977..dd0cbf35 100644 --- a/vendor/github.com/labstack/echo/v4/echo.go +++ b/vendor/github.com/labstack/echo/v4/echo.go @@ -234,7 +234,7 @@ const ( const ( // Version of Echo - Version = "4.2.2" + Version = "4.3.0" website = "https://echo.labstack.com" // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo banner = ` diff --git a/vendor/github.com/labstack/echo/v4/go.mod b/vendor/github.com/labstack/echo/v4/go.mod index 87711707..2510d10c 100644 --- a/vendor/github.com/labstack/echo/v4/go.mod +++ b/vendor/github.com/labstack/echo/v4/go.mod @@ -5,12 +5,12 @@ go 1.15 require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/labstack/gommon v0.3.0 - github.com/mattn/go-colorable v0.1.7 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect github.com/stretchr/testify v1.4.0 github.com/valyala/fasttemplate v1.2.1 - golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a - golang.org/x/net v0.0.0-20200822124328-c89045814202 - golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 // indirect - golang.org/x/text v0.3.3 // indirect + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 + golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 + golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect + golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 ) diff --git a/vendor/github.com/labstack/echo/v4/go.sum b/vendor/github.com/labstack/echo/v4/go.sum index 54ba24e6..d18f10fb 100644 --- a/vendor/github.com/labstack/echo/v4/go.sum +++ b/vendor/github.com/labstack/echo/v4/go.sum @@ -4,12 +4,10 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -20,32 +18,26 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/labstack/echo/v4/middleware/csrf.go b/vendor/github.com/labstack/echo/v4/middleware/csrf.go index 60f809a0..7804997d 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/csrf.go +++ b/vendor/github.com/labstack/echo/v4/middleware/csrf.go @@ -110,7 +110,7 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc { if config.CookieMaxAge == 0 { config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge } - if config.CookieSameSite == SameSiteNoneMode { + if config.CookieSameSite == http.SameSiteNoneMode { config.CookieSecure = true } diff --git a/vendor/github.com/labstack/echo/v4/middleware/csrf_samesite.go b/vendor/github.com/labstack/echo/v4/middleware/csrf_samesite.go deleted file mode 100644 index 9a27dc43..00000000 --- a/vendor/github.com/labstack/echo/v4/middleware/csrf_samesite.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build go1.13 - -package middleware - -import ( - "net/http" -) - -const ( - // SameSiteNoneMode required to be redefined for Go 1.12 support (see #1524) - SameSiteNoneMode http.SameSite = http.SameSiteNoneMode -) diff --git a/vendor/github.com/labstack/echo/v4/middleware/csrf_samesite_1.12.go b/vendor/github.com/labstack/echo/v4/middleware/csrf_samesite_1.12.go deleted file mode 100644 index 22076dd6..00000000 --- a/vendor/github.com/labstack/echo/v4/middleware/csrf_samesite_1.12.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build !go1.13 - -package middleware - -import ( - "net/http" -) - -const ( - // SameSiteNoneMode required to be redefined for Go 1.12 support (see #1524) - SameSiteNoneMode http.SameSite = 4 -) diff --git a/vendor/github.com/labstack/echo/v4/middleware/jwt.go b/vendor/github.com/labstack/echo/v4/middleware/jwt.go index da00ea56..cd35b621 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/jwt.go +++ b/vendor/github.com/labstack/echo/v4/middleware/jwt.go @@ -29,15 +29,19 @@ type ( // ErrorHandlerWithContext is almost identical to ErrorHandler, but it's passed the current context. ErrorHandlerWithContext JWTErrorHandlerWithContext - // Signing key to validate token. Used as fallback if SigningKeys has length 0. - // Required. This or SigningKeys. + // Signing key to validate token. + // This is one of the three options to provide a token validation key. + // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey. + // Required if neither user-defined KeyFunc nor SigningKeys is provided. SigningKey interface{} // Map of signing keys to validate token with kid field usage. - // Required. This or SigningKey. + // This is one of the three options to provide a token validation key. + // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey. + // Required if neither user-defined KeyFunc nor SigningKey is provided. SigningKeys map[string]interface{} - // Signing method, used to check token signing method. + // Signing method used to check the token's signing algorithm. // Optional. Default value HS256. SigningMethod string @@ -64,7 +68,16 @@ type ( // Optional. Default value "Bearer". AuthScheme string - keyFunc jwt.Keyfunc + // KeyFunc defines a user-defined function that supplies the public key for a token validation. + // The function shall take care of verifying the signing algorithm and selecting the proper key. + // A user-defined KeyFunc can be useful if tokens are issued by an external party. + // + // When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored. + // This is one of the three options to provide a token validation key. + // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey. + // Required if neither SigningKeys nor SigningKey is provided. + // Default to an internal implementation verifying the signing algorithm and selecting the proper key. + KeyFunc jwt.Keyfunc } // JWTSuccessHandler defines a function which is executed for a valid token. @@ -99,6 +112,7 @@ var ( TokenLookup: "header:" + echo.HeaderAuthorization, AuthScheme: "Bearer", Claims: jwt.MapClaims{}, + KeyFunc: nil, } ) @@ -123,7 +137,7 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { if config.Skipper == nil { config.Skipper = DefaultJWTConfig.Skipper } - if config.SigningKey == nil && len(config.SigningKeys) == 0 { + if config.SigningKey == nil && len(config.SigningKeys) == 0 && config.KeyFunc == nil { panic("echo: jwt middleware requires signing key") } if config.SigningMethod == "" { @@ -141,35 +155,29 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { if config.AuthScheme == "" { config.AuthScheme = DefaultJWTConfig.AuthScheme } - config.keyFunc = func(t *jwt.Token) (interface{}, error) { - // Check the signing method - if t.Method.Alg() != config.SigningMethod { - return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"]) - } - if len(config.SigningKeys) > 0 { - if kid, ok := t.Header["kid"].(string); ok { - if key, ok := config.SigningKeys[kid]; ok { - return key, nil - } - } - return nil, fmt.Errorf("unexpected jwt key id=%v", t.Header["kid"]) - } - - return config.SigningKey, nil + if config.KeyFunc == nil { + config.KeyFunc = config.defaultKeyFunc } // Initialize - parts := strings.Split(config.TokenLookup, ":") - extractor := jwtFromHeader(parts[1], config.AuthScheme) - switch parts[0] { - case "query": - extractor = jwtFromQuery(parts[1]) - case "param": - extractor = jwtFromParam(parts[1]) - case "cookie": - extractor = jwtFromCookie(parts[1]) - case "form": - extractor = jwtFromForm(parts[1]) + // Split sources + sources := strings.Split(config.TokenLookup, ",") + var extractors []jwtExtractor + for _, source := range sources { + parts := strings.Split(source, ":") + + switch parts[0] { + case "query": + extractors = append(extractors, jwtFromQuery(parts[1])) + case "param": + extractors = append(extractors, jwtFromParam(parts[1])) + case "cookie": + extractors = append(extractors, jwtFromCookie(parts[1])) + case "form": + extractors = append(extractors, jwtFromForm(parts[1])) + case "header": + extractors = append(extractors, jwtFromHeader(parts[1], config.AuthScheme)) + } } return func(next echo.HandlerFunc) echo.HandlerFunc { @@ -181,8 +189,17 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { if config.BeforeFunc != nil { config.BeforeFunc(c) } - - auth, err := extractor(c) + var auth string + var err error + for _, extractor := range extractors { + // Extract token from extractor, if it's not fail break the loop and + // set auth + auth, err = extractor(c) + if err == nil { + break + } + } + // If none of extractor has a token, handle error if err != nil { if config.ErrorHandler != nil { return config.ErrorHandler(err) @@ -193,14 +210,15 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { } return err } + token := new(jwt.Token) // Issue #647, #656 if _, ok := config.Claims.(jwt.MapClaims); ok { - token, err = jwt.Parse(auth, config.keyFunc) + token, err = jwt.Parse(auth, config.KeyFunc) } else { t := reflect.ValueOf(config.Claims).Type().Elem() claims := reflect.New(t).Interface().(jwt.Claims) - token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc) + token, err = jwt.ParseWithClaims(auth, claims, config.KeyFunc) } if err == nil && token.Valid { // Store user information from token into context. @@ -225,6 +243,24 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { } } +// defaultKeyFunc returns a signing key of the given token. +func (config *JWTConfig) defaultKeyFunc(t *jwt.Token) (interface{}, error) { + // Check the signing method + if t.Method.Alg() != config.SigningMethod { + return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"]) + } + if len(config.SigningKeys) > 0 { + if kid, ok := t.Header["kid"].(string); ok { + if key, ok := config.SigningKeys[kid]; ok { + return key, nil + } + } + return nil, fmt.Errorf("unexpected jwt key id=%v", t.Header["kid"]) + } + + return config.SigningKey, nil +} + // jwtFromHeader returns a `jwtExtractor` that extracts token from the request header. func jwtFromHeader(header string, authScheme string) jwtExtractor { return func(c echo.Context) (string, error) { diff --git a/vendor/github.com/labstack/echo/v4/middleware/key_auth.go b/vendor/github.com/labstack/echo/v4/middleware/key_auth.go index 94cfd142..fd169aa2 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/key_auth.go +++ b/vendor/github.com/labstack/echo/v4/middleware/key_auth.go @@ -30,12 +30,19 @@ type ( // Validator is a function to validate key. // Required. Validator KeyAuthValidator + + // ErrorHandler defines a function which is executed for an invalid key. + // It may be used to define a custom error. + ErrorHandler KeyAuthErrorHandler } // KeyAuthValidator defines a function to validate KeyAuth credentials. KeyAuthValidator func(string, echo.Context) (bool, error) keyExtractor func(echo.Context) (string, error) + + // KeyAuthErrorHandler defines a function which is executed for an invalid key. + KeyAuthErrorHandler func(error, echo.Context) error ) var ( @@ -95,10 +102,16 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc { // Extract and verify key key, err := extractor(c) if err != nil { + if config.ErrorHandler != nil { + return config.ErrorHandler(err, c) + } return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } valid, err := config.Validator(key, c) if err != nil { + if config.ErrorHandler != nil { + return config.ErrorHandler(err, c) + } return &echo.HTTPError{ Code: http.StatusUnauthorized, Message: "invalid key", diff --git a/vendor/github.com/labstack/echo/v4/middleware/proxy.go b/vendor/github.com/labstack/echo/v4/middleware/proxy.go index 6f01f3a7..6cfd6731 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/proxy.go +++ b/vendor/github.com/labstack/echo/v4/middleware/proxy.go @@ -1,13 +1,16 @@ package middleware import ( + "context" "fmt" "io" "math/rand" "net" "net/http" + "net/http/httputil" "net/url" "regexp" + "strings" "sync" "sync/atomic" "time" @@ -264,3 +267,37 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc { } } } + +// StatusCodeContextCanceled is a custom HTTP status code for situations +// where a client unexpectedly closed the connection to the server. +// As there is no standard error code for "client closed connection", but +// various well-known HTTP clients and server implement this HTTP code we use +// 499 too instead of the more problematic 5xx, which does not allow to detect this situation +const StatusCodeContextCanceled = 499 + +func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler { + proxy := httputil.NewSingleHostReverseProxy(tgt.URL) + proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) { + desc := tgt.URL.String() + if tgt.Name != "" { + desc = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String()) + } + // If the client canceled the request (usually by closing the connection), we can report a + // client error (4xx) instead of a server error (5xx) to correctly identify the situation. + // The Go standard library (at of late 2020) wraps the exported, standard + // context.Canceled error with unexported garbage value requiring a substring check, see + // https://github.com/golang/go/blob/6965b01ea248cabb70c3749fd218b36089a21efb/src/net/net.go#L416-L430 + if err == context.Canceled || strings.Contains(err.Error(), "operation was canceled") { + httpError := echo.NewHTTPError(StatusCodeContextCanceled, fmt.Sprintf("client closed connection: %v", err)) + httpError.Internal = err + c.Set("_error", httpError) + } else { + httpError := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("remote %s unreachable, could not forward: %v", desc, err)) + httpError.Internal = err + c.Set("_error", httpError) + } + } + proxy.Transport = config.Transport + proxy.ModifyResponse = config.ModifyResponse + return proxy +} diff --git a/vendor/github.com/labstack/echo/v4/middleware/proxy_1_11.go b/vendor/github.com/labstack/echo/v4/middleware/proxy_1_11.go deleted file mode 100644 index 17d142d8..00000000 --- a/vendor/github.com/labstack/echo/v4/middleware/proxy_1_11.go +++ /dev/null @@ -1,47 +0,0 @@ -// +build go1.11 - -package middleware - -import ( - "context" - "fmt" - "net/http" - "net/http/httputil" - "strings" - - "github.com/labstack/echo/v4" -) - -// StatusCodeContextCanceled is a custom HTTP status code for situations -// where a client unexpectedly closed the connection to the server. -// As there is no standard error code for "client closed connection", but -// various well-known HTTP clients and server implement this HTTP code we use -// 499 too instead of the more problematic 5xx, which does not allow to detect this situation -const StatusCodeContextCanceled = 499 - -func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler { - proxy := httputil.NewSingleHostReverseProxy(tgt.URL) - proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) { - desc := tgt.URL.String() - if tgt.Name != "" { - desc = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String()) - } - // If the client canceled the request (usually by closing the connection), we can report a - // client error (4xx) instead of a server error (5xx) to correctly identify the situation. - // The Go standard library (at of late 2020) wraps the exported, standard - // context.Canceled error with unexported garbage value requiring a substring check, see - // https://github.com/golang/go/blob/6965b01ea248cabb70c3749fd218b36089a21efb/src/net/net.go#L416-L430 - if err == context.Canceled || strings.Contains(err.Error(), "operation was canceled") { - httpError := echo.NewHTTPError(StatusCodeContextCanceled, fmt.Sprintf("client closed connection: %v", err)) - httpError.Internal = err - c.Set("_error", httpError) - } else { - httpError := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("remote %s unreachable, could not forward: %v", desc, err)) - httpError.Internal = err - c.Set("_error", httpError) - } - } - proxy.Transport = config.Transport - proxy.ModifyResponse = config.ModifyResponse - return proxy -} diff --git a/vendor/github.com/labstack/echo/v4/middleware/proxy_1_11_n.go b/vendor/github.com/labstack/echo/v4/middleware/proxy_1_11_n.go deleted file mode 100644 index 9a78929f..00000000 --- a/vendor/github.com/labstack/echo/v4/middleware/proxy_1_11_n.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build !go1.11 - -package middleware - -import ( - "net/http" - "net/http/httputil" - - "github.com/labstack/echo/v4" -) - -func proxyHTTP(t *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler { - return httputil.NewSingleHostReverseProxy(t.URL) -} diff --git a/vendor/github.com/labstack/echo/v4/middleware/static.go b/vendor/github.com/labstack/echo/v4/middleware/static.go index ae79cb5f..0106f7ce 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/static.go +++ b/vendor/github.com/labstack/echo/v4/middleware/static.go @@ -42,6 +42,10 @@ type ( // the filesystem path is not doubled // Optional. Default value false. IgnoreBase bool `yaml:"ignoreBase"` + + // Filesystem provides access to the static content. + // Optional. Defaults to http.Dir(config.Root) + Filesystem http.FileSystem `yaml:"-"` } ) @@ -146,6 +150,10 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc { if config.Index == "" { config.Index = DefaultStaticConfig.Index } + if config.Filesystem == nil { + config.Filesystem = http.Dir(config.Root) + config.Root = "." + } // Index template t, err := template.New("index").Parse(html) @@ -178,49 +186,73 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc { } } - fi, err := os.Stat(name) + file, err := openFile(config.Filesystem, name) if err != nil { - if os.IsNotExist(err) { - if err = next(c); err != nil { - if he, ok := err.(*echo.HTTPError); ok { - if config.HTML5 && he.Code == http.StatusNotFound { - return c.File(filepath.Join(config.Root, config.Index)) - } - } - return - } + if !os.IsNotExist(err) { + return err + } + + if err = next(c); err == nil { + return err + } + + he, ok := err.(*echo.HTTPError) + if !(ok && config.HTML5 && he.Code == http.StatusNotFound) { + return err + } + + file, err = openFile(config.Filesystem, filepath.Join(config.Root, config.Index)) + if err != nil { + return err } - return } - if fi.IsDir() { - index := filepath.Join(name, config.Index) - fi, err = os.Stat(index) + defer file.Close() + + info, err := file.Stat() + if err != nil { + return err + } + if info.IsDir() { + index, err := openFile(config.Filesystem, filepath.Join(name, config.Index)) if err != nil { if config.Browse { - return listDir(t, name, c.Response()) + return listDir(t, name, file, c.Response()) } + if os.IsNotExist(err) { return next(c) } - return } - return c.File(index) + defer index.Close() + + info, err = index.Stat() + if err != nil { + return err + } + + return serveFile(c, index, info) } - return c.File(name) + return serveFile(c, file, info) } } } -func listDir(t *template.Template, name string, res *echo.Response) (err error) { - file, err := os.Open(name) - if err != nil { - return - } - files, err := file.Readdir(-1) +func openFile(fs http.FileSystem, name string) (http.File, error) { + pathWithSlashes := filepath.ToSlash(name) + return fs.Open(pathWithSlashes) +} + +func serveFile(c echo.Context, file http.File, info os.FileInfo) error { + http.ServeContent(c.Response(), c.Request(), info.Name(), info.ModTime(), file) + return nil +} + +func listDir(t *template.Template, name string, dir http.File, res *echo.Response) (err error) { + files, err := dir.Readdir(-1) if err != nil { return } diff --git a/vendor/github.com/labstack/echo/v4/middleware/timeout.go b/vendor/github.com/labstack/echo/v4/middleware/timeout.go index 5d23ff45..fb8ae421 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/timeout.go +++ b/vendor/github.com/labstack/echo/v4/middleware/timeout.go @@ -1,5 +1,3 @@ -// +build go1.13 - package middleware import ( @@ -94,6 +92,15 @@ func (t echoHandlerFuncWrapper) ServeHTTP(rw http.ResponseWriter, r *http.Reques originalWriter := t.ctx.Response().Writer t.ctx.Response().Writer = rw + // in case of panic we restore original writer and call panic again + // so it could be handled with global middleware Recover() + defer func() { + if err := recover(); err != nil { + t.ctx.Response().Writer = originalWriter + panic(err) + } + }() + err := t.handler(t.ctx) if ctxErr := r.Context().Err(); ctxErr == context.DeadlineExceeded { if err != nil && t.errHandler != nil { diff --git a/vendor/github.com/labstack/echo/v4/router.go b/vendor/github.com/labstack/echo/v4/router.go index 2dd09fae..5b2474b3 100644 --- a/vendor/github.com/labstack/echo/v4/router.go +++ b/vendor/github.com/labstack/echo/v4/router.go @@ -23,6 +23,10 @@ type ( methodHandler *methodHandler paramChild *node anyChild *node + // isLeaf indicates that node does not have child routes + isLeaf bool + // isHandler indicates that node has at least one handler registered to it + isHandler bool } kind uint8 children []*node @@ -50,6 +54,20 @@ const ( anyLabel = byte('*') ) +func (m *methodHandler) isHandler() bool { + return m.connect != nil || + m.delete != nil || + m.get != nil || + m.head != nil || + m.options != nil || + m.patch != nil || + m.post != nil || + m.propfind != nil || + m.put != nil || + m.trace != nil || + m.report != nil +} + // NewRouter returns a new Router instance. func NewRouter(e *Echo) *Router { return &Router{ @@ -73,6 +91,11 @@ func (r *Router) Add(method, path string, h HandlerFunc) { pnames := []string{} // Param names ppath := path // Pristine path + if h == nil && r.echo.Logger != nil { + // FIXME: in future we should return error + r.echo.Logger.Errorf("Adding route without handler function: %v:%v", method, path) + } + for i, lcpIndex := 0, len(path); i < lcpIndex; i++ { if path[i] == ':' { j := i + 1 @@ -86,6 +109,7 @@ func (r *Router) Add(method, path string, h HandlerFunc) { i, lcpIndex = j, len(path) if i == lcpIndex { + // path node is last fragment of route path. ie. `/users/:id` r.insert(method, path[:i], h, paramKind, ppath, pnames) } else { r.insert(method, path[:i], nil, paramKind, "", nil) @@ -136,6 +160,7 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string currentNode.ppath = ppath currentNode.pnames = pnames } + currentNode.isLeaf = currentNode.staticChildren == nil && currentNode.paramChild == nil && currentNode.anyChild == nil } else if lcpLen < prefixLen { // Split node n := newNode( @@ -149,7 +174,6 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string currentNode.paramChild, currentNode.anyChild, ) - // Update parent path for all children to new node for _, child := range currentNode.staticChildren { child.parent = n @@ -171,6 +195,8 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string currentNode.pnames = nil currentNode.paramChild = nil currentNode.anyChild = nil + currentNode.isLeaf = false + currentNode.isHandler = false // Only Static children could reach here currentNode.addStaticChild(n) @@ -188,6 +214,7 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string // Only Static children could reach here currentNode.addStaticChild(n) } + currentNode.isLeaf = currentNode.staticChildren == nil && currentNode.paramChild == nil && currentNode.anyChild == nil } else if lcpLen < searchLen { search = search[lcpLen:] c := currentNode.findChildWithLabel(search[0]) @@ -207,6 +234,7 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string case anyKind: currentNode.anyChild = n } + currentNode.isLeaf = currentNode.staticChildren == nil && currentNode.paramChild == nil && currentNode.anyChild == nil } else { // Node already exists if h != nil { @@ -233,6 +261,8 @@ func newNode(t kind, pre string, p *node, sc children, mh *methodHandler, ppath methodHandler: mh, paramChild: paramChildren, anyChild: anyChildren, + isLeaf: sc == nil && paramChildren == nil && anyChildren == nil, + isHandler: mh.isHandler(), } } @@ -289,6 +319,12 @@ func (n *node) addHandler(method string, h HandlerFunc) { case REPORT: n.methodHandler.report = h } + + if h != nil { + n.isHandler = true + } else { + n.isHandler = n.methodHandler.isHandler() + } } func (n *node) findHandler(method string) HandlerFunc { @@ -343,6 +379,8 @@ func (r *Router) Find(method, path string, c Context) { currentNode := r.tree // Current node as root var ( + previousBestMatchNode *node + matchedHandler HandlerFunc // search stores the remaining path to check for match. By each iteration we move from start of path to end of the path // and search value gets shorter and shorter. search = path @@ -362,10 +400,11 @@ func (r *Router) Find(method, path string, c Context) { valid = currentNode != nil // Next node type by priority - // NOTE: With the current implementation we never backtrack from an `any` route, so `previous.kind` is - // always `static` or `any` - // If this is changed then for any route next kind would be `static` and this statement should be changed - nextNodeKind = previous.kind + 1 + if previous.kind == anyKind { + nextNodeKind = staticKind + } else { + nextNodeKind = previous.kind + 1 + } if fromKind == staticKind { // when backtracking is done from static kind block we did not change search so nothing to restore @@ -380,6 +419,7 @@ func (r *Router) Find(method, path string, c Context) { // for param/any node.prefix value is always `:` so we can not deduce searchIndex from that and must use pValue // for that index as it would also contain part of path we cut off before moving into node we are backtracking from searchIndex -= len(paramValues[paramIndex]) + paramValues[paramIndex] = "" } search = path[searchIndex:] return @@ -421,7 +461,7 @@ func (r *Router) Find(method, path string, c Context) { // goto Any } else { // Not found (this should never be possible for static node we are looking currently) - return + break } } @@ -429,9 +469,17 @@ func (r *Router) Find(method, path string, c Context) { search = search[lcpLen:] searchIndex = searchIndex + lcpLen - // Finish routing if no remaining search and we are on an leaf node - if search == "" && currentNode.ppath != "" { - break + // Finish routing if no remaining search and we are on a node with handler and matching method type + if search == "" && currentNode.isHandler { + // check if current node has handler registered for http method we are looking for. we store currentNode as + // best matching in case we do no find no more routes matching this path+method + if previousBestMatchNode == nil { + previousBestMatchNode = currentNode + } + if h := currentNode.findHandler(method); h != nil { + matchedHandler = h + break + } } // Static node @@ -446,10 +494,16 @@ func (r *Router) Find(method, path string, c Context) { // Param node if child := currentNode.paramChild; search != "" && child != nil { currentNode = child - // FIXME: when param node does not have any children then param node should act similarly to any node - consider all remaining search as match - i, l := 0, len(search) - for ; i < l && search[i] != '/'; i++ { + i := 0 + l := len(search) + if currentNode.isLeaf { + // when param node does not have any children then param node should act similarly to any node - consider all remaining search as match + i = l + } else { + for ; i < l && search[i] != '/'; i++ { + } } + paramValues[paramIndex] = search[:i] paramIndex++ search = search[i:] @@ -463,29 +517,50 @@ func (r *Router) Find(method, path string, c Context) { // If any node is found, use remaining path for paramValues currentNode = child paramValues[len(currentNode.pnames)-1] = search - break + // update indexes/search in case we need to backtrack when no handler match is found + paramIndex++ + searchIndex += +len(search) + search = "" + + // check if current node has handler registered for http method we are looking for. we store currentNode as + // best matching in case we do no find no more routes matching this path+method + if previousBestMatchNode == nil { + previousBestMatchNode = currentNode + } + if h := currentNode.findHandler(method); h != nil { + matchedHandler = h + break + } } // Let's backtrack to the first possible alternative node of the decision path nk, ok := backtrackToNextNodeKind(anyKind) if !ok { - return // No other possibilities on the decision path + break // No other possibilities on the decision path } else if nk == paramKind { goto Param } else if nk == anyKind { goto Any } else { // Not found - return + break } } - ctx.handler = currentNode.findHandler(method) - ctx.path = currentNode.ppath - ctx.pnames = currentNode.pnames + if currentNode == nil && previousBestMatchNode == nil { + return // nothing matched at all + } - if ctx.handler == nil { + if matchedHandler != nil { + ctx.handler = matchedHandler + } else { + // use previous match as basis. although we have no matching handler we have path match. + // so we can send http.StatusMethodNotAllowed (405) instead of http.StatusNotFound (404) + currentNode = previousBestMatchNode ctx.handler = currentNode.checkMethodNotAllowed() } + ctx.path = currentNode.ppath + ctx.pnames = currentNode.pnames + return } |