Skip to content

Commit

Permalink
Merge pull request #28 from hyperledger-labs/feature/permissioning
Browse files Browse the repository at this point in the history
Implement permissioning
  • Loading branch information
samuelvenzi authored May 7, 2024
2 parents 808a594 + 436e77d commit 3c78ac1
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 48 deletions.
89 changes: 89 additions & 0 deletions accesscontrol/allowCaller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package accesscontrol

import (
"regexp"

"github.com/hyperledger-labs/cc-tools/errors"
"github.com/hyperledger/fabric-chaincode-go/pkg/cid"
"github.com/hyperledger/fabric-chaincode-go/shim"
)

func AllowCaller(stub shim.ChaincodeStubInterface, allowedCallers []Caller) (bool, error) {
if allowedCallers == nil {
return true, nil
}

callerMSP, err := cid.GetMSPID(stub)
if err != nil {
return false, errors.WrapError(err, "could not get MSP id")
}

var grantedPermission bool
for i := 0; i < len(allowedCallers) && !grantedPermission; i++ {
allowed := allowedCallers[i]
isAllowedMSP, err := checkMSP(callerMSP, allowed.MSP)
if err != nil {
return false, errors.WrapError(err, "failed to check MSP")
}

isAllowedOU, err := checkOU(stub, allowed.OU)
if err != nil {
return false, errors.WrapError(err, "failed to check OU")
}

isAllowedAttrs, err := checkAttributes(stub, allowed.Attributes)
if err != nil {
return false, errors.WrapError(err, "failed to check attributes")
}

grantedPermission = isAllowedMSP && isAllowedOU && isAllowedAttrs
}

return grantedPermission, nil
}

func checkMSP(callerMsp, allowedMSP string) (bool, error) {
if len(allowedMSP) <= 1 {
return true, nil
}

// if caller is regexp
if allowedMSP[0] == '$' {
match, err := regexp.MatchString(allowedMSP[1:], callerMsp)
if err != nil {
return false, errors.NewCCError("failed to check if caller matches regexp", 500)
}

return match, nil
}

// if caller is not regexss
return callerMsp == allowedMSP, nil
}

func checkOU(stub shim.ChaincodeStubInterface, allowedOU string) (bool, error) {
if allowedOU == "" {
return true, nil
}

return cid.HasOUValue(stub, allowedOU)
}

func checkAttributes(stub shim.ChaincodeStubInterface, allowedAttrs map[string]string) (bool, error) {
if allowedAttrs == nil {
return true, nil
}

for key, value := range allowedAttrs {
callerValue, _, err := cid.GetAttributeValue(stub, key)
if err != nil {
return false, err
}

if callerValue != value {
return false, nil
}
}

return true, nil
}
7 changes: 7 additions & 0 deletions accesscontrol/caller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package accesscontrol

type Caller struct {
MSP string `json:"msp"`
OU string `json:"ou"`
Attributes map[string]string `json:"attributes"`
}
26 changes: 21 additions & 5 deletions mock/mockstub.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
"github.com/hyperledger/fabric-protos-go/msp"
pb "github.com/hyperledger/fabric-protos-go/peer"
)

Expand Down Expand Up @@ -291,10 +292,10 @@ func (stub *MockStub) GetStateByRange(startKey, endKey string) (shim.StateQueryI
return NewMockStateRangeQueryIterator(stub, startKey, endKey), nil
}

//To ensure that simple keys do not go into composite key namespace,
//we validate simplekey to check whether the key starts with 0x00 (which
//is the namespace for compositeKey). This helps in avoding simple/composite
//key collisions.
// To ensure that simple keys do not go into composite key namespace,
// we validate simplekey to check whether the key starts with 0x00 (which
// is the namespace for compositeKey). This helps in avoding simple/composite
// key collisions.
func validateSimpleKeys(simpleKeys ...string) error {
for _, key := range simpleKeys {
if len(key) > 0 && key[0] == compositeKeyNamespace[0] {
Expand Down Expand Up @@ -508,10 +509,25 @@ func NewMockStub(name string, cc shim.Chaincode) *MockStub {
s.Keys = list.New()
s.ChaincodeEventsChannel = make(chan *pb.ChaincodeEvent, 100) //define large capacity for non-blocking setEvent calls.
s.Decorations = make(map[string][]byte)

s.Creator, _ = newCreator(name, []byte{})
return s
}

// NewMockStubWithCert Constructor to initialize mock stub with a certificate. Useful for transactions with robust permissioning.
func NewMockStubWithCert(name string, cc shim.Chaincode, cert []byte) (*MockStub, error) {
s := NewMockStub(name, cc)
var err error
s.Creator, err = newCreator(name, cert)

return s, err
}

func newCreator(orgMSP string, cert []byte) ([]byte, error) {
sid := &msp.SerializedIdentity{Mspid: orgMSP,
IdBytes: cert}
return proto.Marshal(sid)
}

/*****************************
Range Query Iterator
*****************************/
Expand Down
9 changes: 5 additions & 4 deletions transactions/getTx.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"

"github.com/hyperledger-labs/cc-tools/accesscontrol"
"github.com/hyperledger-labs/cc-tools/errors"
sw "github.com/hyperledger-labs/cc-tools/stubwrapper"
)
Expand Down Expand Up @@ -50,10 +51,10 @@ var getTx = Transaction{

// If user requested asset list
type txListElem struct {
Tag string `json:"tag"`
Label string `json:"label"`
Description string `json:"description"`
Callers []string `json:"callers,omitempty"`
Tag string `json:"tag"`
Label string `json:"label"`
Description string `json:"description"`
Callers []accesscontrol.Caller `json:"callers,omitempty"`
}
var txRetList []txListElem
for _, tx := range txList {
Expand Down
39 changes: 7 additions & 32 deletions transactions/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package transactions

import (
"fmt"
"regexp"

"github.com/hyperledger-labs/cc-tools/accesscontrol"
"github.com/hyperledger-labs/cc-tools/assets"
"github.com/hyperledger-labs/cc-tools/errors"
sw "github.com/hyperledger-labs/cc-tools/stubwrapper"
Expand Down Expand Up @@ -40,38 +40,13 @@ func Run(stub shim.ChaincodeStubInterface) ([]byte, errors.ICCError) {
}

// Verify callers permissions
if tx.Callers != nil {
// Get tx caller MSP ID
txCaller, err := sw.GetMSPID()
if err != nil {
return nil, errors.WrapErrorWithStatus(err, "error getting tx caller", 500)
}
callPermission, err := accesscontrol.AllowCaller(stub, tx.Callers)
if err != nil {
return nil, errors.WrapError(err, "failed to check permissions")
}

// Check if caller is allowed
callPermission := false
for _, c := range tx.Callers {
if len(c) <= 1 {
continue
}
if c[0] == '$' { // if caller is regexp
match, err := regexp.MatchString(c[1:], txCaller)
if err != nil {
return nil, errors.NewCCError("failed to check if caller matches regexp", 500)
}
if match {
callPermission = true
break
}
} else { // if caller is not regexp
if c == txCaller {
callPermission = true
break
}
}
}
if !callPermission {
return nil, errors.NewCCError(fmt.Sprintf("%s cannot call this transaction", txCaller), 403)
}
if !callPermission {
return nil, errors.NewCCError("current caller not allowed", 403)
}

return tx.Routine(sw, reqMap)
Expand Down
8 changes: 4 additions & 4 deletions transactions/startupCheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ func StartupCheck() errors.ICCError {
for _, tx := range txList {
txName := tx.Tag
for _, c := range tx.Callers {
if len(c) <= 1 {
if len(c.MSP) <= 1 {
continue
}
if c[0] == '$' {
_, err := regexp.Compile(c[1:])
if c.MSP[0] == '$' {
_, err := regexp.Compile(c.MSP[1:])
if err != nil {
return errors.WrapErrorWithStatus(err, fmt.Sprintf("invalid caller regular expression %s for tx %s", c, txName), 500)
return errors.WrapErrorWithStatus(err, fmt.Sprintf("invalid caller msp regular expression %s for tx %s", c, txName), 500)
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion transactions/transaction.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package transactions

import (
"github.com/hyperledger-labs/cc-tools/accesscontrol"
"github.com/hyperledger-labs/cc-tools/errors"
sw "github.com/hyperledger-labs/cc-tools/stubwrapper"
)
Expand All @@ -11,7 +12,7 @@ type Transaction struct {
// Regexp is supported by putting '$' before the MSP regexp e.g. []string{`$org\dMSP`}.
// Please note this restriction DOES NOT protect ledger data from being
// read by unauthorized organizations, this should be done with Private Data.
Callers []string `json:"callers,omitempty"`
Callers []accesscontrol.Caller `json:"callers,omitempty"`

// Tag is how the tx will be called.
Tag string `json:"tag"`
Expand Down
14 changes: 12 additions & 2 deletions transactions/txList.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package transactions

import "github.com/hyperledger-labs/cc-tools/assets"
import (
"github.com/hyperledger-labs/cc-tools/accesscontrol"
"github.com/hyperledger-labs/cc-tools/assets"
)

var txList = []Transaction{}

Expand Down Expand Up @@ -45,7 +48,14 @@ func FetchTx(txName string) *Transaction {
func InitTxList(l []Transaction) {
txList = append(l, basicTxs...)
if assets.GetEnabledDynamicAssetType() {
callers := assets.GetAssetAdminsDynamicAssetType()
callersMSP := assets.GetAssetAdminsDynamicAssetType()
var callers []accesscontrol.Caller
for _, msp := range callersMSP {
callers = append(callers, accesscontrol.Caller{
MSP: msp,
})
}

for i := range dynamicAssetTypesTxs {
if dynamicAssetTypesTxs[i].Tag != "loadAssetTypeList" {
dynamicAssetTypesTxs[i].Callers = callers
Expand Down

0 comments on commit 3c78ac1

Please sign in to comment.