1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-10 21:45:57 +00:00

reorganization of _examples and add some new examples such as iris+groupcache+mysql+docker

Former-commit-id: ed635ee95de7160cde11eaabc0c1dcb0e460a620
This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-06-07 15:26:06 +03:00
parent 9fdcb4c7fb
commit ed45c77be5
328 changed files with 4262 additions and 41621 deletions

View File

@@ -3,15 +3,15 @@ Builtin Handlers
| Middleware | Example |
| -----------|-------------|
| [basic authentication](basicauth) | [iris/_examples/authentication/basicauth](https://github.com/kataras/iris/tree/master/_examples/authentication/basicauth) |
| [request logger](logger) | [iris/_examples/http_request/request-logger](https://github.com/kataras/iris/tree/master/_examples/http_request/request-logger) |
| [basic authentication](basicauth) | [iris/_examples/auth/basicauth](https://github.com/kataras/iris/tree/master/_examples/auth/basicauth) |
| [request logger](logger) | [iris/_examples/logging/request-logger](https://github.com/kataras/iris/tree/master/_examples/logging/request-logger) |
| [HTTP method override](methodoverride) | [iris/middleware/methodoverride/methodoverride_test.go](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go) |
| [profiling (pprof)](pprof) | [iris/_examples/miscellaneous/pprof](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/pprof) |
| [Google reCAPTCHA](recaptcha) | [iris/_examples/miscellaneous/recaptcha](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/recaptcha) |
| [hCaptcha](hcaptcha) | [iris/_examples/miscellaneous/recaptcha](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/hcaptcha) |
| [recovery](recover) | [iris/_examples/miscellaneous/recover](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/recover) |
| [rate](rate) | [iris/_examples/miscellaneous/ratelimit](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/ratelimit) |
| [jwt](jwt) | [iris/_examples/miscellaneous/jwt](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/jwt) |
| [profiling (pprof)](pprof) | [iris/_examples/pprof](https://github.com/kataras/iris/tree/master/_examples/pprof) |
| [Google reCAPTCHA](recaptcha) | [iris/_examples/auth/recaptcha](https://github.com/kataras/iris/tree/master/_examples/auth/recaptcha) |
| [hCaptcha](hcaptcha) | [iris/_examples/auth/recaptcha](https://github.com/kataras/iris/tree/master/_examples/auth/hcaptcha) |
| [recovery](recover) | [iris/_examples/recover](https://github.com/kataras/iris/tree/master/_examples/recover) |
| [rate](rate) | [iris/_examples/request-ratelimit](https://github.com/kataras/iris/tree/master/_examples/request-ratelimit) |
| [jwt](jwt) | [iris/_examples/auth/jwt](https://github.com/kataras/iris/tree/master/_examples/auth/jwt) |
| [requestid](requestid) | [iris/middleware/requestid/requestid_test.go](https://github.com/kataras/iris/blob/master/_examples/middleware/requestid/requestid_test.go) |
Community made
@@ -32,7 +32,7 @@ Most of the experimental handlers are ported to work with _iris_'s handler form,
| [raven](https://github.com/iris-contrib/middleware/tree/master/raven)| Sentry client in Go | [iris-contrib/middleware/raven/_example](https://github.com/iris-contrib/middleware/blob/master/raven/_example/main.go) |
| [csrf](https://github.com/iris-contrib/middleware/tree/master/csrf)| Cross-Site Request Forgery Protection | [iris-contrib/middleware/csrf/_example](https://github.com/iris-contrib/middleware/blob/master/csrf/_example/main.go) |
| [go-i18n](https://github.com/iris-contrib/middleware/tree/master/go-i18n)| i18n Iris Loader for nicksnyder/go-i18n | [iris-contrib/middleware/go-i18n/_example](https://github.com/iris-contrib/middleware/blob/master/go-i18n/_example/main.go) |
| [throttler](https://github.com/iris-contrib/middleware/tree/master/throttler)| Rate limiting access to HTTP endpoints | [iris-contrib/middleware/throttler/_example](https://github.com/iris-contrib/middleware/blob/master/throttler/_example/main.go) **NEW** |
| [throttler](https://github.com/iris-contrib/middleware/tree/master/throttler)| Rate limiting access to HTTP endpoints | [iris-contrib/middleware/throttler/_example](https://github.com/iris-contrib/middleware/blob/master/throttler/_example/main.go) |
Third-Party Handlers
------------
@@ -43,15 +43,14 @@ Here's a small list of useful third-party handlers:
| Middleware | Description |
| -----------|-------------|
| [goth](https://github.com/markbates/goth) | OAuth, OAuth2 authentication. [Example](https://github.com/kataras/iris/tree/master/_examples/authentication/oauth2) |
| [goth](https://github.com/markbates/goth) | OAuth, OAuth2 authentication. [Example](https://github.com/kataras/iris/tree/master/_examples/auth/goth) |
| [permissions2](https://github.com/xyproto/permissions2) | Cookies, users and permissions. [Example](https://github.com/kataras/iris/tree/master/_examples/auth/permissions) |
| [csp](https://github.com/awakenetworks/csp) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |
| [delay](https://github.com/jeffbmartinez/delay) | Add delays/latency to endpoints. Useful when testing effects of high latency |
| [onthefly](https://github.com/xyproto/onthefly) | Generate TinySVG, HTML and CSS on the fly |
| [permissions2](https://github.com/xyproto/permissions2) | Cookies, users and permissions |
| [RestGate](https://github.com/pjebs/restgate) | Secure authentication for REST API endpoints |
| [stats](https://github.com/thoas/stats) | Store information about your web application (response time, etc.) |
| [VanGoH](https://github.com/auroratechnologies/vangoh) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC authentication middleware |
| [xrequestid](https://github.com/pilu/xrequestid) | Middleware that assigns a random X-Request-Id header to each request |
| [digits](https://github.com/bamarni/digits) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |
> Feel free to put up your own middleware in this list!

View File

@@ -1,7 +1,7 @@
// Package basicauth provides http basic authentication via middleware. See _examples/authentication/basicauth
// Package basicauth provides http basic authentication via middleware. See _examples/auth/basicauth
package basicauth
// test file: ../../_examples/authentication/basicauth/main_test.go
// test file: ../../_examples/auth/basicauth/main_test.go
import (
"encoding/base64"

View File

@@ -160,7 +160,7 @@ func ParseForm(dataSiteKey, postActionRelativePath string) string {
}
// RenderForm writes the `HTMLForm` to "w" response writer.
// See `_examples/basic/register_form.html` example for a custom form instead.
// See `_examples/auth/hcaptcha/templates/register_form.html` example for a custom form instead.
func RenderForm(ctx context.Context, dataSiteKey, postActionRelativePath string) (int, error) {
return ctx.HTML(ParseForm(dataSiteKey, postActionRelativePath))
}

View File

@@ -10,6 +10,15 @@ type (
Claims = jwt.Claims
// Audience represents the recipients that the token is intended for.
Audience = jwt.Audience
// NumericDate represents date and time as the number of seconds since the
// epoch, including leap seconds. Non-integer values can be represented
// in the serialized format, but we round to the nearest second.
NumericDate = jwt.NumericDate
)
var (
// NewNumericDate constructs NumericDate from time.Time value.
NewNumericDate = jwt.NewNumericDate
)
type (

View File

@@ -155,26 +155,38 @@ func New(maxAge time.Duration, alg SignatureAlgorithm, key interface{}) (*JWT, e
return j, nil
}
// RSA filenames for `DefaultRSA`.
// Default key filenames for `RSA`.
const (
SignFilename = "jwt_sign.key"
EncFilename = "jwt_enc.key"
DefaultSignFilename = "jwt_sign.key"
DefaultEncFilename = "jwt_enc.key"
)
// DefaultRSA returns a new `JWT` instance.
// It tries to parse RSA256 keys from "jwt_sign.key" and (optionally) "jwt_enc.key" files
// in the current working directory, and if not found, it generates and exports the keys.
// RSA returns a new `JWT` instance.
// It tries to parse RSA256 keys from "filenames[0]" (defaults to "jwt_sign.key") and
// "filenames[1]" (defaults to "jwt_enc.key") files or generates and exports new random keys.
//
// It panics on errors.
// Use the `New` package-level function instead for more options.
func DefaultRSA(maxAge time.Duration) *JWT {
// Do not try to create or load enc key if only sign key already exists.
withEncryption := true
if fileExists(SignFilename) {
withEncryption = fileExists(EncFilename)
func RSA(maxAge time.Duration, filenames ...string) *JWT {
var (
signFilename = DefaultSignFilename
encFilename = DefaultEncFilename
)
switch len(filenames) {
case 1:
signFilename = filenames[0]
case 2:
encFilename = filenames[1]
}
sigKey, err := LoadRSA(SignFilename, 2048)
// Do not try to create or load enc key if only sign key already exists.
withEncryption := true
if fileExists(signFilename) {
withEncryption = fileExists(encFilename)
}
sigKey, err := LoadRSA(signFilename, 2048)
if err != nil {
panic(err)
}
@@ -185,7 +197,7 @@ func DefaultRSA(maxAge time.Duration) *JWT {
}
if withEncryption {
encKey, err := LoadRSA(EncFilename, 2048)
encKey, err := LoadRSA(encFilename, 2048)
if err != nil {
panic(err)
}
@@ -212,7 +224,7 @@ func getenv(key string, def string) string {
return v
}
// DefaultHMAC returns a new `JWT` instance.
// HMAC returns a new `JWT` instance.
// It tries to read hmac256 secret keys from system environment variables:
// * JWT_SECRET for signing and verification key and
// * JWT_SECRET_ENC for encryption and decryption key
@@ -220,7 +232,7 @@ func getenv(key string, def string) string {
//
// It panics on errors.
// Use the `New` package-level function instead for more options.
func DefaultHMAC(maxAge time.Duration, keys ...string) *JWT {
func HMAC(maxAge time.Duration, keys ...string) *JWT {
var defaultSignSecret, defaultEncSecret string
switch len(keys) {
@@ -279,8 +291,8 @@ func (j *JWT) WithEncryption(contentEncryption ContentEncryption, alg KeyAlgorit
// See the `JWT.Expiry` method too.
func Expiry(maxAge time.Duration, claims Claims) Claims {
now := time.Now()
claims.Expiry = jwt.NewNumericDate(now.Add(maxAge))
claims.IssuedAt = jwt.NewNumericDate(now)
claims.Expiry = NewNumericDate(now.Add(maxAge))
claims.IssuedAt = NewNumericDate(now)
return claims
}
@@ -308,6 +320,14 @@ func (j *JWT) Expiry(claims Claims) Claims {
// Token generates and returns a new token string.
// See `VerifyToken` too.
func (j *JWT) Token(claims interface{}) (string, error) {
// switch c := claims.(type) {
// case Claims:
// claims = Expiry(j.MaxAge, c)
// case map[string]interface{}: let's not support map.
// now := time.Now()
// c["iat"] = now.Unix()
// c["exp"] = now.Add(j.MaxAge).Unix()
// }
if c, ok := claims.(Claims); ok {
claims = Expiry(j.MaxAge, c)
}
@@ -331,6 +351,39 @@ func (j *JWT) Token(claims interface{}) (string, error) {
return token, nil
}
/* Let's no support maps, typed claim is the way to go.
// validateMapClaims validates claims of map type.
func validateMapClaims(m map[string]interface{}, e jwt.Expected, leeway time.Duration) error {
if !e.Time.IsZero() {
if v, ok := m["nbf"]; ok {
if notBefore, ok := v.(NumericDate); ok {
if e.Time.Add(leeway).Before(notBefore.Time()) {
return ErrNotValidYet
}
}
}
if v, ok := m["exp"]; ok {
if exp, ok := v.(int64); ok {
if e.Time.Add(-leeway).Before(time.Unix(exp, 0)) {
return ErrExpired
}
}
}
if v, ok := m["iat"]; ok {
if issuedAt, ok := v.(int64); ok {
if e.Time.Add(leeway).Before(time.Unix(issuedAt, 0)) {
return ErrIssuedInTheFuture
}
}
}
}
return nil
}
*/
// WriteToken is a helper which just generates(calls the `Token` method) and writes
// a new token to the client in plain text format.
//
@@ -347,21 +400,26 @@ func (j *JWT) WriteToken(ctx context.Context, claims interface{}) error {
}
var (
// ErrTokenMissing when token cannot be extracted from the request.
ErrTokenMissing = errors.New("token is missing")
// ErrTokenInvalid when incoming token is invalid.
ErrTokenInvalid = errors.New("token is invalid")
// ErrTokenExpired when incoming token has expired.
ErrTokenExpired = errors.New("token has expired")
// ErrMissing when token cannot be extracted from the request.
ErrMissing = errors.New("token is missing")
// ErrExpired indicates that token is used after expiry time indicated in exp claim.
ErrExpired = errors.New("token is expired (exp)")
// ErrNotValidYet indicates that token is used before time indicated in nbf claim.
ErrNotValidYet = errors.New("token not valid yet (nbf)")
// ErrIssuedInTheFuture indicates that the iat field is in the future.
ErrIssuedInTheFuture = errors.New("token issued in the future (iat)")
)
type (
claimsValidator interface {
ValidateWithLeeway(e jwt.Expected, leeway time.Duration) error
}
claimsAlternativeValidator interface {
claimsAlternativeValidator interface { // to keep iris-contrib/jwt MapClaims compatible.
Validate() error
}
claimsContextValidator interface {
Validate(ctx context.Context) error
}
)
// IsValidated reports whether a token is already validated through
@@ -372,18 +430,34 @@ func IsValidated(ctx context.Context) bool { // see the `ReadClaims`.
}
func validateClaims(ctx context.Context, claims interface{}) (err error) {
switch claims := claims.(type) {
switch c := claims.(type) {
case claimsValidator:
err = claims.ValidateWithLeeway(jwt.Expected{Time: time.Now()}, 0)
err = c.ValidateWithLeeway(jwt.Expected{Time: time.Now()}, 0)
case claimsAlternativeValidator:
err = claims.Validate()
err = c.Validate()
case claimsContextValidator:
err = c.Validate(ctx)
case *json.RawMessage:
// if the data type is raw message (json []byte)
// then it should contain exp (and iat and nbf) keys.
// Unmarshal raw message to validate against.
v := new(Claims)
err = json.Unmarshal(*c, v)
if err == nil {
return validateClaims(ctx, v)
}
default:
ctx.Values().Set(needsValidationContextKey, struct{}{})
}
if err != nil {
if err == jwt.ErrExpired {
return ErrTokenExpired
switch err {
case jwt.ErrExpired:
return ErrExpired
case jwt.ErrNotValidYet:
return ErrNotValidYet
case jwt.ErrIssuedInTheFuture:
return ErrIssuedInTheFuture
}
}
@@ -403,7 +477,7 @@ func (j *JWT) VerifyToken(ctx context.Context, claimsPtr interface{}) error {
}
if token == "" {
return ErrTokenMissing
return ErrMissing
}
var (
@@ -422,11 +496,11 @@ func (j *JWT) VerifyToken(ctx context.Context, claimsPtr interface{}) error {
parsedToken, err = jwt.ParseSigned(token)
}
if err != nil {
return ErrTokenInvalid
return err
}
if err = parsedToken.Claims(j.VerificationKey, claimsPtr); err != nil {
return ErrTokenInvalid
return err
}
return validateClaims(ctx, claimsPtr)
@@ -463,12 +537,12 @@ func (j *JWT) Verify(ctx context.Context) {
func ReadClaims(ctx context.Context, claimsPtr interface{}) error {
v := ctx.Values().Get(ClaimsContextKey)
if v == nil {
return ErrTokenMissing
return ErrMissing
}
raw, ok := v.(json.RawMessage)
if !ok {
return ErrTokenMissing
return ErrMissing
}
err := json.Unmarshal(raw, claimsPtr)
@@ -476,9 +550,9 @@ func ReadClaims(ctx context.Context, claimsPtr interface{}) error {
return err
}
// If already validated on VerifyToken (a claimsValidator/claimsAlternativeValidator)
// then no need to perform the check again.
if !IsValidated(ctx) {
// If already validated on `Verify/VerifyToken`
// then no need to perform the check again.
ctx.Values().Remove(needsValidationContextKey)
return validateClaims(ctx, claimsPtr)
}
@@ -518,7 +592,7 @@ func ReadClaims(ctx context.Context, claimsPtr interface{}) error {
func Get(ctx context.Context) (interface{}, error) {
claims := ctx.Values().Get(ClaimsContextKey)
if claims == nil {
return nil, ErrTokenMissing
return nil, ErrMissing
}
if !IsValidated(ctx) {

View File

@@ -19,22 +19,22 @@ type userClaims struct {
const testMaxAge = 3 * time.Second
// Random RSA verification and encryption.
func TestDefaultRSA(t *testing.T) {
j := jwt.DefaultRSA(testMaxAge)
func TestRSA(t *testing.T) {
j := jwt.RSA(testMaxAge)
t.Cleanup(func() {
os.Remove(jwt.SignFilename)
os.Remove(jwt.EncFilename)
os.Remove(jwt.DefaultSignFilename)
os.Remove(jwt.DefaultEncFilename)
})
testWriteVerifyToken(t, j)
}
// HMAC verification and encryption.
func TestDefaultHMAC(t *testing.T) {
j := jwt.DefaultHMAC(testMaxAge, "secret", "itsa16bytesecret")
func TestHMAC(t *testing.T) {
j := jwt.HMAC(testMaxAge, "secret", "itsa16bytesecret")
testWriteVerifyToken(t, j)
}
func TestHMAC(t *testing.T) {
func TestNew_HMAC(t *testing.T) {
j, err := jwt.New(testMaxAge, jwt.HS256, []byte("secret"))
if err != nil {
t.Fatal(err)
@@ -81,7 +81,7 @@ func testWriteVerifyToken(t *testing.T, j *jwt.JWT) {
ctx.JSON(claims)
})
app.Post("/restricted_middleware", j.Verify, func(ctx iris.Context) {
app.Post("/restricted_middleware_readclaims", j.Verify, func(ctx iris.Context) {
var claims userClaims
if err := jwt.ReadClaims(ctx, &claims); err != nil {
ctx.StopWithStatus(iris.StatusUnauthorized)
@@ -91,6 +91,16 @@ func testWriteVerifyToken(t *testing.T, j *jwt.JWT) {
ctx.JSON(claims)
})
app.Post("/restricted_middleware_get", j.Verify, func(ctx iris.Context) {
claims, err := jwt.Get(ctx)
if err != nil {
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
ctx.JSON(claims)
})
e := httptest.New(t, app)
// Get token.
@@ -99,7 +109,7 @@ func testWriteVerifyToken(t *testing.T, j *jwt.JWT) {
t.Fatalf("empty token")
}
restrictedPaths := [...]string{"/restricted", "/restricted_middleware"}
restrictedPaths := [...]string{"/restricted", "/restricted_middleware_readclaims", "/restricted_middleware_get"}
now := time.Now()
for _, path := range restrictedPaths {

View File

@@ -1,4 +1,4 @@
// Package logger provides request logging via middleware. See _examples/http_request/request-logger
// Package logger provides request logging via middleware. See _examples/logging/request-logger
package logger
import (

View File

@@ -1,4 +1,4 @@
// Package pprof provides native pprof support via middleware. See _examples/miscellaneous/pprof
// Package pprof provides native pprof support via middleware. See _examples/pprof
package pprof
import (

View File

@@ -1,5 +1,5 @@
// Package rate implements rate limiter for Iris client requests.
// Example can be found at: _examples/miscellaneous/ratelimit/main.go.
// Example can be found at: _examples/request-ratelimit/main.go.
package rate
import (

View File

@@ -1,4 +1,4 @@
// Package recover provides recovery for specific routes or for the whole app via middleware. See _examples/miscellaneous/recover
// Package recover provides recovery for specific routes or for the whole app via middleware. See _examples/recover
package recover
import (

View File

@@ -10,7 +10,7 @@ func init() {
context.SetHandlerName("iris/middleware/requestid.*", "iris.request.id")
}
const xRequestIDHeaderValue = "X-Request-ID"
const xRequestIDHeaderValue = "X-Request-Id"
// Generator defines the function which should extract or generate
// a Request ID. See `DefaultGenerator` and `New` package-level functions.
@@ -40,13 +40,14 @@ var DefaultGenerator Generator = func(ctx context.Context) string {
}
// New returns a new request id middleware.
// It accepts an ID Generator.
// It optionally accepts an ID Generator.
// The Generator can stop the handlers chain with an error or
// return a valid ID (string).
// If it's nil then the `DefaultGenerator` will be used instead.
func New(gen Generator) context.Handler {
if gen == nil {
gen = DefaultGenerator
func New(generator ...Generator) context.Handler {
gen := DefaultGenerator
if len(generator) > 0 {
gen = generator[0]
}
return func(ctx context.Context) {

View File

@@ -17,7 +17,7 @@ func TestRequestID(t *testing.T) {
def := app.Party("/default")
{
def.Use(requestid.New(nil))
def.Use(requestid.New())
def.Get("/", h)
}
@@ -51,7 +51,7 @@ func TestRequestID(t *testing.T) {
ctx.SetID(expectedCustomIDFromOtherMiddleware)
ctx.Next()
})
changeID.Use(requestid.New(nil))
changeID.Use(requestid.New())
changeID.Get("/", h)
}