Skip to content

Commit

Permalink
[FAB-11524] Prover: Request Import
Browse files Browse the repository at this point in the history
This change-set does the following:
- Introduces the Prover service
- Implements support for the SignedCommand related to a request import
- Local interfaces have been introduces to reduce dependency from TMS,
  MSP and AccessControl
- Implement Access Control based on channel's policies

Future change-sets will provide:
- enablement of the Prover at peer's bootstrap

Change-Id: I64f57a82c2db2acc98411d9700b5ccb859cc4801
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
Signed-off-by: Matthew Sykes <sykesmat@us.ibm.com>
Signed-off-by: Mathias Bjoerkqvist <mbj@zurich.ibm.com>
  • Loading branch information
adecaro authored and mbjoerkqvist committed Sep 3, 2018
1 parent 111ae61 commit 556ab7f
Show file tree
Hide file tree
Showing 16 changed files with 1,676 additions and 0 deletions.
1 change: 1 addition & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 45 additions & 0 deletions token/server/accesscontrol.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package server

import (
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/token"
"github.com/pkg/errors"
)

//go:generate counterfeiter -o mock/signed_data_policy_checker.go -fake-name SignedDataPolicyChecker . SignedDataPolicyChecker

type SignedDataPolicyChecker interface {
// CheckPolicyBySignedData checks that the provided signed data is valid with respect to
// specified policy for the specified channel.
CheckPolicyBySignedData(channelID, policyName string, sd []*common.SignedData) error
}

// PolicyBasedAccessControl implements token command access control functions.
type PolicyBasedAccessControl struct {
SignedDataPolicyChecker SignedDataPolicyChecker
}

func (ac *PolicyBasedAccessControl) Check(sc *token.SignedCommand, c *token.Command) error {
switch t := c.GetPayload().(type) {

case *token.Command_ImportRequest:
return ac.SignedDataPolicyChecker.CheckPolicyBySignedData(
c.Header.ChannelId,
policies.ChannelApplicationWriters,
[]*common.SignedData{{
Identity: c.Header.Creator,
Data: sc.Command,
Signature: sc.Signature,
}},
)

default:
return errors.Errorf("command type not recognized: %T", t)
}
}
97 changes: 97 additions & 0 deletions token/server/accesscontrol_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package server_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/pkg/errors"

"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/token"
"github.com/hyperledger/fabric/token/server"
"github.com/hyperledger/fabric/token/server/mock"
)

var _ = Describe("AccessControl", func() {
var (
fakePolicyChecker *mock.SignedDataPolicyChecker
pbac *server.PolicyBasedAccessControl

importRequest *token.ImportRequest
command *token.Command
marshaledCommand []byte
signedCommand *token.SignedCommand
)

BeforeEach(func() {
fakePolicyChecker = &mock.SignedDataPolicyChecker{}
pbac = &server.PolicyBasedAccessControl{
SignedDataPolicyChecker: fakePolicyChecker,
}

importRequest = &token.ImportRequest{}
command = &token.Command{
Header: &token.Header{
ChannelId: "channel-id",
Creator: []byte("creator"),
},
Payload: &token.Command_ImportRequest{
ImportRequest: importRequest,
},
}

marshaledCommand = ProtoMarshal(command)
signedCommand = &token.SignedCommand{
Command: marshaledCommand,
Signature: []byte("signature"),
}
})

It("checks the policy", func() {
err := pbac.Check(signedCommand, command)
Expect(err).NotTo(HaveOccurred())

Expect(fakePolicyChecker.CheckPolicyBySignedDataCallCount()).To(Equal(1))
channelID, policyName, signedData := fakePolicyChecker.CheckPolicyBySignedDataArgsForCall(0)
Expect(channelID).To(Equal("channel-id"))
Expect(policyName).To(Equal(policies.ChannelApplicationWriters))
Expect(signedData).To(ConsistOf(&common.SignedData{
Data: marshaledCommand,
Identity: []byte("creator"),
Signature: []byte("signature"),
}))
})

Context("when the policy checker returns an error", func() {
BeforeEach(func() {
fakePolicyChecker.CheckPolicyBySignedDataReturns(errors.New("no-can-do"))
})

It("returns the error", func() {
err := pbac.Check(signedCommand, command)
Expect(err).To(MatchError("no-can-do"))
})
})

Context("when the command payload type is not recognized", func() {
BeforeEach(func() {
command.Payload = nil
})

It("skips the access control check", func() {
pbac.Check(signedCommand, command)
Expect(fakePolicyChecker.CheckPolicyBySignedDataCallCount()).To(Equal(0))
})

It("returns a error", func() {
err := pbac.Check(signedCommand, command)
Expect(err).To(MatchError("command type not recognized: <nil>"))
})
})
})
98 changes: 98 additions & 0 deletions token/server/marshal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package server

import (
"time"

"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/protos/token"
"github.com/pkg/errors"
)

// UnmarshalCommand unmarshal token.Command messages
func UnmarshalCommand(raw []byte) (*token.Command, error) {
command := &token.Command{}
err := proto.Unmarshal(raw, command)
if err != nil {
return nil, err
}

return command, nil
}

type TimeFunc func() time.Time

// ResponseMarshaler produces token.SignedCommandResponse
type ResponseMarshaler struct {
Signer Signer
Creator []byte
Time TimeFunc
}

func NewResponseMarshaler(signerID SignerIdentity) (*ResponseMarshaler, error) {
creator, err := signerID.Serialize()
if err != nil {
return nil, err
}

return &ResponseMarshaler{
Signer: signerID,
Creator: creator,
Time: time.Now,
}, nil
}

func (s *ResponseMarshaler) MarshalCommandResponse(command []byte, responsePayload interface{}) (*token.SignedCommandResponse, error) {
cr, err := commandResponseFromPayload(responsePayload)
if err != nil {
return nil, err
}

ts, err := ptypes.TimestampProto(s.Time())
if err != nil {
return nil, err
}

cr.Header = &token.CommandResponseHeader{
Creator: s.Creator,
CommandHash: util.ComputeSHA256(command),
Timestamp: ts,
}

return s.createSignedCommandResponse(cr)
}

func (s *ResponseMarshaler) createSignedCommandResponse(cr *token.CommandResponse) (*token.SignedCommandResponse, error) {
raw, err := proto.Marshal(cr)
if err != nil {
return nil, err
}

signature, err := s.Signer.Sign(raw)
if err != nil {
return nil, err
}

return &token.SignedCommandResponse{
Response: raw,
Signature: signature,
}, nil
}

func commandResponseFromPayload(payload interface{}) (*token.CommandResponse, error) {
switch t := payload.(type) {
case *token.CommandResponse_TokenTransaction:
return &token.CommandResponse{Payload: t}, nil
case *token.CommandResponse_Err:
return &token.CommandResponse{Payload: t}, nil
default:
return nil, errors.Errorf("command type not recognized: %T", t)
}
}
Loading

0 comments on commit 556ab7f

Please sign in to comment.