From e63c9158c3991dadc8bd6c3930c1603fedfb89ec Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Mon, 3 Apr 2017 15:25:22 +0200 Subject: [PATCH] [FAB-2969] Access control at CSCC This change-set does the following: 1. Add access control to cscc by verifying that the caller has appropriate rights. Tests have been modified to cover this access control This change-set comes in the context of: 1. https://jira.hyperledger.org/browse/FAB-2969 Change-Id: If8698026a81b5402b5336c83ec553a8c39f2d8e0 Signed-off-by: Angelo De Caro --- core/chaincode/exectransaction_test.go | 263 +++++++++++++------------ core/peer/peer.go | 24 ++- core/policy/mocks.go | 3 +- core/policy/policy_test.go | 5 +- core/scc/cscc/configure.go | 66 ++++++- core/scc/cscc/configure_test.go | 93 +++++++-- events/producer/handler.go | 2 +- msp/mgmt/principal.go | 12 +- 8 files changed, 306 insertions(+), 162 deletions(-) diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index 0d0f2274b0e..576a4e751b5 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -17,7 +17,6 @@ limitations under the License. package chaincode import ( - "encoding/json" "fmt" "math/rand" "net" @@ -25,11 +24,13 @@ import ( "path/filepath" "runtime" "strconv" - "strings" "sync" "testing" "time" + "encoding/json" + "strings" + "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/common/util" "github.com/hyperledger/fabric/core/common/ccprovider" @@ -497,27 +498,6 @@ func _(chainID string, _ string) error { return nil } -// Test deploy of a transaction -func TestExecuteDeployTransaction(t *testing.T) { - //chaincoe is deployed as part of many tests. No need for a separate one for this - t.Skip() - chainID := util.GetTestChainID() - - executeDeployTransaction(t, chainID, "example01", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01") -} - -// Test deploy of a transaction with a GOPATH with multiple elements -func TestGopathExecuteDeployTransaction(t *testing.T) { - //this is no longer critical as chaincode is assembled in the client side (SDK) - t.Skip() - chainID := util.GetTestChainID() - - // add a trailing slash to GOPATH - // and a couple of elements - it doesn't matter what they are - os.Setenv("GOPATH", os.Getenv("GOPATH")+string(os.PathSeparator)+string(os.PathListSeparator)+"/tmp/foo"+string(os.PathListSeparator)+"/tmp/bar") - executeDeployTransaction(t, chainID, "example01", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01") -} - // Disable this temporarily. // TODO: Need to enable this after update chaincode interface of chaincode repo. // Test deploy of a transaction with a chaincode over HTTP. @@ -634,6 +614,118 @@ const ( chaincodeExample06JavaPath = "../../examples/chaincode/java/chaincode_example06" ) +func runChaincodeInvokeChaincode(t *testing.T, chainID string, _ string) (err error) { + var ctxt = context.Background() + + // Deploy first chaincode + url1 := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02" + + cID1 := &pb.ChaincodeID{Name: "example02", Path: url1, Version: "0"} + f := "init" + args := util.ToChaincodeArgs(f, "a", "100", "b", "200") + + spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID1, Input: &pb.ChaincodeInput{Args: args}} + + cccid1 := ccprovider.NewCCContext(chainID, "example02", "0", "", false, nil, nil) + + var nextBlockNumber uint64 + + _, err = deploy(ctxt, cccid1, spec1, nextBlockNumber) + nextBlockNumber++ + ccID1 := spec1.ChaincodeId.Name + if err != nil { + t.Fail() + t.Logf("Error initializing chaincode %s(%s)", ccID1, err) + theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) + return + } + + t.Logf("deployed chaincode_example02 got cID1:% s,\n ccID1:% s", cID1, ccID1) + + time.Sleep(time.Second) + + // Deploy second chaincode + url2 := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example04" + + cID2 := &pb.ChaincodeID{Name: "example04", Path: url2, Version: "0"} + f = "init" + args = util.ToChaincodeArgs(f, "e", "0") + + spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID2, Input: &pb.ChaincodeInput{Args: args}} + + cccid2 := ccprovider.NewCCContext(chainID, "example04", "0", "", false, nil, nil) + + _, err = deploy(ctxt, cccid2, spec2, nextBlockNumber) + nextBlockNumber++ + ccID2 := spec2.ChaincodeId.Name + if err != nil { + t.Fail() + t.Logf("Error initializing chaincode %s(%s)", ccID2, err) + theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) + theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) + return + } + + time.Sleep(time.Second) + + // Invoke second chaincode passing the first chaincode's name as first param, + // which will inturn invoke the first chaincode + f = "invoke" + cid := spec1.ChaincodeId.Name + args = util.ToChaincodeArgs(f, cid, "e", "1") + + spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID2, Input: &pb.ChaincodeInput{Args: args}} + // Invoke chaincode + var uuid string + _, uuid, _, err = invoke(ctxt, chainID, spec2, nextBlockNumber) + + if err != nil { + t.Fail() + t.Logf("Error invoking <%s>: %s", ccID2, err) + theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) + theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) + return + } + + cccid1.TxID = uuid + + // Check the state in the ledger + err = checkFinalState(cccid1) + if err != nil { + t.Fail() + t.Logf("Incorrect final state after transaction for <%s>: %s", ccID1, err) + theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) + theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) + return + } + + theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) + theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) + + return +} + +// Test deploy of a transaction +func TestExecuteDeployTransaction(t *testing.T) { + //chaincoe is deployed as part of many tests. No need for a separate one for this + t.Skip() + chainID := util.GetTestChainID() + + executeDeployTransaction(t, chainID, "example01", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01") +} + +// Test deploy of a transaction with a GOPATH with multiple elements +func TestGopathExecuteDeployTransaction(t *testing.T) { + //this is no longer critical as chaincode is assembled in the client side (SDK) + t.Skip() + chainID := util.GetTestChainID() + + // add a trailing slash to GOPATH + // and a couple of elements - it doesn't matter what they are + os.Setenv("GOPATH", os.Getenv("GOPATH")+string(os.PathSeparator)+string(os.PathListSeparator)+"/tmp/foo"+string(os.PathListSeparator)+"/tmp/bar") + executeDeployTransaction(t, chainID, "example01", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01") +} + func TestExecuteInvokeTransaction(t *testing.T) { testCases := []struct { @@ -746,97 +838,6 @@ func TestChaincodeInvokeChaincode(t *testing.T) { closeListenerAndSleep(lis) } -func runChaincodeInvokeChaincode(t *testing.T, chainID string, _ string) (err error) { - var ctxt = context.Background() - - // Deploy first chaincode - url1 := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02" - - cID1 := &pb.ChaincodeID{Name: "example02", Path: url1, Version: "0"} - f := "init" - args := util.ToChaincodeArgs(f, "a", "100", "b", "200") - - spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID1, Input: &pb.ChaincodeInput{Args: args}} - - cccid1 := ccprovider.NewCCContext(chainID, "example02", "0", "", false, nil, nil) - - var nextBlockNumber uint64 - - _, err = deploy(ctxt, cccid1, spec1, nextBlockNumber) - nextBlockNumber++ - ccID1 := spec1.ChaincodeId.Name - if err != nil { - t.Fail() - t.Logf("Error initializing chaincode %s(%s)", ccID1, err) - theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) - return - } - - t.Logf("deployed chaincode_example02 got cID1:% s,\n ccID1:% s", cID1, ccID1) - - time.Sleep(time.Second) - - // Deploy second chaincode - url2 := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example04" - - cID2 := &pb.ChaincodeID{Name: "example04", Path: url2, Version: "0"} - f = "init" - args = util.ToChaincodeArgs(f, "e", "0") - - spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID2, Input: &pb.ChaincodeInput{Args: args}} - - cccid2 := ccprovider.NewCCContext(chainID, "example04", "0", "", false, nil, nil) - - _, err = deploy(ctxt, cccid2, spec2, nextBlockNumber) - nextBlockNumber++ - ccID2 := spec2.ChaincodeId.Name - if err != nil { - t.Fail() - t.Logf("Error initializing chaincode %s(%s)", ccID2, err) - theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) - theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) - return - } - - time.Sleep(time.Second) - - // Invoke second chaincode passing the first chaincode's name as first param, - // which will inturn invoke the first chaincode - f = "invoke" - cid := spec1.ChaincodeId.Name - args = util.ToChaincodeArgs(f, cid, "e", "1") - - spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID2, Input: &pb.ChaincodeInput{Args: args}} - // Invoke chaincode - var uuid string - _, uuid, _, err = invoke(ctxt, chainID, spec2, nextBlockNumber) - - if err != nil { - t.Fail() - t.Logf("Error invoking <%s>: %s", ccID2, err) - theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) - theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) - return - } - - cccid1.TxID = uuid - - // Check the state in the ledger - err = checkFinalState(cccid1) - if err != nil { - t.Fail() - t.Logf("Incorrect final state after transaction for <%s>: %s", ccID1, err) - theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) - theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) - return - } - - theChaincodeSupport.Stop(ctxt, cccid1, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) - theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) - - return -} - // Test the execution of a chaincode that invokes another chaincode with wrong parameters. Should receive error from // from the called chaincode func TestChaincodeInvokeChaincodeErrorCase(t *testing.T) { @@ -1647,6 +1648,23 @@ func TestChaincodeInitializeInitError(t *testing.T) { } } +func TestMain(m *testing.M) { + var err error + + // setup the MSP manager so that we can sign/verify + mspMgrConfigDir := "../../msp/sampleconfig/" + msptesttools.LoadMSPSetupForTesting(mspMgrConfigDir) + signer, err = mspmgmt.GetLocalMSP().GetDefaultSigningIdentity() + if err != nil { + os.Exit(-1) + fmt.Print("Could not initialize msp/signer") + return + } + + SetupTestConfig() + os.Exit(m.Run()) +} + func deployChaincode(ctx context.Context, chaincodeCtx *ccprovider.CCContext, chaincodeType pb.ChaincodeSpec_Type, path string, args [][]byte, nextBlockNumber uint64) ([]byte, error) { chaincodeSpec := &pb.ChaincodeSpec{ @@ -1670,23 +1688,6 @@ func deployChaincode(ctx context.Context, chaincodeCtx *ccprovider.CCContext, ch var signer msp.SigningIdentity -func TestMain(m *testing.M) { - var err error - - // setup the MSP manager so that we can sign/verify - mspMgrConfigDir := "../../msp/sampleconfig/" - msptesttools.LoadMSPSetupForTesting(mspMgrConfigDir) - signer, err = mspmgmt.GetLocalMSP().GetDefaultSigningIdentity() - if err != nil { - os.Exit(-1) - fmt.Print("Could not initialize msp/signer") - return - } - - SetupTestConfig() - os.Exit(m.Run()) -} - var rng *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) func generateChaincodeName(chaincodeType pb.ChaincodeSpec_Type) string { diff --git a/core/peer/peer.go b/core/peer/peer.go index d766ea83afd..038375c5730 100644 --- a/core/peer/peer.go +++ b/core/peer/peer.go @@ -256,21 +256,33 @@ func MockCreateChain(cid string) error { return err } - i := mockconfigtx.Initializer{ + chains.Lock() + defer chains.Unlock() + + // Here we need to mock also the policy manager + // in order for the ACL to be checked + initializer := mockconfigtx.Initializer{ Resources: mockconfigtx.Resources{ PolicyManagerVal: &mockpolicies.Manager{ Policy: &mockpolicies.Policy{}, }, }, + PolicyProposerVal: &mockconfigtx.PolicyProposer{ + Transactional: mockconfigtx.Transactional{}, + }, + ValueProposerVal: &mockconfigtx.ValueProposer{ + Transactional: mockconfigtx.Transactional{}, + }, + } + + manager := &mockconfigtx.Manager{ + Initializer: initializer, } - chains.Lock() - defer chains.Unlock() chains.list[cid] = &chain{ cs: &chainSupport{ - ledger: ledger, - Manager: &mockconfigtx.Manager{Initializer: i}, - }, + Manager: manager, + ledger: ledger}, } return nil diff --git a/core/policy/mocks.go b/core/policy/mocks.go index 58068dc191a..050c731351d 100644 --- a/core/policy/mocks.go +++ b/core/policy/mocks.go @@ -78,7 +78,7 @@ type MockIdentityDeserializer struct { } func (d *MockIdentityDeserializer) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) { - fmt.Printf("id : [%s], [%s]\n", string(serializedIdentity), string(d.Identity)) + fmt.Printf("[DeserializeIdentity] id : [%s], [%s]\n", string(serializedIdentity), string(d.Identity)) if bytes.Equal(d.Identity, serializedIdentity) { fmt.Printf("GOT : [%s], [%s]\n", string(serializedIdentity), string(d.Identity)) return &MockIdentity{identity: d.Identity, msg: d.Msg}, nil @@ -93,6 +93,7 @@ type MockIdentity struct { } func (id *MockIdentity) SatisfiesPrincipal(p *mspproto.MSPPrincipal) error { + fmt.Printf("[SatisfiesPrincipal] id : [%s], [%s]\n", string(id.identity), string(p.Principal)) if !bytes.Equal(id.identity, p.Principal) { return fmt.Errorf("Different identities [% x]!=[% x]", id.identity, p.Principal) } diff --git a/core/policy/policy_test.go b/core/policy/policy_test.go index ba83d8cf421..306b39f633f 100644 --- a/core/policy/policy_test.go +++ b/core/policy/policy_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/hyperledger/fabric/common/policies" + "github.com/hyperledger/fabric/msp/mgmt" "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" "github.com/stretchr/testify/assert" @@ -73,11 +74,11 @@ func TestPolicyChecker(t *testing.T) { // Alice is a member of the local MSP, policy check must succeed identityDeserializer.Msg = sProp.ProposalBytes - err = pc.CheckPolicyNoChannel("member", sProp) + err = pc.CheckPolicyNoChannel(mgmt.Members, sProp) assert.NoError(t, err) sProp, _ = utils.MockSignedEndorserProposalOrPanic("A", &peer.ChaincodeSpec{}, []byte("Bob"), []byte("msg2")) // Bob is not a member of the local MSP, policy check must fail - err = pc.CheckPolicyNoChannel("member", sProp) + err = pc.CheckPolicyNoChannel(mgmt.Members, sProp) assert.Error(t, err) } diff --git a/core/scc/cscc/configure.go b/core/scc/cscc/configure.go index d6c8ab15aac..50d28a8d8fa 100644 --- a/core/scc/cscc/configure.go +++ b/core/scc/cscc/configure.go @@ -24,10 +24,15 @@ package cscc import ( "fmt" + "errors" + "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/peer" + "github.com/hyperledger/fabric/core/policy" "github.com/hyperledger/fabric/events/producer" + "github.com/hyperledger/fabric/msp/mgmt" pb "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" "github.com/op/go-logging" @@ -37,6 +42,7 @@ import ( // configuration transaction coming in from the ordering service, the // committer calls this system chaincode to process the transaction. type PeerConfiger struct { + policyChecker policy.PolicyChecker } var cnflogger = logging.MustGetLogger("chaincode") @@ -54,6 +60,14 @@ const ( // to any transaction execution on the chain. func (e *PeerConfiger) Init(stub shim.ChaincodeStubInterface) pb.Response { cnflogger.Info("Init CSCC") + + // Init policy checker for access control + e.policyChecker = policy.NewPolicyChecker( + peer.NewChannelPolicyManagerGetter(), + mgmt.GetLocalMSP(), + mgmt.NewLocalMSPPrincipalGetter(), + ) + return shim.Success(nil) } @@ -82,18 +96,43 @@ func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) pb.Response { cnflogger.Debugf("Invoke function: %s", fname) - // TODO: Handle ACL + // Handle ACL: + // 1. get the signed proposal + sp, err := stub.GetSignedProposal() + if err != nil { + return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err)) + } + + switch fname { + case JoinChain: + // 2. check local MSP Admins policy + if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil { + return shim.Error(fmt.Sprintf("\"JoinChain\" request failed authorization check for channel [%s]: [%s]", args[1], err)) + } - if fname == JoinChain { return joinChain(args[1]) - } else if fname == GetConfigBlock { + case GetConfigBlock: + // 2. check the channel reader policy + if err = e.policyChecker.CheckPolicy(string(args[1]), policies.ChannelApplicationReaders, sp); err != nil { + return shim.Error(fmt.Sprintf("\"GetConfigBlock\" request failed authorization check for channel [%s]: [%s]", args[1], err)) + } return getConfigBlock(args[1]) - } else if fname == UpdateConfigBlock { + case UpdateConfigBlock: + // TODO: It needs to be clarified if this is a function invoked by a proposal or not. + // The issue is the following: ChannelApplicationAdmins might require multiple signatures + // but currently a proposal can be signed by a signle entity only. Therefore, the ChannelApplicationAdmins policy + // will be never satisfied. + return updateConfigBlock(args[1]) - } else if fname == GetChannels { + case GetChannels: + // 2. check local MSP Members policy + if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Members, sp); err != nil { + return shim.Error(fmt.Sprintf("\"GetChannels\" request failed authorization check for channel [%s]: [%s]", args[1], err)) + } + return getChannels() - } + } return shim.Error(fmt.Sprintf("Requested function %s not found.", fname)) } @@ -128,6 +167,21 @@ func joinChain(blockBytes []byte) pb.Response { return shim.Success(nil) } +func getChannelFromConfigBlock(blockBytes []byte) (string, error) { + if blockBytes == nil { + return "", errors.New("Configuration block must not be nil.") + } + block, err := utils.GetBlockFromBlockBytes(blockBytes) + if err != nil { + return "", fmt.Errorf("Failed to reconstruct the configuration block, %s", err) + } + chainID, err := utils.GetChainIDFromBlock(block) + if err != nil { + return "", fmt.Errorf("Failed to get the chain ID from the configuration block, %s", err) + } + return chainID, nil +} + func updateConfigBlock(blockBytes []byte) pb.Response { if blockBytes == nil { return shim.Error("Configuration block must not be nil.") diff --git a/core/scc/cscc/configure_test.go b/core/scc/cscc/configure_test.go index a1a60d6ce30..637b78ec0ef 100644 --- a/core/scc/cscc/configure_test.go +++ b/core/scc/cscc/configure_test.go @@ -22,15 +22,19 @@ import ( "testing" "time" + "strings" + "github.com/golang/protobuf/proto" configtxtest "github.com/hyperledger/fabric/common/configtx/test" "github.com/hyperledger/fabric/common/localmsp" + "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/core/chaincode" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/deliverservice" "github.com/hyperledger/fabric/core/deliverservice/blocksprovider" "github.com/hyperledger/fabric/core/ledger/ledgermgmt" "github.com/hyperledger/fabric/core/peer" + "github.com/hyperledger/fabric/core/policy" "github.com/hyperledger/fabric/gossip/api" "github.com/hyperledger/fabric/gossip/service" "github.com/hyperledger/fabric/msp/mgmt" @@ -71,6 +75,12 @@ func (*mockDeliveryClientFactory) Service(g service.GossipService, endpoints []s return &mockDeliveryClient{}, nil } +func TestMain(m *testing.M) { + msptesttools.LoadMSPSetupForTesting("../../../msp/sampleconfig") + + os.Exit(m.Run()) +} + func TestConfigerInit(t *testing.T) { e := new(PeerConfiger) stub := shim.NewMockStub("PeerConfiger", e) @@ -105,10 +115,15 @@ func TestConfigerInvokeJoinChainMissingParams(t *testing.T) { e := new(PeerConfiger) stub := shim.NewMockStub("PeerConfiger", e) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + setupEndpoint(t) // Failed path: Not enough parameters args := [][]byte{[]byte("JoinChain")} - if res := stub.MockInvoke("1", args); res.Status == shim.OK { + if res := stub.MockInvoke("2", args); res.Status == shim.OK { t.Fatalf("cscc invoke JoinChain should have failed with invalid number of args: %v", args) } } @@ -121,11 +136,16 @@ func TestConfigerInvokeJoinChainWrongParams(t *testing.T) { e := new(PeerConfiger) stub := shim.NewMockStub("PeerConfiger", e) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + setupEndpoint(t) // Failed path: wrong parameter type args := [][]byte{[]byte("JoinChain"), []byte("action")} - if res := stub.MockInvoke("1", args); res.Status == shim.OK { + if res := stub.MockInvoke("2", args); res.Status == shim.OK { t.Fatalf("cscc invoke JoinChain should have failed with null genesis block. args: %v", args) } } @@ -142,6 +162,21 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) { e := new(PeerConfiger) stub := shim.NewMockStub("PeerConfiger", e) + // Init the policy checker + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "mytestchainid": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}}}, + }, + } + + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + + e.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + setupEndpoint(t) // Initialize gossip service @@ -151,7 +186,6 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) { go grpcServer.Serve(socket) defer grpcServer.Stop() - msptesttools.LoadMSPSetupForTesting("../../../msp/sampleconfig") identity, _ := mgmt.GetLocalSigningIdentityOrPanic().Serialize() messageCryptoService := mcs.New(&mcs.MockChannelPolicyManagerGetter{}, localmsp.NewSigner(), mgmt.NewDeserializersManager()) service.InitGossipServiceCustomDeliveryFactory(identity, "localhost:13611", grpcServer, &mockDeliveryClientFactory{}, messageCryptoService) @@ -162,9 +196,21 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) { t.Fatalf("cscc invoke JoinChain failed because invalid block") } args := [][]byte{[]byte("JoinChain"), blockBytes} - if res := stub.MockInvoke("1", args); res.Status != shim.OK { - t.Fatalf("cscc invoke JoinChain failed with: %v", err) + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status != shim.OK { + t.Fatalf("cscc invoke JoinChain failed with: %v", res.Message) + } + + // This call must fail + sProp.Signature = nil + res := stub.MockInvokeWithSignedProposal("3", args, sProp) + if res.Status == shim.OK { + t.Fatalf("cscc invoke JoinChain must fail : %v", res.Message) } + assert.True(t, strings.HasPrefix(res.Message, "\"JoinChain\" request failed authorization check for channel")) + sProp.Signature = sProp.ProposalBytes // Query the configuration block //chainID := []byte{143, 222, 22, 192, 73, 145, 76, 110, 167, 154, 118, 66, 132, 204, 113, 168} @@ -173,13 +219,14 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) { t.Fatalf("cscc invoke JoinChain failed with: %v", err) } args = [][]byte{[]byte("GetConfigBlock"), []byte(chainID)} - if res := stub.MockInvoke("1", args); res.Status != shim.OK { - t.Fatalf("cscc invoke GetConfigBlock failed with: %v", err) + policyManagerGetter.Managers["mytestchainid"].(*policy.MockChannelPolicyManager).MockPolicy.(*policy.MockPolicy).Deserializer.(*policy.MockIdentityDeserializer).Msg = sProp.ProposalBytes + if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status != shim.OK { + t.Fatalf("cscc invoke GetConfigBlock failed with: %v", res.Message) } // get channels for the peer args = [][]byte{[]byte(GetChannels)} - res := stub.MockInvoke("1", args) + res = stub.MockInvokeWithSignedProposal("2", args, sProp) if res.Status != shim.OK { t.FailNow() } @@ -200,27 +247,47 @@ func TestConfigerInvokeUpdateConfigBlock(t *testing.T) { e := new(PeerConfiger) stub := shim.NewMockStub("PeerConfiger", e) + // Init the policy checker + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "mytestchainid": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}}}, + }, + } + + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + + e.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + setupEndpoint(t) + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + policyManagerGetter.Managers["mytestchainid"].(*policy.MockChannelPolicyManager).MockPolicy.(*policy.MockPolicy).Deserializer.(*policy.MockIdentityDeserializer).Msg = sProp.ProposalBytes + // Failed path: Not enough parameters args := [][]byte{[]byte("UpdateConfigBlock")} - if res := stub.MockInvoke("1", args); res.Status == shim.OK { + if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status == shim.OK { t.Fatalf("cscc invoke UpdateConfigBlock should have failed with invalid number of args: %v", args) } // Failed path: wrong parameter type args = [][]byte{[]byte("UpdateConfigBlock"), []byte("action")} - if res := stub.MockInvoke("1", args); res.Status == shim.OK { + if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status == shim.OK { t.Fatalf("cscc invoke UpdateConfigBlock should have failed with null genesis block - args: %v", args) } - // Successful path for JoinChain + // Successful path for UpdateConfigBlock blockBytes := mockConfigBlock() if blockBytes == nil { t.Fatalf("cscc invoke UpdateConfigBlock failed because invalid block") } args = [][]byte{[]byte("UpdateConfigBlock"), blockBytes} - if res := stub.MockInvoke("1", args); res.Status != shim.OK { + if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status != shim.OK { t.Fatalf("cscc invoke UpdateConfigBlock failed with: %v", res.Message) } @@ -231,7 +298,7 @@ func TestConfigerInvokeUpdateConfigBlock(t *testing.T) { t.Fatalf("cscc invoke UpdateConfigBlock failed with: %v", err) } args = [][]byte{[]byte("GetConfigBlock"), []byte(chainID)} - if res := stub.MockInvoke("1", args); res.Status != shim.OK { + if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status != shim.OK { t.Fatalf("cscc invoke GetConfigBlock failed with: %v", err) } diff --git a/events/producer/handler.go b/events/producer/handler.go index 86a245ed349..568a32e7e50 100644 --- a/events/producer/handler.go +++ b/events/producer/handler.go @@ -162,7 +162,7 @@ func validateEventMessage(signedEvt *pb.SignedEvent) (*pb.Event, error) { principalGetter := mgmt.NewLocalMSPPrincipalGetter() // Load MSPPrincipal for policy - principal, err := principalGetter.Get("member") + principal, err := principalGetter.Get(mgmt.Members) if err != nil { return nil, fmt.Errorf("failed getting local MSP principal [member]: [%s]", err) } diff --git a/msp/mgmt/principal.go b/msp/mgmt/principal.go index 46592ee3441..2c918aa0248 100644 --- a/msp/mgmt/principal.go +++ b/msp/mgmt/principal.go @@ -23,6 +23,14 @@ import ( "github.com/hyperledger/fabric/protos/msp" ) +const ( + // Admins is the label for the local MSP admins + Admins = "Admins" + + // Members is the label for the local MSP members + Members = "Members" +) + type MSPPrincipalGetter interface { // Get returns an MSP principal for the given role Get(role string) (*msp.MSPPrincipal, error) @@ -42,7 +50,7 @@ func (m *localMSPPrincipalGetter) Get(role string) (*msp.MSPPrincipal, error) { // TODO: put the constants in some more appropriate place switch role { - case "admin": + case Admins: principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ADMIN, MspIdentifier: mspid}) if err != nil { return nil, err @@ -51,7 +59,7 @@ func (m *localMSPPrincipalGetter) Get(role string) (*msp.MSPPrincipal, error) { return &msp.MSPPrincipal{ PrincipalClassification: msp.MSPPrincipal_ROLE, Principal: principalBytes}, nil - case "member": + case Members: principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_MEMBER, MspIdentifier: mspid}) if err != nil { return nil, err