-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A simple interface by which peer can validate access to various resources. It also provides an implementation which defaults to current behavior of using READERS and WRITERS. This implementation can be used if RSCC is disabled (or not defined). - added SCC.FN resources - changed "Acl" to "ACL" uniformly and add aclLogger Change-Id: Ia34964e2714fd4693ef4829b6ea449dea5de6106 Signed-off-by: Srinivasan Muralidharan <muralisr@us.ibm.com>
- Loading branch information
Srinivasan Muralidharan
committed
Aug 8, 2017
1 parent
5101b9e
commit c2c8e20
Showing
4 changed files
with
330 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package aclmgmt | ||
|
||
import ( | ||
"sync" | ||
|
||
"github.com/hyperledger/fabric/common/flogging" | ||
) | ||
|
||
var aclLogger = flogging.MustGetLogger("aclmgmt") | ||
|
||
//fabric resources used for ACL checks. Note that some of the checks | ||
//such as LSCC_INSTALL are "peer wide" (current access checks in peer are | ||
//based on local MSP). These are not currently covered by RSCC or defaultProvider | ||
const ( | ||
PROPOSE = "PROPOSE" | ||
|
||
//LSCC resources | ||
LSCC_INSTALL = "LSCC_INSTALL" | ||
LSCC_DEPLOY = "LSCC_DEPLOY" | ||
LSCC_UPGRADE = "LSCC_UPGRADE" | ||
LSCC_GETCCINFO = "LSCC_GETCCINFO" | ||
LSCC_GETDEPSPEC = "LSCC_GETDEPSPEC" | ||
LSCC_GETCCDATA = "LSCC_GETCCDATA" | ||
LSCC_GETCHAINCODES = "LSCC_GETCHAINCODES" | ||
LSCC_GETINSTALLEDCHAINCODES = "LSCC_GETINSTALLEDCHAINCODES" | ||
|
||
//QSCC resources | ||
QSCC_GetChainInfo = "QSCC_GetChainInfo" | ||
QSCC_GetBlockByNumber = "QSCC_GetBlockByNumber" | ||
QSCC_GetBlockByHash = "QSCC_GetBlockByHash" | ||
QSCC_GetTransactionByID = "QSCC_GetTransactionByID" | ||
QSCC_GetBlockByTxID = "QSCC_GetBlockByTxID" | ||
|
||
//CSCC resources | ||
CSCC_JoinChain = "CSCC_JoinChain" | ||
CSCC_GetConfigBlock = "CSCC_GetConfigBlock" | ||
CSCC_GetChannels = "CSCC_GetChannels" | ||
|
||
//Chaincode-to-Chaincode call | ||
CC2CC = "CC2CC" | ||
|
||
//Events | ||
BLOCKEVENT = "BLOCKEVENT" | ||
FILTEREDBLOCKEVENT = "FILTEREDBLOCKEVENT" | ||
) | ||
|
||
type ACLProvider interface { | ||
//CheckACL checks the ACL for the resource for the channel using the | ||
//idinfo. idinfo is an object such as SignedProposal from which an | ||
//id can be extracted for testing against a policy | ||
CheckACL(resName string, channelID string, idinfo interface{}) error | ||
} | ||
|
||
var aclProvider ACLProvider | ||
|
||
var once sync.Once | ||
|
||
//RegisterACLProvider will be called to register actual SCC if RSCC (an ACLProvider) is enabled | ||
func RegisterACLProvider(r ACLProvider) { | ||
once.Do(func() { | ||
aclProvider = newACLMgmt(r) | ||
}) | ||
} | ||
|
||
//GetACLProvider returns ACLProvider | ||
func GetACLProvider() ACLProvider { | ||
if aclProvider == nil { | ||
panic("-----RegisterACLProvider not called -----") | ||
} | ||
return aclProvider | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package aclmgmt | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
"testing" | ||
|
||
pb "github.com/hyperledger/fabric/protos/peer" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
type mockACLProvider struct { | ||
retErr error | ||
} | ||
|
||
func (m *mockACLProvider) CheckACL(resName string, channelID string, idinfo interface{}) error { | ||
return m.retErr | ||
} | ||
|
||
//treat each test as an independent isolated one | ||
func reinit() { | ||
aclProvider = nil | ||
once = sync.Once{} | ||
} | ||
|
||
func TestPanicOnUnregistered(t *testing.T) { | ||
reinit() | ||
assert.Panics(t, func() { | ||
GetACLProvider() | ||
}, "Should have paniced on unregistered call") | ||
} | ||
|
||
func TestRegisterNilProvider(t *testing.T) { | ||
reinit() | ||
RegisterACLProvider(nil) | ||
assert.NotNil(t, GetACLProvider(), "Expected non-nil retval") | ||
} | ||
|
||
func TestBadID(t *testing.T) { | ||
reinit() | ||
RegisterACLProvider(nil) | ||
err := GetACLProvider().CheckACL(PROPOSE, "somechain", "badidtype") | ||
assert.Error(t, err, "Expected error") | ||
} | ||
|
||
func TestBadResource(t *testing.T) { | ||
reinit() | ||
RegisterACLProvider(nil) | ||
err := GetACLProvider().CheckACL("unknownresource", "somechain", &pb.SignedProposal{}) | ||
assert.Error(t, err, "Expected error") | ||
} | ||
|
||
func TestOverride(t *testing.T) { | ||
reinit() | ||
RegisterACLProvider(nil) | ||
GetACLProvider().(*aclMgmtImpl).aclOverrides[PROPOSE] = func(res, c string, idinfo interface{}) error { | ||
return nil | ||
} | ||
err := GetACLProvider().CheckACL(PROPOSE, "somechain", &pb.SignedProposal{}) | ||
assert.NoError(t, err) | ||
delete(GetACLProvider().(*aclMgmtImpl).aclOverrides, PROPOSE) | ||
} | ||
|
||
func TestWithProvider(t *testing.T) { | ||
reinit() | ||
RegisterACLProvider(&mockACLProvider{}) | ||
err := GetACLProvider().CheckACL(PROPOSE, "somechain", &pb.SignedProposal{}) | ||
assert.NoError(t, err) | ||
} | ||
|
||
func TestBadACL(t *testing.T) { | ||
reinit() | ||
RegisterACLProvider(&mockACLProvider{retErr: fmt.Errorf("badacl")}) | ||
err := GetACLProvider().CheckACL(PROPOSE, "somechain", &pb.SignedProposal{}) | ||
assert.Error(t, err, "Expected error") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package aclmgmt | ||
|
||
import ( | ||
"github.com/hyperledger/fabric/common/flogging" | ||
) | ||
|
||
var aclMgmtLogger = flogging.MustGetLogger("aclmgmt") | ||
|
||
type aclMethod func(resName string, channelID string, idinfo interface{}) error | ||
|
||
//implementation of aclMgmt | ||
type aclMgmtImpl struct { | ||
//by default resources are managed by RSCC. However users may set aclMethod for | ||
//a resource and bypass RSCC if necessary | ||
aclOverrides map[string]aclMethod | ||
} | ||
|
||
var rscc ACLProvider | ||
|
||
//CheckACL checks the ACL for the resource for the channel using the | ||
//idinfo. idinfo is an object such as SignedProposal from which an | ||
//id can be extracted for testing against a policy | ||
func (am *aclMgmtImpl) CheckACL(resName string, channelID string, idinfo interface{}) error { | ||
aclMeth := am.aclOverrides[resName] | ||
if aclMeth != nil { | ||
return aclMeth(resName, channelID, idinfo) | ||
} | ||
|
||
if rscc == nil { | ||
panic("-----RegisterACLProvider not called ----") | ||
} | ||
|
||
return rscc.CheckACL(resName, channelID, idinfo) | ||
} | ||
|
||
func newACLMgmt(r ACLProvider) ACLProvider { | ||
rscc = r | ||
if rscc == nil { | ||
rscc = newDefaultACLProvider() | ||
} | ||
|
||
//by default overrides are not set and all acl checks are referred to rscc | ||
return &aclMgmtImpl{aclOverrides: make(map[string]aclMethod)} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package aclmgmt | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/hyperledger/fabric/common/policies" | ||
"github.com/hyperledger/fabric/core/peer" | ||
"github.com/hyperledger/fabric/core/policy" | ||
"github.com/hyperledger/fabric/msp/mgmt" | ||
pb "github.com/hyperledger/fabric/protos/peer" | ||
) | ||
|
||
const ( | ||
CHANNELREADERS = policies.ChannelApplicationReaders | ||
CHANNELWRITERS = policies.ChannelApplicationWriters | ||
) | ||
|
||
//defaultACLProvider if RSCC not provided use the default pre 1.0 implementation | ||
type defaultACLProvider struct { | ||
policyChecker policy.PolicyChecker | ||
|
||
//peer wide policy (currently not used) | ||
pResourcePolicyMap map[string]string | ||
|
||
//channel specific policy | ||
cResourcePolicyMap map[string]string | ||
} | ||
|
||
func newDefaultACLProvider() ACLProvider { | ||
d := &defaultACLProvider{} | ||
d.initialize() | ||
|
||
return d | ||
} | ||
|
||
func (d *defaultACLProvider) initialize() { | ||
d.policyChecker = policy.NewPolicyChecker( | ||
peer.NewChannelPolicyManagerGetter(), | ||
mgmt.GetLocalMSP(), | ||
mgmt.NewLocalMSPPrincipalGetter(), | ||
) | ||
|
||
d.pResourcePolicyMap = make(map[string]string) | ||
d.cResourcePolicyMap = make(map[string]string) | ||
|
||
//-------------- LSCC -------------- | ||
//p resources (implemented by the chaincode currently) | ||
d.pResourcePolicyMap[LSCC_INSTALL] = "" | ||
d.pResourcePolicyMap[LSCC_GETCHAINCODES] = "" | ||
d.pResourcePolicyMap[LSCC_GETINSTALLEDCHAINCODES] = "" | ||
|
||
//c resources | ||
d.cResourcePolicyMap[LSCC_DEPLOY] = "" //ACL check covered by PROPOSAL | ||
d.cResourcePolicyMap[LSCC_UPGRADE] = "" //ACL check covered by PROPOSAL | ||
d.cResourcePolicyMap[LSCC_GETCCINFO] = CHANNELREADERS | ||
d.cResourcePolicyMap[LSCC_GETDEPSPEC] = CHANNELREADERS | ||
d.cResourcePolicyMap[LSCC_GETCCDATA] = CHANNELREADERS | ||
|
||
//-------------- QSCC -------------- | ||
//p resources (none) | ||
|
||
//c resources | ||
d.cResourcePolicyMap[QSCC_GetChainInfo] = CHANNELREADERS | ||
d.cResourcePolicyMap[QSCC_GetBlockByNumber] = CHANNELREADERS | ||
d.cResourcePolicyMap[QSCC_GetBlockByHash] = CHANNELREADERS | ||
d.cResourcePolicyMap[QSCC_GetTransactionByID] = CHANNELREADERS | ||
d.cResourcePolicyMap[QSCC_GetBlockByTxID] = CHANNELREADERS | ||
|
||
//--------------- CSCC resources ----------- | ||
//p resources (implemented by the chaincode currently) | ||
d.pResourcePolicyMap[CSCC_JoinChain] = "" | ||
d.cResourcePolicyMap[CSCC_GetChannels] = "" | ||
|
||
//c resources | ||
d.pResourcePolicyMap[CSCC_GetConfigBlock] = CHANNELREADERS | ||
|
||
//---------------- non-scc resources ------------ | ||
//Propose | ||
d.cResourcePolicyMap[PROPOSE] = CHANNELWRITERS | ||
|
||
//Chaincode-to-Chaincode | ||
d.cResourcePolicyMap[CC2CC] = CHANNELWRITERS | ||
|
||
//Events (not used currently - for future) | ||
d.cResourcePolicyMap[BLOCKEVENT] = CHANNELREADERS | ||
d.cResourcePolicyMap[FILTEREDBLOCKEVENT] = CHANNELREADERS | ||
} | ||
|
||
//this should cover an exhaustive list of everything called from the peer | ||
func (d *defaultACLProvider) defaultPolicy(resName string, cprovider bool) string { | ||
var pol string | ||
if cprovider { | ||
pol = d.cResourcePolicyMap[resName] | ||
} else { | ||
pol = d.pResourcePolicyMap[resName] | ||
} | ||
return pol | ||
} | ||
|
||
//CheckACL provides default (v 1.0) behavior by mapping resources to their ACL for a channel | ||
func (d *defaultACLProvider) CheckACL(resName string, channelID string, idinfo interface{}) error { | ||
policy := d.defaultPolicy(resName, true) | ||
if policy == "" { | ||
aclLogger.Errorf("Unmapped policy for %s", resName) | ||
return fmt.Errorf("Unmapped policy for %s", resName) | ||
} | ||
|
||
switch idinfo.(type) { | ||
case *pb.SignedProposal: | ||
return d.policyChecker.CheckPolicy(channelID, policy, idinfo.(*pb.SignedProposal)) | ||
default: | ||
aclLogger.Errorf("Unmapped id on checkACL %s", resName) | ||
return fmt.Errorf("Unknown id on checkACL %s", resName) | ||
} | ||
} |