Skip to content

Commit 94d7e9a

Browse files
author
Jason Yellick
committed
[FAB-2487] Restrict channelIDs to CouchDB/Kafka
The current channel ID checks are to ensure that the IDs comply with the Kafka topic naming restrictions. However, it is possible that two different channel IDs such as TestChain and testchain would both map to the same couchdb name, causing an intersection and failure. This CR changes the logic to test for the intersection of the restrictions on CouchDB and Kafka. In particular, the logic now requires that Channel IDs: 1. Contain only lower case ASCII alphanumerics, dots '.' and dashes '-' 2. Are shorter than 250 characters. 3. Start with a letter Note that this is not a true intersection, because the ledger must still map '.' to '_' for CouchDB, but this mapping is bijective so is safe and free from collisions. Because the configuration key name checking was leveraging this same code path, the existing function was duplicated, to leave the same logic checks in place for the config. Although the code diff is relatively high, this is largely due to the copying and renaming. Change-Id: Ie957cf9b8a075233bfcc5d3748e1a91e795ff067 Signed-off-by: Jason Yellick <[email protected]>
1 parent 7253ae5 commit 94d7e9a

File tree

7 files changed

+135
-58
lines changed

7 files changed

+135
-58
lines changed

bddtests/features/bootstrap.feature

+22-22
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Feature: Bootstrap
5252
| Organization |
5353
| ordererOrg0 |
5454

55-
And the ordererBootstrapAdmin generates a GUUID to identify the orderer system chain and refer to it by name as "OrdererSystemChainId"
55+
And the ordererBootstrapAdmin generates a GUUID to identify the orderer system chain and refer to it by name as "orderer-system-chain-id"
5656

5757
# We now have an orderer network with NO peers. Now need to configure and start the peer network
5858
# This can be currently automated through folder creation of the proper form and placing PEMs.
@@ -72,22 +72,22 @@ Feature: Bootstrap
7272

7373
# Order info includes orderer admin/orderer information and address (host:port) from previous steps
7474
# Only the peer organizations can vary.
75-
And the ordererBootstrapAdmin using cert alias "bootstrapCertAlias" creates the genesis block "ordererGenesisBlock" for chain "OrdererSystemChainId" for composition "<ComposeFile>" and consensus "<ConsensusType>" with consortiums modification policy "/Channel/Orderer/Admins" using consortiums:
75+
And the ordererBootstrapAdmin using cert alias "bootstrapCertAlias" creates the genesis block "ordererGenesisBlock" for chain "orderer-system-chain-id" for composition "<ComposeFile>" and consensus "<ConsensusType>" with consortiums modification policy "/Channel/Orderer/Admins" using consortiums:
7676
| Consortium |
7777
# | consortium1 |
7878

7979

80-
And the orderer admins inspect and approve the genesis block for chain "OrdererSystemChainId"
80+
And the orderer admins inspect and approve the genesis block for chain "orderer-system-chain-id"
8181

8282
# to be used for setting the orderer genesis block path parameter in composition
83-
And the orderer admins use the genesis block for chain "OrdererSystemChainId" to configure orderers
83+
And the orderer admins use the genesis block for chain "orderer-system-chain-id" to configure orderers
8484

8585
And we compose "<ComposeFile>"
8686

8787
# Sleep as to allow system up time
8888
And I wait "<SystemUpWaitTime>" seconds
8989

90-
Given user "ordererBootstrapAdmin" gives "OrdererSystemChainId" to user "configAdminOrdererOrg0"
90+
Given user "ordererBootstrapAdmin" gives "orderer-system-chain-id" to user "configAdminOrdererOrg0"
9191
And user "ordererBootstrapAdmin" gives "ordererGenesisBlock" to user "configAdminOrdererOrg0"
9292

9393
And the orderer config admin "configAdminOrdererOrg0" creates a consortium "consortium1" with modification policy "/Channel/Orderer/Admins" for peer orgs who wish to form a network:
@@ -98,9 +98,9 @@ Feature: Bootstrap
9898

9999
And user "configAdminOrdererOrg0" using cert alias "config-admin-cert" connects to deliver function on orderer "<orderer0>"
100100

101-
And user "configAdminOrdererOrg0" retrieves the latest configuration "latestOrdererConfig" from orderer "<orderer0>" for channel "OrdererSystemChainId"
101+
And user "configAdminOrdererOrg0" retrieves the latest configuration "latestOrdererConfig" from orderer "<orderer0>" for channel "orderer-system-chain-id"
102102

103-
And the orderer config admin "configAdminOrdererOrg0" creates a consortiums config update "consortiumsConfigUpdate1" using config "latestOrdererConfig" using orderer system channel ID "OrdererSystemChainId" to add consortiums:
103+
And the orderer config admin "configAdminOrdererOrg0" creates a consortiums config update "consortiumsConfigUpdate1" using config "latestOrdererConfig" using orderer system channel ID "orderer-system-chain-id" to add consortiums:
104104
| Consortium |
105105
| consortium1 |
106106

@@ -113,7 +113,7 @@ Feature: Bootstrap
113113

114114
And the user "configAdminOrdererOrg0" creates a ConfigUpdate Tx "consortiumsConfigUpdateTx1" using cert alias "config-admin-cert" using signed ConfigUpdateEnvelope "consortiumsConfigUpdate1Envelope"
115115

116-
And the user "configAdminOrdererOrg0" using cert alias "config-admin-cert" broadcasts ConfigUpdate Tx "consortiumsConfigUpdateTx1" to orderer "<orderer0>" to create channel "com.acme.blockchain.jdoe.Channel1"
116+
And the user "configAdminOrdererOrg0" using cert alias "config-admin-cert" broadcasts ConfigUpdate Tx "consortiumsConfigUpdateTx1" to orderer "<orderer0>" to create channel "com.acme.blockchain.jdoe.channel1"
117117

118118

119119
Given the following application developers are defined for peer organizations and each saves their cert as alias
@@ -142,7 +142,7 @@ Feature: Bootstrap
142142
# Entry point for creating a channel
143143
And the user "dev0Org0" creates a new channel ConfigUpdate "createChannelConfigUpdate1" using consortium "consortium1"
144144
| ChannelID | PeerOrgSet | PeerAnchorSet |
145-
| com.acme.blockchain.jdoe.Channel1 | peerOrgSet1 | anchors1 |
145+
| com.acme.blockchain.jdoe.channel1 | peerOrgSet1 | anchors1 |
146146

147147
And the user "dev0Org0" creates a configUpdateEnvelope "createChannelConfigUpdate1Envelope" using configUpdate "createChannelConfigUpdate1"
148148

@@ -154,7 +154,7 @@ Feature: Bootstrap
154154

155155
And the user "dev0Org0" creates a ConfigUpdate Tx "configUpdateTx1" using cert alias "consortium1-cert" using signed ConfigUpdateEnvelope "createChannelConfigUpdate1Envelope"
156156

157-
And the user "dev0Org0" using cert alias "consortium1-cert" broadcasts ConfigUpdate Tx "configUpdateTx1" to orderer "<orderer0>" to create channel "com.acme.blockchain.jdoe.Channel1"
157+
And the user "dev0Org0" using cert alias "consortium1-cert" broadcasts ConfigUpdate Tx "configUpdateTx1" to orderer "<orderer0>" to create channel "com.acme.blockchain.jdoe.channel1"
158158

159159
# Sleep as the local orderer needs to bring up the resources that correspond to the new channel
160160
# For the Kafka orderer, this includes setting up a producer and consumer for the channel's partition
@@ -164,7 +164,7 @@ Feature: Bootstrap
164164
When user "dev0Org0" using cert alias "consortium1-cert" connects to deliver function on orderer "<orderer0>"
165165
And user "dev0Org0" sends deliver a seek request on orderer "<orderer0>" with properties:
166166
| ChainId | Start | End |
167-
| com.acme.blockchain.jdoe.Channel1 | 0 | 0 |
167+
| com.acme.blockchain.jdoe.channel1 | 0 | 0 |
168168

169169
Then user "dev0Org0" should get a delivery "genesisBlockForMyNewChannel" from "<orderer0>" of "1" blocks with "1" messages within "1" seconds
170170

@@ -216,7 +216,7 @@ Feature: Bootstrap
216216
| init | a | 100 | b | 200 |
217217

218218
# Under the covers, create a deployment spec, etc.
219-
And user "peer0Admin" using cert alias "peer-admin-cert" creates a install proposal "installProposal1" for channel "com.acme.blockchain.jdoe.Channel1" using chaincode spec "cc_spec"
219+
And user "peer0Admin" using cert alias "peer-admin-cert" creates a install proposal "installProposal1" for channel "com.acme.blockchain.jdoe.channel1" using chaincode spec "cc_spec"
220220

221221
And user "peer0Admin" using cert alias "peer-admin-cert" sends proposal "installProposal1" to endorsers with timeout of "90" seconds with proposal responses "installProposalResponses":
222222
| Endorser |
@@ -229,7 +229,7 @@ Feature: Bootstrap
229229
Given user "peer0Admin" gives "cc_spec" to user "peer2Admin"
230230

231231
# Under the covers, create a deployment spec, etc.
232-
When user "peer2Admin" using cert alias "peer-admin-cert" creates a install proposal "installProposal1" for channel "com.acme.blockchain.jdoe.Channel1" using chaincode spec "cc_spec"
232+
When user "peer2Admin" using cert alias "peer-admin-cert" creates a install proposal "installProposal1" for channel "com.acme.blockchain.jdoe.channel1" using chaincode spec "cc_spec"
233233

234234
And user "peer2Admin" using cert alias "peer-admin-cert" sends proposal "installProposal1" to endorsers with timeout of "90" seconds with proposal responses "installProposalResponses":
235235
| Endorser |
@@ -244,7 +244,7 @@ Feature: Bootstrap
244244
And user "peer0Admin" gives "cc_spec" to user "configAdminPeerOrg0"
245245

246246

247-
When user "configAdminPeerOrg0" using cert alias "config-admin-cert" creates a instantiate proposal "instantiateProposal1" for channel "com.acme.blockchain.jdoe.Channel1" using chaincode spec "cc_spec"
247+
When user "configAdminPeerOrg0" using cert alias "config-admin-cert" creates a instantiate proposal "instantiateProposal1" for channel "com.acme.blockchain.jdoe.channel1" using chaincode spec "cc_spec"
248248

249249
And user "configAdminPeerOrg0" using cert alias "config-admin-cert" sends proposal "instantiateProposal1" to endorsers with timeout of "90" seconds with proposal responses "instantiateProposalResponses":
250250
| Endorser |
@@ -262,9 +262,9 @@ Feature: Bootstrap
262262
| peer0 |
263263
| peer2 |
264264

265-
When the user "configAdminPeerOrg0" creates transaction "instantiateTx1" from proposal "instantiateProposal1" and proposal responses "instantiateProposalResponses" for channel "com.acme.blockchain.jdoe.Channel1"
265+
When the user "configAdminPeerOrg0" creates transaction "instantiateTx1" from proposal "instantiateProposal1" and proposal responses "instantiateProposalResponses" for channel "com.acme.blockchain.jdoe.channel1"
266266

267-
And the user "configAdminPeerOrg0" broadcasts transaction "instantiateTx1" to orderer "<orderer1>" on channel "com.acme.blockchain.jdoe.Channel1"
267+
And the user "configAdminPeerOrg0" broadcasts transaction "instantiateTx1" to orderer "<orderer1>" on channel "com.acme.blockchain.jdoe.channel1"
268268

269269
# Sleep as the local orderer ledger needs to create the block that corresponds to the start number of the seek request
270270
And I wait "<BroadcastWaitTime>" seconds
@@ -273,7 +273,7 @@ Feature: Bootstrap
273273

274274
And user "configAdminPeerOrg0" sends deliver a seek request on orderer "<orderer0>" with properties:
275275
| ChainId | Start | End |
276-
| com.acme.blockchain.jdoe.Channel1 | 1 | 1 |
276+
| com.acme.blockchain.jdoe.channel1 | 1 | 1 |
277277

278278
Then user "configAdminPeerOrg0" should get a delivery "deliveredInstantiateTx1Block" from "<orderer0>" of "1" blocks with "1" messages within "1" seconds
279279

@@ -286,7 +286,7 @@ Feature: Bootstrap
286286
| query | a |
287287

288288
# Under the covers, create a deployment spec, etc.
289-
And user "dev0Org0" using cert alias "consortium1-cert" creates a proposal "queryProposal1" for channel "com.acme.blockchain.jdoe.Channel1" using chaincode spec "querySpec1"
289+
And user "dev0Org0" using cert alias "consortium1-cert" creates a proposal "queryProposal1" for channel "com.acme.blockchain.jdoe.channel1" using chaincode spec "querySpec1"
290290

291291
And user "dev0Org0" using cert alias "consortium1-cert" sends proposal "queryProposal1" to endorsers with timeout of "30" seconds with proposal responses "queryProposal1Responses":
292292
| Endorser |
@@ -310,7 +310,7 @@ Feature: Bootstrap
310310
| invoke | a | b | 10 |
311311

312312
# Under the covers, create a deployment spec, etc.
313-
And user "dev0Org0" using cert alias "consortium1-cert" creates a proposal "invokeProposal1" for channel "com.acme.blockchain.jdoe.Channel1" using chaincode spec "invocationSpec1"
313+
And user "dev0Org0" using cert alias "consortium1-cert" creates a proposal "invokeProposal1" for channel "com.acme.blockchain.jdoe.channel1" using chaincode spec "invocationSpec1"
314314

315315
And user "dev0Org0" using cert alias "consortium1-cert" sends proposal "invokeProposal1" to endorsers with timeout of "30" seconds with proposal responses "invokeProposal1Responses":
316316
| Endorser |
@@ -327,16 +327,16 @@ Feature: Bootstrap
327327
| peer0 |
328328
| peer2 |
329329

330-
When the user "dev0Org0" creates transaction "invokeTx1" from proposal "invokeProposal1" and proposal responses "invokeProposal1Responses" for channel "com.acme.blockchain.jdoe.Channel1"
330+
When the user "dev0Org0" creates transaction "invokeTx1" from proposal "invokeProposal1" and proposal responses "invokeProposal1Responses" for channel "com.acme.blockchain.jdoe.channel1"
331331

332-
And the user "dev0Org0" broadcasts transaction "invokeTx1" to orderer "<orderer2>" on channel "com.acme.blockchain.jdoe.Channel1"
332+
And the user "dev0Org0" broadcasts transaction "invokeTx1" to orderer "<orderer2>" on channel "com.acme.blockchain.jdoe.channel1"
333333

334334
# Sleep as the local orderer ledger needs to create the block that corresponds to the start number of the seek request
335335
And I wait "<BroadcastWaitTime>" seconds
336336

337337
And user "dev0Org0" sends deliver a seek request on orderer "<orderer0>" with properties:
338338
| ChainId | Start | End |
339-
| com.acme.blockchain.jdoe.Channel1 | 2 | 2 |
339+
| com.acme.blockchain.jdoe.channel1 | 2 | 2 |
340340

341341
Then user "dev0Org0" should get a delivery "deliveredInvokeTx1Block" from "<orderer0>" of "1" blocks with "1" messages within "1" seconds
342342

common/configtx/configmap.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ func addToMap(cg comparable, result map[string]comparable) error {
5959
fqPath = PolicyPrefix
6060
}
6161

62-
// TODO rename validateChainID to validateConfigID
63-
if err := validateChainID(cg.key); err != nil {
62+
if err := validateConfigID(cg.key); err != nil {
6463
return fmt.Errorf("Illegal characters in key: %s", fqPath)
6564
}
6665

common/configtx/manager.go

+48-21
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ import (
2929

3030
var logger = flogging.MustGetLogger("common/configtx")
3131

32-
// Constraints for valid chain IDs
32+
// Constraints for valid channel and config IDs
3333
var (
34-
allowedChars = "[a-zA-Z0-9.-]+"
35-
maxLength = 249
36-
illegalNames = map[string]struct{}{
34+
channelAllowedChars = "[a-z][a-z0-9.-]*"
35+
configAllowedChars = "[a-zA-Z0-9.-]+"
36+
maxLength = 249
37+
illegalNames = map[string]struct{}{
3738
".": struct{}{},
3839
"..": struct{}{},
3940
}
@@ -53,31 +54,57 @@ type configManager struct {
5354
current *configSet
5455
}
5556

56-
// validateChainID makes sure that proposed chain IDs (i.e. channel names)
57-
// comply with the following restrictions:
57+
// validateConfigID makes sure that the config element names (ie map key of
58+
// ConfigGroup) comply with the following restrictions
5859
// 1. Contain only ASCII alphanumerics, dots '.', dashes '-'
5960
// 2. Are shorter than 250 characters.
6061
// 3. Are not the strings "." or "..".
61-
//
62-
// Our hand here is forced by:
63-
// https://github.com/apache/kafka/blob/trunk/core/src/main/scala/kafka/common/Topic.scala#L29
64-
func validateChainID(chainID string) error {
65-
re, _ := regexp.Compile(allowedChars)
62+
func validateConfigID(configID string) error {
63+
re, _ := regexp.Compile(configAllowedChars)
6664
// Length
67-
if len(chainID) <= 0 {
68-
return fmt.Errorf("chain ID illegal, cannot be empty")
65+
if len(configID) <= 0 {
66+
return fmt.Errorf("config ID illegal, cannot be empty")
6967
}
70-
if len(chainID) > maxLength {
71-
return fmt.Errorf("chain ID illegal, cannot be longer than %d", maxLength)
68+
if len(configID) > maxLength {
69+
return fmt.Errorf("config ID illegal, cannot be longer than %d", maxLength)
7270
}
7371
// Illegal name
74-
if _, ok := illegalNames[chainID]; ok {
75-
return fmt.Errorf("name '%s' for chain ID is not allowed", chainID)
72+
if _, ok := illegalNames[configID]; ok {
73+
return fmt.Errorf("name '%s' for config ID is not allowed", configID)
74+
}
75+
// Illegal characters
76+
matched := re.FindString(configID)
77+
if len(matched) != len(configID) {
78+
return fmt.Errorf("config ID '%s' contains illegal characters", configID)
7679
}
80+
81+
return nil
82+
}
83+
84+
// validateChannelID makes sure that proposed channel IDs comply with the
85+
// following restrictions:
86+
// 1. Contain only lower case ASCII alphanumerics, dots '.', and dashes '-'
87+
// 2. Are shorter than 250 characters.
88+
// 3. Start with a letter
89+
//
90+
// This is the intersection of the Kafka restrictions and CouchDB restrictions
91+
// with the following exception: '.' is converted to '_' in the CouchDB naming
92+
// This is to accomodate existing channel names with '.', especially in the
93+
// behave tests which rely on the dot notation for their sluggification.
94+
func validateChannelID(channelID string) error {
95+
re, _ := regexp.Compile(channelAllowedChars)
96+
// Length
97+
if len(channelID) <= 0 {
98+
return fmt.Errorf("channel ID illegal, cannot be empty")
99+
}
100+
if len(channelID) > maxLength {
101+
return fmt.Errorf("channel ID illegal, cannot be longer than %d", maxLength)
102+
}
103+
77104
// Illegal characters
78-
matched := re.FindString(chainID)
79-
if len(matched) != len(chainID) {
80-
return fmt.Errorf("Chain ID '%s' contains illegal characters", chainID)
105+
matched := re.FindString(channelID)
106+
if len(matched) != len(channelID) {
107+
return fmt.Errorf("channel ID '%s' contains illegal characters", channelID)
81108
}
82109

83110
return nil
@@ -102,7 +129,7 @@ func NewManagerImpl(envConfig *cb.Envelope, initializer api.Initializer, callOnU
102129
return nil, fmt.Errorf("nil channel group")
103130
}
104131

105-
if err := validateChainID(header.ChannelId); err != nil {
132+
if err := validateChannelID(header.ChannelId); err != nil {
106133
return nil, fmt.Errorf("Bad channel id: %s", err)
107134
}
108135

common/configtx/manager_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030
"github.com/stretchr/testify/assert"
3131
)
3232

33-
var defaultChain = "DefaultChainID"
33+
var defaultChain = "default.chain.id"
3434

3535
func defaultInitializer() *mockconfigtx.Initializer {
3636
return &mockconfigtx.Initializer{

0 commit comments

Comments
 (0)