From c3c121666a5f3086bd12a06b91952dab6042322a Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 19 Apr 2017 16:06:20 +0200 Subject: [PATCH] [FAB-2969] Access control at LSCC This change-set does the following: 1. Add access control to lscc by verifying that the caller has appropriate rights. Tests have been modified to cover this access control It is left to complete access control for instantiation. This will be put in place once the instantation process gets completed. This change-set comes in the context of 1. https://jira.hyperledger.org/browse/FAB-2969 Change-Id: Ife15a69f963e33ce32704598e4febe24742bcb9c Signed-off-by: Angelo De Caro --- core/chaincode/exectransaction_test.go | 26 +- core/committer/txvalidator/validator.go | 44 ++- core/policy/policy.go | 24 ++ core/policyprovider/provider.go | 43 +++ core/scc/lscc/lscc.go | 49 ++++ core/scc/lscc/lscc_test.go | 342 ++++++++++++++++++++++-- 6 files changed, 499 insertions(+), 29 deletions(-) create mode 100644 core/policyprovider/provider.go diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index f64f5f9bb25..3435595f44b 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -42,6 +42,7 @@ import ( "github.com/hyperledger/fabric/core/ledger" "github.com/hyperledger/fabric/core/ledger/util/couchdb" "github.com/hyperledger/fabric/core/peer" + "github.com/hyperledger/fabric/core/policy" "github.com/hyperledger/fabric/core/scc" pb "github.com/hyperledger/fabric/protos/peer" putils "github.com/hyperledger/fabric/protos/utils" @@ -93,6 +94,9 @@ func initPeer(chainIDs ...string) (net.Listener, error) { ccStartupTimeout := time.Duration(chaincodeStartupTimeoutDefault) * time.Millisecond pb.RegisterChaincodeSupportServer(grpcServer, NewChaincodeSupport(getPeerEndpoint, false, ccStartupTimeout)) + // Mock policy checker + policy.RegisterPolicyCheckerFactory(&mockPolicyCheckerFactory{}) + scc.RegisterSysCCs() for _, id := range chainIDs { @@ -313,15 +317,16 @@ func deploy2(ctx context.Context, cccid *ccprovider.CCContext, chaincodeDeployme ccprovider.PutChaincodeIntoFS(chaincodeDeploymentSpec) sysCCVers := util.GetSysCCVersion() - lsccid := ccprovider.NewCCContext(cccid.ChainID, cis.ChaincodeSpec.ChaincodeId.Name, sysCCVers, uuid, true, nil, nil) + sprop, prop := putils.MockSignedEndorserProposalOrPanic(cccid.ChainID, cis.ChaincodeSpec, []byte("Admin"), []byte("msg1")) + lsccid := ccprovider.NewCCContext(cccid.ChainID, cis.ChaincodeSpec.ChaincodeId.Name, sysCCVers, uuid, true, sprop, prop) //write to lscc if _, _, err = ExecuteWithErrorFilter(ctx, lsccid, cis); err != nil { - return nil, fmt.Errorf("Error deploying chaincode: %s", err) + return nil, fmt.Errorf("Error deploying chaincode (1): %s", err) } if b, _, err = ExecuteWithErrorFilter(ctx, cccid, chaincodeDeploymentSpec); err != nil { - return nil, fmt.Errorf("Error deploying chaincode: %s", err) + return nil, fmt.Errorf("Error deploying chaincode(2): %s", err) } return b, nil @@ -356,7 +361,10 @@ func invokeWithVersion(ctx context.Context, chainID string, version string, spec } }() - sprop, prop := putils.MockSignedEndorserProposalOrPanic(util.GetTestChainID(), spec, creator, nil) + if len(creator) == 0 { + creator = []byte("Admin") + } + sprop, prop := putils.MockSignedEndorserProposalOrPanic(chainID, spec, creator, []byte("msg1")) cccid := ccprovider.NewCCContext(chainID, cdInvocationSpec.ChaincodeSpec.ChaincodeId.Name, version, uuid, false, sprop, prop) retval, ccevt, err = ExecuteWithErrorFilter(ctx, cccid, cdInvocationSpec) if err != nil { @@ -1797,3 +1805,13 @@ func (c *CreatorPolicy) Evaluate(signatureSet []*common.SignedData) error { } return fmt.Errorf("Creator not recognized [%s]", string(signatureSet[0].Identity)) } + +type mockPolicyCheckerFactory struct{} + +func (f *mockPolicyCheckerFactory) NewPolicyChecker() policy.PolicyChecker { + return policy.NewPolicyChecker( + peer.NewChannelPolicyManagerGetter(), + &policy.MockIdentityDeserializer{[]byte("Admin"), []byte("msg1")}, + &policy.MockMSPPrincipalGetter{Principal: []byte("Admin")}, + ) +} diff --git a/core/committer/txvalidator/validator.go b/core/committer/txvalidator/validator.go index c31a42adb38..2318cfcf560 100644 --- a/core/committer/txvalidator/validator.go +++ b/core/committer/txvalidator/validator.go @@ -403,11 +403,13 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b // of VSCC and of the policy that should be used // obtain name of the VSCC and the policy from LSCC - vscc, policy, err = v.ccprovider.GetCCValidationInfoFromLSCC(ctxt, txid, nil, nil, chainID, hdrExt.ChaincodeId.Name) + cd, err := v.getCDataForCC(hdrExt.ChaincodeId.Name) if err != nil { - logger.Errorf("Unable to get chaincode data from LSCC for txid %s, due to %s", txid, err) + logger.Errorf("Unable to get chaincode data from ledger for txid %s, due to %s", txid, err) return err } + vscc = cd.Vscc + policy = cd.Policy } else { // when we are validating LSCC, we use the default // VSCC and a default policy that requires one signature @@ -442,3 +444,41 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b return nil } + +func (v *vsccValidatorImpl) getCDataForCC(ccid string) (*ccprovider.ChaincodeData, error) { + l := v.support.Ledger() + if l == nil { + return nil, fmt.Errorf("nil ledger instance") + } + + qe, err := l.NewQueryExecutor() + if err != nil { + return nil, fmt.Errorf("Could not retrieve QueryExecutor, error %s", err) + } + defer qe.Done() + + bytes, err := qe.GetState("lscc", ccid) + if err != nil { + return nil, fmt.Errorf("Could not retrieve state for chaincode %s, error %s", ccid, err) + } + + if bytes == nil { + return nil, fmt.Errorf("lscc's state for [%s] not found.", ccid) + } + + cd := &ccprovider.ChaincodeData{} + err = proto.Unmarshal(bytes, cd) + if err != nil { + return nil, fmt.Errorf("Unmarshalling ChaincodeQueryResponse failed, error %s", err) + } + + if cd.Vscc == "" { + return nil, fmt.Errorf("lscc's state for [%s] is invalid, vscc field must be set.", ccid) + } + + if len(cd.Policy) == 0 { + return nil, fmt.Errorf("lscc's state for [%s] is invalid, policy field must be set.", ccid) + } + + return cd, err +} diff --git a/core/policy/policy.go b/core/policy/policy.go index 8dba1d21519..690e5a168a8 100644 --- a/core/policy/policy.go +++ b/core/policy/policy.go @@ -184,3 +184,27 @@ func (p *policyChecker) CheckPolicyBySignedData(channelID, policyName string, sd return nil } + +var pcFactory PolicyCheckerFactory + +// PolicyCheckerFactory defines a factory interface so +// that the actual implementation can be injected +type PolicyCheckerFactory interface { + NewPolicyChecker() PolicyChecker +} + +// RegisterPolicyCheckerFactory is to be called once to set +// the factory that will be used to obtain instances of PolicyChecker +func RegisterPolicyCheckerFactory(f PolicyCheckerFactory) { + pcFactory = f +} + +// GetPolicyChecker returns instances of PolicyChecker; +// the actual implementation is controlled by the factory that +// is registered via RegisterPolicyCheckerFactory +func GetPolicyChecker() PolicyChecker { + if pcFactory == nil { + panic("The factory must be set first via RegisterPolicyCheckerFactory") + } + return pcFactory.NewPolicyChecker() +} diff --git a/core/policyprovider/provider.go b/core/policyprovider/provider.go new file mode 100644 index 00000000000..46af56b645f --- /dev/null +++ b/core/policyprovider/provider.go @@ -0,0 +1,43 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package policyprovider + +import ( + "github.com/hyperledger/fabric/core/peer" + "github.com/hyperledger/fabric/core/policy" + "github.com/hyperledger/fabric/msp/mgmt" +) + +// init is called when this package is loaded. This implementation registers the factory +func init() { + policy.RegisterPolicyCheckerFactory(&defaultFactory{}) +} + +type defaultFactory struct{} + +func (f *defaultFactory) NewPolicyChecker() policy.PolicyChecker { + return policy.NewPolicyChecker( + peer.NewChannelPolicyManagerGetter(), + mgmt.GetLocalMSP(), + mgmt.NewLocalMSPPrincipalGetter(), + ) +} + +// GetPolicyChecker returns instances of PolicyChecker; +func GetPolicyChecker() policy.PolicyChecker { + return policy.GetPolicyChecker() +} diff --git a/core/scc/lscc/lscc.go b/core/scc/lscc/lscc.go index 35a54b647f4..13767afc932 100644 --- a/core/scc/lscc/lscc.go +++ b/core/scc/lscc/lscc.go @@ -23,10 +23,14 @@ import ( "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/common/cauthdsl" "github.com/hyperledger/fabric/common/flogging" + "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/common/ccprovider" "github.com/hyperledger/fabric/core/common/sysccprovider" "github.com/hyperledger/fabric/core/peer" + "github.com/hyperledger/fabric/core/policy" + "github.com/hyperledger/fabric/core/policyprovider" + "github.com/hyperledger/fabric/msp/mgmt" pb "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" ) @@ -82,6 +86,10 @@ type LifeCycleSysCC struct { // methods of the system chaincode package without // import cycles sccprovider sysccprovider.SystemChaincodeProvider + + // policyChecker is the interface used to perform + // access control + policyChecker policy.PolicyChecker } //----------------errors--------------- @@ -564,6 +572,10 @@ func (lscc *LifeCycleSysCC) executeUpgrade(stub shim.ChaincodeStubInterface, cha //Init only initializes the system chaincode provider func (lscc *LifeCycleSysCC) Init(stub shim.ChaincodeStubInterface) pb.Response { lscc.sccprovider = sysccprovider.GetSystemChaincodeProvider() + + // Init policy checker for access control + lscc.policyChecker = policyprovider.GetPolicyChecker() + return shim.Success(nil) } @@ -580,12 +592,24 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response function := string(args[0]) + // Handle ACL: + // 1. get the signed proposal + sp, err := stub.GetSignedProposal() + if err != nil { + return shim.Error(fmt.Sprintf("Failed retrieving signed proposal on executing %s with error %s", function, err)) + } + switch function { case INSTALL: if len(args) < 2 { return shim.Error(InvalidArgsLenErr(len(args)).Error()) } + // 2. check local MSP Admins policy + if err = lscc.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil { + return shim.Error(fmt.Sprintf("Authorization for INSTALL on %s has been denied with error %s", args[1], err)) + } + depSpec := args[1] err := lscc.executeInstall(stub, depSpec) @@ -598,6 +622,9 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response return shim.Error(InvalidArgsLenErr(len(args)).Error()) } + // TODO: add access control check + // once the instantiation process will be completed. + //chain the chaincode shoud be associated with. It //should be created with a register call chainname := string(args[1]) @@ -652,6 +679,9 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response return shim.Error(InvalidChainNameErr(chainname).Error()) } + // TODO: add access control check + // once the instantiation process will be completed. + depSpec := args[2] // optional arguments here (they can each be nil and may or may not be present) @@ -696,6 +726,13 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response chain := string(args[1]) ccname := string(args[2]) + // 2. check local Channel Readers policy + // Notice that this information are already available on the ledger + // therefore we enforce here that the caller is reader of the channel. + if err = lscc.policyChecker.CheckPolicy(chain, policies.ChannelApplicationReaders, sp); err != nil { + return shim.Error(fmt.Sprintf("Authorization for %s on channel %s has been denied with error %s", function, args[1], err)) + } + cdbytes, err := lscc.getCCInstance(stub, ccname) if err != nil { logger.Errorf("error getting chaincode %s on channel: %s(err:%s)", ccname, chain, err) @@ -722,11 +759,23 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response if len(args) != 1 { return shim.Error(InvalidArgsLenErr(len(args)).Error()) } + + // 2. check local MSP Admins policy + if err = lscc.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil { + return shim.Error(fmt.Sprintf("Authorization for GETCHAINCODES on channel %s has been denied with error %s", args[0], err)) + } + return lscc.getChaincodes(stub) case GETINSTALLEDCHAINCODES: if len(args) != 1 { return shim.Error(InvalidArgsLenErr(len(args)).Error()) } + + // 2. check local MSP Admins policy + if err = lscc.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil { + return shim.Error(fmt.Sprintf("Authorization for GETINSTALLEDCHAINCODES on channel %s has been denied with error %s", args[0], err)) + } + return lscc.getInstalledChaincodes() } diff --git a/core/scc/lscc/lscc_test.go b/core/scc/lscc/lscc_test.go index 87ed8ee4a95..24091756269 100644 --- a/core/scc/lscc/lscc_test.go +++ b/core/scc/lscc/lscc_test.go @@ -31,8 +31,11 @@ import ( "bytes" "compress/gzip" + "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/core/container/util" + "github.com/hyperledger/fabric/core/policy" pb "github.com/hyperledger/fabric/protos/peer" + "github.com/hyperledger/fabric/protos/utils" ) var lscctestpath = "/tmp/lscctest" @@ -94,14 +97,15 @@ func constructDeploymentSpec(name string, path string, version string, initArgs func TestInstall(t *testing.T) { path := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02" - testInstall(t, "example02", "0", path, "") - testInstall(t, "example02-2", "1.0", path, "") - testInstall(t, "example02.go", "0", path, InvalidChaincodeNameErr("example02.go").Error()) - testInstall(t, "", "0", path, EmptyChaincodeNameErr("").Error()) - testInstall(t, "example02", "1{}0", path, InvalidVersionErr("1{}0").Error()) + testInstall(t, "example02", "0", path, "", "Alice") + testInstall(t, "example02-2", "1.0", path, "", "Alice") + testInstall(t, "example02.go", "0", path, InvalidChaincodeNameErr("example02.go").Error(), "Alice") + testInstall(t, "", "0", path, EmptyChaincodeNameErr("").Error(), "Alice") + testInstall(t, "example02", "1{}0", path, InvalidVersionErr("1{}0").Error(), "Alice") + testInstall(t, "example02", "0", path, "Authorization for INSTALL on", "Bob") } -func testInstall(t *testing.T, ccname string, version string, path string, expectedErrorMsg string) { +func testInstall(t *testing.T, ccname string, version string, path string, expectedErrorMsg string, caller string) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lscc", scc) @@ -110,6 +114,19 @@ func testInstall(t *testing.T, ccname string, version string, path string, expec t.FailNow() } + // Init the policy checker + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "test": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: identityDeserializer}}, + }, + } + scc.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + cds, err := constructDeploymentSpec(ccname, path, version, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}, false) if err != nil { t.FailNow() @@ -122,20 +139,27 @@ func testInstall(t *testing.T, ccname string, version string, path string, expec //constructDeploymentSpec puts the depspec on the FS. This should succeed args := [][]byte{[]byte(INSTALL), b} + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte(caller), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + if expectedErrorMsg == "" { defer os.Remove(lscctestpath + "/" + ccname + "." + version) - if res := stub.MockInvoke("1", args); res.Status != shim.OK { + if res := stub.MockInvokeWithSignedProposal("1", args, sProp); res.Status != shim.OK { t.FailNow() } } else { - if res := stub.MockInvoke("1", args); string(res.Message) != expectedErrorMsg { - t.Logf("Received error: %s", res.Message) + if res := stub.MockInvokeWithSignedProposal("1", args, sProp); !strings.HasPrefix(string(res.Message), expectedErrorMsg) { + t.Logf("Received error: [%s]", res.Message) t.FailNow() } } args = [][]byte{[]byte(GETINSTALLEDCHAINCODES)} - res := stub.MockInvoke("1", args) + sProp, _ = utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res := stub.MockInvokeWithSignedProposal("1", args, sProp) if res.Status != shim.OK { t.FailNow() } @@ -242,6 +266,22 @@ func testDeploy(t *testing.T, ccname string, version string, path string, forceB t.FailNow() } + // Init the policy checker + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "test": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: identityDeserializer}}, + }, + } + scc.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + cds, err := constructDeploymentSpec(ccname, path, version, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}, true) if err != nil { t.FailNow() @@ -273,7 +313,7 @@ func testDeploy(t *testing.T, ccname string, version string, path string, forceB } args = [][]byte{[]byte(GETCHAINCODES)} - res = stub.MockInvoke("1", args) + res = stub.MockInvokeWithSignedProposal("1", args, sProp) if res.Status != shim.OK { t.FailNow() } @@ -297,7 +337,7 @@ func testDeploy(t *testing.T, ccname string, version string, path string, forceB } args = [][]byte{[]byte(GETCCINFO), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} - if res := stub.MockInvoke("1", args); res.Status != shim.OK { + if res := stub.MockInvokeWithSignedProposal("1", args, sProp); res.Status != shim.OK { t.FailNow() } } else { @@ -352,6 +392,22 @@ func TestMultipleDeploy(t *testing.T) { t.FailNow() } + // Init the policy checker + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "test": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: identityDeserializer}}, + }, + } + scc.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + //deploy 02 cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "0", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}, true) if err != nil { @@ -369,7 +425,7 @@ func TestMultipleDeploy(t *testing.T) { } args = [][]byte{[]byte(GETCCINFO), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} - if res := stub.MockInvoke("1", args); res.Status != shim.OK { + if res := stub.MockInvokeWithSignedProposal("1", args, sProp); res.Status != shim.OK { t.FailNow() } @@ -389,12 +445,12 @@ func TestMultipleDeploy(t *testing.T) { } args = [][]byte{[]byte(GETCCINFO), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} - if res := stub.MockInvoke("1", args); res.Status != shim.OK { + if res := stub.MockInvokeWithSignedProposal("1", args, sProp); res.Status != shim.OK { t.FailNow() } args = [][]byte{[]byte(GETCHAINCODES)} - res := stub.MockInvoke("1", args) + res := stub.MockInvokeWithSignedProposal("1", args, sProp) if res.Status != shim.OK { t.FailNow() } @@ -421,6 +477,21 @@ func TestRetryFailedDeploy(t *testing.T) { fmt.Println("Init failed", string(res.Message)) t.FailNow() } + // Init the policy checker + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "test": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: identityDeserializer}}, + }, + } + scc.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes //deploy 02 cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "0", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}, true) @@ -454,7 +525,7 @@ func TestRetryFailedDeploy(t *testing.T) { //get the deploymentspec args = [][]byte{[]byte(GETDEPSPEC), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} - if res := stub.MockInvoke("1", args); res.Status != shim.OK || res.Payload == nil { + if res := stub.MockInvokeWithSignedProposal("1", args, sProp); res.Status != shim.OK || res.Payload == nil { t.FailNow() } } @@ -469,10 +540,26 @@ func TestTamperChaincode(t *testing.T) { t.FailNow() } + // Init the policy checker + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "test": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: identityDeserializer}}, + }, + } + scc.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + //deploy 01 cds, err := constructDeploymentSpec("example01", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01", "0", [][]byte{[]byte("init"), []byte("a"), []byte("1"), []byte("b"), []byte("2")}, true) if err != nil { - t.Logf("Could not construct example01.0") + t.Logf("Could not construct example01.0 [%s]", err) t.FailNow() } @@ -527,7 +614,7 @@ func TestTamperChaincode(t *testing.T) { //get the deploymentspec args = [][]byte{[]byte(GETDEPSPEC), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} - if res = stub.MockInvoke("1", args); res.Status == shim.OK { + if res = stub.MockInvokeWithSignedProposal("1", args, sProp); res.Status == shim.OK { t.Logf("Expected error on tampering files but succeeded") t.FailNow() } @@ -630,6 +717,21 @@ func TestGetAPIsWithoutInstall(t *testing.T) { fmt.Println("Init failed", string(res.Message)) t.FailNow() } + // Init the policy checker + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "test": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: identityDeserializer}}, + }, + } + scc.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "0", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}, true) @@ -648,25 +750,25 @@ func TestGetAPIsWithoutInstall(t *testing.T) { //GETCCINFO should still work args = [][]byte{[]byte(GETCCINFO), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} - if res := stub.MockInvoke("1", args); res.Status != shim.OK { + if res := stub.MockInvokeWithSignedProposal("1", args, sProp); res.Status != shim.OK { t.FailNow() } //GETCCDATA should still work args = [][]byte{[]byte(GETCCDATA), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} - if res := stub.MockInvoke("1", args); res.Status != shim.OK { + if res := stub.MockInvokeWithSignedProposal("1", args, sProp); res.Status != shim.OK { t.FailNow() } //GETDEPSPEC should not work args = [][]byte{[]byte(GETDEPSPEC), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} - if res := stub.MockInvoke("1", args); res.Status == shim.OK { + if res := stub.MockInvokeWithSignedProposal("1", args, sProp); res.Status == shim.OK { t.FailNow() } // get instantiated chaincodes args = [][]byte{[]byte(GETCHAINCODES)} - res := stub.MockInvoke("1", args) + res := stub.MockInvokeWithSignedProposal("1", args, sProp) if res.Status != shim.OK { t.FailNow() } @@ -685,7 +787,7 @@ func TestGetAPIsWithoutInstall(t *testing.T) { // get installed chaincodes args = [][]byte{[]byte(GETINSTALLEDCHAINCODES)} - res = stub.MockInvoke("1", args) + res = stub.MockInvokeWithSignedProposal("1", args, sProp) if res.Status != shim.OK { t.FailNow() } @@ -704,6 +806,200 @@ func TestGetAPIsWithoutInstall(t *testing.T) { } +// TestGetInstalledChaincodesAccessRights verifies that only authorized parties can call +// the GETINSTALLEDCHAINCODES function +func TestGetInstalledChaincodesAccessRights(t *testing.T) { + scc := new(LifeCycleSysCC) + stub := shim.NewMockStub("lscc", scc) + + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + + // Init the policy checker + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "test": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: identityDeserializer}}, + }, + } + scc.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + + // Should pass + args := [][]byte{[]byte(GETINSTALLEDCHAINCODES)} + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res := stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status != shim.OK { + t.FailNow() + } + + // Should fail + sProp, _ = utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Bob"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res = stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status == shim.OK { + t.FailNow() + } +} + +// TestGetChaincodesAccessRights verifies that only authorized parties can call +// the GETCHAINCODES function +func TestGetChaincodesAccessRights(t *testing.T) { + scc := new(LifeCycleSysCC) + stub := shim.NewMockStub("lscc", scc) + + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + + // Init the policy checker + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "test": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: identityDeserializer}}, + }, + } + scc.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + + // Should pass + args := [][]byte{[]byte(GETCHAINCODES)} + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res := stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status != shim.OK { + t.FailNow() + } + + // Should fail + sProp, _ = utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Bob"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res = stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status == shim.OK { + t.FailNow() + } +} + +// TestGetCCInfoAccessRights verifies that only authorized parties can call +// the GETCCINFO function +func TestGetCCAccessRights(t *testing.T) { + scc := new(LifeCycleSysCC) + stub := shim.NewMockStub("lscc", scc) + + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + + // Init the policy checker + identityDeserializer := &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")} + policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ + Managers: map[string]policies.Manager{ + "test": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: identityDeserializer}}, + }, + } + scc.policyChecker = policy.NewPolicyChecker( + policyManagerGetter, + identityDeserializer, + &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, + ) + + cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "0", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}, true) + + var b []byte + if b, err = proto.Marshal(cds); err != nil || b == nil { + t.FailNow() + } + + args := [][]byte{[]byte(DEPLOY), []byte("test"), b} + if res := stub.MockInvoke("1", args); res.Status != shim.OK { + t.FailNow() + } + + //Force remove CC + defer os.Remove(lscctestpath + "/example02.0") + + // GETCCINFO + // Should pass + args = [][]byte{[]byte(GETCCINFO), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} + sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res := stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status != shim.OK { + t.Logf("This should pass [%s]", res.Message) + t.FailNow() + } + + // Should fail + sProp, _ = utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Bob"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res = stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status == shim.OK { + t.Logf("This should fail [%s]", res.Message) + t.FailNow() + } + + // GETDEPSPEC + // Should pass + args = [][]byte{[]byte(GETDEPSPEC), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} + sProp, _ = utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res = stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status != shim.OK { + t.Logf("This should pass [%s]", res.Message) + t.FailNow() + } + + // Should fail + sProp, _ = utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Bob"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res = stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status == shim.OK { + t.Logf("This should fail [%s]", res.Message) + t.FailNow() + } + + // GETCCDATA + // Should pass + args = [][]byte{[]byte(GETCCDATA), []byte("test"), []byte(cds.ChaincodeSpec.ChaincodeId.Name)} + sProp, _ = utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res = stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status != shim.OK { + t.Logf("This should pass [%s]", res.Message) + t.FailNow() + } + + // Should fail + sProp, _ = utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Bob"), []byte("msg1")) + identityDeserializer.Msg = sProp.ProposalBytes + sProp.Signature = sProp.ProposalBytes + res = stub.MockInvokeWithSignedProposal("1", args, sProp) + if res.Status == shim.OK { + t.Logf("This should fail [%s]", res.Message) + t.FailNow() + } +} + func TestMain(m *testing.M) { ccprovider.SetChaincodesPath(lscctestpath) sysccprovider.RegisterSystemChaincodeProviderFactory(&mocksccProviderFactory{})