Moved middleware into own package
This commit is contained in:
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
Reference in New Issue
Block a user