/* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package policies import ( "fmt" "reflect" "strconv" "testing" "github.com/golang/protobuf/proto" cb "github.com/hyperledger/fabric-protos-go/common" "github.com/hyperledger/fabric-protos-go/msp" "github.com/hyperledger/fabric/common/crypto/tlsgen" "github.com/hyperledger/fabric/common/flogging/floggingtest" "github.com/hyperledger/fabric/common/policies/mocks" mspi "github.com/hyperledger/fabric/msp" "github.com/hyperledger/fabric/protoutil" "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" ) //go:generate counterfeiter -o mocks/identity_deserializer.go --fake-name IdentityDeserializer . identityDeserializer type identityDeserializer interface { mspi.IdentityDeserializer } //go:generate counterfeiter -o mocks/identity.go --fake-name Identity . identity type identity interface { mspi.Identity } type mockProvider struct{} func (mpp mockProvider) NewPolicy(data []byte) (Policy, proto.Message, error) { return nil, nil, nil } const mockType = int32(0) func defaultProviders() map[int32]Provider { providers := make(map[int32]Provider) providers[mockType] = &mockProvider{} return providers } func TestUnnestedManager(t *testing.T) { config := &cb.ConfigGroup{ Policies: map[string]*cb.ConfigPolicy{ "1": {Policy: &cb.Policy{Type: mockType}}, "2": {Policy: &cb.Policy{Type: mockType}}, "3": {Policy: &cb.Policy{Type: mockType}}, }, } m, err := NewManagerImpl("test", defaultProviders(), config) require.NoError(t, err) require.NotNil(t, m) _, ok := m.Manager([]string{"subGroup"}) require.False(t, ok, "Should not have found a subgroup manager") r, ok := m.Manager([]string{}) require.True(t, ok, "Should have found the root manager") require.Equal(t, m, r) require.Len(t, m.Policies, len(config.Policies)) for policyName := range config.Policies { _, ok := m.GetPolicy(policyName) require.True(t, ok, "Should have found policy %s", policyName) } } func TestNestedManager(t *testing.T) { config := &cb.ConfigGroup{ Policies: map[string]*cb.ConfigPolicy{ "n0a": {Policy: &cb.Policy{Type: mockType}}, "n0b": {Policy: &cb.Policy{Type: mockType}}, "n0c": {Policy: &cb.Policy{Type: mockType}}, }, Groups: map[string]*cb.ConfigGroup{ "nest1": { Policies: map[string]*cb.ConfigPolicy{ "n1a": {Policy: &cb.Policy{Type: mockType}}, "n1b": {Policy: &cb.Policy{Type: mockType}}, "n1c": {Policy: &cb.Policy{Type: mockType}}, }, Groups: map[string]*cb.ConfigGroup{ "nest2a": { Policies: map[string]*cb.ConfigPolicy{ "n2a_1": {Policy: &cb.Policy{Type: mockType}}, "n2a_2": {Policy: &cb.Policy{Type: mockType}}, "n2a_3": {Policy: &cb.Policy{Type: mockType}}, }, }, "nest2b": { Policies: map[string]*cb.ConfigPolicy{ "n2b_1": {Policy: &cb.Policy{Type: mockType}}, "n2b_2": {Policy: &cb.Policy{Type: mockType}}, "n2b_3": {Policy: &cb.Policy{Type: mockType}}, }, }, }, }, }, } m, err := NewManagerImpl("nest0", defaultProviders(), config) require.NoError(t, err) require.NotNil(t, m) r, ok := m.Manager([]string{}) require.True(t, ok, "Should have found the root manager") require.Equal(t, m, r) n1, ok := m.Manager([]string{"nest1"}) require.True(t, ok) n2a, ok := m.Manager([]string{"nest1", "nest2a"}) require.True(t, ok) n2b, ok := m.Manager([]string{"nest1", "nest2b"}) require.True(t, ok) n2as, ok := n1.Manager([]string{"nest2a"}) require.True(t, ok) require.Equal(t, n2a, n2as) n2bs, ok := n1.Manager([]string{"nest2b"}) require.True(t, ok) require.Equal(t, n2b, n2bs) absPrefix := PathSeparator + "nest0" + PathSeparator for policyName := range config.Policies { _, ok := m.GetPolicy(policyName) require.True(t, ok, "Should have found policy %s", policyName) absName := absPrefix + policyName _, ok = m.GetPolicy(absName) require.True(t, ok, "Should have found absolute policy %s", absName) } for policyName := range config.Groups["nest1"].Policies { _, ok := n1.GetPolicy(policyName) require.True(t, ok, "Should have found policy %s", policyName) relPathFromBase := "nest1" + PathSeparator + policyName _, ok = m.GetPolicy(relPathFromBase) require.True(t, ok, "Should have found policy %s", policyName) for i, abs := range []Manager{n1, m} { absName := absPrefix + relPathFromBase _, ok = abs.GetPolicy(absName) require.True(t, ok, "Should have found absolutely policy for manager %d", i) } } for policyName := range config.Groups["nest1"].Groups["nest2a"].Policies { _, ok := n2a.GetPolicy(policyName) require.True(t, ok, "Should have found policy %s", policyName) relPathFromN1 := "nest2a" + PathSeparator + policyName _, ok = n1.GetPolicy(relPathFromN1) require.True(t, ok, "Should have found policy %s", policyName) relPathFromBase := "nest1" + PathSeparator + relPathFromN1 _, ok = m.GetPolicy(relPathFromBase) require.True(t, ok, "Should have found policy %s", policyName) for i, abs := range []Manager{n2a, n1, m} { absName := absPrefix + relPathFromBase _, ok = abs.GetPolicy(absName) require.True(t, ok, "Should have found absolutely policy for manager %d", i) } } for policyName := range config.Groups["nest1"].Groups["nest2b"].Policies { _, ok := n2b.GetPolicy(policyName) require.True(t, ok, "Should have found policy %s", policyName) relPathFromN1 := "nest2b" + PathSeparator + policyName _, ok = n1.GetPolicy(relPathFromN1) require.True(t, ok, "Should have found policy %s", policyName) relPathFromBase := "nest1" + PathSeparator + relPathFromN1 _, ok = m.GetPolicy(relPathFromBase) require.True(t, ok, "Should have found policy %s", policyName) for i, abs := range []Manager{n2b, n1, m} { absName := absPrefix + relPathFromBase _, ok = abs.GetPolicy(absName) require.True(t, ok, "Should have found absolutely policy for manager %d", i) } } } func TestPrincipalUniqueSet(t *testing.T) { var principalSet PrincipalSet addPrincipal := func(i int) { principalSet = append(principalSet, &msp.MSPPrincipal{ PrincipalClassification: msp.MSPPrincipal_Classification(i), Principal: []byte(fmt.Sprintf("%d", i)), }) } addPrincipal(1) addPrincipal(2) addPrincipal(2) addPrincipal(3) addPrincipal(3) addPrincipal(3) for principal, plurality := range principalSet.UniqueSet() { require.Equal(t, int(principal.PrincipalClassification), plurality) require.Equal(t, fmt.Sprintf("%d", plurality), string(principal.Principal)) } v := reflect.Indirect(reflect.ValueOf(msp.MSPPrincipal{})) // Ensure msp.MSPPrincipal has only 2 fields. // This is essential for 'UniqueSet' to work properly // XXX This is a rather brittle check and brittle way to fix the test // There seems to be an assumption that the number of fields in the proto // struct matches the number of fields in the proto message require.Equal(t, 5, v.NumField()) } func TestPrincipalSetContainingOnly(t *testing.T) { var principalSets PrincipalSets var principalSet PrincipalSet for j := 0; j < 3; j++ { for i := 0; i < 10; i++ { principalSet = append(principalSet, &msp.MSPPrincipal{ PrincipalClassification: msp.MSPPrincipal_IDENTITY, Principal: []byte(fmt.Sprintf("%d", j*10+i)), }) } principalSets = append(principalSets, principalSet) principalSet = nil } between20And30 := func(principal *msp.MSPPrincipal) bool { n, _ := strconv.ParseInt(string(principal.Principal), 10, 32) return n >= 20 && n <= 29 } principalSets = principalSets.ContainingOnly(between20And30) require.Len(t, principalSets, 1) require.True(t, principalSets[0].ContainingOnly(between20And30)) } func TestSignatureSetToValidIdentities(t *testing.T) { sd := []*protoutil.SignedData{ { Data: []byte("data1"), Identity: []byte("identity1"), Signature: []byte("signature1"), }, { Data: []byte("data1"), Identity: []byte("identity1"), Signature: []byte("signature1"), }, } fIDDs := &mocks.IdentityDeserializer{} fID := &mocks.Identity{} fID.VerifyReturns(nil) fID.GetIdentifierReturns(&mspi.IdentityIdentifier{ Id: "id", Mspid: "mspid", }) fIDDs.DeserializeIdentityReturns(fID, nil) ids := SignatureSetToValidIdentities(sd, fIDDs) require.Len(t, ids, 1) require.NotNil(t, ids[0].GetIdentifier()) require.Equal(t, "id", ids[0].GetIdentifier().Id) require.Equal(t, "mspid", ids[0].GetIdentifier().Mspid) data, sig := fID.VerifyArgsForCall(0) require.Equal(t, []byte("data1"), data) require.Equal(t, []byte("signature1"), sig) sidBytes := fIDDs.DeserializeIdentityArgsForCall(0) require.Equal(t, []byte("identity1"), sidBytes) } func TestSignatureSetToValidIdentitiesDeserializeErr(t *testing.T) { oldLogger := logger l, recorder := floggingtest.NewTestLogger(t, floggingtest.AtLevel(zapcore.InfoLevel)) logger = l defer func() { logger = oldLogger }() fakeIdentityDeserializer := &mocks.IdentityDeserializer{} fakeIdentityDeserializer.DeserializeIdentityReturns(nil, errors.New("mango")) // generate actual x509 certificate ca, err := tlsgen.NewCA() require.NoError(t, err) client1, err := ca.NewClientCertKeyPair() require.NoError(t, err) id := &msp.SerializedIdentity{ Mspid: "MyMSP", IdBytes: client1.Cert, } idBytes, err := proto.Marshal(id) require.NoError(t, err) tests := []struct { spec string signedData []*protoutil.SignedData expectedLogEntryContains []string }{ { spec: "deserialize identity error - identity is random bytes", signedData: []*protoutil.SignedData{ { Identity: []byte("identity1"), }, }, expectedLogEntryContains: []string{"invalid identity", fmt.Sprintf("serialized-identity=%x", []byte("identity1")), "error=mango"}, }, { spec: "deserialize identity error - actual certificate", signedData: []*protoutil.SignedData{ { Identity: idBytes, }, }, expectedLogEntryContains: []string{"invalid identity", fmt.Sprintf("mspid=MyMSP subject=%s issuer=%s serialnumber=%d", client1.TLSCert.Subject, client1.TLSCert.Issuer, client1.TLSCert.SerialNumber), "error=mango"}, }, } for _, tc := range tests { t.Run(tc.spec, func(t *testing.T) { ids := SignatureSetToValidIdentities(tc.signedData, fakeIdentityDeserializer) require.Len(t, ids, 0) assertLogContains(t, recorder, tc.expectedLogEntryContains...) }) } } func TestSignatureSetToValidIdentitiesVerifyErr(t *testing.T) { sd := []*protoutil.SignedData{ { Data: []byte("data1"), Identity: []byte("identity1"), Signature: []byte("signature1"), }, } fIDDs := &mocks.IdentityDeserializer{} fID := &mocks.Identity{} fID.VerifyReturns(errors.New("bad signature")) fID.GetIdentifierReturns(&mspi.IdentityIdentifier{ Id: "id", Mspid: "mspid", }) fIDDs.DeserializeIdentityReturns(fID, nil) ids := SignatureSetToValidIdentities(sd, fIDDs) require.Len(t, ids, 0) data, sig := fID.VerifyArgsForCall(0) require.Equal(t, []byte("data1"), data) require.Equal(t, []byte("signature1"), sig) sidBytes := fIDDs.DeserializeIdentityArgsForCall(0) require.Equal(t, []byte("identity1"), sidBytes) } func assertLogContains(t *testing.T, r *floggingtest.Recorder, ss ...string) { defer r.Reset() entries := r.Entries() for _, entry := range entries { fmt.Println(entry) } for _, s := range ss { require.NotEmpty(t, r.EntriesContaining(s)) } }