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  }