github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/common/configtx/validator.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package configtx 8 9 import ( 10 "regexp" 11 12 "github.com/golang/protobuf/proto" 13 cb "github.com/hyperledger/fabric-protos-go/common" 14 "github.com/hyperledger/fabric/common/flogging" 15 "github.com/hyperledger/fabric/common/policies" 16 "github.com/hyperledger/fabric/protoutil" 17 "github.com/pkg/errors" 18 ) 19 20 var logger = flogging.MustGetLogger("common.configtx") 21 22 // Constraints for valid channel and config IDs 23 var ( 24 ChannelAllowedChars = "[a-z][a-z0-9.-]*" 25 configAllowedChars = "[a-zA-Z0-9.-]+" 26 MaxLength = 249 27 illegalNames = map[string]struct{}{ 28 ".": {}, 29 "..": {}, 30 } 31 ) 32 33 // ValidatorImpl implements the Validator interface 34 type ValidatorImpl struct { 35 channelID string 36 sequence uint64 37 configMap map[string]comparable 38 configProto *cb.Config 39 namespace string 40 pm policies.Manager 41 } 42 43 // validateConfigID makes sure that the config element names (ie map key of 44 // ConfigGroup) comply with the following restrictions 45 // 1. Contain only ASCII alphanumerics, dots '.', dashes '-' 46 // 2. Are shorter than 250 characters. 47 // 3. Are not the strings "." or "..". 48 func validateConfigID(configID string) error { 49 re, _ := regexp.Compile(configAllowedChars) 50 // Length 51 if len(configID) <= 0 { 52 return errors.New("config ID illegal, cannot be empty") 53 } 54 if len(configID) > MaxLength { 55 return errors.Errorf("config ID illegal, cannot be longer than %d", MaxLength) 56 } 57 // Illegal name 58 if _, ok := illegalNames[configID]; ok { 59 return errors.Errorf("name '%s' for config ID is not allowed", configID) 60 } 61 // Illegal characters 62 matched := re.FindString(configID) 63 if len(matched) != len(configID) { 64 return errors.Errorf("config ID '%s' contains illegal characters", configID) 65 } 66 67 return nil 68 } 69 70 // ValidateChannelID makes sure that proposed channel IDs comply with the 71 // following restrictions: 72 // 1. Contain only lower case ASCII alphanumerics, dots '.', and dashes '-' 73 // 2. Are shorter than 250 characters. 74 // 3. Start with a letter 75 // 76 // This is the intersection of the Kafka restrictions and CouchDB restrictions 77 // with the following exception: '.' is converted to '_' in the CouchDB naming 78 // This is to accommodate existing channel names with '.', especially in the 79 // behave tests which rely on the dot notation for their sluggification. 80 // 81 // note: this function is a copy of the same in core/tx/endorser/parser.go 82 // 83 func ValidateChannelID(channelID string) error { 84 re, _ := regexp.Compile(ChannelAllowedChars) 85 // Length 86 if len(channelID) <= 0 { 87 return errors.Errorf("channel ID illegal, cannot be empty") 88 } 89 if len(channelID) > MaxLength { 90 return errors.Errorf("channel ID illegal, cannot be longer than %d", MaxLength) 91 } 92 93 // Illegal characters 94 matched := re.FindString(channelID) 95 if len(matched) != len(channelID) { 96 return errors.Errorf("'%s' contains illegal characters", channelID) 97 } 98 99 return nil 100 } 101 102 // NewValidatorImpl constructs a new implementation of the Validator interface. 103 func NewValidatorImpl(channelID string, config *cb.Config, namespace string, pm policies.Manager) (*ValidatorImpl, error) { 104 if config == nil { 105 return nil, errors.Errorf("nil config parameter") 106 } 107 108 if config.ChannelGroup == nil { 109 return nil, errors.Errorf("nil channel group") 110 } 111 112 if err := ValidateChannelID(channelID); err != nil { 113 return nil, errors.Errorf("bad channel ID: %s", err) 114 } 115 116 configMap, err := mapConfig(config.ChannelGroup, namespace) 117 if err != nil { 118 return nil, errors.Errorf("error converting config to map: %s", err) 119 } 120 121 return &ValidatorImpl{ 122 namespace: namespace, 123 pm: pm, 124 sequence: config.Sequence, 125 configMap: configMap, 126 channelID: channelID, 127 configProto: config, 128 }, nil 129 } 130 131 // ProposeConfigUpdate takes in an Envelope of type CONFIG_UPDATE and produces a 132 // ConfigEnvelope to be used as the Envelope Payload Data of a CONFIG message 133 func (vi *ValidatorImpl) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) { 134 return vi.proposeConfigUpdate(configtx) 135 } 136 137 func (vi *ValidatorImpl) proposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) { 138 configUpdateEnv, err := protoutil.EnvelopeToConfigUpdate(configtx) 139 if err != nil { 140 return nil, errors.Errorf("error converting envelope to config update: %s", err) 141 } 142 143 configMap, err := vi.authorizeUpdate(configUpdateEnv) 144 if err != nil { 145 return nil, errors.Errorf("error authorizing update: %s", err) 146 } 147 148 channelGroup, err := configMapToConfig(configMap, vi.namespace) 149 if err != nil { 150 return nil, errors.Errorf("could not turn configMap back to channelGroup: %s", err) 151 } 152 153 return &cb.ConfigEnvelope{ 154 Config: &cb.Config{ 155 Sequence: vi.sequence + 1, 156 ChannelGroup: channelGroup, 157 }, 158 LastUpdate: configtx, 159 }, nil 160 } 161 162 // Validate simulates applying a ConfigEnvelope to become the new config 163 func (vi *ValidatorImpl) Validate(configEnv *cb.ConfigEnvelope) error { 164 if configEnv == nil { 165 return errors.Errorf("config envelope is nil") 166 } 167 168 if configEnv.Config == nil { 169 return errors.Errorf("config envelope has nil config") 170 } 171 172 if configEnv.Config.Sequence != vi.sequence+1 { 173 return errors.Errorf("config currently at sequence %d, cannot validate config at sequence %d", vi.sequence, configEnv.Config.Sequence) 174 } 175 176 configUpdateEnv, err := protoutil.EnvelopeToConfigUpdate(configEnv.LastUpdate) 177 if err != nil { 178 return err 179 } 180 181 configMap, err := vi.authorizeUpdate(configUpdateEnv) 182 if err != nil { 183 return err 184 } 185 186 channelGroup, err := configMapToConfig(configMap, vi.namespace) 187 if err != nil { 188 return errors.Errorf("could not turn configMap back to channelGroup: %s", err) 189 } 190 191 // reflect.Equal will not work here, because it considers nil and empty maps as different 192 if !proto.Equal(channelGroup, configEnv.Config.ChannelGroup) { 193 return errors.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result") 194 } 195 196 return nil 197 } 198 199 // ChannelID retrieves the channel ID associated with this manager 200 func (vi *ValidatorImpl) ChannelID() string { 201 return vi.channelID 202 } 203 204 // Sequence returns the sequence number of the config 205 func (vi *ValidatorImpl) Sequence() uint64 { 206 return vi.sequence 207 } 208 209 // ConfigProto returns the config proto which initialized this Validator 210 func (vi *ValidatorImpl) ConfigProto() *cb.Config { 211 return vi.configProto 212 }