github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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  
    23  	cb "github.com/hyperledger/fabric/protos/common"
    24  
    25  	logging "github.com/op/go-logging"
    26  )
    27  
    28  const (
    29  	// Path separator is used to separate policy names in paths
    30  	PathSeparator = "/"
    31  
    32  	// ChannelPrefix is used in the path of standard channel policy managers
    33  	ChannelPrefix = "Channel"
    34  
    35  	// ApplicationPrefix is used in the path of standard application policy paths
    36  	ApplicationPrefix = "Application"
    37  
    38  	// OrdererPrefix is used in the path of standard orderer policy paths
    39  	OrdererPrefix = "Orderer"
    40  
    41  	// ChannelApplicationReaders is the label for the channel's application readers policy
    42  	ChannelApplicationReaders = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Readers"
    43  
    44  	// ChannelApplicationWriters is the label for the channel's application writers policy
    45  	ChannelApplicationWriters = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Writers"
    46  
    47  	// ChannelApplicationAdmins is the label for the channel's application admin policy
    48  	ChannelApplicationAdmins = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Admins"
    49  
    50  	// BlockValidation is the label for the policy which should validate the block signatures for the channel
    51  	BlockValidation = PathSeparator + ChannelPrefix + PathSeparator + OrdererPrefix + PathSeparator + "BlockValidation"
    52  )
    53  
    54  var logger = logging.MustGetLogger("common/policies")
    55  
    56  // Policy is used to determine if a signature is valid
    57  type Policy interface {
    58  	// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
    59  	Evaluate(signatureSet []*cb.SignedData) error
    60  }
    61  
    62  // Manager is a read only subset of the policy ManagerImpl
    63  type Manager interface {
    64  	// GetPolicy returns a policy and true if it was the policy requested, or false if it is the default policy
    65  	GetPolicy(id string) (Policy, bool)
    66  
    67  	// Manager returns the sub-policy manager for a given path and whether it exists
    68  	Manager(path []string) (Manager, bool)
    69  
    70  	// Basepath returns the basePath the manager was instantiated with
    71  	BasePath() string
    72  
    73  	// Policies returns all policy names defined in the manager
    74  	PolicyNames() []string
    75  }
    76  
    77  // Proposer is the interface used by the configtx manager for policy management
    78  type Proposer interface {
    79  	// BeginPolicyProposals starts a policy update transaction
    80  	BeginPolicyProposals(groups []string) ([]Proposer, error)
    81  
    82  	// ProposePolicy createss a pending policy update from a ConfigPolicy
    83  	ProposePolicy(name string, policy *cb.ConfigPolicy) error
    84  
    85  	// RollbackProposals discards the pending policy updates
    86  	RollbackProposals()
    87  
    88  	// CommitProposals commits the pending policy updates
    89  	CommitProposals()
    90  
    91  	// PreCommit tests if a commit will apply
    92  	PreCommit() error
    93  }
    94  
    95  // Provider provides the backing implementation of a policy
    96  type Provider interface {
    97  	// NewPolicy creates a new policy based on the policy bytes
    98  	NewPolicy(data []byte) (Policy, error)
    99  }
   100  
   101  type policyConfig struct {
   102  	policies map[string]Policy
   103  	managers map[string]*ManagerImpl
   104  	imps     []*implicitMetaPolicy
   105  }
   106  
   107  // ManagerImpl is an implementation of Manager and configtx.ConfigHandler
   108  // In general, it should only be referenced as an Impl for the configtx.ConfigManager
   109  type ManagerImpl struct {
   110  	parent        *ManagerImpl
   111  	basePath      string
   112  	fqPrefix      string
   113  	providers     map[int32]Provider
   114  	config        *policyConfig
   115  	pendingConfig *policyConfig
   116  }
   117  
   118  // NewManagerImpl creates a new ManagerImpl with the given CryptoHelper
   119  func NewManagerImpl(basePath string, providers map[int32]Provider) *ManagerImpl {
   120  	_, ok := providers[int32(cb.Policy_IMPLICIT_META)]
   121  	if ok {
   122  		logger.Panicf("ImplicitMetaPolicy type must be provider by the policy manager")
   123  	}
   124  
   125  	return &ManagerImpl{
   126  		basePath:  basePath,
   127  		fqPrefix:  PathSeparator + basePath + PathSeparator,
   128  		providers: providers,
   129  		config: &policyConfig{
   130  			policies: make(map[string]Policy),
   131  			managers: make(map[string]*ManagerImpl),
   132  		},
   133  	}
   134  }
   135  
   136  type rejectPolicy string
   137  
   138  func (rp rejectPolicy) Evaluate(signedData []*cb.SignedData) error {
   139  	return fmt.Errorf("No such policy type: %s", rp)
   140  }
   141  
   142  // Basepath returns the basePath the manager was instnatiated with
   143  func (pm *ManagerImpl) BasePath() string {
   144  	return pm.basePath
   145  }
   146  
   147  func (pm *ManagerImpl) PolicyNames() []string {
   148  	policyNames := make([]string, len(pm.config.policies))
   149  	i := 0
   150  	for policyName := range pm.config.policies {
   151  		policyNames[i] = policyName
   152  		i++
   153  	}
   154  	return policyNames
   155  }
   156  
   157  // Manager returns the sub-policy manager for a given path and whether it exists
   158  func (pm *ManagerImpl) Manager(path []string) (Manager, bool) {
   159  	if len(path) == 0 {
   160  		return pm, true
   161  	}
   162  
   163  	m, ok := pm.config.managers[path[0]]
   164  	if !ok {
   165  		return nil, false
   166  	}
   167  
   168  	return m.Manager(path[1:])
   169  }
   170  
   171  // GetPolicy returns a policy and true if it was the policy requested, or false if it is the default reject policy
   172  func (pm *ManagerImpl) GetPolicy(id string) (Policy, bool) {
   173  	if id == "" {
   174  		logger.Errorf("Returning dummy reject all policy because no policy ID supplied")
   175  		return rejectPolicy(id), false
   176  	}
   177  	var relpath string
   178  
   179  	if strings.HasPrefix(id, PathSeparator) {
   180  		if pm.parent != nil {
   181  			return pm.parent.GetPolicy(id)
   182  		}
   183  		if !strings.HasPrefix(id, pm.fqPrefix) {
   184  			if logger.IsEnabledFor(logging.DEBUG) {
   185  				logger.Debugf("Requested policy from root manager with wrong basePath: %s, returning rejectAll", id)
   186  			}
   187  			return rejectPolicy(id), false
   188  		}
   189  		relpath = id[len(pm.fqPrefix):]
   190  	} else {
   191  		relpath = id
   192  	}
   193  
   194  	policy, ok := pm.config.policies[relpath]
   195  	if !ok {
   196  		if logger.IsEnabledFor(logging.DEBUG) {
   197  			logger.Debugf("Returning dummy reject all policy because %s could not be found in /%s/%s", id, pm.basePath, relpath)
   198  		}
   199  		return rejectPolicy(relpath), false
   200  	}
   201  	if logger.IsEnabledFor(logging.DEBUG) {
   202  		logger.Debugf("Returning policy %s for evaluation", relpath)
   203  	}
   204  	return policy, true
   205  }
   206  
   207  // BeginPolicies is used to start a new config proposal
   208  func (pm *ManagerImpl) BeginPolicyProposals(groups []string) ([]Proposer, error) {
   209  	if pm.pendingConfig != nil {
   210  		logger.Panicf("Programming error, cannot call begin in the middle of a proposal")
   211  	}
   212  
   213  	pm.pendingConfig = &policyConfig{
   214  		policies: make(map[string]Policy),
   215  		managers: make(map[string]*ManagerImpl),
   216  	}
   217  
   218  	managers := make([]Proposer, len(groups))
   219  	for i, group := range groups {
   220  		newManager := NewManagerImpl(group, pm.providers)
   221  		newManager.parent = pm
   222  		pm.pendingConfig.managers[group] = newManager
   223  		managers[i] = newManager
   224  	}
   225  	return managers, nil
   226  }
   227  
   228  // RollbackProposals is used to abandon a new config proposal
   229  func (pm *ManagerImpl) RollbackProposals() {
   230  	pm.pendingConfig = nil
   231  }
   232  
   233  // PreCommit is currently a no-op for the policy manager and always returns nil
   234  func (pm *ManagerImpl) PreCommit() error {
   235  	return nil
   236  }
   237  
   238  // CommitProposals is used to commit a new config proposal
   239  func (pm *ManagerImpl) CommitProposals() {
   240  	if pm.pendingConfig == nil {
   241  		logger.Panicf("Programming error, cannot call commit without an existing proposal")
   242  	}
   243  
   244  	for managerPath, m := range pm.pendingConfig.managers {
   245  		for _, policyName := range m.PolicyNames() {
   246  			fqKey := managerPath + PathSeparator + policyName
   247  			pm.pendingConfig.policies[fqKey], _ = m.GetPolicy(policyName)
   248  			logger.Debugf("In commit adding relative sub-policy %s to %s", fqKey, pm.basePath)
   249  		}
   250  	}
   251  
   252  	// Now that all the policies are present, initialize the meta policies
   253  	for _, imp := range pm.pendingConfig.imps {
   254  		imp.initialize(pm.pendingConfig)
   255  	}
   256  
   257  	pm.config = pm.pendingConfig
   258  	pm.pendingConfig = nil
   259  
   260  	if pm.parent == nil && pm.basePath == ChannelPrefix {
   261  		if _, ok := pm.config.managers[ApplicationPrefix]; ok {
   262  			// Check for default application policies if the application component is defined
   263  			for _, policyName := range []string{
   264  				ChannelApplicationReaders,
   265  				ChannelApplicationWriters,
   266  				ChannelApplicationAdmins} {
   267  				_, ok := pm.GetPolicy(policyName)
   268  				if !ok {
   269  					logger.Warningf("Current configuration has no policy '%s', this will likely cause problems in production systems", policyName)
   270  				} else {
   271  					logger.Debugf("As expected, current configuration has policy '%s'", policyName)
   272  				}
   273  			}
   274  		}
   275  		if _, ok := pm.config.managers[OrdererPrefix]; ok {
   276  			for _, policyName := range []string{BlockValidation} {
   277  				_, ok := pm.GetPolicy(policyName)
   278  				if !ok {
   279  					logger.Warningf("Current configuration has no policy '%s', this will likely cause problems in production systems", policyName)
   280  				} else {
   281  					logger.Debugf("As expected, current configuration has policy '%s'", policyName)
   282  				}
   283  			}
   284  		}
   285  	}
   286  }
   287  
   288  // ProposePolicy takes key, path, and ConfigPolicy and registers it in the proposed PolicyManager, or errors
   289  func (pm *ManagerImpl) ProposePolicy(key string, configPolicy *cb.ConfigPolicy) error {
   290  	policy := configPolicy.Policy
   291  	if policy == nil {
   292  		return fmt.Errorf("Policy cannot be nil")
   293  	}
   294  
   295  	var cPolicy Policy
   296  
   297  	if policy.Type == int32(cb.Policy_IMPLICIT_META) {
   298  		imp, err := newImplicitMetaPolicy(policy.Policy)
   299  		if err != nil {
   300  			return err
   301  		}
   302  		pm.pendingConfig.imps = append(pm.pendingConfig.imps, imp)
   303  		cPolicy = imp
   304  	} else {
   305  		provider, ok := pm.providers[int32(policy.Type)]
   306  		if !ok {
   307  			return fmt.Errorf("Unknown policy type: %v", policy.Type)
   308  		}
   309  
   310  		var err error
   311  		cPolicy, err = provider.NewPolicy(policy.Policy)
   312  		if err != nil {
   313  			return err
   314  		}
   315  	}
   316  
   317  	pm.pendingConfig.policies[key] = cPolicy
   318  
   319  	logger.Debugf("Proposed new policy %s for %s", key, pm.basePath)
   320  	return nil
   321  }