From b9ce148e8e5d0ba16ae4b591ad8ac31706022520 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 29 Jun 2017 20:02:38 +0200 Subject: [PATCH] Add more middleware tests --- stack/chain_test.go | 4 ++- stack/context.go | 1 + stack/helpers.go | 25 ++++++++++++++ stack/helperware.go | 59 +++++++++++++++++++++++++++++++++ stack/middleware_test.go | 70 ++++++++++++++++++++++++++++++++++++++++ stack/recovery_test.go | 1 - 6 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 stack/helperware.go create mode 100644 stack/middleware_test.go diff --git a/stack/chain_test.go b/stack/chain_test.go index faa37acca..b41bd0b57 100644 --- a/stack/chain_test.go +++ b/stack/chain_test.go @@ -5,10 +5,12 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/tendermint/tmlibs/log" + "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/txs" "github.com/tendermint/basecoin/types" - "github.com/tendermint/tmlibs/log" ) func TestChain(t *testing.T) { diff --git a/stack/context.go b/stack/context.go index 60b13b81c..09703b944 100644 --- a/stack/context.go +++ b/stack/context.go @@ -35,6 +35,7 @@ var _ basecoin.Context = secureContext{} func (c secureContext) WithPermissions(perms ...basecoin.Actor) basecoin.Context { // the guard makes sure you only set permissions for the app you are inside for _, p := range perms { + // TODO: also check chainID, limit only certain middleware can set IBC? if p.App != c.app { err := errors.Errorf("Cannot set permission for %s from %s", c.app, p.App) panic(err) diff --git a/stack/helpers.go b/stack/helpers.go index dc81e3e8f..ec7247a6f 100644 --- a/stack/helpers.go +++ b/stack/helpers.go @@ -2,6 +2,9 @@ package stack import ( "github.com/pkg/errors" + + "github.com/tendermint/go-wire/data" + "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/types" ) @@ -10,6 +13,7 @@ const ( NameOK = "ok" NameFail = "fail" NamePanic = "panic" + NameEcho = "echo" ) // OKHandler just used to return okay to everything @@ -33,6 +37,27 @@ func (ok OKHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx base return basecoin.Result{Log: ok.Log}, nil } +// EchoHandler returns success, echoing res.Data = tx bytes +type EchoHandler struct{} + +var _ basecoin.Handler = EchoHandler{} + +func (_ EchoHandler) Name() string { + return NameEcho +} + +// CheckTx always returns an empty success tx +func (_ EchoHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) { + data, err := data.ToWire(tx) + return basecoin.Result{Data: data}, err +} + +// DeliverTx always returns an empty success tx +func (_ EchoHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) { + data, err := data.ToWire(tx) + return basecoin.Result{Data: data}, err +} + // FailHandler always returns an error type FailHandler struct { Err error diff --git a/stack/helperware.go b/stack/helperware.go new file mode 100644 index 000000000..15201b892 --- /dev/null +++ b/stack/helperware.go @@ -0,0 +1,59 @@ +package stack + +import ( + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/errors" + "github.com/tendermint/basecoin/types" +) + +const ( + NameCheck = "chck" + NameGrant = "grnt" +) + +// CheckMiddleware returns an error if the tx doesn't have auth of this +// Required Actor, otherwise passes along the call untouched +type CheckMiddleware struct { + Required basecoin.Actor +} + +var _ Middleware = CheckMiddleware{} + +func (_ CheckMiddleware) Name() string { + return NameCheck +} + +func (p CheckMiddleware) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) { + if !ctx.HasPermission(p.Required) { + return res, errors.Unauthorized() + } + return next.CheckTx(ctx, store, tx) +} + +func (p CheckMiddleware) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) { + if !ctx.HasPermission(p.Required) { + return res, errors.Unauthorized() + } + return next.DeliverTx(ctx, store, tx) +} + +// GrantMiddleware tries to set the permission to this Actor, which may be prohibited +type GrantMiddleware struct { + Auth basecoin.Actor +} + +var _ Middleware = GrantMiddleware{} + +func (_ GrantMiddleware) Name() string { + return NameGrant +} + +func (g GrantMiddleware) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) { + ctx = ctx.WithPermissions(g.Auth) + return next.CheckTx(ctx, store, tx) +} + +func (g GrantMiddleware) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) { + ctx = ctx.WithPermissions(g.Auth) + return next.DeliverTx(ctx, store, tx) +} diff --git a/stack/middleware_test.go b/stack/middleware_test.go new file mode 100644 index 000000000..82f802ebf --- /dev/null +++ b/stack/middleware_test.go @@ -0,0 +1,70 @@ +package stack + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/errors" + "github.com/tendermint/basecoin/txs" + "github.com/tendermint/basecoin/types" + "github.com/tendermint/go-wire/data" + "github.com/tendermint/tmlibs/log" +) + +func TestPermissionSandbox(t *testing.T) { + require := require.New(t) + + // generic args + ctx := NewContext(log.NewNopLogger()) + store := types.NewMemKVStore() + raw := txs.NewRaw([]byte{1, 2, 3, 4}).Wrap() + rawBytes, err := data.ToWire(raw) + require.Nil(err) + + // test cases to make sure permissioning is solid + grantee := basecoin.Actor{App: NameGrant, Address: []byte{1}} + grantee2 := basecoin.Actor{App: NameGrant, Address: []byte{2}} + signer := basecoin.Actor{App: NameSigs, Address: []byte{1}} + cases := []struct { + grant basecoin.Actor + require basecoin.Actor + expectedRes data.Bytes + expectedErr error + }{ + {grantee, grantee, rawBytes, nil}, + {grantee, grantee2, nil, errors.Unauthorized()}, + {grantee, signer, nil, errors.Unauthorized()}, + {signer, signer, nil, errors.InternalError("panic")}, + } + + for i, tc := range cases { + app := New( + Recovery{}, // we need this so panics turn to errors + GrantMiddleware{tc.grant}, + CheckMiddleware{tc.require}, + ).Use(EchoHandler{}) + + res, err := app.CheckTx(ctx, store, raw) + checkPerm(t, i, tc.expectedRes, tc.expectedErr, res, err) + + res, err = app.DeliverTx(ctx, store, raw) + checkPerm(t, i, tc.expectedRes, tc.expectedErr, res, err) + } +} + +func checkPerm(t *testing.T, idx int, data []byte, expected error, res basecoin.Result, err error) { + assert := assert.New(t) + + if expected == nil { + assert.Nil(err, "%d: %+v", idx, err) + assert.EqualValues(data, res.Data) + } else { + assert.NotNil(err, "%d", idx) + // check error code! + shouldCode := errors.Wrap(expected).ErrorCode() + isCode := errors.Wrap(err).ErrorCode() + assert.Equal(shouldCode, isCode, "%d: %+v", idx, err) + } +} diff --git a/stack/recovery_test.go b/stack/recovery_test.go index 6d646c19b..58a6ba66a 100644 --- a/stack/recovery_test.go +++ b/stack/recovery_test.go @@ -48,5 +48,4 @@ func TestRecovery(t *testing.T) { } } - }