Skip to content

Commit 2fc6bc6

Browse files
committed
[FAB-2080] - peer enforces ACLs on proposals
This change set enables the peer to validate proposal messages against channel policies. Change-Id: Ie60a9deefa5e0002dbf8125e512f3f43db78102e Signed-off-by: Alessandro Sorniotti <[email protected]>
1 parent 7c1934a commit 2fc6bc6

File tree

6 files changed

+136
-44
lines changed

6 files changed

+136
-44
lines changed

common/policies/policy.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ const (
3737

3838
// ChannelApplicationReaders is the label for the channel's application readers policy
3939
ChannelApplicationReaders = "/" + ChannelPrefix + "/" + ApplicationPrefix + "/Readers"
40+
41+
// ChannelApplicationWriters is the label for the channel's application writers policy
42+
ChannelApplicationWriters = "/" + ChannelPrefix + "/" + ApplicationPrefix + "/Writers"
4043
)
4144

4245
var logger = logging.MustGetLogger("common/policies")
@@ -248,7 +251,7 @@ func (pm *ManagerImpl) CommitProposals() {
248251
if pm.parent == nil && pm.basePath == ChannelPrefix {
249252
if _, ok := pm.config.managers[ApplicationPrefix]; ok {
250253
// Check for default application policies if the application component is defined
251-
for _, policyName := range []string{ChannelApplicationReaders} {
254+
for _, policyName := range []string{ChannelApplicationReaders, ChannelApplicationWriters} {
252255
_, ok := pm.GetPolicy(policyName)
253256
if !ok {
254257
logger.Warningf("Current configuration has no policy '%s', this will likely cause problems in production systems", policyName)

core/common/validation/msgvalidation.go

-2
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ func ValidateProposalMessage(signedProp *pb.SignedProposal) (*pb.Proposal, *comm
9090
return nil, nil, nil, err
9191
}
9292

93-
// TODO: ensure that creator can transact with us (some ACLs?) which set of APIs is supposed to give us this info?
94-
9593
// Verify that the transaction ID has been computed properly.
9694
// This check is needed to ensure that the lookup into the ledger
9795
// for the same TxID catches duplicates.

core/endorser/endorser.go

+33-16
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"errors"
2727

28+
"github.com/hyperledger/fabric/common/policies"
2829
"github.com/hyperledger/fabric/common/util"
2930
"github.com/hyperledger/fabric/core/chaincode"
3031
"github.com/hyperledger/fabric/core/chaincode/shim"
@@ -54,8 +55,29 @@ func NewEndorserServer() pb.EndorserServer {
5455
return e
5556
}
5657

57-
//TODO - what would Endorser's ACL be ?
58-
func (*Endorser) checkACL(signedProp *pb.SignedProposal, prop *pb.Proposal) error {
58+
func (*Endorser) checkACL(signedProp *pb.SignedProposal, chdr *common.ChannelHeader, shdr *common.SignatureHeader) error {
59+
// get policy manager to check ACLs
60+
pm := peer.GetPolicyManager(chdr.ChannelId)
61+
if pm == nil {
62+
return fmt.Errorf("No policy manager available for chain %s", chdr.ChannelId)
63+
}
64+
65+
// access the writers policy
66+
policy, _ := pm.GetPolicy(policies.ChannelApplicationWriters)
67+
68+
// evaluate that this proposal complies with the writers
69+
err := policy.Evaluate(
70+
[]*common.SignedData{{
71+
Data: signedProp.ProposalBytes,
72+
Identity: shdr.Creator,
73+
Signature: signedProp.Signature,
74+
}})
75+
if err != nil {
76+
return fmt.Errorf("The proposal does not comply with the channel writers for channel %s, error %s",
77+
chdr.ChannelId,
78+
err)
79+
}
80+
5981
return nil
6082
}
6183

@@ -146,12 +168,8 @@ func (e *Endorser) simulateProposal(ctx context.Context, chainID string, txid st
146168
if err != nil {
147169
return nil, nil, nil, nil, err
148170
}
149-
//---1. check ACL
150-
if err = e.checkACL(signedProp, prop); err != nil {
151-
return nil, nil, nil, nil, err
152-
}
153171

154-
//---2. check ESCC and VSCC for the chaincode
172+
//---1. check ESCC and VSCC for the chaincode
155173
if err = e.checkEsccAndVscc(prop); err != nil {
156174
return nil, nil, nil, nil, err
157175
}
@@ -290,13 +308,6 @@ func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedPro
290308

291309
chainID := chdr.ChannelId
292310

293-
//chainless MSPs have "" chain name
294-
ischainless := false
295-
296-
if chainID == "" {
297-
ischainless = true
298-
}
299-
300311
// Check for uniqueness of prop.TxID with ledger
301312
// Notice that ValidateProposalMessage has already verified
302313
// that TxID is computed propertly
@@ -308,14 +319,20 @@ func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedPro
308319

309320
//chainless proposals do not/cannot affect ledger and cannot be submitted as transactions
310321
//ignore uniqueness checks
311-
if !ischainless {
322+
if chainID != "" {
312323
lgr := peer.GetLedger(chainID)
313324
if lgr == nil {
314325
return nil, errors.New(fmt.Sprintf("Failure while looking up the ledger %s", chainID))
315326
}
316327
if _, err := lgr.GetTransactionByID(txid); err == nil {
317328
return nil, fmt.Errorf("Duplicate transaction found [%s]. Creator [%x]. [%s]", txid, shdr.Creator, err)
318329
}
330+
331+
// check ACL - we verify that this proposal
332+
// complies with the "writers" policy of the chain
333+
if err = e.checkACL(signedProp, chdr, shdr); err != nil {
334+
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
335+
}
319336
}
320337

321338
// obtaining once the tx simulator for this proposal. This will be nil
@@ -355,7 +372,7 @@ func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedPro
355372

356373
//TODO till we implement global ESCC, CSCC for system chaincodes
357374
//chainless proposals (such as CSCC) don't have to be endorsed
358-
if ischainless {
375+
if chainID == "" {
359376
pResp = &pb.ProposalResponse{Response: res}
360377
} else {
361378
pResp, err = e.endorseProposal(ctx, chainID, txid, signedProp, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeId, txsim, cd)

core/endorser/endorser_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ import (
2626

2727
"path/filepath"
2828

29+
"errors"
30+
2931
"github.com/golang/protobuf/proto"
32+
mockpolicies "github.com/hyperledger/fabric/common/mocks/policies"
33+
"github.com/hyperledger/fabric/common/policies"
3034
"github.com/hyperledger/fabric/common/util"
3135
"github.com/hyperledger/fabric/core/chaincode"
3236
"github.com/hyperledger/fabric/core/common/ccprovider"
@@ -505,6 +509,70 @@ func TestDeployAndUpgrade(t *testing.T) {
505509
chaincode.GetChain().Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: chaincodeID2}})
506510
}
507511

512+
// TestACLFail deploys a chaincode and then tries to invoke it;
513+
// however we inject a special policy for writers to simulate
514+
// the scenario in which the creator of this proposal is not among
515+
// the writers for the chain
516+
func TestACLFail(t *testing.T) {
517+
chainID := util.GetTestChainID()
518+
var ctxt = context.Background()
519+
520+
url := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01"
521+
chaincodeID := &pb.ChaincodeID{Path: url, Name: "ex01-fail", Version: "0"}
522+
523+
defer deleteChaincodeOnDisk("ex01-fail.0")
524+
525+
args := []string{"10"}
526+
527+
f := "init"
528+
argsDeploy := util.ToChaincodeArgs(f, "a", "100", "b", "200")
529+
spec := &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: argsDeploy}}
530+
531+
cccid := ccprovider.NewCCContext(chainID, "ex01-fail", "0", "", false, nil, nil)
532+
533+
resp, prop, err := deploy(endorserServer, chainID, spec, nil)
534+
chaincodeID1 := spec.ChaincodeId.Name
535+
if err != nil {
536+
t.Fail()
537+
t.Logf("Error deploying <%s>: %s", chaincodeID1, err)
538+
chaincode.GetChain().Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: chaincodeID}})
539+
return
540+
}
541+
var nextBlockNumber uint64 = 3 // The tests that ran before this test created blocks 0-2
542+
err = endorserServer.(*Endorser).commitTxSimulation(prop, chainID, signer, resp, nextBlockNumber)
543+
if err != nil {
544+
t.Fail()
545+
t.Logf("Error committing deploy <%s>: %s", chaincodeID1, err)
546+
chaincode.GetChain().Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: chaincodeID}})
547+
return
548+
}
549+
550+
// here we inject a reject policy for writers
551+
// to simulate the scenario in which the invoker
552+
// is not authorized to issue this proposal
553+
rejectpolicy := &mockpolicies.Policy{
554+
Err: errors.New("The creator of this proposal does not fulfil the writers policy of this chain"),
555+
}
556+
pm := peer.GetPolicyManager(chainID)
557+
pm.(*mockpolicies.Manager).PolicyMap = map[string]*mockpolicies.Policy{policies.ChannelApplicationWriters: rejectpolicy}
558+
559+
f = "invoke"
560+
invokeArgs := append([]string{f}, args...)
561+
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs(invokeArgs...)}}
562+
prop, resp, _, _, err = invoke(chainID, spec)
563+
if err == nil {
564+
t.Fail()
565+
t.Logf("Invocation should have failed")
566+
chaincode.GetChain().Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: chaincodeID}})
567+
return
568+
}
569+
570+
fmt.Println("TestACLFail passed")
571+
t.Logf("TestACLFail passed")
572+
573+
chaincode.GetChain().Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: chaincodeID}})
574+
}
575+
508576
func newTempDir() string {
509577
tempDir, err := ioutil.TempDir("", "fabric-")
510578
if err != nil {

core/peer/peer.go

+16-24
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ import (
2525
"github.com/hyperledger/fabric/common/configtx"
2626
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
2727
configvaluesapi "github.com/hyperledger/fabric/common/configvalues"
28+
mockconfigtx "github.com/hyperledger/fabric/common/mocks/configtx"
29+
mockpolicies "github.com/hyperledger/fabric/common/mocks/policies"
2830
"github.com/hyperledger/fabric/common/policies"
2931
"github.com/hyperledger/fabric/core/comm"
3032
"github.com/hyperledger/fabric/core/committer"
3133
"github.com/hyperledger/fabric/core/committer/txvalidator"
3234
"github.com/hyperledger/fabric/core/ledger"
3335
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
3436
"github.com/hyperledger/fabric/gossip/service"
35-
"github.com/hyperledger/fabric/msp"
3637
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
3738
"github.com/hyperledger/fabric/protos/common"
3839
"github.com/hyperledger/fabric/protos/utils"
@@ -236,9 +237,22 @@ func MockCreateChain(cid string) error {
236237
return err
237238
}
238239

240+
i := mockconfigtx.Initializer{
241+
Resources: mockconfigtx.Resources{
242+
PolicyManagerVal: &mockpolicies.Manager{
243+
Policy: &mockpolicies.Policy{},
244+
},
245+
},
246+
}
247+
239248
chains.Lock()
240249
defer chains.Unlock()
241-
chains.list[cid] = &chain{cs: &chainSupport{ledger: ledger}}
250+
chains.list[cid] = &chain{
251+
cs: &chainSupport{
252+
ledger: ledger,
253+
Manager: &mockconfigtx.Manager{Initializer: i},
254+
},
255+
}
242256

243257
return nil
244258
}
@@ -265,28 +279,6 @@ func GetPolicyManager(cid string) policies.Manager {
265279
return nil
266280
}
267281

268-
// GetMSPMgr returns the MSP manager of the chain with chain ID.
269-
// Note that this call returns nil if chain cid has not been created.
270-
func GetMSPMgr(cid string) msp.MSPManager {
271-
chains.RLock()
272-
defer chains.RUnlock()
273-
if c, ok := chains.list[cid]; ok {
274-
return c.cs.MSPManager()
275-
}
276-
return nil
277-
}
278-
279-
// GetCommitter returns the committer of the chain with chain ID. Note that this
280-
// call returns nil if chain cid has not been created.
281-
func GetCommitter(cid string) committer.Committer {
282-
chains.RLock()
283-
defer chains.RUnlock()
284-
if c, ok := chains.list[cid]; ok {
285-
return c.committer
286-
}
287-
return nil
288-
}
289-
290282
// GetCurrConfigBlock returns the cached config block of the specified chain.
291283
// Note that this call returns nil if chain cid has not been created.
292284
func GetCurrConfigBlock(cid string) *common.Block {

peer/node/start.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@ import (
2727
"syscall"
2828
"time"
2929

30+
"github.com/hyperledger/fabric/common/configtx"
3031
"github.com/hyperledger/fabric/common/configtx/test"
32+
"github.com/hyperledger/fabric/common/configvalues/channel/application"
33+
"github.com/hyperledger/fabric/common/configvalues/msp"
3134
"github.com/hyperledger/fabric/common/genesis"
35+
"github.com/hyperledger/fabric/common/policies"
3236
"github.com/hyperledger/fabric/common/util"
3337
"github.com/hyperledger/fabric/core"
3438
"github.com/hyperledger/fabric/core/chaincode"
@@ -161,9 +165,19 @@ func serve(args []string) error {
161165
if peerDefaultChain {
162166
chainID := util.GetTestChainID()
163167

168+
// add readers, writers and admin policies for the default chain
169+
policyTemplate := configtx.NewSimpleTemplate(
170+
policies.TemplateImplicitMetaAnyPolicy([]string{application.GroupKey}, msp.ReadersPolicyKey),
171+
policies.TemplateImplicitMetaAnyPolicy([]string{application.GroupKey}, msp.WritersPolicyKey),
172+
policies.TemplateImplicitMetaMajorityPolicy([]string{application.GroupKey}, msp.AdminsPolicyKey),
173+
)
174+
164175
// We create a genesis block for the test
165176
// chain with its MSP so that we can transact
166-
block, err := genesis.NewFactoryImpl(test.ApplicationOrgTemplate()).Block(chainID)
177+
block, err := genesis.NewFactoryImpl(
178+
configtx.NewCompositeTemplate(
179+
test.ApplicationOrgTemplate(),
180+
policyTemplate)).Block(chainID)
167181
if nil != err {
168182
panic(fmt.Sprintf("Unable to create genesis block for [%s] due to [%s]", chainID, err))
169183
}

0 commit comments

Comments
 (0)