|
| 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 | +} |
0 commit comments