Skip to content

Commit b4d4fd7

Browse files
committed
[FAB-9873] Function tests for filtered block ACL
This CR adds function tests for the filtered block event ACL policy. Change-Id: I5401ab364a43f2a61955b8590a0fad8eaab7fc58 Signed-off-by: Will Lahti <wtlahti@us.ibm.com>
1 parent a1c892d commit b4d4fd7

File tree

4 files changed

+254
-2
lines changed

4 files changed

+254
-2
lines changed

Gopkg.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integration/e2e/acl_test.go

+231
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
/*
2+
Copyright IBM Corp All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package e2e
8+
9+
import (
10+
"fmt"
11+
"io/ioutil"
12+
"path/filepath"
13+
"syscall"
14+
"time"
15+
16+
docker "github.com/fsouza/go-dockerclient"
17+
"github.com/gogo/protobuf/proto"
18+
"github.com/hyperledger/fabric/common/tools/configtxlator/update"
19+
"github.com/hyperledger/fabric/core/aclmgmt/resources"
20+
"github.com/hyperledger/fabric/integration/world"
21+
"github.com/hyperledger/fabric/protos/common"
22+
pb "github.com/hyperledger/fabric/protos/peer"
23+
"github.com/hyperledger/fabric/protos/utils"
24+
. "github.com/onsi/ginkgo"
25+
. "github.com/onsi/gomega"
26+
"github.com/onsi/gomega/gbytes"
27+
)
28+
29+
var _ = Describe("EndToEndACL", func() {
30+
var (
31+
client *docker.Client
32+
w world.World
33+
)
34+
35+
BeforeEach(func() {
36+
var err error
37+
38+
client, err = docker.NewClientFromEnv()
39+
Expect(err).NotTo(HaveOccurred())
40+
41+
// generating files to bootstrap the network
42+
w = world.GenerateBasicConfig("solo", 2, 2, testDir, components)
43+
44+
// sets up the world for all tests
45+
setupWorld(&w)
46+
})
47+
48+
AfterEach(func() {
49+
// Stop the running chaincode containers
50+
filters := map[string][]string{}
51+
filters["name"] = []string{fmt.Sprintf("%s-%s", w.Deployment.Chaincode.Name, w.Deployment.Chaincode.Version)}
52+
allContainers, _ := client.ListContainers(docker.ListContainersOptions{
53+
Filters: filters,
54+
})
55+
if len(allContainers) > 0 {
56+
for _, container := range allContainers {
57+
client.RemoveContainer(docker.RemoveContainerOptions{
58+
ID: container.ID,
59+
Force: true,
60+
})
61+
}
62+
}
63+
64+
// Remove chaincode image
65+
filters = map[string][]string{}
66+
filters["label"] = []string{fmt.Sprintf("org.hyperledger.fabric.chaincode.id.name=%s", w.Deployment.Chaincode.Name)}
67+
images, _ := client.ListImages(docker.ListImagesOptions{
68+
Filters: filters,
69+
})
70+
if len(images) > 0 {
71+
for _, image := range images {
72+
client.RemoveImage(image.ID)
73+
}
74+
}
75+
76+
// Stop the orderers and peers
77+
for _, localProc := range w.LocalProcess {
78+
localProc.Signal(syscall.SIGTERM)
79+
Eventually(localProc.Wait(), 5*time.Second).Should(Receive())
80+
localProc.Signal(syscall.SIGKILL)
81+
Eventually(localProc.Wait(), 5*time.Second).Should(Receive())
82+
}
83+
84+
// Remove any started networks
85+
if w.Network != nil {
86+
client.RemoveNetwork(w.Network.Name)
87+
}
88+
})
89+
90+
It("tests ACL policies", func() {
91+
Context("when the ACL policy for DeliverFiltered is satisified", func() {
92+
By("setting the filtered block event ACL policy to Org1/Admins")
93+
policyName := resources.Event_FilteredBlock
94+
policy := "/Channel/Application/Org1/Admins"
95+
SetACLPolicy(&w, policyName, policy)
96+
97+
By("waiting for the transaction to commit to the ledger using an Org1 Admin identity")
98+
adminPeer := components.Peer()
99+
adminPeer.ConfigDir = filepath.Join(testDir, "org1.example.com_0")
100+
adminPeer.MSPConfigPath = filepath.Join(testDir, "crypto", "peerOrganizations", "org1.example.com", "users", "Admin@org1.example.com", "msp")
101+
102+
adminRunner := adminPeer.InvokeChaincode(w.Deployment.Chaincode.Name, w.Deployment.Channel, `{"Args":["invoke","a","b","10"]}`, w.Deployment.Orderer, "--waitForEvent")
103+
execute(adminRunner)
104+
Eventually(adminRunner.Err()).Should(gbytes.Say("Chaincode invoke successful. result: status:200"))
105+
})
106+
107+
Context("when the ACL policy for DeliverFiltered is not satisifed", func() {
108+
By("setting the filtered block event ACL policy to Org2/Admins")
109+
policyName := resources.Event_FilteredBlock
110+
policy := "/Channel/Application/Org2/Admins"
111+
SetACLPolicy(&w, policyName, policy)
112+
113+
By("waiting for the transaction to commit to the ledger using an Org1 Admin identity")
114+
adminPeer := components.Peer()
115+
adminPeer.ConfigDir = filepath.Join(testDir, "org1.example.com_0")
116+
adminPeer.MSPConfigPath = filepath.Join(testDir, "crypto", "peerOrganizations", "org1.example.com", "users", "Admin@org1.example.com", "msp")
117+
118+
adminRunner := adminPeer.InvokeChaincode(w.Deployment.Chaincode.Name, w.Deployment.Channel, `{"Args":["invoke","a","b","10"]}`, w.Deployment.Orderer, "--waitForEvent")
119+
execute(adminRunner)
120+
Eventually(adminRunner.Err()).Should(gbytes.Say(`\Qdeliver completed with status (FORBIDDEN)\E`))
121+
})
122+
})
123+
})
124+
125+
// SetACLPolicy sets the ACL policy for the world on a running network. It resets all
126+
// previously defined ACL policies. It performs the generation of the config update,
127+
// signs the configuration with Org2's signer, and then submits the config update
128+
// using Org1
129+
func SetACLPolicy(w *world.World, policyName, policy string) {
130+
outputFile := filepath.Join(testDir, "updated_config.pb")
131+
GenerateACLConfigUpdate(w, policyName, policy, outputFile)
132+
133+
signConfigDir := filepath.Join(testDir, "org2.example.com_0")
134+
signMSPConfigPath := filepath.Join(w.Rootpath, "crypto", "peerOrganizations", "org2.example.com", "users", "Admin@org2.example.com", "msp")
135+
SignConfigUpdate(w, outputFile, signConfigDir, signMSPConfigPath)
136+
137+
sendConfigDir := filepath.Join(testDir, "org1.example.com_0")
138+
sendMSPConfigPath := filepath.Join(w.Rootpath, "crypto", "peerOrganizations", "org1.example.com", "users", "Admin@org1.example.com", "msp")
139+
SendConfigUpdate(w, outputFile, sendConfigDir, sendMSPConfigPath)
140+
}
141+
142+
func GenerateACLConfigUpdate(w *world.World, policyName, policy, outputFile string) {
143+
// fetch the config block
144+
fetchRun := components.Peer()
145+
fetchRun.ConfigDir = filepath.Join(testDir, "org1.example.com_0")
146+
fetchRun.MSPConfigPath = filepath.Join(w.Rootpath, "crypto", "peerOrganizations", "org1.example.com", "users", "Admin@org1.example.com", "msp")
147+
output := filepath.Join(testDir, "config_block.pb")
148+
fRunner := fetchRun.FetchChannel(w.Deployment.Channel, output, "config", w.Deployment.Orderer)
149+
execute(fRunner)
150+
Expect(fRunner.Err()).To(gbytes.Say("Received block: "))
151+
// TODO - enable this once the orderer's logs are available here
152+
// Expect(ordererRunner.Err()).To(gbytes.Say(`\Q[channel: mychan] Done delivering \E`))
153+
154+
// read the config block file
155+
fileBytes, err := ioutil.ReadFile(output)
156+
Expect(err).To(BeNil())
157+
Expect(fileBytes).NotTo(BeNil())
158+
159+
// unmarshal the config block bytes
160+
configBlock := &common.Block{}
161+
err = proto.Unmarshal(fileBytes, configBlock)
162+
Expect(err).To(BeNil())
163+
164+
// unmarshal the envelope bytes
165+
env := &common.Envelope{}
166+
err = proto.Unmarshal(configBlock.Data.Data[0], env)
167+
Expect(err).To(BeNil())
168+
169+
// unmarshal the payload bytes
170+
payload := &common.Payload{}
171+
err = proto.Unmarshal(env.Payload, payload)
172+
Expect(err).To(BeNil())
173+
174+
// unmarshal the config envelope bytes
175+
configEnv := &common.ConfigEnvelope{}
176+
err = proto.Unmarshal(payload.Data, configEnv)
177+
Expect(err).To(BeNil())
178+
179+
// clone the config
180+
config := configEnv.Config
181+
updatedConfig := proto.Clone(config).(*common.Config)
182+
183+
acls := &pb.ACLs{
184+
Acls: make(map[string]*pb.APIResource),
185+
}
186+
187+
// set the policy
188+
apiResource := &pb.APIResource{}
189+
apiResource.PolicyRef = policy
190+
acls.Acls[policyName] = apiResource
191+
cv := &common.ConfigValue{
192+
Value: utils.MarshalOrPanic(acls),
193+
ModPolicy: "Admins",
194+
}
195+
updatedConfig.ChannelGroup.Groups["Application"].Values["ACLs"] = cv
196+
197+
// compute the config update and package it into an envelope
198+
configUpdate, err := update.Compute(config, updatedConfig)
199+
Expect(err).To(BeNil())
200+
configUpdate.ChannelId = w.Deployment.Channel
201+
configUpdateEnvelope := &common.ConfigUpdateEnvelope{
202+
ConfigUpdate: utils.MarshalOrPanic(configUpdate),
203+
}
204+
signedEnv, err := utils.CreateSignedEnvelope(common.HeaderType_CONFIG_UPDATE, w.Deployment.Channel, nil, configUpdateEnvelope, 0, 0)
205+
Expect(err).To(BeNil())
206+
Expect(signedEnv).NotTo(BeNil())
207+
208+
// write the config update envelope to the file system
209+
signedEnvelopeBytes := utils.MarshalOrPanic(signedEnv)
210+
err = ioutil.WriteFile(outputFile, signedEnvelopeBytes, 0660)
211+
Expect(err).To(BeNil())
212+
}
213+
214+
func SignConfigUpdate(w *world.World, outputFile, configDir, mspConfigPath string) {
215+
signRun := components.Peer()
216+
signRun.ConfigDir = configDir
217+
signRun.MSPConfigPath = mspConfigPath
218+
sRunner := signRun.SignConfigTx(outputFile)
219+
// signconfigtx doesn't output anything so check that error is nil
220+
err := execute(sRunner)
221+
Expect(err).To(BeNil())
222+
}
223+
224+
func SendConfigUpdate(w *world.World, outputFile, configDir, mspConfigPath string) {
225+
updateRun := components.Peer()
226+
updateRun.ConfigDir = configDir
227+
updateRun.MSPConfigPath = mspConfigPath
228+
uRunner := updateRun.UpdateChannel(outputFile, w.Deployment.Channel, w.Deployment.Orderer)
229+
execute(uRunner)
230+
Expect(uRunner.Err()).To(gbytes.Say("Successfully submitted channel update"))
231+
}

integration/runner/peer.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,19 @@ func (p *Peer) JoinChannel(transactionFile string) *ginkgomon.Runner {
128128
return r
129129
}
130130

131+
func (p *Peer) SignConfigTx(transactionFile string) *ginkgomon.Runner {
132+
cmd := exec.Command(p.Path, "channel", "signconfigtx", "-f", transactionFile)
133+
p.setupEnvironment(cmd)
134+
135+
r := ginkgomon.New(ginkgomon.Config{
136+
Name: "channel signconfigtx",
137+
AnsiColorCode: "4;38m",
138+
Command: cmd,
139+
})
140+
141+
return r
142+
}
143+
131144
func (p *Peer) UpdateChannel(transactionFile string, channel string, orderer string) *ginkgomon.Runner {
132145
cmd := exec.Command(p.Path, "channel", "update", "-c", channel, "-o", orderer, "-f", transactionFile)
133146
p.setupEnvironment(cmd)
@@ -180,8 +193,12 @@ func (p *Peer) QueryChaincode(name string, channel string, args string) *ginkgom
180193
return r
181194
}
182195

183-
func (p *Peer) InvokeChaincode(name string, channel string, args string, orderer string) *ginkgomon.Runner {
196+
func (p *Peer) InvokeChaincode(name string, channel string, args string, orderer string, extraArgs ...string) *ginkgomon.Runner {
184197
cmd := exec.Command(p.Path, "chaincode", "invoke", "-n", name, "-C", channel, "-c", args, "-o", orderer)
198+
for _, arg := range extraArgs {
199+
cmd.Args = append(cmd.Args, arg)
200+
}
201+
185202
p.setupEnvironment(cmd)
186203

187204
r := ginkgomon.New(ginkgomon.Config{

peer/chaincode/common.go

+4
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,10 @@ func (dg *deliverGroup) ClientWait(dc *deliverClient) {
688688
return
689689
}
690690
}
691+
case *pb.DeliverResponse_Status:
692+
err = errors.Errorf("deliver completed with status (%s) before txid received", r.Status)
693+
dg.setError(err)
694+
return
691695
default:
692696
err = errors.Errorf("received unexpected response type (%T) from %s", r, dc.Address)
693697
dg.setError(err)

0 commit comments

Comments
 (0)