Skip to content

Commit

Permalink
[FAB-5669] SatisfiesPrincipal extended support
Browse files Browse the repository at this point in the history
This change-set does the following:
- It adds support for MSPPrincipal.Role.Client/Peer/Orderer
to identity#SatisfiesPrinciapl.
- It enforces that administrators of an MSP are clients

Tests have been added to verify the change-set

Change-Id: I9663df5ba242db505c4d5fcbbed19c050825f97f
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Nov 27, 2017
1 parent 34a1f3e commit 55ed04d
Show file tree
Hide file tree
Showing 16 changed files with 433 additions and 4 deletions.
60 changes: 59 additions & 1 deletion msp/mspimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,51 @@ func (msp *bccspmsp) Validate(id Identity) error {
}
}

// hasOURole checks that the identity belongs to the organizational unit
// associated to the specified MSPRole.
// This function does not check the certifiers identifier.
// Appropriate validation needs to be enforced before.
func (msp *bccspmsp) hasOURole(id Identity, mspRole m.MSPRole_MSPRoleType) error {
// Check NodeOUs
if !msp.ouEnforcement {
return errors.New("NodeOUs not activated. Cannot tell apart identities.")
}

mspLogger.Debugf("MSP %s checking if the identity is a client", msp.name)

switch id := id.(type) {
// If this identity is of this specific type,
// this is how I can validate it given the
// root of trust this MSP has
case *identity:
return msp.hasOURoleInternal(id, mspRole)
default:
return errors.New("Identity type not recognized")
}
}

func (msp *bccspmsp) hasOURoleInternal(id *identity, mspRole m.MSPRole_MSPRoleType) error {
var nodeOUValue string
switch mspRole {
case m.MSPRole_CLIENT:
nodeOUValue = msp.clientOU.OrganizationalUnitIdentifier
case m.MSPRole_PEER:
nodeOUValue = msp.peerOU.OrganizationalUnitIdentifier
case m.MSPRole_ORDERER:
nodeOUValue = msp.ordererOU.OrganizationalUnitIdentifier
default:
return fmt.Errorf("Invalid MSPRoleType. It must be CLIENT, PEER or ORDERER")
}

for _, OU := range id.GetOrganizationalUnits() {
if OU.OrganizationalUnitIdentifier == nodeOUValue {
return nil
}
}

return fmt.Errorf("The identity does not contain OU [%s], MSP: [%s]", mspRole, msp.name)
}

// DeserializeIdentity returns an Identity given the byte-level
// representation of a SerializedIdentity struct
func (msp *bccspmsp) DeserializeIdentity(serializedID []byte) (Identity, error) {
Expand Down Expand Up @@ -360,8 +405,21 @@ func (msp *bccspmsp) SatisfiesPrincipal(id Identity, principal *m.MSPPrincipal)
return nil
}
}

return errors.New("This identity is not an admin")
case m.MSPRole_CLIENT:
fallthrough
case m.MSPRole_PEER:
fallthrough
case m.MSPRole_ORDERER:
mspLogger.Debugf("Checking if identity satisfies role [%s] for %s", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
if err := msp.Validate(id); err != nil {
return errors.Wrapf(err, "The identity is not valid under this MSP [%s]", msp.name)
}

if err := msp.hasOURole(id, mspRole.Role); err != nil {
return errors.Wrapf(err, "The identity is not a [%s] under this MSP [%s]", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
}
return nil
default:
return errors.Errorf("invalid MSP role type %d", int32(mspRole.Role))
}
Expand Down
30 changes: 28 additions & 2 deletions msp/mspimplsetup.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import (
"crypto/x509/pkix"
"fmt"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/bccsp"
m "github.com/hyperledger/fabric/protos/msp"
"github.com/pkg/errors"
errors "github.com/pkg/errors"
)

func (msp *bccspmsp) getCertifiersIdentifier(certRaw []byte) ([]byte, error) {
Expand Down Expand Up @@ -454,10 +455,35 @@ func (msp *bccspmsp) setupV11(conf *m.FabricMSPConfig) error {
return err
}

err = msp.postSetupV1(conf)
err = msp.postSetupV11(conf)
if err != nil {
return err
}

return nil
}

func (msp *bccspmsp) postSetupV11(conf *m.FabricMSPConfig) error {
// Check for OU enforcement
if !msp.ouEnforcement {
// No enforcement required. Call post setup as per V1
return msp.postSetupV1(conf)
}

// Check that admins are clients
principalBytes, err := proto.Marshal(&m.MSPRole{Role: m.MSPRole_CLIENT, MspIdentifier: msp.name})
if err != nil {
return errors.Wrapf(err, "failed creating MSPRole_CLIENT")
}
principal := &m.MSPPrincipal{
PrincipalClassification: m.MSPPrincipal_ROLE,
Principal: principalBytes}
for i, admin := range msp.admins {
err = admin.SatisfiesPrincipal(principal)
if err != nil {
return errors.WithMessage(err, fmt.Sprintf("admin %d is invalid", i))
}
}

return nil
}
183 changes: 182 additions & 1 deletion msp/nodeous_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package msp
import (
"testing"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/protos/msp"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -130,10 +132,189 @@ func TestInvalidAdminOU(t *testing.T) {
thisMSP, err := getLocalMSPWithVersionAndError(t, "testdata/nodeous4", MSPv1_1)
assert.True(t, thisMSP.(*bccspmsp).ouEnforcement)
assert.Error(t, err)
assert.Contains(t, err.Error(), "admin 0 is invalid: could not validate identity's OUs: certifiersIdentifier does not match")
assert.Contains(t, err.Error(), "admin 0 is invalid: The identity is not valid under this MSP [DEFAULT]: could not validate identity's OUs: certifiersIdentifier does not match")

// MSPv1_0 should not fail as well
thisMSP, err = getLocalMSPWithVersionAndError(t, "testdata/nodeous4", MSPv1_0)
assert.False(t, thisMSP.(*bccspmsp).ouEnforcement)
assert.NoError(t, err)
}

func TestInvalidAdminOUNotAClient(t *testing.T) {
// testdata/nodeous4:
// the configuration enables NodeOUs and admin is not a client
thisMSP, err := getLocalMSPWithVersionAndError(t, "testdata/nodeous8", MSPv1_1)
assert.True(t, thisMSP.(*bccspmsp).ouEnforcement)
assert.Error(t, err)
assert.Contains(t, err.Error(), "The identity does not contain OU [CLIENT]")

// MSPv1_0 should not fail
thisMSP, err = getLocalMSPWithVersionAndError(t, "testdata/nodeous8", MSPv1_0)
assert.False(t, thisMSP.(*bccspmsp).ouEnforcement)
assert.NoError(t, err)
}

func TestSatisfiesPrincipalPeer(t *testing.T) {
// testdata/nodeous3:
// the configuration enables NodeOUs and admin and signing identity are valid
thisMSP := getLocalMSPWithVersion(t, "testdata/nodeous3", MSPv1_1)
assert.True(t, thisMSP.(*bccspmsp).ouEnforcement)

// The default signing identity is a peer
id, err := thisMSP.GetDefaultSigningIdentity()
assert.NoError(t, err)

err = id.Validate()
assert.NoError(t, err)

assert.True(t, t.Run("Check that id is a peer", func(t *testing.T) {
// Check that id is a peer
mspID, err := thisMSP.GetIdentifier()
assert.NoError(t, err)
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_PEER, MspIdentifier: mspID})
assert.NoError(t, err)
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: principalBytes}
err = id.SatisfiesPrincipal(principal)
assert.NoError(t, err)
}))

assert.True(t, t.Run("Check that id is not a orderer", func(t *testing.T) {
// Check that id is not a orderer
mspID, err := thisMSP.GetIdentifier()
assert.NoError(t, err)
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ORDERER, MspIdentifier: mspID})
assert.NoError(t, err)
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: principalBytes}
err = id.SatisfiesPrincipal(principal)
assert.Error(t, err)
assert.Contains(t, err.Error(), "The identity is not a [ORDERER] under this MSP [DEFAULT]")
}))

assert.True(t, t.Run("Check that id is not a client", func(t *testing.T) {
// Check that id is not a client
mspID, err := thisMSP.GetIdentifier()
assert.NoError(t, err)
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_CLIENT, MspIdentifier: mspID})
assert.NoError(t, err)
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: principalBytes}
err = id.SatisfiesPrincipal(principal)
assert.Error(t, err)
assert.Contains(t, err.Error(), "The identity is not a [CLIENT] under this MSP [DEFAULT]")
}))
}

func TestSatisfiesPrincipalClient(t *testing.T) {
// testdata/nodeous3:
// the configuration enables NodeOUs and admin and signing identity are valid
thisMSP := getLocalMSPWithVersion(t, "testdata/nodeous3", MSPv1_1)
assert.True(t, thisMSP.(*bccspmsp).ouEnforcement)

// The admin of this msp is a client
assert.Equal(t, 1, len(thisMSP.(*bccspmsp).admins))
id := thisMSP.(*bccspmsp).admins[0]

err := id.Validate()
assert.NoError(t, err)

// Check that id is a client
assert.True(t, t.Run("Check that id is a client", func(t *testing.T) {
mspID, err := thisMSP.GetIdentifier()
assert.NoError(t, err)
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_CLIENT, MspIdentifier: mspID})
assert.NoError(t, err)
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: principalBytes}
err = id.SatisfiesPrincipal(principal)
assert.NoError(t, err)
}))

assert.True(t, t.Run("Check that id is not a orderer", func(t *testing.T) {
// Check that id is not a orderer
mspID, err := thisMSP.GetIdentifier()
assert.NoError(t, err)
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ORDERER, MspIdentifier: mspID})
assert.NoError(t, err)
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: principalBytes}
err = id.SatisfiesPrincipal(principal)
assert.Error(t, err)
assert.Contains(t, err.Error(), "The identity is not a [ORDERER] under this MSP [DEFAULT]")
}))

assert.True(t, t.Run("Check that id is not a peer", func(t *testing.T) {
// Check that id is not a peer
mspID, err := thisMSP.GetIdentifier()
assert.NoError(t, err)
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_PEER, MspIdentifier: mspID})
assert.NoError(t, err)
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: principalBytes}
err = id.SatisfiesPrincipal(principal)
assert.Error(t, err)
assert.Contains(t, err.Error(), "The identity is not a [PEER] under this MSP [DEFAULT]")
}))
}

func TestSatisfiesPrincipalOrderer(t *testing.T) {
// testdata/nodeous5:
// the configuration enables NodeOUs and admin and signing identity are valid
thisMSP := getLocalMSPWithVersion(t, "testdata/nodeous5", MSPv1_1)
assert.True(t, thisMSP.(*bccspmsp).ouEnforcement)

// The default signing identity is an orderer
id, err := thisMSP.GetDefaultSigningIdentity()
assert.NoError(t, err)

err = id.Validate()
assert.NoError(t, err)

assert.True(t, t.Run("Check that id is a peer", func(t *testing.T) {
// Check that id is a peer
mspID, err := thisMSP.GetIdentifier()
assert.NoError(t, err)
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ORDERER, MspIdentifier: mspID})
assert.NoError(t, err)
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: principalBytes}
err = id.SatisfiesPrincipal(principal)
assert.NoError(t, err)
}))

assert.True(t, t.Run("Check that id is not a orderer", func(t *testing.T) {
// Check that id is not a orderer
mspID, err := thisMSP.GetIdentifier()
assert.NoError(t, err)
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_PEER, MspIdentifier: mspID})
assert.NoError(t, err)
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: principalBytes}
err = id.SatisfiesPrincipal(principal)
assert.Error(t, err)
assert.Contains(t, err.Error(), "The identity is not a [PEER] under this MSP [DEFAULT]")
}))

assert.True(t, t.Run("Check that id is not a client", func(t *testing.T) {
// Check that id is not a client
mspID, err := thisMSP.GetIdentifier()
assert.NoError(t, err)
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_CLIENT, MspIdentifier: mspID})
assert.NoError(t, err)
principal := &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: principalBytes}
err = id.SatisfiesPrincipal(principal)
assert.Error(t, err)
assert.Contains(t, err.Error(), "The identity is not a [CLIENT] under this MSP [DEFAULT]")
}))
}
14 changes: 14 additions & 0 deletions msp/testdata/nodeous5/admincerts/peer0-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICETCCAbigAwIBAgIQZddB0WG9CC+fM24ExJi2TTAKBggqhkjOPQQDAjBpMQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
YW5jaXNjbzEUMBIGA1UEChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1w
bGUuY29tMB4XDTE3MDgxNTAyMTAzMFoXDTI3MDgxMzAyMTAzMFowXjELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lz
Y28xEjAQBgNVBAsMCU9VX2NsaWVudDEOMAwGA1UEAxMFcGVlcjAwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAARQ0e5cIZjSKwVNq2ua/hFezvYgsqBYeRHKo+ooUEaI
9Gni/7m9NHfy5BP/8jizmpAw0B2DP9U2oOSskHnEH6ozo00wSzAOBgNVHQ8BAf8E
BAMCB4AwDAYDVR0TAQH/BAIwADArBgNVHSMEJDAigCDzBa8g+ch+NQUz2wrrYdbZ
i6z5+nUH88XquRiv4ZbhrjAKBggqhkjOPQQDAgNHADBEAiAXfvQKCOlw8Qhu+Kgl
WoTFYJiu09Ti8avULBA4kqdXpwIgXSfA8qdRUUsxQ9uUnwu7PbK9YIS7ERWRB0T1
Yhjnu5g=
-----END CERTIFICATE-----
14 changes: 14 additions & 0 deletions msp/testdata/nodeous5/cacerts/ca.example.com-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICLzCCAdWgAwIBAgIQbjjR/DkfNMHkSfydFAcwBzAKBggqhkjOPQQDAjBpMQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
YW5jaXNjbzEUMBIGA1UEChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1w
bGUuY29tMB4XDTE3MDgxNTAyMTAzMFoXDTI3MDgxMzAyMTAzMFowaTELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lz
Y28xFDASBgNVBAoTC2V4YW1wbGUuY29tMRcwFQYDVQQDEw5jYS5leGFtcGxlLmNv
bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLLFDYqD6GH3vMtS7E1s1PDPmYRN
dYS5g4RCU/7rY/2cD9ZiFp+0hv2tewnIWPaRkGpmQg16RmxQcjVBVSJXwTujXzBd
MA4GA1UdDwEB/wQEAwIBpjAPBgNVHSUECDAGBgRVHSUAMA8GA1UdEwEB/wQFMAMB
Af8wKQYDVR0OBCIEIPMFryD5yH41BTPbCuth1tmLrPn6dQfzxeq5GK/hluGuMAoG
CCqGSM49BAMCA0gAMEUCIQCvMhfCQgQj4Zf/qaA0kVKOvdyP/8ubxWk8yaB8todx
SwIgCW1SVtEhqyCCk2/JKn7EFIbOwr2/1PsgxyDUOA7WnqQ=
-----END CERTIFICATE-----
12 changes: 12 additions & 0 deletions msp/testdata/nodeous5/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
NodeOUs:
Enable: true
ClientOUIdentifier:
OrganizationalUnitIdentifier: "OU_client"
PeerOUIdentifier:
OrganizationalUnitIdentifier: "OU_peer"
OrdererOUIdentifier:
OrganizationalUnitIdentifier: "OU_orderer"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgtB2spI0XSZlUFpFr
oKarKXD+XrE8qYatbwivuiEbznuhRANCAARQ0e5cIZjSKwVNq2ua/hFezvYgsqBY
eRHKo+ooUEaI9Gni/7m9NHfy5BP/8jizmpAw0B2DP9U2oOSskHnEH6oz
-----END PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg7Sbw+MrH0R4iw1Ly
omN3QyKoj30iWP0mssxCvVe9mdChRANCAASgkPNAlBuRv9/LHbwExyFAXc3KT2DF
c1aRgawkqctuhj1NIiFUdoPtjpBR3as67Dh3Z9yY6chTs6LFz2DOSU6+
-----END PRIVATE KEY-----
14 changes: 14 additions & 0 deletions msp/testdata/nodeous5/signcerts/peer0-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICEzCCAbmgAwIBAgIQIsUAZioeSk3X+G9d205DQjAKBggqhkjOPQQDAjBpMQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
YW5jaXNjbzEUMBIGA1UEChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1w
bGUuY29tMB4XDTE3MDgxNTAyMTAzMFoXDTI3MDgxMzAyMTAzMFowXzELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lz
Y28xEzARBgNVBAsMCk9VX29yZGVyZXIxDjAMBgNVBAMTBXBlZXIwMFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAEUNHuXCGY0isFTatrmv4RXs72ILKgWHkRyqPqKFBG
iPRp4v+5vTR38uQT//I4s5qQMNAdgz/VNqDkrJB5xB+qM6NNMEswDgYDVR0PAQH/
BAQDAgeAMAwGA1UdEwEB/wQCMAAwKwYDVR0jBCQwIoAg8wWvIPnIfjUFM9sK62HW
2Yus+fp1B/PF6rkYr+GW4a4wCgYIKoZIzj0EAwIDSAAwRQIhAPpkgs5sLN3iO/J2
TqoFfq0gWWmrJOz2AY2KesJh4+oUAiAkkYE/ub1ktBxpJPJWSsPXS/XO/hvUg7Sc
vqacmSoaiA==
-----END CERTIFICATE-----
Loading

0 comments on commit 55ed04d

Please sign in to comment.