Skip to content

Commit

Permalink
[FAB-5667] Enhance Policy Language
Browse files Browse the repository at this point in the history
This change-set does the following:
- Enhance the policy language to accommodate system
entity based principals

Tests have been added to verify the change-set

Change-Id: I4e90aa009d06fc6f72dec27e029fd7b4dc7c41e6
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Dec 1, 2017
1 parent 8e8a8d8 commit 1285081
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 6 deletions.
47 changes: 46 additions & 1 deletion common/cauthdsl/cauthdsl_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,34 @@ func SignedBy(index int32) *cb.SignaturePolicy {
// SignedByMspMember creates a SignaturePolicyEnvelope
// requiring 1 signature from any member of the specified MSP
func SignedByMspMember(mspId string) *cb.SignaturePolicyEnvelope {
return signedByFabricEntity(mspId, msp.MSPRole_MEMBER)
}

// SignedByMspClient creates a SignaturePolicyEnvelope
// requiring 1 signature from any client of the specified MSP
func SignedByMspClient(mspId string) *cb.SignaturePolicyEnvelope {
return signedByFabricEntity(mspId, msp.MSPRole_CLIENT)
}

// SignedByMspPeer creates a SignaturePolicyEnvelope
// requiring 1 signature from any peer of the specified MSP
func SignedByMspPeer(mspId string) *cb.SignaturePolicyEnvelope {
return signedByFabricEntity(mspId, msp.MSPRole_PEER)
}

// SignedByMspOrderer creates a SignaturePolicyEnvelope
// requiring 1 signature from any orderer of the specified MSP
func SignedByMspOrderer(mspId string) *cb.SignaturePolicyEnvelope {
return signedByFabricEntity(mspId, msp.MSPRole_ORDERER)
}

// SignedByFabricEntity creates a SignaturePolicyEnvelope
// requiring 1 signature from any fabric entity, having the passed role, of the specified MSP
func signedByFabricEntity(mspId string, role msp.MSPRole_MSPRoleType) *cb.SignaturePolicyEnvelope {
// specify the principal: it's a member of the msp we just found
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: utils.MarshalOrPanic(&msp.MSPRole{Role: msp.MSPRole_MEMBER, MspIdentifier: mspId})}
Principal: utils.MarshalOrPanic(&msp.MSPRole{Role: role, MspIdentifier: mspId})}

// create the policy: it requires exactly 1 signature from the first (and only) principal
p := &cb.SignaturePolicyEnvelope{
Expand Down Expand Up @@ -144,6 +168,27 @@ func SignedByAnyMember(ids []string) *cb.SignaturePolicyEnvelope {
return signedByAnyOfGivenRole(msp.MSPRole_MEMBER, ids)
}

// SignedByAnyClient returns a policy that requires one valid
// signature from a client of any of the orgs whose ids are
// listed in the supplied string array
func SignedByAnyClient(ids []string) *cb.SignaturePolicyEnvelope {
return signedByAnyOfGivenRole(msp.MSPRole_CLIENT, ids)
}

// SignedByAnyOrderer returns a policy that requires one valid
// signature from an orderer of any of the orgs whose ids are
// listed in the supplied string array
func SignedByAnyOrderer(ids []string) *cb.SignaturePolicyEnvelope {
return signedByAnyOfGivenRole(msp.MSPRole_ORDERER, ids)
}

// SignedByAnyPeer returns a policy that requires one valid
// signature from an orderer of any of the orgs whose ids are
// listed in the supplied string array
func SignedByAnyPeer(ids []string) *cb.SignaturePolicyEnvelope {
return signedByAnyOfGivenRole(msp.MSPRole_PEER, ids)
}

// SignedByAnyAdmin returns a policy that requires one valid
// signature from a admin of any of the orgs whose ids are
// listed in the supplied string array
Expand Down
66 changes: 66 additions & 0 deletions common/cauthdsl/cauthdsl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,69 @@ func TestDeduplicate(t *testing.T) {
assert.Equal(t, []*cb.SignedData{}, result, "No valid identities")
})
}

func TestSignedByMspClient(t *testing.T) {
e := SignedByMspClient("A")
assert.Equal(t, 1, len(e.Identities))

role := &mb.MSPRole{}
err := proto.Unmarshal(e.Identities[0].Principal, role)
assert.NoError(t, err)

assert.Equal(t, role.MspIdentifier, "A")
assert.Equal(t, role.Role, mb.MSPRole_CLIENT)

e = SignedByAnyClient([]string{"A"})
assert.Equal(t, 1, len(e.Identities))

role = &mb.MSPRole{}
err = proto.Unmarshal(e.Identities[0].Principal, role)
assert.NoError(t, err)

assert.Equal(t, role.MspIdentifier, "A")
assert.Equal(t, role.Role, mb.MSPRole_CLIENT)
}

func TestSignedByMspPeer(t *testing.T) {
e := SignedByMspPeer("A")
assert.Equal(t, 1, len(e.Identities))

role := &mb.MSPRole{}
err := proto.Unmarshal(e.Identities[0].Principal, role)
assert.NoError(t, err)

assert.Equal(t, role.MspIdentifier, "A")
assert.Equal(t, role.Role, mb.MSPRole_PEER)

e = SignedByAnyPeer([]string{"A"})
assert.Equal(t, 1, len(e.Identities))

role = &mb.MSPRole{}
err = proto.Unmarshal(e.Identities[0].Principal, role)
assert.NoError(t, err)

assert.Equal(t, role.MspIdentifier, "A")
assert.Equal(t, role.Role, mb.MSPRole_PEER)
}

func TestSignedByMspOrderer(t *testing.T) {
e := SignedByMspOrderer("A")
assert.Equal(t, 1, len(e.Identities))

role := &mb.MSPRole{}
err := proto.Unmarshal(e.Identities[0].Principal, role)
assert.NoError(t, err)

assert.Equal(t, role.MspIdentifier, "A")
assert.Equal(t, role.Role, mb.MSPRole_ORDERER)

e = SignedByAnyOrderer([]string{"A"})
assert.Equal(t, 1, len(e.Identities))

role = &mb.MSPRole{}
err = proto.Unmarshal(e.Identities[0].Principal, role)
assert.NoError(t, err)

assert.Equal(t, role.MspIdentifier, "A")
assert.Equal(t, role.Role, mb.MSPRole_ORDERER)
}
19 changes: 14 additions & 5 deletions common/cauthdsl/policyparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"github.com/hyperledger/fabric/protos/utils"
)

var regex *regexp.Regexp = regexp.MustCompile("^([[:alnum:]]+)([.])(member|admin)$")
var regex *regexp.Regexp = regexp.MustCompile("^([[:alnum:]]+)([.])(member|admin|client|peer|orderer)$")
var regexErr *regexp.Regexp = regexp.MustCompile("^No parameter '([^']+)' found[.]$")

// a stub function - it returns the same string as it's passed.
Expand Down Expand Up @@ -139,7 +139,7 @@ func secondPass(args ...interface{}) (interface{}, error) {
switch t := principal.(type) {
/* if it's a string, we expect it to be formed as
<MSP_ID> . <ROLE>, where MSP_ID is the MSP identifier
and ROLE is either a member of an admin*/
and ROLE is either a member, an admin, a client, a peer or an orderer*/
case string:
/* split the string */
subm := regex.FindAllStringSubmatch(t, -1)
Expand All @@ -149,10 +149,19 @@ func secondPass(args ...interface{}) (interface{}, error) {

/* get the right role */
var r msp.MSPRole_MSPRoleType
if subm[0][3] == "member" {
switch subm[0][3] {
case "member":
r = msp.MSPRole_MEMBER
} else {
case "admin":
r = msp.MSPRole_ADMIN
case "client":
r = msp.MSPRole_CLIENT
case "peer":
r = msp.MSPRole_PEER
case "orderer":
r = msp.MSPRole_ORDERER
default:
return nil, fmt.Errorf("Error parsing role %s", t)
}

/* build the principal we've been told */
Expand Down Expand Up @@ -210,7 +219,7 @@ func newContext() *context {
//
// where
// - ORG is a string (representing the MSP identifier)
// - ROLE is either the string "member" or the string "admin" representing the required role
// - ROLE is either the string "member", "admin", "client", "peer", or the string "orderer" representing the required role
func FromString(policy string) (*common.SignaturePolicyEnvelope, error) {
// first we translate the and/or business into outof gates
intermediate, err := govaluate.NewEvaluableExpressionWithFunctions(policy, map[string]govaluate.ExpressionFunction{"AND": and, "and": and, "OR": or, "or": or, "OUTOF": outof, "outof": outof, "OutOf": outof})
Expand Down
45 changes: 45 additions & 0 deletions common/cauthdsl/policyparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package cauthdsl

import (
"reflect"
"testing"

"github.com/hyperledger/fabric/protos/common"
Expand Down Expand Up @@ -94,6 +95,50 @@ func TestAnd(t *testing.T) {
assert.Equal(t, p1, p2)
}

func TestAndClientPeerOrderer(t *testing.T) {
p1, err := FromString("AND('A.client', 'B.peer')")
assert.NoError(t, err)

principals := make([]*msp.MSPPrincipal, 0)

principals = append(principals, &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: utils.MarshalOrPanic(&msp.MSPRole{Role: msp.MSPRole_CLIENT, MspIdentifier: "A"})})

principals = append(principals, &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: utils.MarshalOrPanic(&msp.MSPRole{Role: msp.MSPRole_PEER, MspIdentifier: "B"})})

p2 := &common.SignaturePolicyEnvelope{
Version: 0,
Rule: And(SignedBy(0), SignedBy(1)),
Identities: principals,
}

assert.True(t, reflect.DeepEqual(p1, p2))

p1, err = FromString("AND('A.peer', 'B.orderer')")
assert.NoError(t, err)

principals = make([]*msp.MSPPrincipal, 0)

principals = append(principals, &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: utils.MarshalOrPanic(&msp.MSPRole{Role: msp.MSPRole_PEER, MspIdentifier: "A"})})

principals = append(principals, &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: utils.MarshalOrPanic(&msp.MSPRole{Role: msp.MSPRole_ORDERER, MspIdentifier: "B"})})

p2 = &common.SignaturePolicyEnvelope{
Version: 0,
Rule: And(SignedBy(0), SignedBy(1)),
Identities: principals,
}

assert.True(t, reflect.DeepEqual(p1, p2))
}

func TestOr(t *testing.T) {
p1, err := FromString("OR('A.member', 'B.member')")
assert.NoError(t, err)
Expand Down

0 comments on commit 1285081

Please sign in to comment.