-
Notifications
You must be signed in to change notification settings - Fork 0
/
rscc.go
308 lines (246 loc) · 8.68 KB
/
rscc.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
299
300
301
302
303
304
305
306
307
308
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package rscc
import (
"fmt"
"sync"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/resourcesconfig"
"github.com/hyperledger/fabric/core/aclmgmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/msp/mgmt"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
)
const (
//CHANNEL name
CHANNEL = "channel"
//POLICY for the channel
POLICY = "policy"
//Init Errors (for UT)
//NOCHANNEL channel not found
NOCHANNEL = "nochannel"
//NOPOLICY policy not found for channel
NOPOLICY = "nopolicy"
//BADPOLICY bad policy
BADPOLICY = "badpolicy"
)
//the basic policyProvider for the channel, consists of a
//default and rscc policy providers
type policyProvider struct {
//maybe we should use a separate default policy provider
//than the only from aclmgmt (which is 1.0 defaults)
defaultProvider aclmgmt.ACLProvider
rsccProvider rsccPolicyProvider
}
//Rscc SCC implementing resouce->Policy mapping for the fabric
type Rscc struct {
sync.RWMutex
//the cache of RSCC policies for all the channels
policyCache map[string]*policyProvider
}
var rsccLogger = flogging.MustGetLogger("rscc")
//NewRscc get an initialzed new Rscc
func NewRscc() *Rscc {
return &Rscc{policyCache: make(map[string]*policyProvider)}
}
//--------- errors ---------
//NoPolicyProviderInCache in cache for channel
type NoPolicyProviderInCache string
func (e NoPolicyProviderInCache) Error() string {
return fmt.Sprintf("cannot find policy provider in cache for channel %s", string(e))
}
//PolicyProviderNotFound for channel
type PolicyProviderNotFound string
func (e PolicyProviderNotFound) Error() string {
return fmt.Sprintf("cannot find policy provider for channel %s", string(e))
}
//-------- ACLProvider interface ------
//CheckACL rscc implements AClProvider's CheckACL interface. This is the key interface
// . CheckACL works off the cache
// . CheckACL uses two providers - the RSCC provider from channel config and default provider
// that implements 1.0 functions
// . If a resource in RSCC Provider it'll use the policy defined there. Otherwise it'll defer
// to default provider
func (rscc *Rscc) CheckACL(resName string, channelID string, idinfo interface{}) error {
rsccLogger.Debugf("rscc acl check(%s, %s)", resName, channelID)
rscc.RLock()
defer rscc.RUnlock()
pp := rscc.policyCache[channelID]
if pp == nil {
return NoPolicyProviderInCache(channelID)
}
//found policyProvider
if pp.rsccProvider != nil {
//get the policy mapping if any
if policyName := pp.rsccProvider.GetPolicyName(resName); policyName != "" {
return pp.rsccProvider.CheckACL(policyName, idinfo)
}
}
//try default provider
if pp.defaultProvider != nil {
return pp.defaultProvider.CheckACL(resName, channelID, idinfo)
}
return PolicyProviderNotFound(channelID)
}
//GenerateSimulationResults called to add config state. Currently only handles "join" requests.
//Note that this is just a ledger hook and does not modify RSCC data structures
func (rscc *Rscc) GenerateSimulationResults(txEnv *common.Envelope, sim ledger.TxSimulator) error {
//should never happen, but check anyway
if txEnv == nil || sim == nil {
return fmt.Errorf("nil parameters")
}
payl := &common.Payload{}
if err := proto.Unmarshal(txEnv.Payload, payl); err != nil {
rsccLogger.Errorf("error on Payload unmarshal %s", err)
return err
}
cenv := &common.ConfigEnvelope{}
if err := proto.Unmarshal(payl.Data, cenv); err != nil {
rsccLogger.Errorf("error on ConfigEnvelope unmarshal %s", err)
return err
}
if cenv.Config.Sequence != 1 {
rsccLogger.Errorf("ignore non genesis block config updates (%d) for modifying resource policies", cenv.Config.Sequence)
return nil
}
if cenv.LastUpdate == nil {
rsccLogger.Errorf("nil LastUpdate")
return fmt.Errorf("nil LastUpdate")
}
if err := proto.Unmarshal(cenv.LastUpdate.Payload, payl); err != nil {
rsccLogger.Errorf("error on LastUpdate unmarshal %s", err)
return err
}
coe := &common.ConfigUpdateEnvelope{}
if err := proto.Unmarshal(payl.Data, coe); err != nil {
rsccLogger.Errorf("error on ConfigUpdateEnvelope unmarshal %s", err)
return err
}
cup := &common.ConfigUpdate{}
if err := proto.Unmarshal(coe.ConfigUpdate, cup); err != nil {
rsccLogger.Errorf("error on ConfigUpdate unmarshal %s", err)
return err
}
rsccLogger.Debugf("RSCC processing config tx for channel %s", cup.ChannelId)
//preparation for extracting RWSet from transaction
if err := sim.SetState("rscc", CHANNEL, []byte(cup.ChannelId)); err != nil {
rsccLogger.Errorf("error on setting channel state %s", err)
return err
}
iso := cup.IsolatedData
if err := sim.SetState("rscc", POLICY, iso[pb.RSCCSeedDataKey]); err != nil {
rsccLogger.Errorf("error on setting policy state %s", err)
return err
}
return nil
}
//---------- misc functions ---------
func (rscc *Rscc) putPolicyProvider(channel string, pp *policyProvider) {
rscc.Lock()
defer rscc.Unlock()
rscc.policyCache[channel] = pp
}
func (rscc *Rscc) setPolicyProvider(channel string, defProv aclmgmt.ACLProvider, rsccProv rsccPolicyProvider) {
pp := &policyProvider{defProv, rsccProv}
rscc.putPolicyProvider(channel, pp)
}
func (rscc *Rscc) getEnvelopeFromConfig(channel string, cfgb []byte) (*common.Envelope, error) {
cfg := &common.Config{}
err := proto.Unmarshal(cfgb, cfg)
if err != nil {
return nil, err
}
cenv := &common.ConfigEnvelope{Config: cfg}
data, err := proto.Marshal(cenv)
if err != nil {
return nil, err
}
chdr, _ := proto.Marshal(&common.ChannelHeader{
ChannelId: channel,
Type: int32(common.HeaderType_CONFIG),
})
payl, _ := proto.Marshal(&common.Payload{
Header: &common.Header{
ChannelHeader: chdr,
},
Data: data,
})
return &common.Envelope{Payload: payl}, nil
}
//create the evaluator to provide evaluation services for resources
func (rscc *Rscc) createPolicyEvaluator(env *common.Envelope, mspMgr msp.MSPManager, polMgr policies.Manager) (policyEvaluator, error) {
bundle, err := resourcesconfig.New(env, mspMgr, polMgr)
if err != nil {
return nil, err
}
return &policyEvaluatorImpl{bundle}, nil
}
//NOTE - this is the core method that should be called to set the entire
//configuration of the RSCC. For now this is at Join time only but will
//have to export with proper interfacing for update processing via Invoke flow
func (rscc *Rscc) updateConfig(channel string, cfgb []byte, defProv aclmgmt.ACLProvider) error {
var rsccProv rsccPolicyProvider
//guranteed to set policy provider at least with the default provider
defer func() {
rscc.setPolicyProvider(channel, defProv, rsccProv)
}()
env, err := rscc.getEnvelopeFromConfig(channel, cfgb)
if err != nil {
return err
}
mspMgr := mgmt.GetManagerForChain(channel)
if mspMgr == nil {
return fmt.Errorf("msp manager not found for %s", channel)
}
polMgr := peer.GetPolicyManager(channel)
if polMgr == nil {
return fmt.Errorf("policies manager not found for %s", channel)
}
//associate the evaluator with the managers
pEvaluator, err := rscc.createPolicyEvaluator(env, mspMgr, polMgr)
if err != nil {
return fmt.Errorf("cannot create provider for channel %s(%s)", channel, err)
}
//this will get set by the deferred func
rsccProv = &rsccPolicyProviderImpl{channel, pEvaluator}
return nil
}
//-------- CC interface ------------
// Init RSCC - Init is just used to initialize policy assuming it is found in the
// channel ledger. Don't return error and get out. For example, we might still serve
// Invokes to set policy
func (rscc *Rscc) Init(stub shim.ChaincodeStubInterface) pb.Response {
rsccLogger.Info("Init RSCC")
b, err := stub.GetState(CHANNEL)
if err != nil || len(b) == 0 {
rsccLogger.Errorf("cannot find channel name")
return shim.Success([]byte(NOCHANNEL))
}
channel := string(b)
defProv := aclmgmt.NewDefaultACLProvider()
b, err = stub.GetState(POLICY)
if err != nil || len(b) == 0 {
//we do not have policy, create just defaults
rscc.setPolicyProvider(channel, defProv, nil)
rsccLogger.Errorf("cannot find policy for channel %s", channel)
return shim.Success([]byte(NOPOLICY))
}
//update the config
if err = rscc.updateConfig(channel, b, defProv); err != nil {
rsccLogger.Errorf("error on update config %s\n", err)
return shim.Success([]byte(BADPOLICY))
}
return shim.Success(nil)
}
//Invoke - update policies
func (rscc *Rscc) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Error("--TBD---")
}