Skip to content

Commit b4424f2

Browse files
committed
add control peer rest api
Signed-off-by: Fedor Partanskiy <fredprtnsk@gmail.com>
1 parent cdd7bea commit b4424f2

File tree

94 files changed

+26424
-44
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+26424
-44
lines changed

core/aclmgmt/defaultaclprovider.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0
77
package aclmgmt
88

99
import (
10+
"crypto/x509"
11+
"encoding/pem"
1012
"fmt"
1113

1214
"github.com/hyperledger/fabric-protos-go-apiv2/common"
@@ -149,6 +151,8 @@ func (d *defaultACLProviderImpl) CheckACL(resName string, channelID string, idin
149151
return d.policyChecker.CheckPolicyBySignedData(channelID, policy, []*protoutil.SignedData{typedData})
150152
case []*protoutil.SignedData:
151153
return d.policyChecker.CheckPolicyBySignedData(channelID, policy, typedData)
154+
case *x509.Certificate:
155+
return d.policyChecker.CheckPolicyByCert(channelID, policy, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: typedData.Raw}))
152156
default:
153157
aclLogger.Errorf("Unmapped id on checkACL %s", resName)
154158
return fmt.Errorf("Unknown id on checkACL %s", resName)
@@ -174,6 +178,8 @@ func (d *defaultACLProviderImpl) CheckACLNoChannel(resName string, idinfo interf
174178
return d.policyChecker.CheckPolicyNoChannelBySignedData(policy, sd)
175179
case []*protoutil.SignedData:
176180
return d.policyChecker.CheckPolicyNoChannelBySignedData(policy, typedData)
181+
case *x509.Certificate:
182+
return d.policyChecker.CheckPolicyNoChannelByCert(policy, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: typedData.Raw}))
177183
default:
178184
aclLogger.Errorf("Unmapped id on channelless checkACL %s", resName)
179185
return fmt.Errorf("Unknown id on channelless checkACL %s", resName)

core/peer/config.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,26 @@ type ExternalBuilder struct {
4848
Path string `yaml:"path"`
4949
}
5050

51+
// AdminConf configuration for peer administration
52+
type AdminConf struct {
53+
// ListenAddress provides the host and port for the admin server
54+
ListenAddress string
55+
// TLSEnabled enables/disables TLS for admins.
56+
TLSEnabled bool
57+
// TLSCertFile provides the path to PEM encoded server certificate for
58+
// the admin server.
59+
TLSCertFile string
60+
// TLSKeyFile provides the path to PEM encoded server key for the
61+
// admin server.
62+
TLSKeyFile string
63+
// TLSClientAuthRequired enables/disables the requirements for client
64+
// certificate authentication at the TLS layer to access all resource.
65+
TLSClientAuthRequired bool
66+
// TLSClientRootCAs provides the path to PEM encoded ca certiricates to
67+
// trust for client authentication.
68+
TLSClientRootCAs []string
69+
}
70+
5171
// Config is the struct that defines the Peer configurations.
5272
type Config struct {
5373
// LocalMSPID is the identifier of the local MSP.
@@ -219,6 +239,9 @@ type Config struct {
219239
// interact with fabric networks
220240

221241
GatewayOptions gatewayconfig.Options
242+
243+
// ----- Admin config -----
244+
Admin AdminConf
222245
}
223246

224247
// GlobalConfig obtains a set of configuration from viper, build and returns
@@ -321,6 +344,16 @@ func (c *Config) load() error {
321344
c.OperationsTLSClientRootCAs = append(c.OperationsTLSClientRootCAs, config.TranslatePath(configDir, rca))
322345
}
323346

347+
c.Admin.ListenAddress = viper.GetString("admin.listenAddress")
348+
c.Admin.TLSEnabled = viper.GetBool("admin.tls.enabled")
349+
c.Admin.TLSCertFile = config.GetPath("admin.tls.cert.file")
350+
c.Admin.TLSKeyFile = config.GetPath("admin.tls.key.file")
351+
c.Admin.TLSClientAuthRequired = viper.GetBool("admin.tls.clientAuthRequired")
352+
353+
for _, rca := range viper.GetStringSlice("admin.tls.clientRootCAs.files") {
354+
c.Admin.TLSClientRootCAs = append(c.Admin.TLSClientRootCAs, config.TranslatePath(configDir, rca))
355+
}
356+
324357
c.MetricsProvider = viper.GetString("metrics.provider")
325358
c.StatsdNetwork = viper.GetString("metrics.statsd.network")
326359
c.StatsdAaddress = viper.GetString("metrics.statsd.address")

core/peer/config_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,13 @@ func TestGlobalConfig(t *testing.T) {
299299
viper.Set("operations.tls.clientAuthRequired", false)
300300
viper.Set("operations.tls.clientRootCAs.files", []string{"relative/file1", "/absolute/file2"})
301301

302+
viper.Set("admin.listenAddress", "127.0.0.1:9444")
303+
viper.Set("admin.tls.enabled", false)
304+
viper.Set("admin.tls.cert.file", "test/tls/cert/file")
305+
viper.Set("admin.tls.key.file", "test/tls/key/file")
306+
viper.Set("admin.tls.clientAuthRequired", false)
307+
viper.Set("admin.tls.clientRootCAs.files", []string{"relative/file1", "/absolute/file2"})
308+
302309
viper.Set("metrics.provider", "disabled")
303310
viper.Set("metrics.statsd.network", "udp")
304311
viper.Set("metrics.statsd.address", "127.0.0.1:8125")
@@ -385,6 +392,17 @@ func TestGlobalConfig(t *testing.T) {
385392
BroadcastTimeout: 10 * time.Second,
386393
DialTimeout: 60 * time.Second,
387394
},
395+
Admin: AdminConf{
396+
ListenAddress: "127.0.0.1:9444",
397+
TLSEnabled: false,
398+
TLSCertFile: filepath.Join(cwd, "test/tls/cert/file"),
399+
TLSKeyFile: filepath.Join(cwd, "test/tls/key/file"),
400+
TLSClientAuthRequired: false,
401+
TLSClientRootCAs: []string{
402+
filepath.Join(cwd, "relative", "file1"),
403+
"/absolute/file2",
404+
},
405+
},
388406
}
389407

390408
require.Equal(t, coreConfig, expectedConfig)

core/policy/policy.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ type PolicyChecker interface {
3939
// CheckPolicyNoChannelBySignedData checks that the passed signed data is valid with the respect to
4040
// passed policy on the local MSP.
4141
CheckPolicyNoChannelBySignedData(policyName string, signedData []*protoutil.SignedData) error
42+
43+
// CheckPolicyByCert checks that the passed cert is valid with the respect to
44+
// passed policy on the passed channel.
45+
// If no channel is passed, CheckPolicyNoChannelByCert is invoked directly.
46+
CheckPolicyByCert(channelID, policyName string, cert []byte) error
47+
48+
// CheckPolicyNoChannelByCert checks that the passed cert is valid with the respect to
49+
// passed policy on the local MSP.
50+
CheckPolicyNoChannelByCert(policyName string, cert []byte) error
4251
}
4352

4453
type policyChecker struct {
@@ -226,3 +235,96 @@ func (p *policyChecker) CheckPolicyNoChannelBySignedData(policyName string, sign
226235

227236
return nil
228237
}
238+
239+
// CheckPolicyByCert checks that the passed cert is valid with the respect to
240+
// passed policy on the passed channel.
241+
// If no channel is passed, CheckPolicyNoChannelByCert is invoked directly.
242+
func (p *policyChecker) CheckPolicyByCert(channelID, policyName string, cert []byte) error {
243+
if channelID == "" {
244+
return p.CheckPolicyNoChannelByCert(policyName, cert)
245+
}
246+
247+
if policyName == "" {
248+
return fmt.Errorf("Invalid policy name during check policy on channel [%s]. Name must be different from nil.", channelID)
249+
}
250+
251+
if cert == nil {
252+
return fmt.Errorf("Invalid cert during check policy on channel [%s] with policy [%s]", channelID, policyName)
253+
}
254+
255+
gi, err := p.getIdentityer()
256+
if err != nil {
257+
return err
258+
}
259+
260+
id, err := gi.GetIdentityFromCert(cert)
261+
if err != nil {
262+
logger.Warnw("Failed get identity during check policy on channel check policy", "error", err, "policyName", policyName, "cert", cert, "channelID", channelID)
263+
return fmt.Errorf("Failed get identity during check policy on channel [%s] with policy [%s]: [%s]", channelID, policyName, err)
264+
}
265+
266+
// Get Policy
267+
policyManager := p.channelPolicyManagerGetter.Manager(channelID)
268+
if policyManager == nil {
269+
return fmt.Errorf("Failed to get policy manager for channel [%s]", channelID)
270+
}
271+
272+
// Recall that get policy always returns a policy object
273+
policy, _ := policyManager.GetPolicy(policyName)
274+
275+
// Evaluate the policy
276+
err = policy.EvaluateIdentities([]msp.Identity{id})
277+
if err != nil {
278+
logger.Warnw("Failed evaluating policy on Identity", "error", err, "policyName", policyName, "identities", id, "channelID", channelID)
279+
return fmt.Errorf("Failed evaluating policy on Identity during check policy on channel [%s] with policy [%s]: [%s]", channelID, policyName, err)
280+
}
281+
282+
return nil
283+
}
284+
285+
// CheckPolicyNoChannelByCert checks that the passed cert is valid with the respect to
286+
// passed policy on the local MSP.
287+
func (p *policyChecker) CheckPolicyNoChannelByCert(policyName string, cert []byte) error {
288+
if policyName == "" {
289+
return errors.New("Invalid policy name during channelless check policy. Name must be different from nil.")
290+
}
291+
292+
if cert == nil {
293+
return fmt.Errorf("Invalid cert during channelless check policy with policy [%s]", policyName)
294+
}
295+
296+
gi, err := p.getIdentityer()
297+
if err != nil {
298+
return err
299+
}
300+
301+
id, err := gi.GetIdentityFromCert(cert)
302+
if err != nil {
303+
logger.Warnw("Failed get identity during channelless check policy", "error", err, "policyName", policyName, "cert", cert)
304+
return fmt.Errorf("Failed get identity during channelless check policy with policy [%s]: [%s]", policyName, err)
305+
}
306+
307+
// Load MSPPrincipal for policy
308+
principal, err := p.principalGetter.Get(policyName)
309+
if err != nil {
310+
return fmt.Errorf("Failed getting local MSP principal during channelless check policy with policy [%s]: [%s]", policyName, err)
311+
}
312+
313+
// Verify that proposal's creator satisfies the principal
314+
err = id.SatisfiesPrincipal(principal)
315+
if err != nil {
316+
logger.Warnw("Failed verifying that identity satisfies local MSP principal during channelless check policy", "error", err, "policyName", policyName, "requiredPrincipal", principal)
317+
return fmt.Errorf("Failed verifying identity satisfies local MSP principal during channelless check policy with policy [%s]: [%s]", policyName, err)
318+
}
319+
320+
return nil
321+
}
322+
323+
func (p *policyChecker) getIdentityer() (msp.GetIdentityer, error) {
324+
gi, ok := p.localMSP.(msp.GetIdentityer)
325+
if !ok {
326+
return nil, errors.New("MSP does not support the GetIdentityer interface.")
327+
}
328+
329+
return gi, nil
330+
}

core/scc/cscc/configure.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,10 @@ func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) *pb.Response {
125125
return shim.Error(fmt.Sprintf("Rejecting invoke of CSCC from another chaincode, original invocation for '%s'", name))
126126
}
127127

128-
return e.InvokeNoShim(args, sp)
128+
return e.InvokeNoShim(args, sp, true)
129129
}
130130

131-
func (e *PeerConfiger) InvokeNoShim(args [][]byte, sp *pb.SignedProposal) *pb.Response {
132-
var err error
131+
func (e *PeerConfiger) InvokeNoShim(args [][]byte, sp *pb.SignedProposal, checkACL bool) *pb.Response {
133132
fname := string(args[0])
134133

135134
switch fname {
@@ -150,14 +149,16 @@ func (e *PeerConfiger) InvokeNoShim(args [][]byte, sp *pb.SignedProposal) *pb.Re
150149
}
151150

152151
// 1. check config block's format and capabilities requirement.
153-
if err := validateConfigBlock(block, e.bccsp); err != nil {
152+
if err = validateConfigBlock(block, e.bccsp); err != nil {
154153
return shim.Error(fmt.Sprintf("\"JoinChain\" for channelID = %s failed because of validation "+
155154
"of configuration block, because of %s", cid, err))
156155
}
157156

158157
// 2. check join policy.
159-
if err = e.aclProvider.CheckACL(resources.Cscc_JoinChain, "", sp); err != nil {
160-
return shim.Error(fmt.Sprintf("access denied for [%s][%s]: [%s]", fname, cid, err))
158+
if checkACL {
159+
if err = e.aclProvider.CheckACL(resources.Cscc_JoinChain, "", sp); err != nil {
160+
return shim.Error(fmt.Sprintf("access denied for [%s][%s]: [%s]", fname, cid, err))
161+
}
161162
}
162163

163164
// Initialize txsFilter if it does not yet exist. We can do this safely since
@@ -175,19 +176,19 @@ func (e *PeerConfiger) InvokeNoShim(args [][]byte, sp *pb.SignedProposal) *pb.Re
175176
return shim.Error("Cannot join the channel, no snapshot directory provided")
176177
}
177178
// check policy
178-
if err = e.aclProvider.CheckACL(resources.Cscc_JoinChainBySnapshot, "", sp); err != nil {
179+
if err := e.aclProvider.CheckACL(resources.Cscc_JoinChainBySnapshot, "", sp); err != nil {
179180
return shim.Error(fmt.Sprintf("access denied for [%s]: [%s]", fname, err))
180181
}
181182
snapshotDir := string(args[1])
182183
return e.JoinChainBySnapshot(snapshotDir, e.deployedCCInfoProvider, e.legacyLifecycle, e.newLifecycle)
183184
case JoinBySnapshotStatus:
184-
if err = e.aclProvider.CheckACL(resources.Cscc_JoinBySnapshotStatus, "", sp); err != nil {
185+
if err := e.aclProvider.CheckACL(resources.Cscc_JoinBySnapshotStatus, "", sp); err != nil {
185186
return shim.Error(fmt.Sprintf("access denied for [%s]: %s", fname, err))
186187
}
187188
return e.joinBySnapshotStatus()
188189
case GetConfigBlock:
189190
// 2. check policy
190-
if err = e.aclProvider.CheckACL(resources.Cscc_GetConfigBlock, string(args[1]), sp); err != nil {
191+
if err := e.aclProvider.CheckACL(resources.Cscc_GetConfigBlock, string(args[1]), sp); err != nil {
191192
return shim.Error(fmt.Sprintf("access denied for [%s][%s]: %s", fname, args[1], err))
192193
}
193194

@@ -196,13 +197,13 @@ func (e *PeerConfiger) InvokeNoShim(args [][]byte, sp *pb.SignedProposal) *pb.Re
196197
if len(args[1]) == 0 {
197198
return shim.Error("empty channel name provided")
198199
}
199-
if err = e.aclProvider.CheckACL(resources.Cscc_GetChannelConfig, string(args[1]), sp); err != nil {
200+
if err := e.aclProvider.CheckACL(resources.Cscc_GetChannelConfig, string(args[1]), sp); err != nil {
200201
return shim.Error(fmt.Sprintf("access denied for [%s][%s]: %s", fname, args[1], err))
201202
}
202203
return e.getChannelConfig(args[1])
203204
case GetChannels:
204205
// 2. check get channels policy
205-
if err = e.aclProvider.CheckACL(resources.Cscc_GetChannels, "", sp); err != nil {
206+
if err := e.aclProvider.CheckACL(resources.Cscc_GetChannels, "", sp); err != nil {
206207
return shim.Error(fmt.Sprintf("access denied for [%s]: %s", fname, err))
207208
}
208209

core/scc/cscc/configure_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ func TestConfigerInvokeJoinChainMissingParams(t *testing.T) {
206206
bccsp: cryptoProvider,
207207
}
208208
mockStub := &mocks.ChaincodeStub{}
209-
mockStub.GetArgsReturns([][]byte{[]byte("JoinChain")})
209+
mockStub.GetArgsReturns([][]byte{[]byte(JoinChain)})
210210
res := cscc.Invoke(mockStub)
211211
require.NotEqual(
212212
t,
@@ -224,7 +224,7 @@ func TestConfigerInvokeJoinChainWrongParams(t *testing.T) {
224224
bccsp: cryptoProvider,
225225
}
226226
mockStub := &mocks.ChaincodeStub{}
227-
mockStub.GetArgsReturns([][]byte{[]byte("JoinChain"), []byte("action")})
227+
mockStub.GetArgsReturns([][]byte{[]byte(JoinChain), []byte("action")})
228228
mockStub.GetSignedProposalReturns(validSignedProposal(), nil)
229229
res := cscc.Invoke(mockStub)
230230
require.NotEqual(
@@ -262,15 +262,15 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) {
262262
if blockBytes == nil {
263263
t.Fatalf("cscc invoke JoinChain failed because invalid block")
264264
}
265-
args := [][]byte{[]byte("JoinChain"), blockBytes}
265+
args := [][]byte{[]byte(JoinChain), blockBytes}
266266
sProp := validSignedProposal()
267267
sProp.Signature = sProp.ProposalBytes
268268

269269
// Try fail path with nil block
270-
mockStub.GetArgsReturns([][]byte{[]byte("JoinChain"), nil})
270+
mockStub.GetArgsReturns([][]byte{[]byte(JoinChain), nil})
271271
mockStub.GetSignedProposalReturns(sProp, nil)
272272
res := cscc.Invoke(mockStub)
273-
// res := stub.MockInvokeWithSignedProposal("2", [][]byte{[]byte("JoinChain"), nil}, sProp)
273+
// res := stub.MockInvokeWithSignedProposal("2", [][]byte{[]byte(JoinChain), nil}, sProp)
274274
require.Equal(t, int32(shim.ERROR), res.Status)
275275

276276
// Try fail path with block and nil payload header
@@ -284,9 +284,9 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) {
284284
},
285285
}
286286
badBlockBytes := protoutil.MarshalOrPanic(badBlock)
287-
mockStub.GetArgsReturns([][]byte{[]byte("JoinChain"), badBlockBytes})
287+
mockStub.GetArgsReturns([][]byte{[]byte(JoinChain), badBlockBytes})
288288
res = cscc.Invoke(mockStub)
289-
// res = stub.MockInvokeWithSignedProposal("2", [][]byte{[]byte("JoinChain"), badBlockBytes}, sProp)
289+
// res = stub.MockInvokeWithSignedProposal("2", [][]byte{[]byte(JoinChain), badBlockBytes}, sProp)
290290
require.Equal(t, int32(shim.ERROR), res.Status)
291291

292292
// Now, continue with valid execution path
@@ -532,7 +532,7 @@ func TestPeerConfiger_SubmittingOrdererGenesis(t *testing.T) {
532532
}
533533
mockStub := &mocks.ChaincodeStub{}
534534
// Failed path: wrong parameter type
535-
args := [][]byte{[]byte("JoinChain"), blockBytes}
535+
args := [][]byte{[]byte(JoinChain), blockBytes}
536536
mockStub.GetArgsReturns(args)
537537
mockStub.GetSignedProposalReturns(validSignedProposal(), nil)
538538
res := cscc.Invoke(mockStub)

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ require (
1515
github.com/go-viper/mapstructure/v2 v2.4.0
1616
github.com/gorilla/handlers v1.5.2
1717
github.com/gorilla/mux v1.8.1
18+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
1819
github.com/hyperledger-labs/SmartBFT v0.0.0-20250503203013-eb005eef8866
1920
github.com/hyperledger/fabric-chaincode-go/v2 v2.3.0
2021
github.com/hyperledger/fabric-config v0.3.0
@@ -36,6 +37,8 @@ require (
3637
go.etcd.io/etcd/raft/v3 v3.5.16
3738
go.etcd.io/etcd/server/v3 v3.5.16
3839
go.uber.org/zap v1.27.0
40+
google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4
41+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4
3942
google.golang.org/grpc v1.72.0
4043
google.golang.org/protobuf v1.36.10
4144
gopkg.in/alecthomas/kingpin.v2 v2.2.6
@@ -116,5 +119,4 @@ require (
116119
golang.org/x/sys v0.38.0 // indirect
117120
golang.org/x/text v0.31.0 // indirect
118121
golang.org/x/tools v0.39.0 // indirect
119-
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
120122
)

0 commit comments

Comments
 (0)