permission: simplify permission checking logic

- add unit tests
- minor enhancements on handling JSON unmarshalling
This commit is contained in:
Trung Nguyen 2019-08-14 11:55:42 -04:00
parent 1f0da48be6
commit d3c2c59b94
No known key found for this signature in database
GPG Key ID: 4636434ED9505EB7
8 changed files with 119 additions and 74 deletions

View File

@ -20,9 +20,6 @@ import (
"bufio"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/permission"
"io"
"os"
"reflect"
@ -162,8 +159,8 @@ func makeFullNode(ctx *cli.Context) *node.Node {
ethChan := utils.RegisterEthService(stack, &cfg.Eth)
if utils.IsPermissionEnabled(cfg.Node.DataDir, cfg.Node.EnableNodePermission) {
RegisterPermissionService(ctx, stack)
if cfg.Node.IsPermissionEnabled() {
utils.RegisterPermissionService(ctx, stack)
}
if ctx.GlobalBool(utils.RaftModeFlag.Name) {
@ -197,28 +194,6 @@ func makeFullNode(ctx *cli.Context) *node.Node {
return stack
}
func RegisterPermissionService(ctx *cli.Context, stack *node.Node) {
if err := stack.Register(func(sctx *node.ServiceContext) (node.Service, error) {
dataDir := ctx.GlobalString(utils.DataDirFlag.Name)
var permissionConfig types.PermissionConfig
var err error
if permissionConfig, err = permission.ParsePermissionConifg(dataDir); err != nil {
utils.Fatalf("loading of permission-config.json failed", "error", err)
}
// start the permissions management service
pc, err := permission.NewQuorumPermissionCtrl(stack, &permissionConfig)
if err != nil {
utils.Fatalf("Failed to load the permission contracts as given in permissions-config.json %v", err)
}
log.Info("permission service created")
return pc, nil
}); err != nil {
utils.Fatalf("Failed to register the permission service: %v", err)
}
log.Info("permission service registered")
}
// dumpConfig is the dumpconfig command.
func dumpConfig(ctx *cli.Context) error {
_, cfg := makeConfigNode(ctx)

View File

@ -344,7 +344,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
}()
//initialize permission as we can create eth client only after the node and RPC are started
if utils.IsPermissionEnabled(stack.Server().DataDir, stack.Server().EnableNodePermission) {
if stack.IsPermissionEnabled() {
permission.StartPermissionService(stack)
}

View File

@ -20,11 +20,9 @@ package utils
import (
"compress/gzip"
"fmt"
"github.com/ethereum/go-ethereum/params"
"io"
"os"
"os/signal"
"path/filepath"
"runtime"
"strings"
"syscall"
@ -65,18 +63,6 @@ func Fatalf(format string, args ...interface{}) {
os.Exit(1)
}
func IsPermissionEnabled(dataDir string, permissionFlag bool) bool {
if permissionFlag {
fullPath := filepath.Join(dataDir, params.PERMISSION_MODEL_CONFIG)
if _, err := os.Stat(fullPath); err != nil {
log.Warn("permission-config.json file is missing. permission service will be disabled", "err", err)
return false
}
return true
}
return false
}
func StartNode(stack *node.Node) {
if err := stack.Start(); err != nil {

View File

@ -27,6 +27,8 @@ import (
"strconv"
"strings"
"github.com/ethereum/go-ethereum/permission"
"time"
"github.com/ethereum/go-ethereum/accounts"
@ -1390,6 +1392,27 @@ func RegisterEthStatsService(stack *node.Node, url string) {
}
}
// Quorum
//
// Configure smart-contract-based permissioning service
func RegisterPermissionService(ctx *cli.Context, stack *node.Node) {
if err := stack.Register(func(sctx *node.ServiceContext) (node.Service, error) {
permissionConfig, err := permission.ParsePermissionConfig(stack.DataDir())
if err != nil {
Fatalf("loading of %s failed due to %v", params.PERMISSION_MODEL_CONFIG, err)
}
// start the permissions management service
pc, err := permission.NewQuorumPermissionCtrl(stack, &permissionConfig)
if err != nil {
Fatalf("Failed to load the permission contracts as given in %s due to %v", params.PERMISSION_MODEL_CONFIG, err)
}
return pc, nil
}); err != nil {
Fatalf("Failed to register the permission service: %v", err)
}
log.Info("permission service registered")
}
func SetupMetrics(ctx *cli.Context) {
if metrics.Enabled {
log.Info("Enabling metrics collection")

View File

@ -25,6 +25,8 @@ import (
"runtime"
"strings"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/accounts/usbwallet"
@ -406,6 +408,21 @@ func (c *Config) AccountConfig() (int, int, string, error) {
return scryptN, scryptP, keydir, err
}
// Quorum
//
// check if smart-contract-based permissioning is enabled by reading `--permissioned` flag and checking permission config file
func (c *Config) IsPermissionEnabled() bool {
if c.EnableNodePermission {
fullPath := filepath.Join(c.DataDir, params.PERMISSION_MODEL_CONFIG)
if _, err := os.Stat(fullPath); err != nil {
log.Warn(fmt.Sprintf("%s file is missing. Smart-contract-based permission service will be disabled", params.PERMISSION_MODEL_CONFIG), "error", err)
return false
}
return true
}
return false
}
func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
scryptN, scryptP, keydir, err := conf.AccountConfig()
var ephemeral string

View File

@ -20,10 +20,14 @@ import (
"bytes"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime"
"testing"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/assert"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p"
)
@ -148,3 +152,45 @@ func TestNodeKeyPersistency(t *testing.T) {
t.Fatalf("ephemeral node key persisted to disk")
}
}
// Quorum
//
func TestConfig_IsPermissionEnabled_whenTypical(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "q-")
if err != nil {
t.Fatal(err)
}
defer func() {
_ = os.RemoveAll(tmpdir)
}()
if err := ioutil.WriteFile(path.Join(tmpdir, params.PERMISSION_MODEL_CONFIG), []byte("foo"), 0644); err != nil {
t.Fatal(err)
}
testObject := &Config{
EnableNodePermission: true,
DataDir: tmpdir,
}
assert.True(t, testObject.IsPermissionEnabled())
}
// Quorum
//
func TestConfig_IsPermissionEnabled_whenPermissionedFlagIsFalse(t *testing.T) {
testObject := &Config{
EnableNodePermission: false,
}
assert.False(t, testObject.IsPermissionEnabled())
}
// Quorum
//
func TestConfig_IsPermissionEnabled_whenPermissionConfigIsNotAvailable(t *testing.T) {
testObject := &Config{
EnableNodePermission: true,
DataDir: os.TempDir(),
}
assert.False(t, testObject.IsPermissionEnabled())
}

View File

@ -74,19 +74,6 @@ type Node struct {
log log.Logger
}
func (n *Node) GetRPC(name string) interface{} {
for _, v := range n.rpcAPIs {
if v.Namespace == name {
return v.Service
}
}
return nil
}
func (n *Node) GetNodeKey() *ecdsa.PrivateKey {
return n.config.NodeKey()
}
// New creates a new P2P node, ready for protocol registration.
func New(conf *Config) (*Node, error) {
// Copy config and resolve the datadir so future changes to the current
@ -542,6 +529,20 @@ func (n *Node) Service(service interface{}) error {
return ErrServiceUnknown
}
// Quorum
//
// delegate call to node.Config
func (n *Node) IsPermissionEnabled() bool {
return n.config.IsPermissionEnabled()
}
// Quorum
//
// delegate call to node.Config
func (n *Node) GetNodeKey() *ecdsa.PrivateKey {
return n.config.NodeKey()
}
// DataDir retrieves the current datadir used by the protocol stack.
// Deprecated: No files should be stored in this directory, use InstanceDir instead.
func (n *Node) DataDir() string {

View File

@ -4,9 +4,7 @@ import (
"crypto/ecdsa"
"encoding/json"
"errors"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/raft"
"github.com/ethereum/go-ethereum/rpc"
"fmt"
"io/ioutil"
"math/big"
"os"
@ -14,6 +12,10 @@ import (
"sync"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/raft"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
@ -101,34 +103,29 @@ func populateConfig(config PermissionLocalConfig) types.PermissionConfig {
// function reads the permissions config file passed and populates the
// config structure accordingly
func ParsePermissionConifg(dir string) (types.PermissionConfig, error) {
fileName := "permission-config.json"
fullPath := filepath.Join(dir, fileName)
if _, err := os.Stat(fullPath); err != nil {
log.Warn("permission-config.json file is missing", "err", err)
return types.PermissionConfig{}, err
}
blob, err := ioutil.ReadFile(fullPath)
func ParsePermissionConfig(dir string) (types.PermissionConfig, error) {
fullPath := filepath.Join(dir, params.PERMISSION_MODEL_CONFIG)
f, err := os.Open(fullPath)
if err != nil {
log.Error("error reading permission-config.json file", err)
log.Error("can't open file", "file", fullPath, "error", err)
return types.PermissionConfig{}, err
}
defer func() {
_ = f.Close()
}()
var permlocConfig PermissionLocalConfig
err = json.Unmarshal(blob, &permlocConfig)
if err != nil {
log.Error("error unmarshalling permission-config.json file", err)
if err := json.NewDecoder(f).Decode(&permlocConfig); err != nil {
log.Error("error unmarshalling file", "file", fullPath, "error", err)
return types.PermissionConfig{}, err
}
permConfig := populateConfig(permlocConfig)
if len(permConfig.Accounts) == 0 {
return types.PermissionConfig{}, errors.New("no accounts given in permission-config.json. Network cannot boot up")
return types.PermissionConfig{}, fmt.Errorf("no accounts given in %s. Network cannot boot up", params.PERMISSION_MODEL_CONFIG)
}
if permConfig.SubOrgDepth.Cmp(big.NewInt(0)) == 0 || permConfig.SubOrgBreadth.Cmp(big.NewInt(0)) == 0 {
return types.PermissionConfig{}, errors.New("sub org breadth depth not passed in permission-config.json. Network cannot boot up")
return types.PermissionConfig{}, fmt.Errorf("sub org breadth depth not passed in %s. Network cannot boot up", params.PERMISSION_MODEL_CONFIG)
}
return permConfig, nil