Moved middleware into own package

This commit is contained in:
Ethan Frey
2017-06-30 18:37:50 +02:00
parent 98e65124a1
commit 1caeec218f
3 changed files with 3 additions and 3 deletions
+104
View File
@@ -0,0 +1,104 @@
package stack
import (
"bytes"
"math/rand"
"github.com/pkg/errors"
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/types"
)
// store nonce as it's own type so no one can even try to fake it
type nonce int64
type secureContext struct {
id nonce
app string
perms []basecoin.Permission
}
func NewContext() basecoin.Context {
return secureContext{
id: nonce(rand.Int63()),
}
}
var _ basecoin.Context = secureContext{}
// WithPermissions will panic if they try to set permission without the proper app
func (c secureContext) WithPermissions(perms ...basecoin.Permission) basecoin.Context {
// the guard makes sure you only set permissions for the app you are inside
for _, p := range perms {
if p.App != c.app {
err := errors.Errorf("Cannot set permission for %s from %s", c.app, p.App)
panic(err)
}
}
return secureContext{
id: c.id,
perms: append(c.perms, perms...),
}
}
func (c secureContext) HasPermission(perm basecoin.Permission) bool {
for _, p := range c.perms {
if perm.App == p.App && bytes.Equal(perm.Address, p.Address) {
return true
}
}
return false
}
// IsParent ensures that this is derived from the given secureClient
func (c secureContext) IsParent(other basecoin.Context) bool {
so, ok := other.(secureContext)
if !ok {
return false
}
return c.id == so.id
}
// Reset should give a fresh context,
// but carry on knowledge that this is a child
func (c secureContext) Reset() basecoin.Context {
return secureContext{
id: c.id,
app: c.app,
}
}
// withApp is a private method that we can use to properly set the
// app controls in the middleware
func withApp(ctx basecoin.Context, app string) basecoin.Context {
sc, ok := ctx.(secureContext)
if !ok {
return ctx
}
return secureContext{
id: sc.id,
app: app,
perms: sc.perms,
}
}
func secureCheck(h basecoin.Checker, parent basecoin.Context) basecoin.Checker {
next := func(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
if !parent.IsParent(ctx) {
return res, errors.New("Passing in non-child Context")
}
return h.CheckTx(ctx, store, tx)
}
return basecoin.CheckerFunc(next)
}
func secureDeliver(h basecoin.Deliver, parent basecoin.Context) basecoin.Deliver {
next := func(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
if !parent.IsParent(ctx) {
return res, errors.New("Passing in non-child Context")
}
return h.DeliverTx(ctx, store, tx)
}
return basecoin.DeliverFunc(next)
}
+29
View File
@@ -0,0 +1,29 @@
package stack
import (
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/types"
)
const (
NameVoid = "void"
)
// voidHandler just used to return okay to everything
type voidHandler struct{}
var _ basecoin.Handler = voidHandler{}
func (_ voidHandler) Name() string {
return NameVoid
}
// CheckTx always returns an empty success tx
func (_ voidHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
return
}
// DeliverTx always returns an empty success tx
func (_ voidHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
return
}
+73
View File
@@ -0,0 +1,73 @@
package stack
import (
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/types"
)
// middleware lets us wrap a whole stack up into one Handler
//
// heavily inspired by negroni's design
type middleware struct {
middleware basecoin.Middleware
next basecoin.Handler
}
var _ basecoin.Handler = &middleware{}
func (m *middleware) Name() string {
return m.middleware.Name()
}
// CheckTx always returns an empty success tx
func (m *middleware) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (basecoin.Result, error) {
// make sure we pass in proper context to child
next := secureCheck(m.next, ctx)
// set the permissions for this app
ctx = withApp(ctx, m.Name())
return m.middleware.CheckTx(ctx, store, tx, next)
}
// DeliverTx always returns an empty success tx
func (m *middleware) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
// make sure we pass in proper context to child
next := secureDeliver(m.next, ctx)
// set the permissions for this app
ctx = withApp(ctx, m.Name())
return m.middleware.DeliverTx(ctx, store, tx, next)
}
// Stack is the entire application stack
type Stack struct {
middles []basecoin.Middleware
handler basecoin.Handler
basecoin.Handler // the compiled version, which we expose
}
var _ basecoin.Handler = &Stack{}
// NewStack prepares a middleware stack, you must `.Use()` a Handler
// before you can execute it.
func NewStack(middlewares ...basecoin.Middleware) *Stack {
return &Stack{
middles: middlewares,
}
}
// Use sets the final handler for the stack and prepares it for use
func (s *Stack) Use(handler basecoin.Handler) *Stack {
if handler == nil {
panic("Cannot have a Stack without an end handler")
}
s.handler = handler
s.Handler = build(s.middles, s.handler)
return s
}
func build(mid []basecoin.Middleware, end basecoin.Handler) basecoin.Handler {
if len(mid) == 0 {
return end
}
next := build(mid[1:], end)
return &middleware{mid[0], next}
}