Skip to content

Commit fccd54d

Browse files
author
Jason Yellick
committed
[FAB-5637] Add resources config code
The rscc_seed_data is to be populated with a config structure, representing the resource ACLs for the system. It will look something like this: { Values: { "QSCC_GetChainInfo": { "policy_ref": "Foo" }, "QSCC_GetBlockByHash": { "policy_ref": "Foo" } } Policies: { "Foo": NoOutOf ... } Groups: { // Optional, may be used for implicit policies } } This CR adds the config code necessary to handle this, and provides an interface for retrieving the policy reference for a resource. Change-Id: I06eddbc0be3a144b97c476eb066168c6d815cdc9 Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
1 parent 9a4172e commit fccd54d

File tree

4 files changed

+384
-0
lines changed

4 files changed

+384
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package resources
8+
9+
import (
10+
"fmt"
11+
12+
"github.com/hyperledger/fabric/common/cauthdsl"
13+
"github.com/hyperledger/fabric/common/config"
14+
"github.com/hyperledger/fabric/common/configtx"
15+
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
16+
"github.com/hyperledger/fabric/common/policies"
17+
"github.com/hyperledger/fabric/msp"
18+
cb "github.com/hyperledger/fabric/protos/common"
19+
20+
"github.com/golang/protobuf/proto"
21+
)
22+
23+
const RootGroupKey = "Resources"
24+
25+
type policyProposerRoot struct {
26+
policyManager policies.Proposer
27+
}
28+
29+
// BeginPolicyProposals is used to start a new config proposal
30+
func (p *policyProposerRoot) BeginPolicyProposals(tx interface{}, groups []string) ([]policies.Proposer, error) {
31+
if len(groups) != 1 {
32+
logger.Panicf("Initializer only supports having one root group")
33+
}
34+
return []policies.Proposer{p.policyManager}, nil
35+
}
36+
37+
func (i *policyProposerRoot) ProposePolicy(tx interface{}, key string, policy *cb.ConfigPolicy) (proto.Message, error) {
38+
return nil, fmt.Errorf("Programming error, this should never be invoked")
39+
}
40+
41+
// PreCommit is a no-op and returns nil
42+
func (i *policyProposerRoot) PreCommit(tx interface{}) error {
43+
return nil
44+
}
45+
46+
// RollbackConfig is used to abandon a new config proposal
47+
func (i *policyProposerRoot) RollbackProposals(tx interface{}) {}
48+
49+
// CommitConfig is used to commit a new config proposal
50+
func (i *policyProposerRoot) CommitProposals(tx interface{}) {}
51+
52+
type Bundle struct {
53+
ppr *policyProposerRoot
54+
vpr *valueProposerRoot
55+
cm configtxapi.Manager
56+
pm policies.Manager
57+
}
58+
59+
// New creates a new resources config bundle
60+
func New(envConfig *cb.Envelope, mspManager msp.MSPManager) (*Bundle, error) {
61+
policyProviderMap := make(map[int32]policies.Provider)
62+
for pType := range cb.Policy_PolicyType_name {
63+
rtype := cb.Policy_PolicyType(pType)
64+
switch rtype {
65+
case cb.Policy_UNKNOWN:
66+
// Do not register a handler
67+
case cb.Policy_SIGNATURE:
68+
policyProviderMap[pType] = cauthdsl.NewPolicyProvider(mspManager)
69+
case cb.Policy_MSP:
70+
// Add hook for MSP Handler here
71+
}
72+
}
73+
74+
pm := policies.NewManagerImpl(RootGroupKey, policyProviderMap)
75+
76+
b := &Bundle{
77+
vpr: newValueProposerRoot(),
78+
ppr: &policyProposerRoot{
79+
policyManager: pm,
80+
},
81+
pm: pm,
82+
}
83+
var err error
84+
b.cm, err = configtx.NewManagerImpl(envConfig, b, nil)
85+
if err != nil {
86+
return nil, err
87+
}
88+
return b, err
89+
}
90+
91+
func (b *Bundle) RootGroupKey() string {
92+
return RootGroupKey
93+
}
94+
95+
func (b *Bundle) PolicyProposer() policies.Proposer {
96+
logger.Debug("Boo", b)
97+
logger.Debug("boo", b.ppr)
98+
return b.ppr
99+
}
100+
101+
func (b *Bundle) ValueProposer() config.ValueProposer {
102+
return b.vpr
103+
}
104+
105+
func (b *Bundle) ConfigtxManager() configtxapi.Manager {
106+
return b.cm
107+
}
108+
109+
func (b *Bundle) PolicyManager() policies.Manager {
110+
return b.pm
111+
}
112+
113+
func (b *Bundle) ResourcePolicyMapper() PolicyMapper {
114+
return b.vpr
115+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package resources
8+
9+
import (
10+
"testing"
11+
12+
"github.com/hyperledger/fabric/common/cauthdsl"
13+
cb "github.com/hyperledger/fabric/protos/common"
14+
pb "github.com/hyperledger/fabric/protos/peer"
15+
"github.com/hyperledger/fabric/protos/utils"
16+
17+
logging "github.com/op/go-logging"
18+
"github.com/stretchr/testify/assert"
19+
)
20+
21+
func init() {
22+
logging.SetLevel(logging.DEBUG, "")
23+
}
24+
25+
var dummyPolicy = &cb.ConfigPolicy{
26+
Policy: &cb.Policy{
27+
Type: int32(cb.Policy_SIGNATURE),
28+
Value: utils.MarshalOrPanic(cauthdsl.AcceptAllPolicy),
29+
},
30+
}
31+
32+
func TestBundleGreenPath(t *testing.T) {
33+
env, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, "foo", nil, &cb.ConfigEnvelope{
34+
Config: &cb.Config{
35+
ChannelGroup: &cb.ConfigGroup{
36+
Values: map[string]*cb.ConfigValue{
37+
"Foo": &cb.ConfigValue{
38+
Value: utils.MarshalOrPanic(&pb.Resource{
39+
PolicyRef: "foo",
40+
}),
41+
},
42+
"Bar": &cb.ConfigValue{
43+
Value: utils.MarshalOrPanic(&pb.Resource{
44+
PolicyRef: "/Channel/foo",
45+
}),
46+
},
47+
},
48+
Policies: map[string]*cb.ConfigPolicy{
49+
"foo": dummyPolicy,
50+
"bar": dummyPolicy,
51+
},
52+
Groups: map[string]*cb.ConfigGroup{
53+
"subGroup": &cb.ConfigGroup{
54+
Policies: map[string]*cb.ConfigPolicy{
55+
"other": dummyPolicy,
56+
},
57+
},
58+
},
59+
},
60+
},
61+
}, 0, 0)
62+
assert.NoError(t, err)
63+
64+
b, err := New(env, nil)
65+
assert.NotNil(t, b)
66+
assert.NoError(t, err)
67+
assert.Equal(t, "/Resources/foo", b.ResourcePolicyMapper().PolicyRefForResource("Foo"))
68+
assert.Equal(t, "/Channel/foo", b.ResourcePolicyMapper().PolicyRefForResource("Bar"))
69+
70+
t.Run("Code coverage nits", func(t *testing.T) {
71+
assert.Equal(t, b.RootGroupKey(), RootGroupKey)
72+
assert.NotNil(t, b.PolicyProposer())
73+
assert.NotNil(t, b.ValueProposer())
74+
assert.NotNil(t, b.ConfigtxManager())
75+
assert.NotNil(t, b.PolicyManager())
76+
})
77+
}
78+
79+
func TestBundleBadSubGroup(t *testing.T) {
80+
env, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, "foo", nil, &cb.ConfigEnvelope{
81+
Config: &cb.Config{
82+
ChannelGroup: &cb.ConfigGroup{
83+
Groups: map[string]*cb.ConfigGroup{
84+
"subGroup": &cb.ConfigGroup{
85+
Values: map[string]*cb.ConfigValue{
86+
"Foo": &cb.ConfigValue{
87+
Value: utils.MarshalOrPanic(&pb.Resource{
88+
PolicyRef: "foo",
89+
}),
90+
},
91+
},
92+
Policies: map[string]*cb.ConfigPolicy{
93+
"other": dummyPolicy,
94+
},
95+
},
96+
},
97+
},
98+
},
99+
}, 0, 0)
100+
assert.NoError(t, err)
101+
102+
_, err = New(env, nil)
103+
assert.Error(t, err)
104+
assert.Regexp(t, "sub-groups not allowed to have values", err.Error())
105+
}

common/config/resources/resources.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package resources
8+
9+
import (
10+
"github.com/hyperledger/fabric/common/config"
11+
pb "github.com/hyperledger/fabric/protos/peer"
12+
13+
"github.com/golang/protobuf/proto"
14+
)
15+
16+
// ResourceGroup represents the ConfigGroup at the base of the resource configuration
17+
type resourceGroup struct {
18+
pendingResourceToPolicyRef map[string]string
19+
vpr *valueProposerRoot
20+
}
21+
22+
func newResourceGroup(vpr *valueProposerRoot) *resourceGroup {
23+
return &resourceGroup{
24+
vpr: vpr,
25+
pendingResourceToPolicyRef: make(map[string]string),
26+
}
27+
}
28+
29+
// BeginValueProposals is invoked for each sub-group. These sub-groups are only for defining policies, not other config
30+
// so, an emptyGroup is returned to handle them
31+
func (rg *resourceGroup) BeginValueProposals(tx interface{}, groups []string) (config.ValueDeserializer, []config.ValueProposer, error) {
32+
subGroups := make([]config.ValueProposer, len(groups))
33+
for i := range subGroups {
34+
subGroups[i] = emptyGroup{}
35+
}
36+
return &resourceGroupDeserializer{rg: rg}, subGroups, nil
37+
}
38+
39+
// RollbackConfig a no-op
40+
func (rg *resourceGroup) RollbackProposals(tx interface{}) {}
41+
42+
// PreCommit is a no-op
43+
func (rg *resourceGroup) PreCommit(tx interface{}) error { return nil }
44+
45+
// CommitProposals writes the pendingResourceToPolicyRef map to the resource config root
46+
func (rg *resourceGroup) CommitProposals(tx interface{}) {
47+
rg.vpr.updatePolicyRefForResources(rg.pendingResourceToPolicyRef)
48+
}
49+
50+
type resourceGroupDeserializer struct {
51+
rg *resourceGroup
52+
}
53+
54+
// Deserialize unmarshals bytes to a pb.Resource
55+
func (rgd *resourceGroupDeserializer) Deserialize(key string, value []byte) (proto.Message, error) {
56+
resource := &pb.Resource{}
57+
if err := proto.Unmarshal(value, resource); err != nil {
58+
return nil, err
59+
}
60+
61+
// If the policy is fully qualified, ie to /Channel/Application/Readers leave it alone
62+
// otherwise, make it fully qualified referring to /Resources/policyName
63+
if '/' != resource.PolicyRef[0] {
64+
rgd.rg.pendingResourceToPolicyRef[key] = "/" + RootGroupKey + "/" + resource.PolicyRef
65+
} else {
66+
rgd.rg.pendingResourceToPolicyRef[key] = resource.PolicyRef
67+
}
68+
69+
return resource, nil
70+
}
71+
72+
type emptyGroup struct{}
73+
74+
func (eg emptyGroup) BeginValueProposals(tx interface{}, groups []string) (config.ValueDeserializer, []config.ValueProposer, error) {
75+
subGroups := make([]config.ValueProposer, len(groups))
76+
for i := range subGroups {
77+
subGroups[i] = emptyGroup{}
78+
}
79+
return failDeserializer("sub-groups not allowed to have values"), subGroups, nil
80+
}
81+
82+
// RollbackConfig a no-op
83+
func (eg emptyGroup) RollbackProposals(tx interface{}) {}
84+
85+
// PreCommit is a no-op
86+
func (eg emptyGroup) PreCommit(tx interface{}) error { return nil }
87+
88+
// CommitConfig is a no-op
89+
func (eg emptyGroup) CommitProposals(tx interface{}) {}

common/config/resources/root.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package resources
8+
9+
import (
10+
"fmt"
11+
"sync/atomic"
12+
13+
"github.com/hyperledger/fabric/common/config"
14+
15+
"github.com/golang/protobuf/proto"
16+
"github.com/hyperledger/fabric/common/flogging"
17+
)
18+
19+
var logger = flogging.MustGetLogger("common/config/resource")
20+
21+
// PolicyMapper is an interface for
22+
type PolicyMapper interface {
23+
// PolicyRefForResource takes the name of a resource, and returns the policy name for a resource
24+
// or the empty string is the resource is not found
25+
PolicyRefForResource(resourceName string) string
26+
}
27+
28+
type valueProposerRoot struct {
29+
resourceToPolicyRefMap atomic.Value
30+
}
31+
32+
// newValueProposerRoot creates a new instance of the resources config root
33+
func newValueProposerRoot() *valueProposerRoot {
34+
vpr := &valueProposerRoot{}
35+
vpr.resourceToPolicyRefMap.Store(map[string]string{})
36+
return vpr
37+
}
38+
39+
type failDeserializer string
40+
41+
func (fd failDeserializer) Deserialize(key string, value []byte) (proto.Message, error) {
42+
return nil, fmt.Errorf(string(fd))
43+
}
44+
45+
// BeginValueProposals is used to start a new config proposal
46+
func (vpr *valueProposerRoot) BeginValueProposals(tx interface{}, groups []string) (config.ValueDeserializer, []config.ValueProposer, error) {
47+
if len(groups) != 1 {
48+
return nil, nil, fmt.Errorf("Root config only supports having one base group")
49+
}
50+
if groups[0] != RootGroupKey {
51+
return nil, nil, fmt.Errorf("Root group must be %s", RootGroupKey)
52+
}
53+
return failDeserializer("Programming error, this should never be invoked"), []config.ValueProposer{newResourceGroup(vpr)}, nil
54+
}
55+
56+
// RollbackConfig a no-op
57+
func (vpr *valueProposerRoot) RollbackProposals(tx interface{}) {}
58+
59+
// PreCommit is a no-op
60+
func (vpr *valueProposerRoot) PreCommit(tx interface{}) error { return nil }
61+
62+
// CommitConfig a no-op
63+
func (vpr *valueProposerRoot) CommitProposals(tx interface{}) {}
64+
65+
// PolicyRefForResources implements the PolicyMapper interface
66+
func (vpr *valueProposerRoot) PolicyRefForResource(resourceName string) string {
67+
return vpr.resourceToPolicyRefMap.Load().(map[string]string)[resourceName]
68+
}
69+
70+
// updatePolicyRefForResources should be called to update the policyRefForResources map
71+
// it wraps the operation in the atomic package for thread safety.
72+
func (vpr *valueProposerRoot) updatePolicyRefForResources(newMap map[string]string) {
73+
logger.Debugf("Updating policy ref map for %p", vpr)
74+
vpr.resourceToPolicyRefMap.Store(newMap)
75+
}

0 commit comments

Comments
 (0)