200 lines
5.9 KiB
Go
200 lines
5.9 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package aclmgmt
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hyperledger/fabric-protos-go/common"
|
|
pb "github.com/hyperledger/fabric-protos-go/peer"
|
|
"github.com/hyperledger/fabric/common/channelconfig"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
)
|
|
|
|
//--------- errors ---------
|
|
|
|
// PolicyNotFound cache for resource
|
|
type PolicyNotFound string
|
|
|
|
func (e PolicyNotFound) Error() string {
|
|
return fmt.Sprintf("policy %s not found", string(e))
|
|
}
|
|
|
|
// InvalidIdInfo
|
|
type InvalidIdInfo string
|
|
|
|
func (e InvalidIdInfo) Error() string {
|
|
return fmt.Sprintf("Invalid id for policy [%s]", string(e))
|
|
}
|
|
|
|
//---------- policyEvaluator ------
|
|
|
|
// policyEvalutor interface provides the interfaces for policy evaluation
|
|
type policyEvaluator interface {
|
|
PolicyRefForAPI(resName string) string
|
|
Evaluate(polName string, id []*protoutil.SignedData) error
|
|
}
|
|
|
|
// policyEvaluatorImpl implements policyEvaluator
|
|
type policyEvaluatorImpl struct {
|
|
bundle channelconfig.Resources
|
|
}
|
|
|
|
func (pe *policyEvaluatorImpl) PolicyRefForAPI(resName string) string {
|
|
app, exists := pe.bundle.ApplicationConfig()
|
|
if !exists {
|
|
return ""
|
|
}
|
|
|
|
pm := app.APIPolicyMapper()
|
|
if pm == nil {
|
|
return ""
|
|
}
|
|
|
|
return pm.PolicyRefForAPI(resName)
|
|
}
|
|
|
|
func (pe *policyEvaluatorImpl) Evaluate(polName string, sd []*protoutil.SignedData) error {
|
|
policy, ok := pe.bundle.PolicyManager().GetPolicy(polName)
|
|
if !ok {
|
|
return PolicyNotFound(polName)
|
|
}
|
|
|
|
err := policy.EvaluateSignedData(sd)
|
|
if err != nil {
|
|
aclLogger.Warnw("EvaluateSignedData policy check failed", "error", err, "policyName", polName, policy, "policy", "signingIdentities", protoutil.LogMessageForSerializedIdentities(sd))
|
|
}
|
|
return err
|
|
}
|
|
|
|
//------ resourcePolicyProvider ----------
|
|
|
|
// aclmgmtPolicyProvider is the interface implemented by resource based ACL.
|
|
type aclmgmtPolicyProvider interface {
|
|
// GetPolicyName returns policy name given resource name
|
|
GetPolicyName(resName string) string
|
|
|
|
// CheckACL backs ACLProvider interface
|
|
CheckACL(polName string, idinfo interface{}) error
|
|
}
|
|
|
|
// aclmgmtPolicyProviderImpl holds the bytes from state of the ledger
|
|
type aclmgmtPolicyProviderImpl struct {
|
|
pEvaluator policyEvaluator
|
|
}
|
|
|
|
// GetPolicyName returns the policy name given the resource string
|
|
func (rp *aclmgmtPolicyProviderImpl) GetPolicyName(resName string) string {
|
|
return rp.pEvaluator.PolicyRefForAPI(resName)
|
|
}
|
|
|
|
// CheckACL implements AClProvider's CheckACL interface so it can be registered
|
|
// as a provider with aclmgmt
|
|
func (rp *aclmgmtPolicyProviderImpl) CheckACL(polName string, idinfo interface{}) error {
|
|
aclLogger.Debugf("acl check(%s)", polName)
|
|
|
|
// we will implement other identifiers. In the end we just need a SignedData
|
|
var sd []*protoutil.SignedData
|
|
switch idinfo := idinfo.(type) {
|
|
case *pb.SignedProposal:
|
|
signedProp := idinfo
|
|
proposal, err := protoutil.UnmarshalProposal(signedProp.ProposalBytes)
|
|
if err != nil {
|
|
return fmt.Errorf("Failing extracting proposal during check policy with policy [%s]: [%s]", polName, err)
|
|
}
|
|
|
|
header, err := protoutil.UnmarshalHeader(proposal.Header)
|
|
if err != nil {
|
|
return fmt.Errorf("Failing extracting header during check policy [%s]: [%s]", polName, err)
|
|
}
|
|
|
|
shdr, err := protoutil.UnmarshalSignatureHeader(header.SignatureHeader)
|
|
if err != nil {
|
|
return fmt.Errorf("Invalid Proposal's SignatureHeader during check policy [%s]: [%s]", polName, err)
|
|
}
|
|
|
|
sd = []*protoutil.SignedData{{
|
|
Data: signedProp.ProposalBytes,
|
|
Identity: shdr.Creator,
|
|
Signature: signedProp.Signature,
|
|
}}
|
|
|
|
case *common.Envelope:
|
|
var err error
|
|
sd, err = protoutil.EnvelopeAsSignedData(idinfo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
case *protoutil.SignedData:
|
|
sd = []*protoutil.SignedData{idinfo}
|
|
|
|
default:
|
|
return InvalidIdInfo(polName)
|
|
}
|
|
|
|
err := rp.pEvaluator.Evaluate(polName, sd)
|
|
if err != nil {
|
|
return fmt.Errorf("failed evaluating policy on signed data during check policy [%s]: [%s]", polName, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
//-------- resource provider - entry point API used by aclmgmtimpl for doing resource based ACL ----------
|
|
|
|
// resource getter gets channelconfig.Resources given channel ID
|
|
type ResourceGetter func(channelID string) channelconfig.Resources
|
|
|
|
// resource provider that uses the resource configuration information to provide ACL support
|
|
type resourceProvider struct {
|
|
// resource getter
|
|
resGetter ResourceGetter
|
|
|
|
// default provider to be used for undefined resources
|
|
defaultProvider defaultACLProvider
|
|
}
|
|
|
|
// create a new resourceProvider
|
|
func newResourceProvider(rg ResourceGetter, defprov defaultACLProvider) *resourceProvider {
|
|
return &resourceProvider{rg, defprov}
|
|
}
|
|
|
|
func (rp *resourceProvider) enforceDefaultBehavior(resName string, channelID string, idinfo interface{}) bool {
|
|
// we currently enforce using p types if defined. In future we will allow p types
|
|
// to be overridden through peer configuration
|
|
return rp.defaultProvider.IsPtypePolicy(resName)
|
|
}
|
|
|
|
// CheckACL implements the ACL
|
|
func (rp *resourceProvider) CheckACL(resName string, channelID string, idinfo interface{}) error {
|
|
if !rp.enforceDefaultBehavior(resName, channelID, idinfo) {
|
|
resCfg := rp.resGetter(channelID)
|
|
|
|
if resCfg != nil {
|
|
pp := &aclmgmtPolicyProviderImpl{&policyEvaluatorImpl{resCfg}}
|
|
policyName := pp.GetPolicyName(resName)
|
|
if policyName != "" {
|
|
aclLogger.Debugf("acl policy %s found in config for resource %s", policyName, resName)
|
|
return pp.CheckACL(policyName, idinfo)
|
|
}
|
|
aclLogger.Debugf("acl policy not found in config for resource %s", resName)
|
|
}
|
|
}
|
|
|
|
return rp.defaultProvider.CheckACL(resName, channelID, idinfo)
|
|
}
|
|
|
|
// CheckACLNoChannel implements the ACLProvider interface function
|
|
func (rp *resourceProvider) CheckACLNoChannel(resName string, idinfo interface{}) error {
|
|
if !rp.enforceDefaultBehavior(resName, "", idinfo) {
|
|
return fmt.Errorf("cannot override peer type policy for channeless ACL check")
|
|
}
|
|
|
|
return rp.defaultProvider.CheckACLNoChannel(resName, idinfo)
|
|
}
|