Skip to content

Commit c119567

Browse files
committed
[FAB-10238] Filter collections on peers, not principals
The collection support in the discovery service supports collections by filtering out principal sets that aren't authorized by the principals derived from the policy in the collection config. This is problematic in cases when the collection config is defined with principals which have no obvious IsA relation with the endorsement policy: Consider a case where the endorsement policy is defined with principals of Org1.peer or and org2.peer, and the collection is defined with OUs of Org. Any principal based policy can't accept nor reject such a scenario, since a peer role is based on an OU that is hidden from the principal itself. This change set moves the filtering to the peer identities instead of filtering the principal sets of the combinations of the endorsement policy. It also raises the code coverage from 97% to 99% Change-Id: I6d2f9f1e735f667fcffe99c3d3b6bfe6a6ed8e98 Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent 44cf4a1 commit c119567

File tree

6 files changed

+334
-235
lines changed

6 files changed

+334
-235
lines changed

common/policies/inquire/compare.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -181,18 +181,3 @@ func (cps ComparablePrincipalSet) Clone() ComparablePrincipalSet {
181181
}
182182
return res
183183
}
184-
185-
// IsCoveredBy returns whether the ComparablePrincipalSet is covered by the given ComparablePrincipalSet.
186-
// A ComparablePrincipalSet X is covered by another ComparablePrincipalSet Y if:
187-
// for each ComparablePrincipal x in X there is a ComparablePrincipal y in Y,
188-
// such that x.IsA(y) is true.
189-
// This method is useful for determining if the all elements of a ComparablePrincipalSet
190-
// are accepted by another ComparablePrincipalSet.
191-
func (cps ComparablePrincipalSet) IsCoveredBy(set ComparablePrincipalSet) bool {
192-
for _, cp := range cps {
193-
if !cp.IsFound(set...) {
194-
return false
195-
}
196-
}
197-
return true
198-
}

common/policies/inquire/compare_test.go

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -174,32 +174,6 @@ func TestNewComparablePrincipalSet(t *testing.T) {
174174
})
175175
}
176176

177-
func TestIsCoveredBy(t *testing.T) {
178-
t.Run("Covered", func(t *testing.T) {
179-
ou1 := NewComparablePrincipal(ou("Org1MSP"))
180-
peer1 := NewComparablePrincipal(peer("Org1MSP"))
181-
member1 := NewComparablePrincipal(member("Org1MSP"))
182-
member2 := NewComparablePrincipal(member("Org2MSP"))
183-
184-
covered := ComparablePrincipalSet([]*ComparablePrincipal{ou1, peer1})
185-
cover := ComparablePrincipalSet([]*ComparablePrincipal{member1, member2})
186-
187-
assert.True(t, covered.IsCoveredBy(cover))
188-
assert.False(t, cover.IsCoveredBy(covered))
189-
})
190-
191-
t.Run("Not Covered", func(t *testing.T) {
192-
peer1 := NewComparablePrincipal(peer("Org1MSP"))
193-
peer2 := NewComparablePrincipal(peer("Org1MSP"))
194-
member1 := NewComparablePrincipal(member("Org1MSP"))
195-
196-
covered := ComparablePrincipalSet([]*ComparablePrincipal{peer1, peer2})
197-
cover := ComparablePrincipalSet([]*ComparablePrincipal{member1})
198-
199-
assert.False(t, cover.IsCoveredBy(covered))
200-
})
201-
}
202-
203177
func member(orgName string) *msp.MSPPrincipal {
204178
return &msp.MSPPrincipal{
205179
PrincipalClassification: msp.MSPPrincipal_ROLE,

discovery/endorsement/collection.go

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,42 +8,27 @@ package endorsement
88

99
import (
1010
"github.com/hyperledger/fabric/common/policies"
11-
"github.com/hyperledger/fabric/common/policies/inquire"
1211
"github.com/hyperledger/fabric/core/common/privdata"
12+
"github.com/hyperledger/fabric/gossip/api"
13+
. "github.com/hyperledger/fabric/protos/discovery"
1314
"github.com/pkg/errors"
1415
)
1516

16-
type filterPrincipalSets func(collectionName string, principalSets policies.PrincipalSets) (policies.PrincipalSets, error)
17-
18-
func (f filterPrincipalSets) forCollections(ccName string, collections ...string) filterFunc {
19-
return func(principalSets policies.PrincipalSets) (policies.PrincipalSets, error) {
20-
var err error
21-
for _, col := range collections {
22-
principalSets, err = f(col, principalSets)
23-
if err != nil {
24-
logger.Warningf("Failed filtering collection for chaincode %s, collection %s: %v", ccName, col, err)
25-
return nil, err
26-
}
27-
}
28-
return principalSets, nil
29-
}
30-
}
31-
32-
func newCollectionFilter(configBytes []byte) (filterPrincipalSets, error) {
33-
mapFilter := make(principalSetsByCollectionName)
17+
func principalsFromCollectionConfig(configBytes []byte) (principalSetsByCollectionName, error) {
18+
principalSetsByCollections := make(principalSetsByCollectionName)
3419
if len(configBytes) == 0 {
35-
return mapFilter.filter, nil
20+
return principalSetsByCollections, nil
3621
}
3722
ccp, err := privdata.ParseCollectionConfig(configBytes)
3823
if err != nil {
3924
return nil, errors.Wrapf(err, "invalid collection bytes")
4025
}
41-
for _, cfg := range ccp.Config {
42-
staticCol := cfg.GetStaticCollectionConfig()
26+
for _, colConfig := range ccp.Config {
27+
staticCol := colConfig.GetStaticCollectionConfig()
4328
if staticCol == nil {
4429
// Right now we only support static collections, so if we got something else
4530
// we should refuse to process further
46-
return nil, errors.Errorf("expected a static collection but got %v instead", cfg)
31+
return nil, errors.Errorf("expected a static collection but got %v instead", colConfig)
4732
}
4833
if staticCol.MemberOrgsPolicy == nil {
4934
return nil, errors.Errorf("MemberOrgsPolicy of %s is nil", staticCol.Name)
@@ -57,31 +42,56 @@ func newCollectionFilter(configBytes []byte) (filterPrincipalSets, error) {
5742
for _, principal := range pol.Identities {
5843
principals = append(principals, principal)
5944
}
60-
principalSet := inquire.NewComparablePrincipalSet(principals)
61-
if principalSet == nil {
62-
return nil, errors.Errorf("failed constructing principal set for %s: principals given are %v", staticCol.Name, pol.Identities)
63-
}
64-
mapFilter[staticCol.Name] = principalSet
45+
principalSetsByCollections[staticCol.Name] = principals
6546
}
66-
return mapFilter.filter, nil
47+
return principalSetsByCollections, nil
6748
}
6849

69-
type principalSetsByCollectionName map[string]inquire.ComparablePrincipalSet
50+
type principalSetsByCollectionName map[string]policies.PrincipalSet
7051

71-
func (psbc principalSetsByCollectionName) filter(collectionName string, principalSets policies.PrincipalSets) (policies.PrincipalSets, error) {
72-
collectionPrincipals, exists := psbc[collectionName]
73-
if !exists {
74-
return nil, errors.Errorf("collection %s wasn't found in configuration", collectionName)
52+
// toIdentityFilter converts this principalSetsByCollectionName mapping to a filter
53+
// which accepts or rejects identities of peers.
54+
func (psbc principalSetsByCollectionName) toIdentityFilter(channel string, evaluator principalEvaluator, cc *ChaincodeCall) (identityFilter, error) {
55+
var principalSets policies.PrincipalSets
56+
for _, col := range cc.CollectionNames {
57+
// Each collection we're interested in should exist in the principalSetsByCollectionName mapping.
58+
// Otherwise, we have no way of computing a filter because we can't locate the principals the peer identities
59+
// need to satisfy.
60+
principalSet, exists := psbc[col]
61+
if !exists {
62+
return nil, errors.Errorf("collection %s doesn't exist in collection config for chaincode %s", col, cc.Name)
63+
}
64+
principalSets = append(principalSets, principalSet)
7565
}
76-
var res policies.PrincipalSets
77-
for _, ps := range principalSets {
78-
comparablePS := inquire.NewComparablePrincipalSet(ps)
79-
if comparablePS == nil {
80-
return nil, errors.Errorf("principal set %v is invalid", ps)
66+
return filterForPrincipalSets(channel, evaluator, principalSets), nil
67+
}
68+
69+
// filterForPrincipalSets creates a filter of peer identities out of the given PrincipalSets
70+
func filterForPrincipalSets(channel string, evaluator principalEvaluator, sets policies.PrincipalSets) identityFilter {
71+
return func(identity api.PeerIdentityType) bool {
72+
// Iterate over all principal sets and ensure each principal set
73+
// authorizes the identity.
74+
for _, principalSet := range sets {
75+
if !isIdentityAuthorizedByPrincipalSet(channel, evaluator, principalSet, identity) {
76+
return false
77+
}
8178
}
82-
if comparablePS.IsCoveredBy(collectionPrincipals) {
83-
res = append(res, ps)
79+
return true
80+
}
81+
}
82+
83+
// isIdentityAuthorizedByPrincipalSet returns whether the given identity satisfies some principal out of the given PrincipalSet
84+
func isIdentityAuthorizedByPrincipalSet(channel string, evaluator principalEvaluator, principalSet policies.PrincipalSet, identity api.PeerIdentityType) bool {
85+
// We look for a principal which authorizes the identity
86+
// among all principals in the principalSet
87+
for _, principal := range principalSet {
88+
err := evaluator.SatisfiesPrincipal(channel, identity, principal)
89+
if err != nil {
90+
continue
8491
}
92+
// Else, err is nil, so we found a principal which authorized
93+
// the given identity.
94+
return true
8595
}
86-
return res, nil
96+
return false
8797
}

0 commit comments

Comments
 (0)