forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Middleware system with secure auth contexts
- Loading branch information
Showing
7 changed files
with
258 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package basecoin | ||
|
||
import "github.com/tendermint/go-wire/data" | ||
|
||
type Permission struct { | ||
App string // Which app authorized this? | ||
Address data.Bytes // App-specific identifier | ||
} | ||
|
||
func NewPermission(app string, addr []byte) Permission { | ||
return Permission{App: app, Address: addr} | ||
} | ||
|
||
// Context is an interface, so we can implement "secure" variants that | ||
// rely on private fields to control the actions | ||
type Context interface { | ||
// context.Context | ||
WithPermissions(perms ...Permission) Context | ||
HasPermission(perm Permission) bool | ||
IsParent(ctx Context) bool | ||
Reset() Context | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package handlers | ||
|
||
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package handlers | ||
|
||
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} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package handlers | ||
|
||
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 | ||
} |