From 1285081f8fac551271704f9d6bacb26be18cd5e1 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Tue, 15 Aug 2017 11:02:19 +0800 Subject: [PATCH] [FAB-5667] Enhance Policy Language 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 --- common/cauthdsl/cauthdsl_builder.go | 47 +++++++++++++++++++- common/cauthdsl/cauthdsl_test.go | 66 ++++++++++++++++++++++++++++ common/cauthdsl/policyparser.go | 19 +++++--- common/cauthdsl/policyparser_test.go | 45 +++++++++++++++++++ 4 files changed, 171 insertions(+), 6 deletions(-) diff --git a/common/cauthdsl/cauthdsl_builder.go b/common/cauthdsl/cauthdsl_builder.go index 2eee89bd78c..ed414a885c6 100644 --- a/common/cauthdsl/cauthdsl_builder.go +++ b/common/cauthdsl/cauthdsl_builder.go @@ -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{ @@ -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 diff --git a/common/cauthdsl/cauthdsl_test.go b/common/cauthdsl/cauthdsl_test.go index f1573ed085f..f05ed366962 100644 --- a/common/cauthdsl/cauthdsl_test.go +++ b/common/cauthdsl/cauthdsl_test.go @@ -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) +} diff --git a/common/cauthdsl/policyparser.go b/common/cauthdsl/policyparser.go index 7f8037b571c..82d5261fef9 100644 --- a/common/cauthdsl/policyparser.go +++ b/common/cauthdsl/policyparser.go @@ -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. @@ -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 . , 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) @@ -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 */ @@ -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}) diff --git a/common/cauthdsl/policyparser_test.go b/common/cauthdsl/policyparser_test.go index e6f64fb308e..fbc4448e771 100644 --- a/common/cauthdsl/policyparser_test.go +++ b/common/cauthdsl/policyparser_test.go @@ -17,6 +17,7 @@ limitations under the License. package cauthdsl import ( + "reflect" "testing" "github.com/hyperledger/fabric/protos/common" @@ -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)