Skip to content

Commit

Permalink
Access control at QSCC
Browse files Browse the repository at this point in the history
This change-set does the following:
1. Add access control to qscc by verifying that the caller
has read access to the channel.
A new test has been added to cover this access control

This change-set comes in the context of:
1. https://jira.hyperledger.org/browse/FAB-2969

Change-Id: Ieca0babd3b65ecffda450b29cc82797a101f8671
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Apr 4, 2017
1 parent 7a3af1d commit fa7faec
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 56 deletions.
21 changes: 20 additions & 1 deletion core/chaincode/shim/mockstub.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ type MockStub struct {
TxID string

TxTimestamp *timestamp.Timestamp

// mocked signedProposal
signedProposal *pb.SignedProposal
}

func (stub *MockStub) GetTxID() string {
Expand Down Expand Up @@ -96,11 +99,13 @@ func (stub *MockStub) GetFunctionAndParameters() (function string, params []stri
// MockStub doesn't support concurrent transactions at present.
func (stub *MockStub) MockTransactionStart(txid string) {
stub.TxID = txid
stub.setSignedProposal(&pb.SignedProposal{})
stub.setTxTimestamp(util.CreateUtcTimestamp())
}

// End a mocked transaction, clearing the UUID.
func (stub *MockStub) MockTransactionEnd(uuid string) {
stub.signedProposal = nil
stub.TxID = ""
}

Expand Down Expand Up @@ -129,6 +134,16 @@ func (stub *MockStub) MockInvoke(uuid string, args [][]byte) pb.Response {
return res
}

// Invoke this chaincode, also starts and ends a transaction.
func (stub *MockStub) MockInvokeWithSignedProposal(uuid string, args [][]byte, sp *pb.SignedProposal) pb.Response {
stub.args = args
stub.MockTransactionStart(uuid)
stub.signedProposal = sp
res := stub.cc.Invoke(stub)
stub.MockTransactionEnd(uuid)
return res
}

// GetState retrieves the value for a given key from the ledger
func (stub *MockStub) GetState(key string) ([]byte, error) {
value := stub.State[key]
Expand Down Expand Up @@ -272,7 +287,11 @@ func (stub *MockStub) GetBinding() ([]byte, error) {

// Not implemented
func (stub *MockStub) GetSignedProposal() (*pb.SignedProposal, error) {
return nil, nil
return stub.signedProposal, nil
}

func (stub *MockStub) setSignedProposal(sp *pb.SignedProposal) {
stub.signedProposal = sp
}

// Not implemented
Expand Down
72 changes: 36 additions & 36 deletions core/policy/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,93 +29,93 @@ import (
mspproto "github.com/hyperledger/fabric/protos/msp"
)

type mockChannelPolicyManagerGetter struct {
managers map[string]policies.Manager
type MockChannelPolicyManagerGetter struct {
Managers map[string]policies.Manager
}

func (c *mockChannelPolicyManagerGetter) Manager(channelID string) (policies.Manager, bool) {
return c.managers[channelID], true
func (c *MockChannelPolicyManagerGetter) Manager(channelID string) (policies.Manager, bool) {
return c.Managers[channelID], true
}

type mockChannelPolicyManager struct {
mockPolicy policies.Policy
type MockChannelPolicyManager struct {
MockPolicy policies.Policy
}

func (m *mockChannelPolicyManager) GetPolicy(id string) (policies.Policy, bool) {
return m.mockPolicy, true
func (m *MockChannelPolicyManager) GetPolicy(id string) (policies.Policy, bool) {
return m.MockPolicy, true
}

func (m *mockChannelPolicyManager) Manager(path []string) (policies.Manager, bool) {
func (m *MockChannelPolicyManager) Manager(path []string) (policies.Manager, bool) {
panic("Not implemented")
}

func (m *mockChannelPolicyManager) BasePath() string {
func (m *MockChannelPolicyManager) BasePath() string {
panic("Not implemented")
}

func (m *mockChannelPolicyManager) PolicyNames() []string {
func (m *MockChannelPolicyManager) PolicyNames() []string {
panic("Not implemented")
}

type mockPolicy struct {
deserializer msp.IdentityDeserializer
type MockPolicy struct {
Deserializer msp.IdentityDeserializer
}

// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
func (m *mockPolicy) Evaluate(signatureSet []*common.SignedData) error {
func (m *MockPolicy) Evaluate(signatureSet []*common.SignedData) error {
fmt.Printf("Evaluate [%s], [% x], [% x]\n", string(signatureSet[0].Identity), string(signatureSet[0].Data), string(signatureSet[0].Signature))
identity, err := m.deserializer.DeserializeIdentity(signatureSet[0].Identity)
identity, err := m.Deserializer.DeserializeIdentity(signatureSet[0].Identity)
if err != nil {
return err
}

return identity.Verify(signatureSet[0].Data, signatureSet[0].Signature)
}

type mockIdentityDeserializer struct {
identity []byte
msg []byte
type MockIdentityDeserializer struct {
Identity []byte
Msg []byte
}

func (d *mockIdentityDeserializer) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) {
fmt.Printf("id : [%s], [%s]\n", string(serializedIdentity), string(d.identity))
if bytes.Equal(d.identity, serializedIdentity) {
fmt.Printf("GOT : [%s], [%s]\n", string(serializedIdentity), string(d.identity))
return &mockIdentity{identity: d.identity, msg: d.msg}, nil
func (d *MockIdentityDeserializer) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) {
fmt.Printf("id : [%s], [%s]\n", string(serializedIdentity), string(d.Identity))
if bytes.Equal(d.Identity, serializedIdentity) {
fmt.Printf("GOT : [%s], [%s]\n", string(serializedIdentity), string(d.Identity))
return &MockIdentity{identity: d.Identity, msg: d.Msg}, nil
}

return nil, errors.New("Invalid identity")
return nil, errors.New("Invalid Identity")
}

type mockIdentity struct {
type MockIdentity struct {
identity []byte
msg []byte
}

func (id *mockIdentity) SatisfiesPrincipal(p *mspproto.MSPPrincipal) error {
func (id *MockIdentity) SatisfiesPrincipal(p *mspproto.MSPPrincipal) error {
if !bytes.Equal(id.identity, p.Principal) {
return fmt.Errorf("Different identities [% x]!=[% x]", id.identity, p.Principal)
}
return nil
}

func (id *mockIdentity) GetIdentifier() *msp.IdentityIdentifier {
func (id *MockIdentity) GetIdentifier() *msp.IdentityIdentifier {
return &msp.IdentityIdentifier{Mspid: "mock", Id: "mock"}
}

func (id *mockIdentity) GetMSPIdentifier() string {
func (id *MockIdentity) GetMSPIdentifier() string {
return "mock"
}

func (id *mockIdentity) Validate() error {
func (id *MockIdentity) Validate() error {
return nil
}

func (id *mockIdentity) GetOrganizationalUnits() []string {
func (id *MockIdentity) GetOrganizationalUnits() []string {
return []string{"dunno"}
}

func (id *mockIdentity) Verify(msg []byte, sig []byte) error {
func (id *MockIdentity) Verify(msg []byte, sig []byte) error {
fmt.Printf("VERIFY [% x], [% x], [% x]\n", string(id.msg), string(msg), string(sig))
if bytes.Equal(id.msg, msg) {
if bytes.Equal(msg, sig) {
Expand All @@ -126,22 +126,22 @@ func (id *mockIdentity) Verify(msg []byte, sig []byte) error {
return errors.New("Invalid Signature")
}

func (id *mockIdentity) VerifyOpts(msg []byte, sig []byte, opts msp.SignatureOpts) error {
func (id *MockIdentity) VerifyOpts(msg []byte, sig []byte, opts msp.SignatureOpts) error {
return nil
}

func (id *mockIdentity) VerifyAttributes(proof []byte, spec *msp.AttributeProofSpec) error {
func (id *MockIdentity) VerifyAttributes(proof []byte, spec *msp.AttributeProofSpec) error {
return nil
}

func (id *mockIdentity) Serialize() ([]byte, error) {
func (id *MockIdentity) Serialize() ([]byte, error) {
return []byte("cert"), nil
}

type mockMSPPrincipalGetter struct {
type MockMSPPrincipalGetter struct {
Principal []byte
}

func (m *mockMSPPrincipalGetter) Get(role string) (*mspproto.MSPPrincipal, error) {
func (m *MockMSPPrincipalGetter) Get(role string) (*mspproto.MSPPrincipal, error) {
return &mspproto.MSPPrincipal{Principal: m.Principal}, nil
}
16 changes: 8 additions & 8 deletions core/policy/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@ import (
)

func TestPolicyChecker(t *testing.T) {
policyManagerGetter := &mockChannelPolicyManagerGetter{
policyManagerGetter := &MockChannelPolicyManagerGetter{
map[string]policies.Manager{
"A": &mockChannelPolicyManager{&mockPolicy{&mockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}}},
"B": &mockChannelPolicyManager{&mockPolicy{&mockIdentityDeserializer{[]byte("Bob"), []byte("msg2")}}},
"C": &mockChannelPolicyManager{&mockPolicy{&mockIdentityDeserializer{[]byte("Alice"), []byte("msg3")}}},
"A": &MockChannelPolicyManager{&MockPolicy{&MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}}},
"B": &MockChannelPolicyManager{&MockPolicy{&MockIdentityDeserializer{[]byte("Bob"), []byte("msg2")}}},
"C": &MockChannelPolicyManager{&MockPolicy{&MockIdentityDeserializer{[]byte("Alice"), []byte("msg3")}}},
},
}
identityDeserializer := &mockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}
identityDeserializer := &MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}
pc := NewPolicyChecker(
policyManagerGetter,
identityDeserializer,
&mockMSPPrincipalGetter{Principal: []byte("Alice")},
&MockMSPPrincipalGetter{Principal: []byte("Alice")},
)

// Check that (non-empty channel, empty policy) fails
Expand All @@ -58,7 +58,7 @@ func TestPolicyChecker(t *testing.T) {

// Validate Alice signatures against channel A's readers
sProp, _ := utils.MockSignedEndorserProposalOrPanic("A", &peer.ChaincodeSpec{}, []byte("Alice"), []byte("msg1"))
policyManagerGetter.managers["A"].(*mockChannelPolicyManager).mockPolicy.(*mockPolicy).deserializer.(*mockIdentityDeserializer).msg = sProp.ProposalBytes
policyManagerGetter.Managers["A"].(*MockChannelPolicyManager).MockPolicy.(*MockPolicy).Deserializer.(*MockIdentityDeserializer).Msg = sProp.ProposalBytes
sProp.Signature = sProp.ProposalBytes
err = pc.CheckPolicy("A", "readers", sProp)
assert.NoError(t, err)
Expand All @@ -72,7 +72,7 @@ func TestPolicyChecker(t *testing.T) {
assert.Error(t, err)

// Alice is a member of the local MSP, policy check must succeed
identityDeserializer.msg = sProp.ProposalBytes
identityDeserializer.Msg = sProp.ProposalBytes
err = pc.CheckPolicyNoChannel("member", sProp)
assert.NoError(t, err)

Expand Down
23 changes: 22 additions & 1 deletion core/scc/qscc/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ import (

"github.com/op/go-logging"

"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/core/policy"
"github.com/hyperledger/fabric/msp/mgmt"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
)
Expand All @@ -35,6 +38,7 @@ import (
// - GetBlockByHash returns a block
// - GetTransactionByID returns a transaction
type LedgerQuerier struct {
policyChecker policy.PolicyChecker
}

var qscclogger = logging.MustGetLogger("qscc")
Expand All @@ -54,6 +58,13 @@ const (
func (e *LedgerQuerier) Init(stub shim.ChaincodeStubInterface) pb.Response {
qscclogger.Info("Init QSCC")

// Init policy checker for access control
e.policyChecker = policy.NewPolicyChecker(
peer.NewChannelPolicyManagerGetter(),
mgmt.GetLocalMSP(),
mgmt.NewLocalMSPPrincipalGetter(),
)

return shim.Success(nil)
}

Expand Down Expand Up @@ -85,7 +96,17 @@ func (e *LedgerQuerier) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
qscclogger.Debugf("Invoke function: %s on chain: %s", fname, cid)
}

// TODO: Handle ACL
// Handle ACL:
// 1. get the signed proposal
sp, err := stub.GetSignedProposal()
if err != nil {
return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub, %s: %s", cid, err))
}

// 2. check the channel reader policy
if err = e.policyChecker.CheckPolicy(cid, policies.ChannelApplicationReaders, sp); err != nil {
return shim.Error(fmt.Sprintf("Authorization request failed %s: %s", cid, err))
}

switch fname {
case GetTransactionByID:
Expand Down
Loading

0 comments on commit fa7faec

Please sign in to comment.