add experimental handlers examples

Former-commit-id: 8d8a0d15afce2554dc5926f6b9bd9c42cb95dad0
This commit is contained in:
Gerasimos (Makis) Maropoulos 2019-08-16 16:57:03 +03:00
parent db433f7dca
commit a7635afe62
21 changed files with 644 additions and 2 deletions

View File

@ -253,6 +253,7 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
### ORM ### ORM
- [Using xorm(Mysql, MyMysql, Postgres, Tidb, **SQLite**, MsSql, MsSql, Oracle)](orm/xorm/main.go) - [Using xorm(Mysql, MyMysql, Postgres, Tidb, **SQLite**, MsSql, MsSql, Oracle)](orm/xorm/main.go)
- [Using gorm](orm/gorm/main.go)
### Miscellaneous ### Miscellaneous

View File

@ -0,0 +1,5 @@
# Install iris-contrib/middleware
```sh
$ go get -u github.com/iris-contrib/middleware/...
```

View File

@ -0,0 +1,14 @@
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")

View File

@ -0,0 +1,5 @@
p, alice, /dataset1/*, GET
p, alice, /dataset1/resource1, POST
p, bob, /dataset2/resource1, *
p, bob, /dataset2/resource2, GET
p, bob, /dataset2/folder1/*, POST
1 p alice /dataset1/* GET
2 p alice /dataset1/resource1 POST
3 p bob /dataset2/resource1 *
4 p bob /dataset2/resource2 GET
5 p bob /dataset2/folder1/* POST

View File

@ -0,0 +1,43 @@
package main
import (
"github.com/kataras/iris"
"github.com/casbin/casbin/v2"
cm "github.com/iris-contrib/middleware/casbin"
)
// $ go get github.com/casbin/casbin/v2
// $ go run main.go
// Enforcer maps the model and the policy for the casbin service, we use this variable on the main_test too.
var Enforcer, _ = casbin.NewEnforcer("casbinmodel.conf", "casbinpolicy.csv")
func newApp() *iris.Application {
casbinMiddleware := cm.New(Enforcer)
app := iris.New()
app.Use(casbinMiddleware.ServeHTTP)
app.Get("/", hi)
app.Get("/dataset1/{p:path}", hi) // p, alice, /dataset1/*, GET
app.Post("/dataset1/resource1", hi)
app.Get("/dataset2/resource2", hi)
app.Post("/dataset2/folder1/{p:path}", hi)
app.Any("/dataset2/resource1", hi)
return app
}
func main() {
app := newApp()
app.Run(iris.Addr(":8080"))
}
func hi(ctx iris.Context) {
ctx.Writef("Hello %s", cm.Username(ctx.Request()))
}

View File

@ -0,0 +1,48 @@
package main
import (
"testing"
"github.com/kataras/iris/httptest"
)
func TestCasbinMiddleware(t *testing.T) {
app := newApp()
e := httptest.New(t, app, httptest.Debug(false))
type ttcasbin struct {
username string
path string
method string
status int
}
tt := []ttcasbin{
{"alice", "/dataset1/resource1", "GET", 200},
{"alice", "/dataset1/resource1", "POST", 200},
{"alice", "/dataset1/resource2", "GET", 200},
{"alice", "/dataset1/resource2", "POST", 404},
{"bob", "/dataset2/resource1", "GET", 200},
{"bob", "/dataset2/resource1", "POST", 200},
{"bob", "/dataset2/resource1", "DELETE", 200},
{"bob", "/dataset2/resource2", "GET", 200},
{"bob", "/dataset2/resource2", "POST", 404},
{"bob", "/dataset2/resource2", "DELETE", 404},
{"bob", "/dataset2/folder1/item1", "GET", 404},
{"bob", "/dataset2/folder1/item1", "POST", 200},
{"bob", "/dataset2/folder1/item1", "DELETE", 404},
{"bob", "/dataset2/folder1/item2", "GET", 404},
{"bob", "/dataset2/folder1/item2", "POST", 200},
{"bob", "/dataset2/folder1/item2", "DELETE", 404},
}
for _, tt := range tt {
check(e, tt.method, tt.path, tt.username, tt.status)
}
}
func check(e *httptest.Expect, method, path, username string, status int) {
e.Request(method, path).WithBasicAuth(username, "password").Expect().Status(status)
}

View File

@ -0,0 +1,14 @@
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")

View File

@ -0,0 +1,7 @@
p, alice, /dataset1/*, GET
p, alice, /dataset1/resource1, POST
p, bob, /dataset2/resource1, *
p, bob, /dataset2/resource2, GET
p, bob, /dataset2/folder1/*, POST
p, dataset1_admin, /dataset1/*, *
g, cathrin, dataset1_admin
1 p, alice, /dataset1/*, GET
2 p, alice, /dataset1/resource1, POST
3 p, bob, /dataset2/resource1, *
4 p, bob, /dataset2/resource2, GET
5 p, bob, /dataset2/folder1/*, POST
6 p, dataset1_admin, /dataset1/*, *
7 g, cathrin, dataset1_admin

View File

@ -0,0 +1,43 @@
package main
import (
"github.com/kataras/iris"
"github.com/casbin/casbin/v2"
cm "github.com/iris-contrib/middleware/casbin"
)
// $ go get github.com/casbin/casbin
// $ go run main.go
// Enforcer maps the model and the policy for the casbin service, we use this variable on the main_test too.
var Enforcer, _ = casbin.NewEnforcer("casbinmodel.conf", "casbinpolicy.csv")
func newApp() *iris.Application {
casbinMiddleware := cm.New(Enforcer)
app := iris.New()
app.WrapRouter(casbinMiddleware.Wrapper())
app.Get("/", hi)
app.Any("/dataset1/{p:path}", hi) // p, dataset1_admin, /dataset1/*, * && p, alice, /dataset1/*, GET
app.Post("/dataset1/resource1", hi)
app.Get("/dataset2/resource2", hi)
app.Post("/dataset2/folder1/{p:path}", hi)
app.Any("/dataset2/resource1", hi)
return app
}
func main() {
app := newApp()
app.Run(iris.Addr(":8080"))
}
func hi(ctx iris.Context) {
ctx.Writef("Hello %s", cm.Username(ctx.Request()))
}

View File

@ -0,0 +1,79 @@
package main
import (
"testing"
"github.com/kataras/iris/httptest"
)
func TestCasbinWrapper(t *testing.T) {
app := newApp()
e := httptest.New(t, app, httptest.Debug(true))
type ttcasbin struct {
username string
path string
method string
status int
}
tt := []ttcasbin{
{"alice", "/dataset1/resource1", "GET", 200},
{"alice", "/dataset1/resource1", "POST", 200},
{"alice", "/dataset1/resource2", "GET", 200},
{"alice", "/dataset1/resource2", "POST", 403},
{"bob", "/dataset2/resource1", "GET", 200},
{"bob", "/dataset2/resource1", "POST", 200},
{"bob", "/dataset2/resource1", "DELETE", 200},
{"bob", "/dataset2/resource2", "GET", 200},
{"bob", "/dataset2/resource2", "POST", 403},
{"bob", "/dataset2/resource2", "DELETE", 403},
{"bob", "/dataset2/folder1/item1", "GET", 403},
{"bob", "/dataset2/folder1/item1", "POST", 200},
{"bob", "/dataset2/folder1/item1", "DELETE", 403},
{"bob", "/dataset2/folder1/item2", "GET", 403},
{"bob", "/dataset2/folder1/item2", "POST", 200},
{"bob", "/dataset2/folder1/item2", "DELETE", 403},
}
for _, tt := range tt {
check(e, tt.method, tt.path, tt.username, tt.status)
}
println("ADMIN ROLES")
ttAdmin := []ttcasbin{
{"cathrin", "/dataset1/item", "GET", 200},
{"cathrin", "/dataset1/item", "POST", 200},
{"cathrin", "/dataset1/item", "DELETE", 200},
{"cathrin", "/dataset2/item", "GET", 403},
{"cathrin", "/dataset2/item", "POST", 403},
{"cathrin", "/dataset2/item", "DELETE", 403},
}
for _, tt := range ttAdmin {
check(e, tt.method, tt.path, tt.username, tt.status)
}
println("ADMIN ROLE FOR cathrin DELETED")
Enforcer.DeleteRolesForUser("cathrin")
ttAdminDeleted := []ttcasbin{
{"cathrin", "/dataset1/item", "GET", 403},
{"cathrin", "/dataset1/item", "POST", 403},
{"cathrin", "/dataset1/item", "DELETE", 403},
{"cathrin", "/dataset2/item", "GET", 403},
{"cathrin", "/dataset2/item", "POST", 403},
{"cathrin", "/dataset2/item", "DELETE", 403},
}
for _, tt := range ttAdminDeleted {
check(e, tt.method, tt.path, tt.username, tt.status)
}
}
func check(e *httptest.Expect, method, path, username string, status int) {
e.Request(method, path).WithBasicAuth(username, "password").Expect().Status(status)
}

View File

@ -0,0 +1,50 @@
package main
import (
"time"
"github.com/kataras/iris"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
cw "github.com/iris-contrib/middleware/cloudwatch"
)
// $ go get github.com/aws/aws-sdk-go/...
// $ go run main.go
func main() {
app := iris.New()
app.Use(cw.New("us-east-1", "test").ServeHTTP)
app.Get("/", func(ctx iris.Context) {
put := cw.GetPutFunc(ctx)
put([]*cloudwatch.MetricDatum{
{
MetricName: aws.String("MyMetric"),
Dimensions: []*cloudwatch.Dimension{
{
Name: aws.String("ThingOne"),
Value: aws.String("something"),
},
{
Name: aws.String("ThingTwo"),
Value: aws.String("other"),
},
},
Timestamp: aws.Time(time.Now()),
Unit: aws.String("Count"),
Value: aws.Float64(42),
},
})
ctx.StatusCode(iris.StatusOK)
ctx.Text("success!\n")
})
// http://localhost:8080
// should give: NoCredentialProviders
// which is correct, you have to authorize your aws, we asumme that you know how to.
app.Run(iris.Addr(":8080"))
}

View File

@ -0,0 +1,38 @@
package main
import (
"github.com/kataras/iris"
)
// NOTE: THIS IS OPTIONALLY.
// It is just an example of communication between cors/simple/main.go and your app
// based on issues that beginners had with it.
// You should use your own favourite library for HTTP requests (any programming language ofc).
//
// Replace the '8fc93b1c.ngrok.io' with a domain which
// exposes the cors/simple/main.go server side.
const url = "http://8fc93b1c.ngrok.io/api/v1/mailer"
var clientSide = []byte(`<script type="text/javascript">
fetch("` + url + `", {
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
method: "POST",
mode: "cors",
body: JSON.stringify({ email: "mymail@mail.com" }),
});
</script>`)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.Write(clientSide)
})
// Start and navigate to http://localhost:8080
// and go to the previous terminal of your running cors/simple/main.go server
// and see the logs.
app.Run(iris.Addr(":8080"))
}

View File

@ -0,0 +1,54 @@
package main
import (
"github.com/kataras/iris"
)
func main() {
app := iris.New()
crs := func(ctx iris.Context) {
ctx.Header("Access-Control-Allow-Origin", "*")
ctx.Header("Access-Control-Allow-Credentials", "true")
ctx.Header("Access-Control-Allow-Headers", "Access-Control-Allow-Origin,Content-Type")
ctx.Next()
} // or "github.com/iris-contrib/middleware/cors"
v1 := app.Party("/api/v1", crs).AllowMethods(iris.MethodOptions) // <- important for the preflight.
{
v1.Post("/mailer", func(ctx iris.Context) {
var any iris.Map
err := ctx.ReadJSON(&any)
if err != nil {
ctx.WriteString(err.Error())
ctx.StatusCode(iris.StatusBadRequest)
return
}
ctx.Application().Logger().Infof("received %#+v", any)
})
v1.Get("/home", func(ctx iris.Context) {
ctx.WriteString("Hello from /home")
})
v1.Get("/about", func(ctx iris.Context) {
ctx.WriteString("Hello from /about")
})
v1.Post("/send", func(ctx iris.Context) {
ctx.WriteString("sent")
})
v1.Put("/send", func(ctx iris.Context) {
ctx.WriteString("updated")
})
v1.Delete("/send", func(ctx iris.Context) {
ctx.WriteString("deleted")
})
}
// iris.WithoutPathCorrectionRedirection | iris#Configuration.DisablePathCorrectionRedirection:
// CORS needs the allow origin headers in the redirect response as well, we have a solution for this:
// If you use iris >= v11.0.4 then add the `app.Run(..., iris.WithoutPathCorrectionRedirection)`
// on the server side if you wish
// to directly fire the handler instead of redirection (which is the default behavior)
// on request paths like "/v1/mailer/" when "/v1/mailer" route handler is registered.
app.Run(iris.Addr(":80"), iris.WithoutPathCorrectionRedirection)
}

View File

@ -0,0 +1,55 @@
// This middleware provides Cross-Site Request Forgery
// protection.
//
// It securely generates a masked (unique-per-request) token that
// can be embedded in the HTTP response (e.g. form field or HTTP header).
// The original (unmasked) token is stored in the session, which is inaccessible
// by an attacker (provided you are using HTTPS). Subsequent requests are
// expected to include this token, which is compared against the session token.
// Requests that do not provide a matching token are served with a HTTP 403
// 'Forbidden' error response.
package main
// $ go get -u github.com/iris-contrib/middleware/...
import (
"github.com/kataras/iris"
"github.com/iris-contrib/middleware/csrf"
)
func main() {
app := iris.New()
app.RegisterView(iris.HTML("./views", ".html"))
// Note that the authentication key provided should be 32 bytes
// long and persist across application restarts.
protect := csrf.Protect([]byte("9AB0F421E53A477C084477AEA06096F5"),
csrf.Secure(false)) // Defaults to true, but pass `false` while no https (devmode).
users := app.Party("/user", protect)
{
users.Get("/signup", getSignupForm)
// // POST requests without a valid token will return a HTTP 403 Forbidden.
users.Post("/signup", postSignupForm)
}
// GET: http://localhost:8080/user/signup
// POST: http://localhost:8080/user/signup
app.Run(iris.Addr(":8080"))
}
func getSignupForm(ctx iris.Context) {
// views/user/signup.html just needs a {{ .csrfField }} template tag for
// csrf.TemplateField to inject the CSRF token into. Easy!
ctx.ViewData(csrf.TemplateTag, csrf.TemplateField(ctx))
ctx.View("user/signup.html")
// We could also retrieve the token directly from csrf.Token(ctx) and
// set it in the request header - ctx.GetHeader("X-CSRF-Token", token)
// This is useful if you're sending JSON to clients or a front-end JavaScript
// framework.
}
func postSignupForm(ctx iris.Context) {
ctx.Writef("You're welcome mate!")
}

View File

@ -0,0 +1,4 @@
<form method="POST" action="/user/signup">
{{ .csrfField }}
<button type="submit">Proceed</button>
</form>

View File

@ -0,0 +1,46 @@
// iris provides some basic middleware, most for your learning curve.
// You can use any net/http compatible middleware with iris.FromStd wrapper.
//
// JWT net/http video tutorial for golang newcomers: https://www.youtube.com/watch?v=dgJFeqeXVKw
//
// This middleware is the only one cloned from external source: https://github.com/auth0/go-jwt-middleware
// (because it used "context" to define the user but we don't need that so a simple iris.FromStd wouldn't work as expected.)
package main
// $ go get -u github.com/dgrijalva/jwt-go
// $ go run main.go
import (
"github.com/kataras/iris"
"github.com/dgrijalva/jwt-go"
jwtmiddleware "github.com/iris-contrib/middleware/jwt"
)
func myHandler(ctx iris.Context) {
user := ctx.Values().Get("jwt").(*jwt.Token)
ctx.Writef("This is an authenticated request\n")
ctx.Writef("Claim content:\n")
ctx.Writef("%s", user.Signature)
}
func main() {
app := iris.New()
jwtHandler := jwtmiddleware.New(jwtmiddleware.Config{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
return []byte("My Secret"), nil
},
// When set, the middleware verifies that tokens are signed with the specific signing algorithm
// If the signing method is not constant the ValidationKeyGetter callback can be used to implement additional checks
// Important to avoid security issues described here: https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
SigningMethod: jwt.SigningMethodHS256,
})
app.Use(jwtHandler.Serve)
app.Get("/ping", myHandler)
app.Run(iris.Addr("localhost:3001"))
} // don't forget to look ../jwt_test.go to see how to set your own custom claims

View File

@ -0,0 +1,24 @@
package main
import (
"github.com/kataras/iris"
"github.com/iris-contrib/middleware/newrelic"
)
func main() {
app := iris.New()
config := newrelic.Config("APP_SERVER_NAME", "NEWRELIC_LICENSE_KEY")
config.Enabled = true
m, err := newrelic.New(config)
if err != nil {
app.Logger().Fatal(err)
}
app.Use(m.ServeHTTP)
app.Get("/", func(ctx iris.Context) {
ctx.Writef("success!\n")
})
app.Run(iris.Addr(":8080"))
}

View File

@ -0,0 +1,39 @@
package main
import (
"math/rand"
"time"
"github.com/kataras/iris"
prometheusMiddleware "github.com/iris-contrib/middleware/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
app := iris.New()
m := prometheusMiddleware.New("serviceName", 0.3, 1.2, 5.0)
app.Use(m.ServeHTTP)
app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
// error code handlers are not sharing the same middleware as other routes, so we have
// to call them inside their body.
m.ServeHTTP(ctx)
ctx.Writef("Not Found")
})
app.Get("/", func(ctx iris.Context) {
sleep := rand.Intn(4999) + 1
time.Sleep(time.Duration(sleep) * time.Millisecond)
ctx.Writef("Slept for %d milliseconds", sleep)
})
app.Get("/metrics", iris.FromStd(promhttp.Handler()))
// http://localhost:8080/
// http://localhost:8080/anotfound
// http://localhost:8080/metrics
app.Run(iris.Addr(":8080"))
}

View File

@ -0,0 +1,38 @@
package main
import (
"github.com/kataras/iris"
"github.com/iris-contrib/middleware/secure"
)
func main() {
s := secure.New(secure.Options{
AllowedHosts: []string{"ssl.example.com"}, // AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
SSLRedirect: true, // If SSLRedirect is set to true, then only allow HTTPS requests. Default is false.
SSLTemporaryRedirect: false, // If SSLTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
SSLHost: "ssl.example.com", // SSLHost is the host name that is used to redirect HTTP requests to HTTPS. Default is "", which indicates to use the same host.
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"}, // SSLProxyHeaders is set of header keys with associated values that would indicate a valid HTTPS request. Useful when using Nginx: `map[string]string{"X-Forwarded-Proto": "https"}`. Default is blank map.
STSSeconds: 315360000, // STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
STSIncludeSubdomains: true, // If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
STSPreload: true, // If STSPreload is set to true, the `preload` flag will be appended to the Strict-Transport-Security header. Default is false.
ForceSTSHeader: false, // STS header is only included when the connection is HTTPS. If you want to force it to always be added, set to true. `IsDevelopment` still overrides this. Default is false.
FrameDeny: true, // If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is false.
CustomFrameOptionsValue: "SAMEORIGIN", // CustomFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option.
ContentTypeNosniff: true, // If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is false.
BrowserXSSFilter: true, // If BrowserXssFilter is true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is false.
ContentSecurityPolicy: "default-src 'self'", // ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
PublicKey: `pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubdomains; report-uri="https://www.example.com/hpkp-report"`, // PublicKey implements HPKP to prevent MITM attacks with forged certificates. Default is "".
IsDevelopment: true, // This will cause the AllowedHosts, SSLRedirect, and STSSeconds/STSIncludeSubdomains options to be ignored during development. When deploying to production, be sure to set this to false.
})
app := iris.New()
app.Use(s.Serve)
app.Get("/home", func(ctx iris.Context) {
ctx.Writef("Hello from /home")
})
app.Run(iris.Addr(":8080"))
}

View File

@ -0,0 +1,31 @@
package main
import (
"github.com/kataras/iris"
"github.com/didip/tollbooth"
"github.com/iris-contrib/middleware/tollboothic"
)
// $ go get github.com/didip/tollbooth
// $ go run main.go
func main() {
app := iris.New()
limiter := tollbooth.NewLimiter(1, nil)
//
// or create a limiter with expirable token buckets
// This setting means:
// create a 1 request/second limiter and
// every token bucket in it will expire 1 hour after it was initially set.
// limiter := tollbooth.NewLimiter(1, &limiter.ExpirableOptions{DefaultExpirationTTL: time.Hour})
app.Get("/", tollboothic.LimitHandler(limiter), func(ctx iris.Context) {
ctx.HTML("<b>Hello, world!</b>")
})
app.Run(iris.Addr(":8080"))
}
// Read more at: https://github.com/didip/tollbooth

View File

@ -4,5 +4,9 @@ package httptest
import "github.com/gavv/httpexpect" import "github.com/gavv/httpexpect"
// Request type alias. type (
type Request = httpexpect.Request // Request type alias.
Request = httpexpect.Request
// Expect type alias.
Expect = httpexpect.Expect
)