Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
static key for mapping, allow to set only state namespace (#70)
Browse files Browse the repository at this point in the history
* const key for mapping 
* owner extension refactoring
  • Loading branch information
vitiko authored Sep 20, 2021
1 parent a1cf54a commit 04fd12b
Show file tree
Hide file tree
Showing 15 changed files with 184 additions and 47 deletions.
9 changes: 6 additions & 3 deletions extensions/owner/modifier.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package owner

import (
"fmt"

"github.com/pkg/errors"

"github.com/s7techlab/cckit/router"
)

Expand All @@ -13,10 +16,10 @@ var (
// Only allow access from chain code owner
func Only(next router.HandlerFunc, pos ...int) router.HandlerFunc {
return func(c router.Context) (interface{}, error) {
invokerIsOwner, err := IsInvoker(c)
if invokerIsOwner && err == nil {
err := IsTxCreator(c)
if err == nil {
return next(c)
}
return nil, ErrOwnerOnly
return nil, fmt.Errorf(`%s: %w`, err, ErrOwnerOnly)
}
}
53 changes: 39 additions & 14 deletions extensions/owner/owner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
package owner

import (
"github.com/pkg/errors"
"errors"
"fmt"

"github.com/s7techlab/cckit/identity"
r "github.com/s7techlab/cckit/router"
)
Expand All @@ -16,6 +18,9 @@ var (

// ErrOwnerAlreadySetted owner already setted
ErrOwnerAlreadySetted = errors.New(`owner already setted`)

// ErrMSPIdentifierNotEqual occurs when tx creator and cc owner certificate did not match
ErrMSPIdentifierNotEqual = errors.New(`msp identifier not equal`)
)

func IsSetted(c r.Context) (bool, error) {
Expand Down Expand Up @@ -49,6 +54,7 @@ func SetFromCreator(c r.Context) (*identity.Entry, error) {
if err != nil {
return nil, err
}

return identityEntry, c.State().Insert(OwnerStateKey, identityEntry)
}

Expand All @@ -69,11 +75,11 @@ func SetFromArgs(c r.Context) (*identity.Entry, error) {
return Get(c)
}

// Insert
// Insert information about owner to chaincode state
func Insert(c r.Context, mspID string, cert []byte) (*identity.Entry, error) {

if ownerSetted, err := IsSetted(c); err != nil {
return nil, errors.Wrap(err, `check owner is set`)
return nil, fmt.Errorf(`check owner is set: %w`, err)
} else if ownerSetted {
return nil, ErrOwnerAlreadySetted
}
Expand All @@ -85,15 +91,15 @@ func Insert(c r.Context, mspID string, cert []byte) (*identity.Entry, error) {

identityEntry, err := identity.CreateEntry(id)
if err != nil {
return nil, errors.Wrap(err, `create owner entry`)
return nil, fmt.Errorf(`create owner entry: %w`, err)
}
return identityEntry, c.State().Insert(OwnerStateKey, identityEntry)
}

// IsInvokerOr checks tx creator and compares with owner of another identity
func IsInvokerOr(c r.Context, allowedTo ...identity.Identity) (bool, error) {
if isOwner, err := IsInvoker(c); isOwner || err != nil {
return isOwner, err
if err := IsTxCreator(c); err == nil {
return true, nil
}
if len(allowedTo) == 0 {
return false, nil
Expand All @@ -111,7 +117,7 @@ func IsInvokerOr(c r.Context, allowedTo ...identity.Identity) (bool, error) {
return false, nil
}

// IdentityFromState
// IdentityEntryFromState returns identity.Entry with chaincode owner certificate
func IdentityEntryFromState(c r.Context) (identity.Entry, error) {
res, err := c.State().Get(OwnerStateKey, &identity.Entry{})
if err != nil {
Expand All @@ -121,16 +127,35 @@ func IdentityEntryFromState(c r.Context) (identity.Entry, error) {
return res.(identity.Entry), nil
}

// IsInvoker checks than tx creator is chain code owner
func IsInvoker(c r.Context) (bool, error) {
invoker, err := identity.FromStub(c.Stub())
if err != nil {
// Deprecated: IsInvoker checks than tx creator is chain code owner
// use IsTxCreator
func IsInvoker(ctx r.Context) (bool, error) {
if err := IsTxCreator(ctx); err != nil {
return false, err
}
ownerEntry, err := IdentityEntryFromState(c)

return true, nil
}

// IsTxCreator returns error if owner identity (msp_id + certificate) did not match tx creator identity
func IsTxCreator(ctx r.Context) error {
invoker, err := identity.FromStub(ctx.Stub())
if err != nil {
return err
}

ownerEntry, err := IdentityEntryFromState(ctx)
if err != nil {
return false, err
return err
}

if ownerEntry.GetMSPID() != invoker.GetMSPIdentifier() {
return fmt.Errorf(`%s : %w`, ErrMSPIdentifierNotEqual, ErrOwnerOnly)
}

if err = identity.CertEqual(invoker, ownerEntry); err != nil {
return fmt.Errorf(`%s : %w`, err, ErrOwnerOnly)
}

return ownerEntry.MSPId == invoker.MspID && ownerEntry.Subject == invoker.GetSubject(), nil
return nil
}
11 changes: 6 additions & 5 deletions identity/cert_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package identity
import (
"crypto/x509"
"encoding/pem"
"fmt"
"time"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-chaincode-go/pkg/cid"
"github.com/hyperledger/fabric-chaincode-go/shim"
protomsp "github.com/hyperledger/fabric-protos-go/msp"
"github.com/hyperledger/fabric/msp"
"github.com/pkg/errors"
)

// New creates CertIdentity struct from an mspID and certificate
Expand All @@ -23,18 +23,18 @@ func New(mspID string, certPEM []byte) (ci *CertIdentity, err error) {
}

// FromStub creates Identity interface from tx creator mspID and certificate (stub.GetCreator)
func FromStub(stub shim.ChaincodeStubInterface) (ci *CertIdentity, err error) {
func FromStub(stub shim.ChaincodeStubInterface) (*CertIdentity, error) {
clientIdentity, err := cid.New(stub)
if err != nil {
return nil, errors.Wrap(err, `client identity from stub`)
return nil, fmt.Errorf(`client identity from stub: %w`, err)
}
mspID, err := clientIdentity.GetMSPID()
if err != nil {
return
return nil, err
}
cert, err := clientIdentity.GetX509Certificate()
if err != nil {
return
return nil, err
}
return &CertIdentity{mspID, cert}, nil
}
Expand All @@ -56,6 +56,7 @@ func (ci CertIdentity) GetID() string {
}

// Deprecated: GetMSPID returns invoker's membership service provider id
// Use GetMSPIdentifier()
func (ci CertIdentity) GetMSPID() string {
return ci.MspID
}
Expand Down
2 changes: 1 addition & 1 deletion identity/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (e Entry) GetIdentityEntry() Entry {
// CreateEntry creates IdentityEntry structure from an identity interface
func CreateEntry(i Identity) (g *Entry, err error) {
return &Entry{
MSPId: i.GetMSPID(),
MSPId: i.GetMSPIdentifier(),
Subject: i.GetSubject(),
Issuer: i.GetIssuer(),
PEM: i.GetPEM(),
Expand Down
34 changes: 34 additions & 0 deletions identity/equal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package identity

import (
"errors"
)

var (
ErrSubjectNotEqual = errors.New(`certificate subject not equal`)
ErrIssuerNotEqual = errors.New(`certificate issuer not equal`)
)

type (
SubjectIssuer interface {
GetSubject() string
GetIssuer() string
}
)

// CertEqual checks certificate equality
func CertEqual(cert1, cert2 SubjectIssuer) error {

if cert1 == nil || cert2 == nil {
return errors.New(`certificate empty`)
}
if cert1.GetSubject() != cert2.GetSubject() {
return ErrSubjectNotEqual
}

if cert1.GetIssuer() != cert2.GetIssuer() {
return ErrIssuerNotEqual
}

return nil
}
4 changes: 2 additions & 2 deletions identity/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ type Identity interface {
// Deprecated: GetMSPID msp identifier. Use GetMspIdentifier instead
GetMSPID() string

// GetSubject string representation of X.509 cert subject
// GetSubject string representation of X.509 cert subject
GetSubject() string
// GetIssuer string representation of X.509 cert issuer
// GetIssuer string representation of X.509 cert issuer
GetIssuer() string

// GetPublicKey *rsa.PublicKey or *dsa.PublicKey or *ecdsa.PublicKey:
Expand Down
23 changes: 23 additions & 0 deletions router/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package router

import (
"errors"
"fmt"
)

var ErrInvalidRequest = errors.New(`invalid request`)

type (
Validator interface {
Validate() error
}
)

// ValidateRequest use Validator interface and create error, allow to use error.Is(ErrInvalidRequest)
func ValidateRequest(request Validator) error {
if err := request.Validate(); err != nil {
return fmt.Errorf(`%s: %w`, err.Error(), ErrInvalidRequest)
}

return nil
}
17 changes: 4 additions & 13 deletions state/mapping/mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ var (

Owner = identitytestdata.Certificates[0].MustIdentity(`SOME_MSP`)
)
var _ = Describe(`Mapping`, func() {
var _ = Describe(`State mapping in chaincode`, func() {

BeforeSuite(func() {

Expand Down Expand Up @@ -162,23 +162,14 @@ var _ = Describe(`Mapping`, func() {
It("Allow to insert entry once more time", func() {
expectcc.ResponseOk(compositeIDCC.Invoke(`create`, create1))
})

It("Check entry keying", func() {

})

})

Describe(`Entity with complex id`, func() {

ent1 := &schema.EntityWithComplexId{Id: &schema.EntityComplexId{
IdPart1: []string{`aaa`, `bb`},
IdPart2: `ccc`,
IdPart3: testcc.MustTime(`2020-01-28T17:00:00Z`),
}}
ent1 := testdata.CreateEntityWithComplextId[0]

It("Allow to add data to chaincode state", func() {
expectcc.ResponseOk(complexIDCC.Invoke(`entityInsert`, ent1))
expectcc.ResponseOk(complexIDCC.From(Owner).Invoke(`entityInsert`, ent1))
keys := expectcc.PayloadIs(complexIDCC.From(Owner).Invoke(
`debugStateKeys`, `EntityWithComplexId`), &[]string{}).([]string)
Expect(len(keys)).To(Equal(1))
Expand Down Expand Up @@ -380,7 +371,7 @@ var _ = Describe(`Mapping`, func() {
}

It("Disallow to get config before set", func() {
expectcc.ResponseError(configCC.Invoke(`configGet`), `state entry not found: Config | config`)
expectcc.ResponseError(configCC.Invoke(`configGet`), `state entry not found: Config`)
})

It("Allow to set config", func() {
Expand Down
2 changes: 1 addition & 1 deletion state/mapping/state_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func (smm StateMappings) GetByNamespace(namespace state.Key) (StateMapper, error
return m, nil
}
}
return nil, fmt.Errorf(`%s: %s`, ErrStateMappingNotFound, namespace)
return nil, fmt.Errorf(`namespace=%s: %w`, namespace, ErrStateMappingNotFound)
}

func (smm StateMappings) GetBySchema(schema interface{}) (StateMapper, error) {
Expand Down
15 changes: 10 additions & 5 deletions state/mapping/state_mapping_opt.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@ const (
TimestampKeyLayout = `2006-01-02`
)

// WithStateNamespace sets namespace for mapping
func WithStateNamespace(namespace state.Key) StateMappingOpt {
// WithNamespace sets namespace for mapping
func WithNamespace(namespace state.Key) StateMappingOpt {
return func(sm *StateMapping, smm StateMappings) {
sm.namespace = namespace
}
}

// WithStaticPKey set static key for all instances of mapped entry
func WithStaticPKey(key state.Key) StateMappingOpt {
func WithConstPKey(keys ...state.Key) StateMappingOpt {
return func(sm *StateMapping, smm StateMappings) {
key := state.Key{}
for _, k := range keys {
key = key.Append(k)
}

sm.primaryKeyer = func(_ interface{}) (state.Key, error) {
return key, nil
}
Expand Down Expand Up @@ -108,7 +113,7 @@ func PKeySchema(pkeySchema interface{}) StateMappingOpt {
//add mapping for schema identifier
smm.Add(
pkeySchema,
WithStateNamespace(namespace),
WithNamespace(namespace),
PKeyAttr(attrs...),
KeyerFor(sm.schema))
}
Expand Down Expand Up @@ -140,7 +145,7 @@ func PKeyComplexId(pkeySchema interface{}) StateMappingOpt {
return func(sm *StateMapping, smm StateMappings) {
sm.primaryKeyer = attrsKeyer([]string{`Id`})
smm.Add(pkeySchema,
WithStateNamespace(SchemaNamespace(sm.schema)),
WithNamespace(SchemaNamespace(sm.schema)),
PKeyAttr(attrsFrom(pkeySchema)...),
KeyerFor(sm.schema))
}
Expand Down
Loading

0 comments on commit 04fd12b

Please sign in to comment.