Skip to content

Commit

Permalink
wallet: Fip filter rule integration
Browse files Browse the repository at this point in the history
  • Loading branch information
magik6k committed Apr 10, 2022
1 parent a65cac8 commit ade6cec
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 10 deletions.
136 changes: 136 additions & 0 deletions cmd/lotus-wallet/filtered.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package main

import (
"bytes"
"context"
"github.com/ipfs/go-cid"
"golang.org/x/xerrors"

"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
)

type FilteredWallet struct {
under api.Wallet
mustAccept bool
}

func (c *FilteredWallet) WalletNew(ctx context.Context, typ types.KeyType) (address.Address, error) {
log.Infow("WalletNew", "type", typ)
// TODO FILTER!

return c.under.WalletNew(ctx, typ)
}

func (c *FilteredWallet) WalletHas(ctx context.Context, addr address.Address) (bool, error) {
log.Infow("WalletHas", "address", addr)
// TODO FILTER!

return c.under.WalletHas(ctx, addr)
}

func (c *FilteredWallet) WalletList(ctx context.Context) ([]address.Address, error) {
// TODO FILTER!

return c.under.WalletList(ctx)
}

func (c *FilteredWallet) WalletSign(ctx context.Context, k address.Address, msg []byte, meta api.MsgMeta) (*crypto.Signature, error) {
p, ok := ctx.Value(tokenKey).(jwtPayload)
if !ok {
return nil, xerrors.Errorf("jwt payload not set on request context")
}
if c.mustAccept && p.Rules == nil {
return nil, xerrors.Errorf("token with no rules")
}

var filterParams map[FilterParam]interface{}

switch meta.Type {
case api.MTChainMsg:
var cmsg types.Message
if err := cmsg.UnmarshalCBOR(bytes.NewReader(meta.Extra)); err != nil {
return nil, xerrors.Errorf("unmarshalling message: %w", err)
}

_, bc, err := cid.CidFromBytes(msg)
if err != nil {
return nil, xerrors.Errorf("getting cid from signing bytes: %w", err)
}

if !cmsg.Cid().Equals(bc) {
return nil, xerrors.Errorf("cid(meta.Extra).bytes() != msg")
}

filterParams = map[FilterParam]interface{}{
ParamAction: ActionSign,
ParamSignType: api.MTChainMsg,

ParamSource: cmsg.From,
ParamDestination: cmsg.To,
ParamValue: cmsg.Value,
ParamMethod: cmsg.Method,
ParamMaxFee: cmsg.RequiredFunds(),
}
case api.MTBlock:
// TODO FILTER PARAMS!
filterParams = map[FilterParam]interface{}{
ParamAction: ActionSign,
ParamSignType: api.MTBlock,
}
case api.MTDealProposal:
// TODO FILTER PARAMS!
filterParams = map[FilterParam]interface{}{
ParamAction: ActionSign,
ParamSignType: api.MTDealProposal,
}
default:
// TODO FILTER PARAMS!
filterParams = map[FilterParam]interface{}{
ParamAction: ActionSign,
ParamSignType: api.MTUnknown,
}
}

if p.Rules != nil {
f, err := ParseRule(ctx, *p.Rules)
if err != nil {
return nil, xerrors.Errorf("parsing rules: %w", err)
}

fres := f(filterParams)
switch fres {
case nil:
if c.mustAccept {
return nil, xerrors.Errorf("filter didn't accept")
}
fallthrough
case ErrAccept:

default:
return nil, xerrors.Errorf("filter error: %w", fres)
}
}

return c.under.WalletSign(ctx, k, msg, meta)
}

func (c *FilteredWallet) WalletExport(ctx context.Context, a address.Address) (*types.KeyInfo, error) {
log.Infow("WalletExport", "address", a)
// TODO FILTER!
return c.under.WalletExport(ctx, a)
}

func (c *FilteredWallet) WalletImport(ctx context.Context, ki *types.KeyInfo) (address.Address, error) {
log.Infow("WalletImport", "type", ki.Type)
// TODO FILTER!
return c.under.WalletImport(ctx, ki)
}

func (c *FilteredWallet) WalletDelete(ctx context.Context, addr address.Address) error {
log.Infow("WalletDelete", "address", addr)
// TODO FILTER!
return c.under.WalletDelete(ctx, addr)
}
73 changes: 69 additions & 4 deletions cmd/lotus-wallet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package main

import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"strings"
"time"

"github.com/filecoin-project/lotus/api/v0api"
Expand Down Expand Up @@ -99,16 +101,38 @@ To configure your lotus node to use a remote wallet:
var getApiKeyCmd = &cli.Command{
Name: "get-api-key",
Usage: "Generate API Key",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "rules",
Usage: "filtering rules",
},
},
Action: func(cctx *cli.Context) error {
lr, ks, err := openRepo(cctx)
if err != nil {
return err
}
defer lr.Close() // nolint

var rules Rule
if cctx.IsSet("rules") {
var r interface{}
if err := json.Unmarshal([]byte(cctx.String("rules")), &r); err != nil {
return xerrors.Errorf("unmarshalling rules: %w", err)
}

_, err := ParseRule(cctx.Context, r)
if err != nil {
return xerrors.Errorf("parsing rules: %w", err)
}

rules = &r
}

p := jwtPayload{
Allow: []auth.Permission{api.PermAdmin},
Created: time.Now(),
Rules: &rules,
}

authKey, err := modules.APISecret(ks, lr)
Expand All @@ -126,6 +150,10 @@ var getApiKeyCmd = &cli.Command{
},
}

type jwtTok struct{}

var tokenKey jwtTok

var runCmd = &cli.Command{
Name: "run",
Usage: "Start lotus wallet",
Expand All @@ -152,6 +180,10 @@ var runCmd = &cli.Command{
Usage: "(insecure) disable api auth",
Hidden: true,
},
&cli.BoolFlag{
Name: "rule-must-accept",
Usage: "require all operations to be accepted by rule filters",
},
},
Description: "For setup instructions see 'lotus-wallet --help'",
Action: func(cctx *cli.Context) error {
Expand Down Expand Up @@ -192,6 +224,11 @@ var runCmd = &cli.Command{
}
}

w = &FilteredWallet{
under: w,
mustAccept: cctx.Bool("rule-must-accept"),
}

address := cctx.String("listen")
mux := mux.NewRouter()

Expand Down Expand Up @@ -234,19 +271,47 @@ var runCmd = &cli.Command{
}

authVerify := func(ctx context.Context, token string) ([]auth.Permission, error) {
var payload jwtPayload
if _, err := jwt.Verify([]byte(token), (*jwt.HMACSHA)(authKey), &payload); err != nil {
return nil, xerrors.Errorf("JWT Verification failed: %w", err)
payload, ok := ctx.Value(tokenKey).(jwtPayload)
if !ok {
return nil, xerrors.Errorf("jwt payload not set on request context")
}

return payload.Allow, nil
}

log.Info("API auth enabled, use 'lotus-wallet get-api-key' to get API key")
handler = &auth.Handler{
ah := &auth.Handler{
Verify: authVerify,
Next: mux.ServeHTTP,
}

handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
token = r.FormValue("token")
if token != "" {
token = "Bearer " + token
}
}

if token != "" {
if !strings.HasPrefix(token, "Bearer ") {
log.Warn("missing Bearer prefix in auth header")
w.WriteHeader(401)
return
}
token = strings.TrimPrefix(token, "Bearer ")

var payload jwtPayload
if _, err := jwt.Verify([]byte(token), (*jwt.HMACSHA)(authKey), &payload); err != nil {
http.Error(w, fmt.Sprintf("JWT Verification failed: %s", err), http.StatusForbidden)
}

r = r.Clone(context.WithValue(r.Context(), tokenKey, payload))
}

ah.ServeHTTP(w, r)
})
}

srv := &http.Server{
Expand Down
12 changes: 6 additions & 6 deletions cmd/lotus-wallet/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func finalRule(err error) func(ctx context.Context, r Rule) (Filter, error) {
}

if len(rules) != 0 {
return nil, xerrors.Errorf("final rule must be an empty map", rules)
return nil, xerrors.Errorf("final rule must be an empty map, had %d elements", rules)
}

return func(params map[FilterParam]interface{}) error {
Expand Down Expand Up @@ -157,11 +157,11 @@ func AnyAccepts(ctx context.Context, r Rule) (Filter, error) {
func ParseRule(ctx context.Context, r Rule) (Filter, error) {
rules, ok := r.(map[string]interface{})
if !ok {
return nil, xerrors.Errorf("expected rule to be a map")
return nil, xerrors.Errorf("expected rule to be a map, was %T", r)
}

if len(rules) != 1 {
return nil, xerrors.Errorf("expected one rule, had %d", rules)
return nil, xerrors.Errorf("expected one rule, had %d", len(rules))
}

for p, r := range rules {
Expand Down Expand Up @@ -196,7 +196,7 @@ func Sign(ctx context.Context, r Rule) (Filter, error) {

sub, err := ParseRule(ctx, r)
if err != nil {
return nil, xerrors.Errorf("parsing next rule %d: %w", err)
return nil, xerrors.Errorf("parsing next rule: %w", err)
}

return func(params map[FilterParam]interface{}) error {
Expand All @@ -215,7 +215,7 @@ func Message(ctx context.Context, r Rule) (Filter, error) {

sub, err := ParseRule(ctx, r)
if err != nil {
return nil, xerrors.Errorf("parsing next rule %d: %w", err)
return nil, xerrors.Errorf("parsing next rule: %w", err)
}

return func(params map[FilterParam]interface{}) error {
Expand Down Expand Up @@ -249,7 +249,7 @@ func DealProposal(ctx context.Context, r Rule) (Filter, error) {

sub, err := ParseRule(ctx, r)
if err != nil {
return nil, xerrors.Errorf("parsing next rule %d: %w", err)
return nil, xerrors.Errorf("parsing next rule: %w", err)
}

return func(params map[FilterParam]interface{}) error {
Expand Down

0 comments on commit ade6cec

Please sign in to comment.