Skip to content

Commit

Permalink
[FAB-5583] create basic resource system chaincode
Browse files Browse the repository at this point in the history
The RSCC has the basic function to read policy for a channel from
the Join genesis block. It uses the default ACL provider used by
the ACL management framework as a fall back for resources not
defined in the channel policy.

The channel read policy is currently just a stub waiting for the
RSCC policy to be set in RSCC ledger state. As soon as that work
is ready and integrated, RSCC will update its cache from the
channel ledger.

RSCC is currently "disabled" in core.yaml. Once enabled, CheckACL
calls to aclmgmt will be routed to RSCC's CheckACL.

Change-Id: I9f039fd5a068827f2b232a640f5949a5f0872b24
Signed-off-by: Srinivasan Muralidharan <muralisr@us.ibm.com>
Srinivasan Muralidharan committed Aug 8, 2017
1 parent c2c8e20 commit 8cd32bf
Showing 8 changed files with 438 additions and 8 deletions.
6 changes: 6 additions & 0 deletions core/aclmgmt/aclmgmt.go
Original file line number Diff line number Diff line change
@@ -75,3 +75,9 @@ func GetACLProvider() ACLProvider {
}
return aclProvider
}

//NewDefaultACLProvider constructs a new default provider for other systems
//such as RSCC to use
func NewDefaultACLProvider() ACLProvider {
return newDefaultACLProvider()
}
24 changes: 23 additions & 1 deletion core/scc/importsysccs.go
Original file line number Diff line number Diff line change
@@ -17,11 +17,14 @@ limitations under the License.
package scc

import (
"github.com/hyperledger/fabric/core/aclmgmt"

//import system chain codes here
"github.com/hyperledger/fabric/core/scc/cscc"
"github.com/hyperledger/fabric/core/scc/escc"
"github.com/hyperledger/fabric/core/scc/lscc"
"github.com/hyperledger/fabric/core/scc/qscc"
"github.com/hyperledger/fabric/core/scc/rscc"
"github.com/hyperledger/fabric/core/scc/vscc"
)

@@ -67,14 +70,33 @@ var systemChaincodes = []*SystemChaincode{
InvokableExternal: true, // qscc can be invoked to retrieve blocks
InvokableCC2CC: true, // qscc can be invoked to retrieve blocks also by a cc
},
{
Enabled: true,
Name: "rscc",
Path: "github.com/hyperledger/fabric/core/chaincode/rscc",
InitArgs: [][]byte{[]byte("")},
Chaincode: rscc.NewRscc(),
InvokableExternal: true, // rscc can be invoked to update policies
InvokableCC2CC: false, // rscc cannot be invoked from a cc
},
}

//RegisterSysCCs is the hook for system chaincodes where system chaincodes are registered with the fabric
//note the chaincode must still be deployed and launched like a user chaincode will be
func RegisterSysCCs() {
var aclProvider aclmgmt.ACLProvider
for _, sysCC := range systemChaincodes {
RegisterSysCC(sysCC)
if reg, _ := registerSysCC(sysCC); reg {
//rscc is registered, lets make it the aclProvider
if sysCC.Name == "rscc" {
aclProvider = sysCC.Chaincode.(aclmgmt.ACLProvider)
}
}
}
//a nil aclProvider will initialize defaultACLProvider
//which will provide 1.0 ACL defaults
aclmgmt.RegisterACLProvider(aclProvider)

}

//DeploySysCCs is the hook for system chaincodes where system chaincodes are registered with the fabric
175 changes: 175 additions & 0 deletions core/scc/rscc/rscc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package rscc

import (
"fmt"
"sync"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/core/aclmgmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
)

const (
//CHANNEL name
CHANNEL = "channel"

//POLICY for the channel
POLICY = "policy"

//Init Errors (for UT)

//NOCHANNEL channel not found
NOCHANNEL = "nochannel"

//NOPOLICY policy not found for channel
NOPOLICY = "nopolicy"

//BADPOLICY bad policy
BADPOLICY = "badpolicy"
)

//the basic policyProvider for the channel, consists of a
//default and rscc policy providers
type policyProvider struct {
//maybe we should use a separate default policy provider
//than the only from aclmgmt (which is 1.0 defaults)
defaultProvider aclmgmt.ACLProvider

rsccProvider rsccPolicyProvider
}

//Rscc SCC implementing resouce->Policy mapping for the fabric
type Rscc struct {
sync.RWMutex

//the cache of RSCC policies for all the channels
policyCache map[string]*policyProvider
}

var rsccLogger = flogging.MustGetLogger("rscc")

//NewRscc get an initialzed new Rscc
func NewRscc() *Rscc {
return &Rscc{policyCache: make(map[string]*policyProvider)}
}

//--------- errors ---------

//NoPolicyProviderInCache in cache for channel
type NoPolicyProviderInCache string

func (e NoPolicyProviderInCache) Error() string {
return fmt.Sprintf("cannot find policy provider in cache for channel %s", string(e))
}

//PolicyProviderNotFound for channel
type PolicyProviderNotFound string

func (e PolicyProviderNotFound) Error() string {
return fmt.Sprintf("cannot find policy provider for channel %s", string(e))
}

//-------- ACLProvider interface ------

//CheckACL rscc implements AClProvider's CheckACL interface. This is the key interface
// . CheckACL works off the cache
// . CheckACL uses two providers - the RSCC provider from channel config and default provider
// that implements 1.0 functions
// . If a resource in RSCC Provider it'll use the policy defined there. Otherwise it'll defer
// to default provider
func (rscc *Rscc) CheckACL(resName string, channelID string, idinfo interface{}) error {
rsccLogger.Debugf("acl check(%s, %s)", resName, channelID)
rscc.RLock()
defer rscc.RUnlock()
pp := rscc.policyCache[channelID]
if pp == nil {
return NoPolicyProviderInCache(channelID)
}

//found policyProvider
if pp.rsccProvider != nil {
//get the policy mapping if any
if policyName := pp.rsccProvider.GetPolicyName(resName); policyName != "" {
return pp.rsccProvider.CheckACL(policyName, idinfo)
}
}

//try default provider
if pp.defaultProvider != nil {
return pp.defaultProvider.CheckACL(resName, channelID, idinfo)
}

return PolicyProviderNotFound(channelID)
}

//---------- misc functions ---------
func (rscc *Rscc) putPolicyProvider(channel string, pp *policyProvider) {
rscc.Lock()
defer rscc.Unlock()
rscc.policyCache[channel] = pp
}

func (rscc *Rscc) setPolicyProvider(channel string, defProv aclmgmt.ACLProvider, rsccProv rsccPolicyProvider) {
pp := &policyProvider{defProv, rsccProv}
rscc.putPolicyProvider(channel, pp)
}

//-------- CC interface ------------

// Init RSCC - Init is just used to initialize policy assuming it is found in the
// channel ledger. Don't return error and get out. For example, we might still serve
// Invokes to set policy
func (rscc *Rscc) Init(stub shim.ChaincodeStubInterface) pb.Response {
rsccLogger.Info("Init RSCC")

b, err := stub.GetState(CHANNEL)
if err != nil || len(b) == 0 {
rsccLogger.Errorf("cannot find channel name")
return shim.Success([]byte(NOCHANNEL))
}

channel := string(b)

defProv := aclmgmt.NewDefaultACLProvider()

b, err = stub.GetState(POLICY)
if err != nil || len(b) == 0 {
//we do not have policy, create just defaults
rscc.setPolicyProvider(channel, defProv, nil)
rsccLogger.Errorf("cannot find policy for channel %s", channel)
return shim.Success([]byte(NOPOLICY))
}

//TODO this is a place holder.. will change based on what is stored in
//ledger
cg := &common.ConfigGroup{}
err = proto.Unmarshal(b, cg)
if err != nil {
rscc.setPolicyProvider(channel, defProv, nil)
rsccLogger.Errorf("cannot unmarshal policy for channel %s", channel)
return shim.Success([]byte(BADPOLICY))
}

rsccpp, err := newRsccPolicyProvider(cg)
if err != nil {
rsccLogger.Errorf("cannot create policy provider for channel %s", channel)
}

rscc.setPolicyProvider(channel, defProv, rsccpp)

return shim.Success(nil)
}

//Invoke - update policies
func (rscc *Rscc) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Error("--TBD---")
}
Loading

0 comments on commit 8cd32bf

Please sign in to comment.