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

Commit 820c5a0

Browse files
authored
owner ext as service (#79)
owner ext as service
1 parent ce2a4c0 commit 820c5a0

18 files changed

Lines changed: 3271 additions & 46 deletions

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
GOFLAGS ?= -mod=vendor
22

33
PROTO_PACKAGES_GO := examples/cpaper_extended/schema state/schema examples/cpaper_asservice/schema
4-
PROTO_PACKAGES_CCGW := extensions/debug examples/cpaper_asservice/service
4+
PROTO_PACKAGES_CCGW := extensions/debug extensions/owner examples/cpaper_asservice/service
55

66
test:
77
@echo "go test -mod vendor ./..."

examples/erc20/erc20_ops.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ func invokeTransfer(c r.Context) (interface{}, error) {
6262
}
6363

6464
// Disallow to transfer token to same account
65-
if invoker.GetMSPID() == toMspId && invoker.GetID() == toCertId {
65+
if invoker.GetMSPIdentifier() == toMspId && invoker.GetID() == toCertId {
6666
return nil, ErrForbiddenToTransferToSameAccount
6767
}
6868

6969
// get information about invoker balance from state
70-
invokerBalance, err := getBalance(c, invoker.GetMSPID(), invoker.GetID())
70+
invokerBalance, err := getBalance(c, invoker.GetMSPIdentifier(), invoker.GetID())
7171
if err != nil {
7272
return nil, err
7373
}
@@ -84,7 +84,7 @@ func invokeTransfer(c r.Context) (interface{}, error) {
8484
}
8585

8686
// Update payer and recipient balance
87-
if err = setBalance(c, invoker.GetMSPID(), invoker.GetID(), invokerBalance-amount); err != nil {
87+
if err = setBalance(c, invoker.GetMSPIdentifier(), invoker.GetID(), invokerBalance-amount); err != nil {
8888
return nil, err
8989
}
9090

@@ -95,7 +95,7 @@ func invokeTransfer(c r.Context) (interface{}, error) {
9595
// Trigger event with name "transfer" and payload - serialized to json Transfer structure
9696
if err = c.SetEvent(`transfer`, &Transfer{
9797
From: identity.Id{
98-
MSP: invoker.GetMSPID(),
98+
MSP: invoker.GetMSPIdentifier(),
9999
Cert: invoker.GetID(),
100100
},
101101
To: identity.Id{
@@ -128,13 +128,13 @@ func invokeApprove(c r.Context) (interface{}, error) {
128128
return nil, err
129129
}
130130

131-
if err = setAllowance(c, invoker.GetMSPID(), invoker.GetID(), spenderMspId, spenderCertId, amount); err != nil {
131+
if err = setAllowance(c, invoker.GetMSPIdentifier(), invoker.GetID(), spenderMspId, spenderCertId, amount); err != nil {
132132
return nil, err
133133
}
134134

135135
if err = c.SetEvent(`approve`, &Approve{
136136
From: identity.Id{
137-
MSP: invoker.GetMSPID(),
137+
MSP: invoker.GetMSPIdentifier(),
138138
Cert: invoker.GetID(),
139139
},
140140
Spender: identity.Id{
@@ -166,7 +166,7 @@ func invokeTransferFrom(c r.Context) (interface{}, error) {
166166
}
167167

168168
// check method invoker has allowances
169-
allowance, err := getAllowance(c, fromMspId, fromCertId, invoker.GetMSPID(), invoker.GetID())
169+
allowance, err := getAllowance(c, fromMspId, fromCertId, invoker.GetMSPIdentifier(), invoker.GetID())
170170
if err != nil {
171171
return nil, err
172172
}
@@ -204,7 +204,7 @@ func invokeTransferFrom(c r.Context) (interface{}, error) {
204204
}
205205

206206
// decrease invoker allowance
207-
if err = setAllowance(c, fromMspId, fromCertId, invoker.GetMSPID(), invoker.GetID(), allowance-amount); err != nil {
207+
if err = setAllowance(c, fromMspId, fromCertId, invoker.GetMSPIdentifier(), invoker.GetID(), allowance-amount); err != nil {
208208
return nil, err
209209
}
210210

extensions/owner/README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
# Owner - access control hyperledger fabric chaincode extension
22

33
In many cases during chaincode instantiating we need to define permissions for chaincode functions -
4-
"who is allowed to do this thing", incredibly important in the world of smart contracts, but in many examples access control
5-
implemented at the application level but not at the blockchain layer.
4+
"who is allowed to do this thing", incredibly important in the world of smart contracts,
5+
but in many examples access control implemented at the application level but not at the blockchain layer.
66

7-
The most common and basic form of access control is the concept of `ownership`: there's one account (combination
8-
of MSP and certificate identifiers) that is the owner and can do administrative tasks on contracts. This
7+
The most common and basic form of access control is the concept of `ownership`: there's one or more accounts
8+
(in case of Hyperledger Fabric chaincode model - combination of MSP and certificate) that is the
9+
owners and can do administrative tasks on contracts. This
910
approach is perfectly reasonable for contracts that only have a single administrative user.
1011

11-
CCKit provides `owner` extension for implementing ownership and access control in Hyperledger Fabric chaincodes.
12+
CCKit provides `owner` extension for implementing ownership and access control in Hyperledger Fabric chaincodes.
13+
14+
Owner extension implemented in two version:
15+
16+
1. As chaincode [handlers](handler.go)
17+
2. As [service](chaincode_owner.proto), that can be embedded in chaincode, using [chaincode-as-service mode](../../gateway)
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
package owner
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/golang/protobuf/ptypes/empty"
9+
"google.golang.org/protobuf/types/known/timestamppb"
10+
11+
"github.com/s7techlab/cckit/identity"
12+
"github.com/s7techlab/cckit/router"
13+
)
14+
15+
var (
16+
ErrTxInvokerIsNotOwner = errors.New(`tx invoker is not owner`)
17+
ErrDeleteLastOwnerIsNotAllowed = errors.New(`delete last owner is not allowed`)
18+
ErrNewCertSameAsOldCert = errors.New(`new cert same as old cert`)
19+
)
20+
21+
func (x *ChaincodeOwner) GetMSPIdentifier() string {
22+
return x.MspId
23+
}
24+
25+
func NewService() *ChaincodeOwnerService {
26+
return &ChaincodeOwnerService{}
27+
}
28+
29+
type ChaincodeOwnerService struct {
30+
}
31+
32+
// IsTxCreator - wrapper for TxCreatorIsOwner for local calls
33+
func (c *ChaincodeOwnerService) IsTxCreator(ctx router.Context) (*ChaincodeOwner, error) {
34+
return c.TxCreatorIsOwner(ctx, &empty.Empty{})
35+
}
36+
37+
// RegisterTxCreator Wrapper for OwnerRegisterTxCreator
38+
func (c *ChaincodeOwnerService) RegisterTxCreator(ctx router.Context) (*ChaincodeOwner, error) {
39+
return c.OwnerRegisterTxCreator(ctx, &empty.Empty{})
40+
}
41+
42+
func (c *ChaincodeOwnerService) TxCreatorIsOwner(ctx router.Context, _ *empty.Empty) (*ChaincodeOwner, error) {
43+
txCreator, err := identity.FromStub(ctx.Stub())
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
owner, err := c.OwnerGet(ctx, &OwnerId{
49+
MspId: txCreator.GetMSPIdentifier(),
50+
Subject: txCreator.GetSubject(),
51+
})
52+
53+
if err != nil {
54+
return nil, fmt.Errorf(`find owner by tx creator's msp_id and cert subject: %w`, err)
55+
}
56+
57+
if err := identity.Equal(txCreator, owner); err != nil {
58+
return nil, fmt.Errorf(`owner with tx creator's' msp_id and cert subject found, but: %w`, err)
59+
}
60+
61+
return owner, nil
62+
}
63+
64+
func (c *ChaincodeOwnerService) OwnersList(ctx router.Context, _ *empty.Empty) (*ChaincodeOwners, error) {
65+
if res, err := State(ctx).List(&ChaincodeOwner{}); err != nil {
66+
return nil, err
67+
} else {
68+
return res.(*ChaincodeOwners), nil
69+
}
70+
}
71+
72+
func (c *ChaincodeOwnerService) OwnerGet(ctx router.Context, id *OwnerId) (*ChaincodeOwner, error) {
73+
if err := router.ValidateRequest(id); err != nil {
74+
return nil, err
75+
}
76+
77+
if res, err := State(ctx).Get(id, &ChaincodeOwner{}); err != nil {
78+
return nil, err
79+
} else {
80+
return res.(*ChaincodeOwner), nil
81+
}
82+
}
83+
84+
func (c *ChaincodeOwnerService) allowToModifyBy(ctx router.Context, invoker identity.Identity) error {
85+
currentOwners, err := c.OwnersList(ctx, &empty.Empty{})
86+
if err != nil {
87+
return err
88+
}
89+
90+
// no owners, allow to register
91+
if len(currentOwners.Items) == 0 {
92+
return nil
93+
}
94+
95+
for _, owner := range currentOwners.Items {
96+
if err = identity.Equal(owner, invoker); err == nil {
97+
return nil
98+
}
99+
}
100+
101+
return ErrTxInvokerIsNotOwner
102+
}
103+
104+
func (c *ChaincodeOwnerService) txCreatorAllowedToModify(ctx router.Context) (identity.Identity, error) {
105+
txCreator, err := identity.FromStub(ctx.Stub())
106+
if err != nil {
107+
return nil, err
108+
}
109+
110+
return txCreator, c.allowToModifyBy(ctx, txCreator)
111+
}
112+
113+
func (c *ChaincodeOwnerService) OwnerRegisterTxCreator(ctx router.Context, _ *empty.Empty) (*ChaincodeOwner, error) {
114+
txCreator, err := identity.FromStub(ctx.Stub())
115+
if err != nil {
116+
return nil, err
117+
}
118+
119+
return c.OwnerRegister(ctx, &OwnerRegisterRequest{
120+
MspId: txCreator.GetMSPIdentifier(),
121+
Cert: txCreator.GetPEM(),
122+
})
123+
}
124+
125+
func (c *ChaincodeOwnerService) OwnerRegister(ctx router.Context, registerRequest *OwnerRegisterRequest) (*ChaincodeOwner, error) {
126+
if err := router.ValidateRequest(registerRequest); err != nil {
127+
return nil, err
128+
}
129+
130+
txCreator, err := c.txCreatorAllowedToModify(ctx)
131+
if err != nil {
132+
return nil, err
133+
}
134+
135+
id, err := identity.New(registerRequest.MspId, registerRequest.Cert)
136+
if err != nil {
137+
return nil, fmt.Errorf(`parse certificate: %w`, err)
138+
}
139+
140+
txTimestamp, _ := ctx.Stub().GetTxTimestamp()
141+
chaincodeOwner := &ChaincodeOwner{
142+
MspId: id.GetMSPIdentifier(),
143+
Subject: id.GetSubject(),
144+
145+
Issuer: id.GetIssuer(),
146+
ExpiresAt: timestamppb.New(id.ExpiresAt()),
147+
Cert: registerRequest.Cert,
148+
UpdatedByMspId: txCreator.GetMSPIdentifier(),
149+
UpdatedByCert: txCreator.GetPEM(),
150+
UpdatedAt: txTimestamp,
151+
}
152+
153+
if err = State(ctx).Insert(chaincodeOwner); err != nil {
154+
return nil, err
155+
}
156+
157+
if err = Event(ctx).Set(&ChaincodeOwnerRegistered{
158+
MspId: chaincodeOwner.MspId,
159+
Subject: chaincodeOwner.Subject,
160+
Issuer: chaincodeOwner.Issuer,
161+
ExpiresAt: chaincodeOwner.ExpiresAt,
162+
}); err != nil {
163+
return nil, err
164+
}
165+
166+
return chaincodeOwner, nil
167+
}
168+
169+
func (c ChaincodeOwnerService) OwnerUpdate(ctx router.Context, updateRequest *OwnerUpdateRequest) (*ChaincodeOwner, error) {
170+
if err := router.ValidateRequest(updateRequest); err != nil {
171+
return nil, err
172+
}
173+
174+
txCreator, err := c.txCreatorAllowedToModify(ctx)
175+
if err != nil {
176+
return nil, err
177+
}
178+
179+
id, err := identity.New(updateRequest.MspId, updateRequest.Cert)
180+
if err != nil {
181+
return nil, fmt.Errorf(`parse certificate: %w`, err)
182+
}
183+
184+
curOwner, err := c.OwnerGet(ctx, &OwnerId{
185+
MspId: id.GetMSPIdentifier(),
186+
Subject: id.GetSubject(),
187+
})
188+
189+
if err != nil {
190+
return nil, fmt.Errorf(`current owner with equal msp_id and cert_subject: %w`, err)
191+
}
192+
193+
if bytes.Equal(curOwner.Cert, updateRequest.Cert) {
194+
return nil, ErrNewCertSameAsOldCert
195+
}
196+
197+
if err = identity.Equal(curOwner, id); err != nil {
198+
return nil, err
199+
}
200+
201+
txTimestamp, _ := ctx.Stub().GetTxTimestamp()
202+
chaincodeOwner := &ChaincodeOwner{
203+
MspId: id.GetMSPIdentifier(),
204+
Subject: id.GetSubject(),
205+
206+
Issuer: id.GetIssuer(),
207+
ExpiresAt: timestamppb.New(id.ExpiresAt()),
208+
Cert: updateRequest.Cert,
209+
UpdatedByMspId: txCreator.GetMSPIdentifier(),
210+
UpdatedByCert: txCreator.GetPEM(),
211+
UpdatedAt: txTimestamp,
212+
}
213+
214+
if err = State(ctx).Put(chaincodeOwner); err != nil {
215+
return nil, err
216+
}
217+
218+
if err = Event(ctx).Set(&ChaincodeOwnerUpdated{
219+
MspId: chaincodeOwner.MspId,
220+
Subject: chaincodeOwner.Subject,
221+
ExpiresAt: chaincodeOwner.ExpiresAt,
222+
}); err != nil {
223+
return nil, err
224+
}
225+
226+
return chaincodeOwner, nil
227+
}
228+
229+
func (c ChaincodeOwnerService) OwnerDelete(ctx router.Context, id *OwnerId) (*ChaincodeOwner, error) {
230+
if err := router.ValidateRequest(id); err != nil {
231+
return nil, err
232+
}
233+
234+
if _, err := c.txCreatorAllowedToModify(ctx); err != nil {
235+
return nil, err
236+
}
237+
238+
deletedOwner, err := c.OwnerGet(ctx, id)
239+
if err != nil {
240+
return nil, err
241+
}
242+
243+
currentOwners, err := c.OwnersList(ctx, &empty.Empty{})
244+
if err != nil {
245+
return nil, err
246+
}
247+
248+
if len(currentOwners.Items) == 1 {
249+
return nil, ErrDeleteLastOwnerIsNotAllowed
250+
}
251+
252+
if err := State(ctx).Delete(id); err != nil {
253+
return nil, err
254+
}
255+
256+
if err := Event(ctx).Set(&ChaincodeOwnerDeleted{
257+
MspId: id.MspId,
258+
Subject: id.Subject,
259+
}); err != nil {
260+
return nil, err
261+
}
262+
263+
return deletedOwner, nil
264+
}

0 commit comments

Comments
 (0)