github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/common/policies/policy.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package policies 18 19 import ( 20 "fmt" 21 "strings" 22 "sync" 23 24 cb "github.com/hyperledger/fabric/protos/common" 25 26 "github.com/golang/protobuf/proto" 27 "github.com/hyperledger/fabric/common/flogging" 28 logging "github.com/op/go-logging" 29 ) 30 31 const ( 32 // Path separator is used to separate policy names in paths 33 PathSeparator = "/" 34 35 // ChannelPrefix is used in the path of standard channel policy managers 36 ChannelPrefix = "Channel" 37 38 // ApplicationPrefix is used in the path of standard application policy paths 39 ApplicationPrefix = "Application" 40 41 // OrdererPrefix is used in the path of standard orderer policy paths 42 OrdererPrefix = "Orderer" 43 44 // ChannelReaders is the label for the channel's readers policy (encompassing both orderer and application readers) 45 ChannelReaders = PathSeparator + ChannelPrefix + PathSeparator + "Readers" 46 47 // ChannelWriters is the label for the channel's writers policy (encompassing both orderer and application writers) 48 ChannelWriters = PathSeparator + ChannelPrefix + PathSeparator + "Writers" 49 50 // ChannelApplicationReaders is the label for the channel's application readers policy 51 ChannelApplicationReaders = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Readers" 52 53 // ChannelApplicationWriters is the label for the channel's application writers policy 54 ChannelApplicationWriters = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Writers" 55 56 // ChannelApplicationAdmins is the label for the channel's application admin policy 57 ChannelApplicationAdmins = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Admins" 58 59 // BlockValidation is the label for the policy which should validate the block signatures for the channel 60 BlockValidation = PathSeparator + ChannelPrefix + PathSeparator + OrdererPrefix + PathSeparator + "BlockValidation" 61 ) 62 63 var logger = flogging.MustGetLogger("policies") 64 65 // Policy is used to determine if a signature is valid 66 type Policy interface { 67 // Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy 68 Evaluate(signatureSet []*cb.SignedData) error 69 } 70 71 // Manager is a read only subset of the policy ManagerImpl 72 type Manager interface { 73 // GetPolicy returns a policy and true if it was the policy requested, or false if it is the default policy 74 GetPolicy(id string) (Policy, bool) 75 76 // Manager returns the sub-policy manager for a given path and whether it exists 77 Manager(path []string) (Manager, bool) 78 79 // Basepath returns the basePath the manager was instantiated with 80 BasePath() string 81 82 // Policies returns all policy names defined in the manager 83 PolicyNames() []string 84 } 85 86 // Proposer is the interface used by the configtx manager for policy management 87 type Proposer interface { 88 // BeginPolicyProposals starts a policy update transaction 89 BeginPolicyProposals(tx interface{}, groups []string) ([]Proposer, error) 90 91 // ProposePolicy createss a pending policy update from a ConfigPolicy and returns the deserialized 92 // value of the Policy representation 93 ProposePolicy(tx interface{}, name string, policy *cb.ConfigPolicy) (proto.Message, error) 94 95 // RollbackProposals discards the pending policy updates 96 RollbackProposals(tx interface{}) 97 98 // CommitProposals commits the pending policy updates 99 CommitProposals(tx interface{}) 100 101 // PreCommit tests if a commit will apply 102 PreCommit(tx interface{}) error 103 } 104 105 // Provider provides the backing implementation of a policy 106 type Provider interface { 107 // NewPolicy creates a new policy based on the policy bytes 108 NewPolicy(data []byte) (Policy, proto.Message, error) 109 } 110 111 // ChannelPolicyManagerGetter is a support interface 112 // to get access to the policy manager of a given channel 113 type ChannelPolicyManagerGetter interface { 114 // Returns the policy manager associated to the passed channel 115 // and true if it was the manager requested, or false if it is the default manager 116 Manager(channelID string) (Manager, bool) 117 } 118 119 type policyConfig struct { 120 policies map[string]Policy 121 managers map[string]*ManagerImpl 122 imps []*implicitMetaPolicy 123 } 124 125 // ManagerImpl is an implementation of Manager and configtx.ConfigHandler 126 // In general, it should only be referenced as an Impl for the configtx.ConfigManager 127 type ManagerImpl struct { 128 parent *ManagerImpl 129 basePath string 130 fqPrefix string 131 providers map[int32]Provider 132 config *policyConfig 133 pendingConfig map[interface{}]*policyConfig 134 pendingLock sync.RWMutex 135 136 // SuppressSanityLogMessages when set to true will prevent the sanity checking log 137 // messages. Useful for novel cases like channel templates 138 SuppressSanityLogMessages bool 139 } 140 141 // NewManagerImpl creates a new ManagerImpl with the given CryptoHelper 142 func NewManagerImpl(basePath string, providers map[int32]Provider) *ManagerImpl { 143 _, ok := providers[int32(cb.Policy_IMPLICIT_META)] 144 if ok { 145 logger.Panicf("ImplicitMetaPolicy type must be provider by the policy manager") 146 } 147 148 return &ManagerImpl{ 149 basePath: basePath, 150 fqPrefix: PathSeparator + basePath + PathSeparator, 151 providers: providers, 152 config: &policyConfig{ 153 policies: make(map[string]Policy), 154 managers: make(map[string]*ManagerImpl), 155 }, 156 pendingConfig: make(map[interface{}]*policyConfig), 157 } 158 } 159 160 type rejectPolicy string 161 162 func (rp rejectPolicy) Evaluate(signedData []*cb.SignedData) error { 163 return fmt.Errorf("No such policy type: %s", rp) 164 } 165 166 // Basepath returns the basePath the manager was instnatiated with 167 func (pm *ManagerImpl) BasePath() string { 168 return pm.basePath 169 } 170 171 func (pm *ManagerImpl) PolicyNames() []string { 172 policyNames := make([]string, len(pm.config.policies)) 173 i := 0 174 for policyName := range pm.config.policies { 175 policyNames[i] = policyName 176 i++ 177 } 178 return policyNames 179 } 180 181 // Manager returns the sub-policy manager for a given path and whether it exists 182 func (pm *ManagerImpl) Manager(path []string) (Manager, bool) { 183 if len(path) == 0 { 184 return pm, true 185 } 186 187 m, ok := pm.config.managers[path[0]] 188 if !ok { 189 return nil, false 190 } 191 192 return m.Manager(path[1:]) 193 } 194 195 // GetPolicy returns a policy and true if it was the policy requested, or false if it is the default reject policy 196 func (pm *ManagerImpl) GetPolicy(id string) (Policy, bool) { 197 if id == "" { 198 logger.Errorf("Returning dummy reject all policy because no policy ID supplied") 199 return rejectPolicy(id), false 200 } 201 var relpath string 202 203 if strings.HasPrefix(id, PathSeparator) { 204 if pm.parent != nil { 205 return pm.parent.GetPolicy(id) 206 } 207 if !strings.HasPrefix(id, pm.fqPrefix) { 208 if logger.IsEnabledFor(logging.DEBUG) { 209 logger.Debugf("Requested policy from root manager with wrong basePath: %s, returning rejectAll", id) 210 } 211 return rejectPolicy(id), false 212 } 213 relpath = id[len(pm.fqPrefix):] 214 } else { 215 relpath = id 216 } 217 218 policy, ok := pm.config.policies[relpath] 219 if !ok { 220 if logger.IsEnabledFor(logging.DEBUG) { 221 logger.Debugf("Returning dummy reject all policy because %s could not be found in /%s/%s", id, pm.basePath, relpath) 222 } 223 return rejectPolicy(relpath), false 224 } 225 if logger.IsEnabledFor(logging.DEBUG) { 226 logger.Debugf("Returning policy %s for evaluation", relpath) 227 } 228 return policy, true 229 } 230 231 // BeginPolicies is used to start a new config proposal 232 func (pm *ManagerImpl) BeginPolicyProposals(tx interface{}, groups []string) ([]Proposer, error) { 233 pm.pendingLock.Lock() 234 defer pm.pendingLock.Unlock() 235 pendingConfig, ok := pm.pendingConfig[tx] 236 if ok { 237 logger.Panicf("Serious Programming error: cannot call begin multiply for the same proposal") 238 } 239 240 pendingConfig = &policyConfig{ 241 policies: make(map[string]Policy), 242 managers: make(map[string]*ManagerImpl), 243 } 244 pm.pendingConfig[tx] = pendingConfig 245 246 managers := make([]Proposer, len(groups)) 247 for i, group := range groups { 248 newManager := NewManagerImpl(group, pm.providers) 249 newManager.parent = pm 250 pendingConfig.managers[group] = newManager 251 managers[i] = newManager 252 } 253 return managers, nil 254 } 255 256 // RollbackProposals is used to abandon a new config proposal 257 func (pm *ManagerImpl) RollbackProposals(tx interface{}) { 258 pm.pendingLock.Lock() 259 defer pm.pendingLock.Unlock() 260 delete(pm.pendingConfig, tx) 261 } 262 263 // PreCommit is currently a no-op for the policy manager and always returns nil 264 func (pm *ManagerImpl) PreCommit(tx interface{}) error { 265 return nil 266 } 267 268 // CommitProposals is used to commit a new config proposal 269 func (pm *ManagerImpl) CommitProposals(tx interface{}) { 270 pm.pendingLock.Lock() 271 defer pm.pendingLock.Unlock() 272 pendingConfig, ok := pm.pendingConfig[tx] 273 if !ok { 274 logger.Panicf("Programming error, cannot call begin in the middle of a proposal") 275 } 276 277 if pendingConfig == nil { 278 logger.Panicf("Programming error, cannot call commit without an existing proposal") 279 } 280 281 for managerPath, m := range pendingConfig.managers { 282 for _, policyName := range m.PolicyNames() { 283 fqKey := managerPath + PathSeparator + policyName 284 pendingConfig.policies[fqKey], _ = m.GetPolicy(policyName) 285 logger.Debugf("In commit adding relative sub-policy %s to %s", fqKey, pm.basePath) 286 } 287 } 288 289 // Now that all the policies are present, initialize the meta policies 290 for _, imp := range pendingConfig.imps { 291 imp.initialize(pendingConfig) 292 } 293 294 pm.config = pendingConfig 295 delete(pm.pendingConfig, tx) 296 297 if pm.parent == nil && pm.basePath == ChannelPrefix && !pm.SuppressSanityLogMessages { 298 for _, policyName := range []string{ChannelReaders, ChannelWriters} { 299 _, ok := pm.GetPolicy(policyName) 300 if !ok { 301 logger.Warningf("Current configuration has no policy '%s', this will likely cause problems in production systems", policyName) 302 } else { 303 logger.Debugf("As expected, current configuration has policy '%s'", policyName) 304 } 305 } 306 if _, ok := pm.config.managers[ApplicationPrefix]; ok { 307 // Check for default application policies if the application component is defined 308 for _, policyName := range []string{ 309 ChannelApplicationReaders, 310 ChannelApplicationWriters, 311 ChannelApplicationAdmins} { 312 _, ok := pm.GetPolicy(policyName) 313 if !ok { 314 logger.Warningf("Current configuration has no policy '%s', this will likely cause problems in production systems", policyName) 315 } else { 316 logger.Debugf("As expected, current configuration has policy '%s'", policyName) 317 } 318 } 319 } 320 if _, ok := pm.config.managers[OrdererPrefix]; ok { 321 for _, policyName := range []string{BlockValidation} { 322 _, ok := pm.GetPolicy(policyName) 323 if !ok { 324 logger.Warningf("Current configuration has no policy '%s', this will likely cause problems in production systems", policyName) 325 } else { 326 logger.Debugf("As expected, current configuration has policy '%s'", policyName) 327 } 328 } 329 } 330 } 331 } 332 333 // ProposePolicy takes key, path, and ConfigPolicy and registers it in the proposed PolicyManager, or errors 334 // It also returns the deserialized policy value for tracking and inspection at the invocation side. 335 func (pm *ManagerImpl) ProposePolicy(tx interface{}, key string, configPolicy *cb.ConfigPolicy) (proto.Message, error) { 336 pm.pendingLock.RLock() 337 pendingConfig, ok := pm.pendingConfig[tx] 338 pm.pendingLock.RUnlock() 339 if !ok { 340 logger.Panicf("Serious Programming error: called Propose without Begin") 341 } 342 343 policy := configPolicy.Policy 344 if policy == nil { 345 return nil, fmt.Errorf("Policy cannot be nil") 346 } 347 348 var cPolicy Policy 349 var deserialized proto.Message 350 351 if policy.Type == int32(cb.Policy_IMPLICIT_META) { 352 imp, err := newImplicitMetaPolicy(policy.Value) 353 if err != nil { 354 return nil, err 355 } 356 pendingConfig.imps = append(pendingConfig.imps, imp) 357 cPolicy = imp 358 deserialized = imp.conf 359 } else { 360 provider, ok := pm.providers[int32(policy.Type)] 361 if !ok { 362 return nil, fmt.Errorf("Unknown policy type: %v", policy.Type) 363 } 364 365 var err error 366 cPolicy, deserialized, err = provider.NewPolicy(policy.Value) 367 if err != nil { 368 return nil, err 369 } 370 } 371 372 pendingConfig.policies[key] = cPolicy 373 374 logger.Debugf("Proposed new policy %s for %s", key, pm.basePath) 375 return deserialized, nil 376 }