auth: Put auth methods in API

This commit is contained in:
Łukasz Magiera 2019-07-23 19:27:45 +02:00
parent fa4bf5178a
commit 309ecc4052
9 changed files with 78 additions and 31 deletions

View File

@ -33,6 +33,10 @@ type MsgWait struct {
// API is a low-level interface to the Filecoin network // API is a low-level interface to the Filecoin network
type API interface { type API interface {
// Auth
AuthVerify(ctx context.Context, token string) ([]string, error)
AuthNew(ctx context.Context, perms []string) ([]byte, error)
// chain // chain
ChainHead(context.Context) (*chain.TipSet, error) // TODO: check serialization ChainHead(context.Context) (*chain.TipSet, error) // TODO: check serialization

View File

@ -17,6 +17,7 @@ const (
// todo: more perms once needed (network / sign / call/invoke / miner / etc) // todo: more perms once needed (network / sign / call/invoke / miner / etc)
) )
var AllPermissions = []string{PermRead, PermWrite}
var defaultPerms = []string{PermRead} var defaultPerms = []string{PermRead}
func WithPerm(ctx context.Context, perms []string) context.Context { func WithPerm(ctx context.Context, perms []string) context.Context {

View File

@ -14,6 +14,9 @@ import (
// Struct implements API passing calls to user-provided function values. // Struct implements API passing calls to user-provided function values.
type Struct struct { type Struct struct {
Internal struct { Internal struct {
AuthVerify func(ctx context.Context, token string) ([]string, error)
AuthNew func(ctx context.Context, perms []string) ([]byte, error) `perm:"write"`
ID func(context.Context) (peer.ID, error) ID func(context.Context) (peer.ID, error)
Version func(context.Context) (Version, error) Version func(context.Context) (Version, error)
@ -46,6 +49,14 @@ type Struct struct {
} }
} }
func (c *Struct) AuthVerify(ctx context.Context, token string) ([]string, error) {
return c.Internal.AuthVerify(ctx, token)
}
func (c *Struct) AuthNew(ctx context.Context, perms []string) ([]byte, error) {
return c.Internal.AuthNew(ctx, perms)
}
func (c *Struct) ClientListImports(ctx context.Context) ([]Import, error) { func (c *Struct) ClientListImports(ctx context.Context) ([]Import, error) {
return c.Internal.ClientListImports(ctx) return c.Internal.ClientListImports(ctx)
} }

View File

@ -1,16 +1,16 @@
package types package types
// KeyInfo is used for storying keys in KeyStore // KeyInfo is used for storing keys in KeyStore
type KeyInfo struct { type KeyInfo struct {
Type string Type string
PrivateKey []byte PrivateKey []byte
} }
// KeyStore is used for storying secret keys // KeyStore is used for storing secret keys
type KeyStore interface { type KeyStore interface {
// List lists all the keys stored in the KeyStore // List lists all the keys stored in the KeyStore
List() ([]string, error) List() ([]string, error)
// Get gets a key out of keystore and returns KeyInfo coresponding to named key // Get gets a key out of keystore and returns KeyInfo corresponding to named key
Get(string) (KeyInfo, error) Get(string) (KeyInfo, error)
// Put saves a key info under given name // Put saves a key info under given name
Put(string, KeyInfo) error Put(string, KeyInfo) error

View File

@ -6,7 +6,6 @@ import (
"context" "context"
"github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multiaddr"
"go.uber.org/fx"
"gopkg.in/urfave/cli.v2" "gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/go-lotus/node" "github.com/filecoin-project/go-lotus/node"
@ -45,16 +44,12 @@ var Cmd = &cli.Command{
} }
return lr.SetAPIEndpoint(apima) return lr.SetAPIEndpoint(apima)
}), }),
node.Override(node.ServeRPCKey, func(lc fx.Lifecycle) error {
}),
) )
if err != nil { if err != nil {
return err return err
} }
// TODO: properly parse api endpoint (or make it a URL) // TODO: properly parse api endpoint (or make it a URL)
return serveRPC(api, "127.0.0.1:"+cctx.String("api")) return serveRPC(api, "127.0.0.1:"+cctx.String("api"), api.AuthVerify)
}, },
} }

View File

@ -1,20 +1,20 @@
package daemon package daemon
import ( import (
"context"
"github.com/filecoin-project/go-lotus/lib/auth" "github.com/filecoin-project/go-lotus/lib/auth"
"github.com/gbrlsnchs/jwt/v3"
"net/http" "net/http"
"github.com/filecoin-project/go-lotus/api" "github.com/filecoin-project/go-lotus/api"
"github.com/filecoin-project/go-lotus/lib/jsonrpc" "github.com/filecoin-project/go-lotus/lib/jsonrpc"
) )
func serveRPC(a api.API, addr string, authSecret []byte) error { func serveRPC(a api.API, addr string, verify func(ctx context.Context, token string) ([]string, error)) error {
rpcServer := jsonrpc.NewServer() rpcServer := jsonrpc.NewServer()
rpcServer.Register("Filecoin", api.Permissioned(a)) rpcServer.Register("Filecoin", api.Permissioned(a))
authHandler := &auth.Handler{ authHandler := &auth.Handler{
Secret: jwt.NewHS256(authSecret), Verify: verify,
Next: rpcServer.ServeHTTP, Next: rpcServer.ServeHTTP,
} }

View File

@ -1,18 +1,18 @@
package auth package auth
import ( import (
"context"
"net/http" "net/http"
"strings" "strings"
"github.com/filecoin-project/go-lotus/api" "github.com/filecoin-project/go-lotus/api"
"github.com/gbrlsnchs/jwt/v3"
logging "github.com/ipfs/go-log" logging "github.com/ipfs/go-log"
) )
var log = logging.Logger("auth") var log = logging.Logger("auth")
type Handler struct { type Handler struct {
Secret *jwt.HMACSHA Verify func(ctx context.Context, token string) ([]string, error)
Next http.HandlerFunc Next http.HandlerFunc
} }
@ -28,28 +28,15 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
token = token[len("Bearer "):] token = token[len("Bearer "):]
var payload jwtPayload allow, err := h.Verify(ctx, token)
if _, err := jwt.Verify([]byte(token), h.Secret, &payload); err != nil { if err != nil {
log.Warnf("JWT Verification failed: %s", err) log.Warnf("JWT Verification failed: %s", err)
w.WriteHeader(401) w.WriteHeader(401)
return return
} }
ctx = api.WithPerm(ctx, payload.Allow) ctx = api.WithPerm(ctx, allow)
} }
h.Next(w, r.WithContext(ctx)) h.Next(w, r.WithContext(ctx))
} }
type jwtPayload struct {
Allow []string
}
/*func init() {
p := jwtPayload{
Allow: []string{"read", "write"},
}
r, _ := jwt.Sign(&p, secret)
log.Infof("WRITE TOKEN: %s", string(r))
}
*/

View File

@ -11,13 +11,18 @@ import (
"github.com/filecoin-project/go-lotus/miner" "github.com/filecoin-project/go-lotus/miner"
"github.com/filecoin-project/go-lotus/node/client" "github.com/filecoin-project/go-lotus/node/client"
"github.com/gbrlsnchs/jwt/v3"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log"
"github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub"
ma "github.com/multiformats/go-multiaddr" ma "github.com/multiformats/go-multiaddr"
"golang.org/x/xerrors"
) )
var log = logging.Logger("node")
type API struct { type API struct {
client.LocalStorage client.LocalStorage
@ -26,6 +31,51 @@ type API struct {
PubSub *pubsub.PubSub PubSub *pubsub.PubSub
Mpool *chain.MessagePool Mpool *chain.MessagePool
Wallet *chain.Wallet Wallet *chain.Wallet
Keystore types.KeyStore
}
const JWTSecretName = "auth-jwt-private"
type jwtPayload struct {
Allow []string
}
func (a *API) AuthVerify(ctx context.Context, token string) ([]string, error) {
key, err := a.Keystore.Get(JWTSecretName)
if err != nil {
return nil, xerrors.Errorf("couldn't get JWT secret: %w", err)
}
var payload jwtPayload
if _, err := jwt.Verify([]byte(token), jwt.NewHS256(key.PrivateKey), &payload); err != nil {
return nil, xerrors.Errorf("JWT Verification failed: %w", err)
}
return payload.Allow, nil
}
func (a *API) AuthNew(ctx context.Context, perms []string) ([]byte, error) {
key, err := a.Keystore.Get(JWTSecretName)
if err != nil {
log.Warn("Generating new API secret")
key = types.KeyInfo{
Type: "jwt-hmac-secret",
PrivateKey: make([]byte, 32),
}
if err := a.Keystore.Put(JWTSecretName, key); err != nil {
return nil, xerrors.Errorf("writing API secret: %w", err)
}
// TODO: put cli token in repo
}
p := jwtPayload{
Allow: perms, // TODO: consider checking validity
}
return jwt.Sign(&p, jwt.NewHS256(key.PrivateKey))
} }
func (a *API) ChainSubmitBlock(ctx context.Context, blk *chain.BlockMsg) error { func (a *API) ChainSubmitBlock(ctx context.Context, blk *chain.BlockMsg) error {

View File

@ -72,7 +72,6 @@ const (
// daemon // daemon
SetApiEndpointKey SetApiEndpointKey
ServeRPCKey
_nInvokes // keep this last _nInvokes // keep this last
) )