-
Notifications
You must be signed in to change notification settings - Fork 0
/
policy.go
298 lines (247 loc) · 9.22 KB
/
policy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policies
import (
"fmt"
"strings"
"github.com/hyperledger/fabric/common/flogging"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/protos/msp"
"github.com/op/go-logging"
"github.com/pkg/errors"
)
const (
// Path separator is used to separate policy names in paths
PathSeparator = "/"
// ChannelPrefix is used in the path of standard channel policy managers
ChannelPrefix = "Channel"
// ApplicationPrefix is used in the path of standard application policy paths
ApplicationPrefix = "Application"
// OrdererPrefix is used in the path of standard orderer policy paths
OrdererPrefix = "Orderer"
// ChannelReaders is the label for the channel's readers policy (encompassing both orderer and application readers)
ChannelReaders = PathSeparator + ChannelPrefix + PathSeparator + "Readers"
// ChannelWriters is the label for the channel's writers policy (encompassing both orderer and application writers)
ChannelWriters = PathSeparator + ChannelPrefix + PathSeparator + "Writers"
// ChannelApplicationReaders is the label for the channel's application readers policy
ChannelApplicationReaders = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Readers"
// ChannelApplicationWriters is the label for the channel's application writers policy
ChannelApplicationWriters = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Writers"
// ChannelApplicationAdmins is the label for the channel's application admin policy
ChannelApplicationAdmins = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Admins"
// BlockValidation is the label for the policy which should validate the block signatures for the channel
BlockValidation = PathSeparator + ChannelPrefix + PathSeparator + OrdererPrefix + PathSeparator + "BlockValidation"
)
var logger = flogging.MustGetLogger("policies")
// PrincipalSet is a collection of MSPPrincipals
type PrincipalSet []*msp.MSPPrincipal
// PrincipalSets aggregates PrincipalSets
type PrincipalSets []PrincipalSet
// ContainingOnly returns PrincipalSets that contain only principals of the given predicate
func (psSets PrincipalSets) ContainingOnly(f func(*msp.MSPPrincipal) bool) PrincipalSets {
var res PrincipalSets
for _, set := range psSets {
if !set.ContainingOnly(f) {
continue
}
res = append(res, set)
}
return res
}
// ContainingOnly returns whether the given PrincipalSet contains only Principals
// that satisfy the given predicate
func (ps PrincipalSet) ContainingOnly(f func(*msp.MSPPrincipal) bool) bool {
for _, principal := range ps {
if !f(principal) {
return false
}
}
return true
}
// UniqueSet returns a histogram that is induced by the PrincipalSet
func (ps PrincipalSet) UniqueSet() map[*msp.MSPPrincipal]int {
// Create a histogram that holds the MSPPrincipals and counts them
histogram := make(map[struct {
cls int32
principal string
}]int)
// Now, populate the histogram
for _, principal := range ps {
key := struct {
cls int32
principal string
}{
cls: int32(principal.PrincipalClassification),
principal: string(principal.Principal),
}
histogram[key]++
}
// Finally, convert to a histogram of MSPPrincipal pointers
res := make(map[*msp.MSPPrincipal]int)
for principal, count := range histogram {
res[&msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_Classification(principal.cls),
Principal: []byte(principal.principal),
}] = count
}
return res
}
// Policy is used to determine if a signature is valid
type Policy interface {
// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
Evaluate(signatureSet []*cb.SignedData) error
}
// InquireablePolicy is a Policy that one can inquire
type InquireablePolicy interface {
// SatisfiedBy returns a slice of PrincipalSets that each of them
// satisfies the policy.
SatisfiedBy() []PrincipalSet
}
// Manager is a read only subset of the policy ManagerImpl
type Manager interface {
// GetPolicy returns a policy and true if it was the policy requested, or false if it is the default policy
GetPolicy(id string) (Policy, bool)
// Manager returns the sub-policy manager for a given path and whether it exists
Manager(path []string) (Manager, bool)
}
// Provider provides the backing implementation of a policy
type Provider interface {
// NewPolicy creates a new policy based on the policy bytes
NewPolicy(data []byte) (Policy, proto.Message, error)
}
// ChannelPolicyManagerGetter is a support interface
// to get access to the policy manager of a given channel
type ChannelPolicyManagerGetter interface {
// Returns the policy manager associated to the passed channel
// and true if it was the manager requested, or false if it is the default manager
Manager(channelID string) (Manager, bool)
}
// ManagerImpl is an implementation of Manager and configtx.ConfigHandler
// In general, it should only be referenced as an Impl for the configtx.ConfigManager
type ManagerImpl struct {
path string // The group level path
policies map[string]Policy
managers map[string]*ManagerImpl
}
// NewManagerImpl creates a new ManagerImpl with the given CryptoHelper
func NewManagerImpl(path string, providers map[int32]Provider, root *cb.ConfigGroup) (*ManagerImpl, error) {
var err error
_, ok := providers[int32(cb.Policy_IMPLICIT_META)]
if ok {
logger.Panicf("ImplicitMetaPolicy type must be provider by the policy manager")
}
managers := make(map[string]*ManagerImpl)
for groupName, group := range root.Groups {
managers[groupName], err = NewManagerImpl(path+PathSeparator+groupName, providers, group)
if err != nil {
return nil, err
}
}
policies := make(map[string]Policy)
for policyName, configPolicy := range root.Policies {
policy := configPolicy.Policy
if policy == nil {
return nil, fmt.Errorf("policy %s at path %s was nil", policyName, path)
}
var cPolicy Policy
if policy.Type == int32(cb.Policy_IMPLICIT_META) {
imp, err := newImplicitMetaPolicy(policy.Value, managers)
if err != nil {
return nil, errors.Wrapf(err, "implicit policy %s at path %s did not compile", policyName, path)
}
cPolicy = imp
} else {
provider, ok := providers[int32(policy.Type)]
if !ok {
return nil, fmt.Errorf("policy %s at path %s has unknown policy type: %v", policyName, path, policy.Type)
}
var err error
cPolicy, _, err = provider.NewPolicy(policy.Value)
if err != nil {
return nil, errors.Wrapf(err, "policy %s at path %s did not compile", policyName, path)
}
}
policies[policyName] = cPolicy
logger.Debugf("Proposed new policy %s for %s", policyName, path)
}
for groupName, manager := range managers {
for policyName, policy := range manager.policies {
policies[groupName+PathSeparator+policyName] = policy
}
}
return &ManagerImpl{
path: path,
policies: policies,
managers: managers,
}, nil
}
type rejectPolicy string
func (rp rejectPolicy) Evaluate(signedData []*cb.SignedData) error {
return fmt.Errorf("No such policy: '%s'", rp)
}
// Manager returns the sub-policy manager for a given path and whether it exists
func (pm *ManagerImpl) Manager(path []string) (Manager, bool) {
logger.Debugf("Manager %s looking up path %v", pm.path, path)
for manager := range pm.managers {
logger.Debugf("Manager %s has managers %s", pm.path, manager)
}
if len(path) == 0 {
return pm, true
}
m, ok := pm.managers[path[0]]
if !ok {
return nil, false
}
return m.Manager(path[1:])
}
type policyLogger struct {
policy Policy
policyName string
}
func (pl *policyLogger) Evaluate(signatureSet []*cb.SignedData) error {
if logger.IsEnabledFor(logging.DEBUG) {
logger.Debugf("== Evaluating %T Policy %s ==", pl.policy, pl.policyName)
defer logger.Debugf("== Done Evaluating %T Policy %s", pl.policy, pl.policyName)
}
err := pl.policy.Evaluate(signatureSet)
if err != nil {
logger.Debugf("Signature set did not satisfy policy %s", pl.policyName)
} else {
logger.Debugf("Signature set satisfies policy %s", pl.policyName)
}
return err
}
// GetPolicy returns a policy and true if it was the policy requested, or false if it is the default reject policy
func (pm *ManagerImpl) GetPolicy(id string) (Policy, bool) {
if id == "" {
logger.Errorf("Returning dummy reject all policy because no policy ID supplied")
return rejectPolicy(id), false
}
var relpath string
if strings.HasPrefix(id, PathSeparator) {
if !strings.HasPrefix(id, PathSeparator+pm.path) {
if logger.IsEnabledFor(logging.DEBUG) {
logger.Debugf("Requested absolute policy %s from %s, returning rejectAll", id, pm.path)
}
return rejectPolicy(id), false
}
// strip off the leading slash, the path, and the trailing slash
relpath = id[1+len(pm.path)+1:]
} else {
relpath = id
}
policy, ok := pm.policies[relpath]
if !ok {
if logger.IsEnabledFor(logging.DEBUG) {
logger.Debugf("Returning dummy reject all policy because %s could not be found in %s/%s", id, pm.path, relpath)
}
return rejectPolicy(relpath), false
}
return &policyLogger{
policy: policy,
policyName: PathSeparator + pm.path + PathSeparator + relpath,
}, true
}