Skip to content

Commit 3a2dd8e

Browse files
author
Jason Yellick
committed
[FAB-5309] Set mod_policy for new channel policies
The current configtxgen creates a channel creation tx with policies of Reader/Writer/Admin, where these each do not have a mod_policy set. This makes it difficult to change these policies, because the policy does not exist. It is possible through a roundabout way to change these policies (by removing and re-adding the policy with a different mod_policy), but this is certainly not a painless procedure and requires assistance of the orderer org. This CR updates to configtx processing to validate mod_policy values such that they are valid names. This means that the policy is not empty, and if the policy is path specified (ie using '/'), that each component of the path can be described as a valid config element (does not violate the config element naming rules). This CR additionally sets the mod_policy on the policies generated by configtxgen to "Admins". For users of the old v1.0.0 configtxgen, channel creation will fail with an error for v1.0.1 orderers. However, v1.0.1 configtxgen users will have their transaction appropriately consumed by v1.0.0 orderers. So in summary, upgrading configtxgen without fabric is okay. Upgrading fabric without configtxgen will cause breakage for new channel creation. An additional bug in the configtxgen output was discovered which was not setting a mod_policy on the anchor peers element. There was also an error in the bddtests which mimic the missing policies for the application level policies. Change-Id: Ic2bc120cfb6170f3e4e6cbeac5be145363a64861 Signed-off-by: Jason Yellick <[email protected]>
1 parent 8737eba commit 3a2dd8e

File tree

6 files changed

+68
-6
lines changed

6 files changed

+68
-6
lines changed

bddtests/steps/bootstrap_util.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,7 @@ def create_channel_config_update(system_channel_version, channel_id, consortium_
11021102
Policy(type=typeImplicitMeta, value=IMP(
11031103
rule=ruleMajority, sub_policy=BootstrapHelper.KEY_POLICY_ADMINS).SerializeToString()))
11041104
write_set.groups[ApplicationGroup].mod_policy = "Admins"
1105-
for k, v in write_set.groups[ApplicationGroup].groups.iteritems():
1105+
for k, v in write_set.groups[ApplicationGroup].policies.iteritems():
11061106
v.mod_policy=BootstrapHelper.KEY_POLICY_ADMINS
11071107
config_update = common_dot_configtx_pb2.ConfigUpdate(channel_id=channel_id,
11081108
read_set=read_set,

common/configtx/template.go

+3
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,11 @@ func (cct *channelCreationTemplate) Envelope(channelID string) (*cb.ConfigUpdate
229229

230230
wSet.Groups[config.ApplicationGroupKey].ModPolicy = configmsp.AdminsPolicyKey
231231
wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.AdminsPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.AdminsPolicyKey, cb.ImplicitMetaPolicy_MAJORITY)
232+
wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.AdminsPolicyKey].ModPolicy = configmsp.AdminsPolicyKey
232233
wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.WritersPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.WritersPolicyKey, cb.ImplicitMetaPolicy_ANY)
234+
wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.WritersPolicyKey].ModPolicy = configmsp.AdminsPolicyKey
233235
wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.ReadersPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.ReadersPolicyKey, cb.ImplicitMetaPolicy_ANY)
236+
wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.ReadersPolicyKey].ModPolicy = configmsp.AdminsPolicyKey
234237
wSet.Groups[config.ApplicationGroupKey].Version = 1
235238

236239
return &cb.ConfigUpdateEnvelope{

common/configtx/template_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"testing"
2222

2323
"github.com/hyperledger/fabric/common/config"
24+
configmsp "github.com/hyperledger/fabric/common/config/msp"
2425
mmsp "github.com/hyperledger/fabric/common/mocks/msp"
2526
cb "github.com/hyperledger/fabric/protos/common"
2627
"github.com/hyperledger/fabric/protos/utils"
@@ -144,8 +145,11 @@ func TestNewChainTemplate(t *testing.T) {
144145
assert.Len(t, configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups, len(orgs))
145146

146147
for _, org := range orgs {
147-
_, ok := configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org]
148+
group, ok := configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org]
148149
assert.True(t, ok, "Expected to find %s but did not", org)
150+
for _, policy := range group.Policies {
151+
assert.Equal(t, configmsp.AdminsPolicyKey, policy.ModPolicy)
152+
}
149153
}
150154
}
151155

common/configtx/tool/configtxgen/main.go

+4
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ func doOutputAnchorPeersUpdate(conf *genesisconfig.Profile, channelID string, ou
123123
}
124124

125125
configGroup := config.TemplateAnchorPeers(org.Name, anchorPeers)
126+
configGroup.Groups[config.ApplicationGroupKey].Groups[org.Name].Values[config.AnchorPeersKey].ModPolicy = mspconfig.AdminsPolicyKey
126127
configUpdate := &cb.ConfigUpdate{
127128
ChannelId: channelID,
128129
WriteSet: configGroup,
@@ -132,6 +133,7 @@ func doOutputAnchorPeersUpdate(conf *genesisconfig.Profile, channelID string, ou
132133
// Add all the existing config to the readset
133134
configUpdate.ReadSet.Groups[config.ApplicationGroupKey] = cb.NewConfigGroup()
134135
configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Version = 1
136+
configUpdate.ReadSet.Groups[config.ApplicationGroupKey].ModPolicy = mspconfig.AdminsPolicyKey
135137
configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Groups[org.Name] = cb.NewConfigGroup()
136138
configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Values[config.MSPKey] = &cb.ConfigValue{}
137139
configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Policies[mspconfig.ReadersPolicyKey] = &cb.ConfigPolicy{}
@@ -140,7 +142,9 @@ func doOutputAnchorPeersUpdate(conf *genesisconfig.Profile, channelID string, ou
140142

141143
// Add all the existing at the same versions to the writeset
142144
configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Version = 1
145+
configUpdate.WriteSet.Groups[config.ApplicationGroupKey].ModPolicy = mspconfig.AdminsPolicyKey
143146
configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Version = 1
147+
configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].ModPolicy = mspconfig.AdminsPolicyKey
144148
configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Values[config.MSPKey] = &cb.ConfigValue{}
145149
configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Policies[mspconfig.ReadersPolicyKey] = &cb.ConfigPolicy{}
146150
configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Policies[mspconfig.WritersPolicyKey] = &cb.ConfigPolicy{}

common/configtx/update.go

+25
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package configtx
1818

1919
import (
2020
"fmt"
21+
"strings"
2122

2223
"github.com/hyperledger/fabric/common/policies"
2324
cb "github.com/hyperledger/fabric/protos/common"
@@ -54,12 +55,36 @@ func ComputeDeltaSet(readSet, writeSet map[string]comparable) map[string]compara
5455
return result
5556
}
5657

58+
func validateModPolicy(modPolicy string) error {
59+
if modPolicy == "" {
60+
return fmt.Errorf("mod_policy not set")
61+
}
62+
63+
trimmed := modPolicy
64+
if modPolicy[0] == '/' {
65+
trimmed = modPolicy[1:]
66+
}
67+
68+
for i, pathElement := range strings.Split(trimmed, PathSeparator) {
69+
err := validateConfigID(pathElement)
70+
if err != nil {
71+
return fmt.Errorf("path element at %d is invalid: %s", i, err)
72+
}
73+
}
74+
return nil
75+
76+
}
77+
5778
func (cm *configManager) verifyDeltaSet(deltaSet map[string]comparable, signedData []*cb.SignedData) error {
5879
if len(deltaSet) == 0 {
5980
return fmt.Errorf("Delta set was empty. Update would have no effect.")
6081
}
6182

6283
for key, value := range deltaSet {
84+
if err := validateModPolicy(value.modPolicy()); err != nil {
85+
return fmt.Errorf("invalid mod_policy for element %s: %s", key, err)
86+
}
87+
6388
existing, ok := cm.current.configMap[key]
6489
if !ok {
6590
if value.version() != 0 {

common/configtx/update_test.go

+30-4
Original file line numberDiff line numberDiff line change
@@ -89,31 +89,39 @@ func TestVerifyDeltaSet(t *testing.T) {
8989
t.Run("Green path", func(t *testing.T) {
9090
deltaSet := make(map[string]comparable)
9191

92-
deltaSet["foo"] = comparable{ConfigValue: &cb.ConfigValue{Version: 1}}
92+
deltaSet["foo"] = comparable{ConfigValue: &cb.ConfigValue{Version: 1, ModPolicy: "foo"}}
9393

9494
assert.NoError(t, cm.verifyDeltaSet(deltaSet, nil), "Good update")
9595
})
9696

97+
t.Run("Bad mod policy", func(t *testing.T) {
98+
deltaSet := make(map[string]comparable)
99+
100+
deltaSet["foo"] = comparable{ConfigValue: &cb.ConfigValue{Version: 1}}
101+
102+
assert.Regexp(t, "invalid mod_policy for element", cm.verifyDeltaSet(deltaSet, nil))
103+
})
104+
97105
t.Run("Big Skip", func(t *testing.T) {
98106
deltaSet := make(map[string]comparable)
99107

100-
deltaSet["foo"] = comparable{ConfigValue: &cb.ConfigValue{Version: 2}}
108+
deltaSet["foo"] = comparable{ConfigValue: &cb.ConfigValue{Version: 2, ModPolicy: "foo"}}
101109

102110
assert.Error(t, cm.verifyDeltaSet(deltaSet, nil), "Version skip from 0 to 2")
103111
})
104112

105113
t.Run("New item high version", func(t *testing.T) {
106114
deltaSet := make(map[string]comparable)
107115

108-
deltaSet["bar"] = comparable{ConfigValue: &cb.ConfigValue{Version: 1}}
116+
deltaSet["bar"] = comparable{ConfigValue: &cb.ConfigValue{Version: 1, ModPolicy: "foo"}}
109117

110118
assert.Error(t, cm.verifyDeltaSet(deltaSet, nil), "New key not at version 0")
111119
})
112120

113121
t.Run("Policy evalaution to false", func(t *testing.T) {
114122
deltaSet := make(map[string]comparable)
115123

116-
deltaSet["foo"] = comparable{ConfigValue: &cb.ConfigValue{Version: 1}}
124+
deltaSet["foo"] = comparable{ConfigValue: &cb.ConfigValue{Version: 1, ModPolicy: "foo"}}
117125
cm.Resources.(*mockconfigtx.Resources).PolicyManagerVal.Policy = &mockpolicies.Policy{Err: fmt.Errorf("Err")}
118126

119127
assert.Error(t, cm.verifyDeltaSet(deltaSet, nil), "Policy evaluation should have failed")
@@ -182,3 +190,21 @@ func TestPolicyForItem(t *testing.T) {
182190
assert.True(t, ok)
183191
assert.Equal(t, policy, fooPolicy, "Should have found relative foo policy for foo group")
184192
}
193+
194+
func TestValidateModPolicy(t *testing.T) {
195+
t.Run("Valid", func(t *testing.T) {
196+
assert.Nil(t, validateModPolicy("/foo/bar"))
197+
})
198+
t.Run("Empty", func(t *testing.T) {
199+
assert.Regexp(t, "mod_policy not set", validateModPolicy(""))
200+
})
201+
t.Run("InvalidFirstChar", func(t *testing.T) {
202+
assert.Regexp(t, "path element at 0 is invalid", validateModPolicy("^foo"))
203+
})
204+
t.Run("InvalidRootPath", func(t *testing.T) {
205+
assert.Regexp(t, "path element at 0 is invalid", validateModPolicy("/"))
206+
})
207+
t.Run("InvalidSubPath", func(t *testing.T) {
208+
assert.Regexp(t, "path element at 1 is invalid", validateModPolicy("foo//bar"))
209+
})
210+
}

0 commit comments

Comments
 (0)