Skip to content

Commit 6dc9301

Browse files
Matthias Neugschwandtneryacovm
authored andcommitted
[FAB-5869] Implement a base collection object
This simple collection object implements a base collection type. It is set up using a StaticCollectionConfig, with the member access policy defined by a SignaturePolicyEnvelope. Change-Id: I96acb2dc5730b3bc4aa731c2fd34d6faf4f96b24 Signed-off-by: Matthias Neugschwandtner <eug@zurich.ibm.com>
1 parent 8cdcd5e commit 6dc9301

File tree

11 files changed

+371
-59
lines changed

11 files changed

+371
-59
lines changed

core/common/privdata/collection.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,29 @@ type Collection interface {
1616
// as txid, nonce, creator -- for future use
1717
// SetTxContext(parameters ...interface{})
1818

19-
// GetCollectionID returns this collection's ID
20-
GetCollectionID() string
19+
// CollectionID returns this collection's ID
20+
CollectionID() string
2121

2222
// GetEndorsementPolicy returns the endorsement policy for validation -- for
2323
// future use
2424
// GetEndorsementPolicy() string
2525

26-
// GetMemberOrgs returns the collection's members as MSP IDs. This serves as
26+
// MemberOrgs returns the collection's members as MSP IDs. This serves as
2727
// a human-readable way of quickly identifying who is part of a collection.
28-
GetMemberOrgs() []string
28+
MemberOrgs() []string
2929
}
3030

31-
// CollectionAccess encapsulates functions for the access policy of a collection
31+
// CollectionAccessPolicy encapsulates functions for the access policy of a collection
3232
type CollectionAccessPolicy interface {
33-
// GetAccessFilter returns a member filter function for a collection
34-
GetAccessFilter() Filter
33+
// AccessFilter returns a member filter function for a collection
34+
AccessFilter() Filter
3535

3636
// RequiredExternalPeerCount returns the minimum number of external peers
37-
// required to hold private data
37+
// required to send private data to
3838
RequiredExternalPeerCount() int
3939

4040
// RequiredExternalPeerCount returns the minimum number of internal peers
41-
// required to hold private data
41+
// required to send private data to
4242
RequiredInternalPeerCount() int
4343
}
4444

@@ -59,8 +59,8 @@ type CollectionStore interface {
5959
// latest configuration that was committed into the ledger before this txID
6060
// was committed.
6161
// Else - it's the latest configuration for the collection.
62-
GetCollection(common.CollectionCriteria) Collection
62+
RetrieveCollection(common.CollectionCriteria) Collection
6363

6464
// GetCollectionAccessPolicy retrieves a collection's access policy
65-
GetCollectionAccessPolicy(common.CollectionCriteria) CollectionAccessPolicy
65+
RetrieveCollectionAccessPolicy(common.CollectionCriteria) CollectionAccessPolicy
6666
}

core/common/privdata/nopcollection.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,11 @@ import (
1515
type NopCollection struct {
1616
}
1717

18-
func (nc *NopCollection) GetCollectionID() string {
18+
func (nc *NopCollection) CollectionID() string {
1919
return ""
2020
}
2121

22-
func (nc *NopCollection) GetEndorsementPolicy() string {
23-
return ""
24-
}
25-
26-
func (nc *NopCollection) GetMemberOrgs() []string {
22+
func (nc *NopCollection) MemberOrgs() []string {
2723
return nil
2824
}
2925

@@ -35,7 +31,7 @@ func (nc *NopCollection) RequiredInternalPeerCount() int {
3531
return viper.GetInt("peer.gossip.pvtData.minInternalPeers")
3632
}
3733

38-
func (nc *NopCollection) GetAccessFilter() Filter {
34+
func (nc *NopCollection) AccessFilter() Filter {
3935
// return true for all
4036
return func(common.SignedData) bool {
4137
return true
@@ -45,10 +41,10 @@ func (nc *NopCollection) GetAccessFilter() Filter {
4541
type NopCollectionStore struct {
4642
}
4743

48-
func (*NopCollectionStore) GetCollection(common.CollectionCriteria) Collection {
44+
func (*NopCollectionStore) RetrieveCollection(common.CollectionCriteria) Collection {
4945
return &NopCollection{}
5046
}
5147

52-
func (*NopCollectionStore) GetCollectionAccessPolicy(common.CollectionCriteria) CollectionAccessPolicy {
48+
func (*NopCollectionStore) RetrieveCollectionAccessPolicy(common.CollectionCriteria) CollectionAccessPolicy {
5349
return &NopCollection{}
5450
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package privdata
8+
9+
import (
10+
"fmt"
11+
12+
"github.com/golang/protobuf/proto"
13+
"github.com/hyperledger/fabric/common/cauthdsl"
14+
"github.com/hyperledger/fabric/common/policies"
15+
"github.com/hyperledger/fabric/msp"
16+
"github.com/hyperledger/fabric/protos/common"
17+
m "github.com/hyperledger/fabric/protos/msp"
18+
"github.com/pkg/errors"
19+
)
20+
21+
// SimpleCollection implements a collection with static properties
22+
// and a public member set
23+
type SimpleCollection struct {
24+
name string
25+
accessPolicy policies.Policy
26+
memberOrgs []string
27+
requiredExternalPeerCount int
28+
requiredInternalPeerCount int
29+
}
30+
31+
// CollectionID returns the collection's ID
32+
func (sc *SimpleCollection) CollectionID() string {
33+
return sc.name
34+
}
35+
36+
// MemberOrgs returns the MSP IDs that are part of this collection
37+
func (sc *SimpleCollection) MemberOrgs() []string {
38+
return sc.memberOrgs
39+
}
40+
41+
// RequiredExternalPeerCount returns the minimum number of external peers
42+
// required to send private data to
43+
func (sc *SimpleCollection) RequiredExternalPeerCount() int {
44+
return sc.requiredExternalPeerCount
45+
}
46+
47+
// RequiredInternalPeerCount returns the minimum number of internal peers
48+
// required to send private data to
49+
func (sc *SimpleCollection) RequiredInternalPeerCount() int {
50+
return sc.requiredInternalPeerCount
51+
}
52+
53+
// AccessFilter returns the member filter function that evaluates signed data
54+
// against the member access policy of this collection
55+
func (sc *SimpleCollection) AccessFilter() Filter {
56+
return func(sd common.SignedData) bool {
57+
if err := sc.accessPolicy.Evaluate([]*common.SignedData{&sd}); err != nil {
58+
return false
59+
}
60+
return true
61+
}
62+
}
63+
64+
// Setup configures a simple collection object based on a given
65+
// StaticCollectionConfig proto that has all the necessary information
66+
func (sc *SimpleCollection) Setup(collectionConfig *common.StaticCollectionConfig, deserializer msp.IdentityDeserializer) error {
67+
if collectionConfig == nil {
68+
return errors.New("Nil config passed to collection setup")
69+
}
70+
sc.name = collectionConfig.GetName()
71+
72+
// get the access signature policy envelope
73+
collectionPolicyConfig := collectionConfig.GetMemberOrgsPolicy()
74+
if collectionPolicyConfig == nil {
75+
return errors.New("Collection config policy is nil")
76+
}
77+
accessPolicyEnvelope := collectionPolicyConfig.GetSignaturePolicy()
78+
if accessPolicyEnvelope == nil {
79+
return errors.New("Collection config access policy is nil")
80+
}
81+
82+
// create access policy from the envelope
83+
npp := cauthdsl.NewPolicyProvider(deserializer)
84+
polBytes, err := proto.Marshal(accessPolicyEnvelope)
85+
if err != nil {
86+
return err
87+
}
88+
sc.accessPolicy, _, err = npp.NewPolicy(polBytes)
89+
if err != nil {
90+
return err
91+
}
92+
93+
// get member org MSP IDs from the envelope
94+
for _, principal := range accessPolicyEnvelope.Identities {
95+
switch principal.PrincipalClassification {
96+
case m.MSPPrincipal_ROLE:
97+
// Principal contains the msp role
98+
mspRole := &m.MSPRole{}
99+
err := proto.Unmarshal(principal.Principal, mspRole)
100+
if err != nil {
101+
return errors.Wrap(err, "Could not unmarshal MSPRole from principal")
102+
}
103+
sc.memberOrgs = append(sc.memberOrgs, mspRole.MspIdentifier)
104+
case m.MSPPrincipal_IDENTITY:
105+
principalId, err := deserializer.DeserializeIdentity(principal.Principal)
106+
if err != nil {
107+
return errors.Wrap(err, "Invalid identity principal, not a certificate")
108+
}
109+
sc.memberOrgs = append(sc.memberOrgs, principalId.GetMSPIdentifier())
110+
case m.MSPPrincipal_ORGANIZATION_UNIT:
111+
OU := &m.OrganizationUnit{}
112+
err := proto.Unmarshal(principal.Principal, OU)
113+
if err != nil {
114+
return errors.Wrap(err, "Could not unmarshal OrganizationUnit from principal")
115+
}
116+
sc.memberOrgs = append(sc.memberOrgs, OU.MspIdentifier)
117+
default:
118+
return errors.New(fmt.Sprintf("Invalid principal type %d", int32(principal.PrincipalClassification)))
119+
}
120+
}
121+
122+
// set required peer counts
123+
sc.requiredInternalPeerCount = int(collectionConfig.GetRequiredInternalPeerCount())
124+
sc.requiredExternalPeerCount = int(collectionConfig.GetRequiredExternalPeerCount())
125+
126+
return nil
127+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package privdata
8+
9+
import (
10+
"bytes"
11+
"errors"
12+
"testing"
13+
"time"
14+
15+
"github.com/hyperledger/fabric/common/cauthdsl"
16+
"github.com/hyperledger/fabric/msp"
17+
pb "github.com/hyperledger/fabric/protos/common"
18+
mb "github.com/hyperledger/fabric/protos/msp"
19+
"github.com/stretchr/testify/assert"
20+
)
21+
22+
func createCollectionPolicyConfig(accessPolicy *pb.SignaturePolicyEnvelope) *pb.CollectionPolicyConfig {
23+
cpcSp := &pb.CollectionPolicyConfig_SignaturePolicy{
24+
SignaturePolicy: accessPolicy,
25+
}
26+
cpc := &pb.CollectionPolicyConfig{
27+
Payload: cpcSp,
28+
}
29+
return cpc
30+
}
31+
32+
type mockIdentity struct {
33+
idBytes []byte
34+
}
35+
36+
func (id *mockIdentity) ExpiresAt() time.Time {
37+
return time.Time{}
38+
}
39+
40+
func (id *mockIdentity) SatisfiesPrincipal(p *mb.MSPPrincipal) error {
41+
if bytes.Compare(id.idBytes, p.Principal) == 0 {
42+
return nil
43+
}
44+
return errors.New("Principals do not match")
45+
}
46+
47+
func (id *mockIdentity) GetIdentifier() *msp.IdentityIdentifier {
48+
return &msp.IdentityIdentifier{Mspid: "Mock", Id: string(id.idBytes)}
49+
}
50+
51+
func (id *mockIdentity) GetMSPIdentifier() string {
52+
return string(id.idBytes)
53+
}
54+
55+
func (id *mockIdentity) Validate() error {
56+
return nil
57+
}
58+
59+
func (id *mockIdentity) GetOrganizationalUnits() []*msp.OUIdentifier {
60+
return nil
61+
}
62+
63+
func (id *mockIdentity) Verify(msg []byte, sig []byte) error {
64+
if bytes.Compare(sig, []byte("badsigned")) == 0 {
65+
return errors.New("Invalid signature")
66+
}
67+
return nil
68+
}
69+
70+
func (id *mockIdentity) Serialize() ([]byte, error) {
71+
return id.idBytes, nil
72+
}
73+
74+
type mockDeserializer struct {
75+
fail error
76+
}
77+
78+
func (md *mockDeserializer) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) {
79+
if md.fail != nil {
80+
return nil, md.fail
81+
}
82+
return &mockIdentity{idBytes: serializedIdentity}, nil
83+
}
84+
85+
func TestSetupBadConfig(t *testing.T) {
86+
// set up simple collection with invalid data
87+
var sc SimpleCollection
88+
err := sc.Setup(&pb.StaticCollectionConfig{}, &mockDeserializer{})
89+
assert.Error(t, err)
90+
}
91+
92+
func TestSetupGoodConfigCollection(t *testing.T) {
93+
// create member access policy
94+
var signers = [][]byte{[]byte("signer0"), []byte("signer1")}
95+
policyEnvelope := cauthdsl.Envelope(cauthdsl.Or(cauthdsl.SignedBy(0), cauthdsl.SignedBy(1)), signers)
96+
accessPolicy := createCollectionPolicyConfig(policyEnvelope)
97+
98+
// create static collection config
99+
collectionConfig := &pb.StaticCollectionConfig{
100+
Name: "test collection",
101+
RequiredInternalPeerCount: 1,
102+
RequiredExternalPeerCount: 1,
103+
MemberOrgsPolicy: accessPolicy,
104+
}
105+
106+
// set up simple collection with valid data
107+
var sc SimpleCollection
108+
err := sc.Setup(collectionConfig, &mockDeserializer{})
109+
assert.NoError(t, err)
110+
111+
// check name
112+
assert.True(t, sc.CollectionID() == "test collection")
113+
114+
// check members
115+
members := sc.MemberOrgs()
116+
assert.True(t, members[0] == "signer0")
117+
assert.True(t, members[1] == "signer1")
118+
119+
// check required peer count
120+
assert.True(t, sc.RequiredInternalPeerCount() == 1)
121+
assert.True(t, sc.RequiredExternalPeerCount() == 1)
122+
}
123+
124+
func TestSimpleCollectionFilter(t *testing.T) {
125+
// create member access policy
126+
var signers = [][]byte{[]byte("signer0"), []byte("signer1")}
127+
policyEnvelope := cauthdsl.Envelope(cauthdsl.Or(cauthdsl.SignedBy(0), cauthdsl.SignedBy(1)), signers)
128+
accessPolicy := createCollectionPolicyConfig(policyEnvelope)
129+
130+
// create static collection config
131+
collectionConfig := &pb.StaticCollectionConfig{
132+
Name: "test collection",
133+
RequiredInternalPeerCount: 1,
134+
RequiredExternalPeerCount: 1,
135+
MemberOrgsPolicy: accessPolicy,
136+
}
137+
138+
// set up simple collection
139+
var sc SimpleCollection
140+
err := sc.Setup(collectionConfig, &mockDeserializer{})
141+
assert.NoError(t, err)
142+
143+
// get the collection access filter
144+
var cap CollectionAccessPolicy
145+
cap = &sc
146+
accessFilter := cap.AccessFilter()
147+
148+
// check filter: not a member of the collection
149+
notMember := pb.SignedData{
150+
Identity: []byte{1, 2, 3},
151+
Signature: []byte{},
152+
Data: []byte{},
153+
}
154+
assert.False(t, accessFilter(notMember))
155+
156+
// check filter: member of the collection
157+
member := pb.SignedData{
158+
Identity: signers[0],
159+
Signature: []byte{},
160+
Data: []byte{},
161+
}
162+
assert.True(t, accessFilter(member))
163+
}

0 commit comments

Comments
 (0)