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

static key for mapping, allow to set only state namespace #70

Merged
merged 7 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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