/* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package endorsement import ( "bytes" "fmt" "testing" "github.com/hyperledger/fabric-protos-go/common" "github.com/hyperledger/fabric-protos-go/msp" "github.com/hyperledger/fabric-protos-go/peer" "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/gossip/api" gcommon "github.com/hyperledger/fabric/gossip/common" disc "github.com/hyperledger/fabric/gossip/discovery" "github.com/hyperledger/fabric/protoutil" "github.com/stretchr/testify/require" ) func TestPrincipalsFromCollectionConfig(t *testing.T) { t.Run("Empty config", func(t *testing.T) { res, err := principalsFromCollectionConfig(nil) require.NoError(t, err) require.Empty(t, res) }) t.Run("Not empty config", func(t *testing.T) { org1AndOrg2 := []*msp.MSPPrincipal{orgPrincipal("Org1MSP"), orgPrincipal("Org2MSP")} org3AndOrg4 := []*msp.MSPPrincipal{orgPrincipal("Org3MSP"), orgPrincipal("Org4MSP")} col2principals := map[string][]*msp.MSPPrincipal{ "foo": org1AndOrg2, "bar": org3AndOrg4, } config := buildCollectionConfig(col2principals) res, err := principalsFromCollectionConfig(config) require.NoError(t, err) assertEqualPrincipalSets(t, policies.PrincipalSet(org1AndOrg2), res["foo"]) assertEqualPrincipalSets(t, policies.PrincipalSet(org3AndOrg4), res["bar"]) require.Empty(t, res["baz"]) }) } func TestNewCollectionFilterInvalidInput(t *testing.T) { t.Run("Invalid collection type", func(t *testing.T) { collections := &peer.CollectionConfigPackage{} collections.Config = []*peer.CollectionConfig{ { Payload: nil, }, } filter, err := principalsFromCollectionConfig(collections) require.Nil(t, filter) require.Contains(t, err.Error(), "expected a static collection") }) t.Run("Invalid membership policy", func(t *testing.T) { collections := &peer.CollectionConfigPackage{} collections.Config = []*peer.CollectionConfig{ { Payload: &peer.CollectionConfig_StaticCollectionConfig{ StaticCollectionConfig: &peer.StaticCollectionConfig{ Name: "foo", }, }, }, } filter, err := principalsFromCollectionConfig(collections) require.Nil(t, filter) require.Contains(t, err.Error(), "MemberOrgsPolicy of foo is nil") }) t.Run("Missing policy", func(t *testing.T) { collections := &peer.CollectionConfigPackage{} collections.Config = []*peer.CollectionConfig{ { Payload: &peer.CollectionConfig_StaticCollectionConfig{ StaticCollectionConfig: &peer.StaticCollectionConfig{ Name: "foo", MemberOrgsPolicy: &peer.CollectionPolicyConfig{}, }, }, }, } filter, err := principalsFromCollectionConfig(collections) require.Nil(t, filter) require.Contains(t, err.Error(), "policy of foo is nil") }) } func TestToIdentityFilter(t *testing.T) { col2principals := make(principalSetsByCollectionName) col2principals["foo"] = []*msp.MSPPrincipal{orgPrincipal("Org1MSP"), orgPrincipal("Org2MSP")} t.Run("collection doesn't exist in mapping", func(t *testing.T) { filter, err := col2principals.toIdentityFilter("mychannel", &principalEvaluatorMock{}, &peer.ChaincodeCall{ Name: "mycc", CollectionNames: []string{"bar"}, }) require.Nil(t, filter) require.Equal(t, "collection bar doesn't exist in collection config for chaincode mycc", err.Error()) }) t.Run("collection exists in mapping", func(t *testing.T) { filter, err := col2principals.toIdentityFilter("mychannel", &principalEvaluatorMock{}, &peer.ChaincodeCall{ Name: "mycc", CollectionNames: []string{"foo"}, }) require.NoError(t, err) identity := protoutil.MarshalOrPanic(&msp.SerializedIdentity{ Mspid: "Org2MSP", }) require.True(t, filter(identity)) identity = protoutil.MarshalOrPanic(&msp.SerializedIdentity{ Mspid: "Org3MSP", }) require.False(t, filter(identity)) }) } func TestCombine(t *testing.T) { filter1 := identityFilter(func(identity api.PeerIdentityType) bool { return bytes.Equal([]byte("p1"), identity) || bytes.Equal([]byte("p2"), identity) }) filter2 := identityFilter(func(identity api.PeerIdentityType) bool { return bytes.Equal([]byte("p2"), identity) || bytes.Equal([]byte("p3"), identity) }) filter := identityFilters{filter1, filter2}.combine() require.False(t, filter(api.PeerIdentityType("p1"))) require.True(t, filter(api.PeerIdentityType("p2"))) require.False(t, filter(api.PeerIdentityType("p3"))) require.False(t, filter(api.PeerIdentityType("p4"))) } func TestToMemberFilter(t *testing.T) { unauthorizedIdentity := api.PeerIdentityType("unauthorizedIdentity") authorizedIdentity := api.PeerIdentityType("authorizedIdentity") notPresentIdentity := api.PeerIdentityType("api.PeerIdentityInfo") filter := identityFilter(func(identity api.PeerIdentityType) bool { return bytes.Equal(authorizedIdentity, identity) }) identityInfoByID := map[string]api.PeerIdentityInfo{ "authorizedIdentity": {PKIId: gcommon.PKIidType(authorizedIdentity), Identity: authorizedIdentity}, "unauthorizedIdentity": {PKIId: gcommon.PKIidType(unauthorizedIdentity), Identity: unauthorizedIdentity}, } memberFilter := filter.toMemberFilter(identityInfoByID) t.Run("Member is authorized", func(t *testing.T) { authorized := memberFilter(disc.NetworkMember{ PKIid: gcommon.PKIidType(authorizedIdentity), }) require.True(t, authorized) }) t.Run("Member is unauthorized", func(t *testing.T) { authorized := memberFilter(disc.NetworkMember{ PKIid: gcommon.PKIidType(unauthorizedIdentity), }) require.False(t, authorized) }) t.Run("Member is not found in mapping", func(t *testing.T) { authorized := memberFilter(disc.NetworkMember{ PKIid: gcommon.PKIidType(notPresentIdentity), }) require.False(t, authorized) }) } func TestIsIdentityAuthorizedByPrincipalSet(t *testing.T) { principals := []*msp.MSPPrincipal{orgPrincipal("Org1MSP"), orgPrincipal("Org2MSP")} t.Run("Authorized", func(t *testing.T) { identity := protoutil.MarshalOrPanic(&msp.SerializedIdentity{ Mspid: "Org1MSP", }) authorized := isIdentityAuthorizedByPrincipalSet("mychannel", &principalEvaluatorMock{}, principals, identity) require.True(t, authorized) }) t.Run("Unauthorized", func(t *testing.T) { identity := protoutil.MarshalOrPanic(&msp.SerializedIdentity{ Mspid: "Org3MSP", }) authorized := isIdentityAuthorizedByPrincipalSet("mychannel", &principalEvaluatorMock{}, principals, identity) require.False(t, authorized) }) } func TestFilterForPrincipalSets(t *testing.T) { org1AndOrg2 := []*msp.MSPPrincipal{orgPrincipal("Org1MSP"), orgPrincipal("Org2MSP")} org2AndOrg3 := []*msp.MSPPrincipal{orgPrincipal("Org2MSP"), orgPrincipal("Org3MSP")} org3AndOrg4 := []*msp.MSPPrincipal{orgPrincipal("Org3MSP"), orgPrincipal("Org4MSP")} identity := protoutil.MarshalOrPanic(&msp.SerializedIdentity{ Mspid: "Org2MSP", }) t.Run("Identity is authorized by all principals", func(t *testing.T) { filter := filterForPrincipalSets("mychannel", &principalEvaluatorMock{}, policies.PrincipalSets{org1AndOrg2, org2AndOrg3}) require.True(t, filter(identity)) }) t.Run("Identity is not authorized by all principals", func(t *testing.T) { filter := filterForPrincipalSets("mychannel", &principalEvaluatorMock{}, policies.PrincipalSets{org1AndOrg2, org3AndOrg4}) require.False(t, filter(identity)) }) } func buildCollectionConfig(col2principals map[string][]*msp.MSPPrincipal) *peer.CollectionConfigPackage { collections := &peer.CollectionConfigPackage{} for col, principals := range col2principals { collections.Config = append(collections.Config, &peer.CollectionConfig{ Payload: &peer.CollectionConfig_StaticCollectionConfig{ StaticCollectionConfig: &peer.StaticCollectionConfig{ Name: col, MemberOrgsPolicy: &peer.CollectionPolicyConfig{ Payload: &peer.CollectionPolicyConfig_SignaturePolicy{ SignaturePolicy: &common.SignaturePolicyEnvelope{ Identities: principals, }, }, }, }, }, }) } return collections } func orgPrincipal(mspID string) *msp.MSPPrincipal { return &msp.MSPPrincipal{ PrincipalClassification: msp.MSPPrincipal_ROLE, Principal: protoutil.MarshalOrPanic(&msp.MSPRole{ MspIdentifier: mspID, Role: msp.MSPRole_PEER, }), } } func assertEqualPrincipalSets(t *testing.T, ps1, ps2 policies.PrincipalSet) { ps1s := fmt.Sprintf("%v", ps1) ps2s := fmt.Sprintf("%v", ps2) require.Equal(t, ps1s, ps2s) }