-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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 <[email protected]>
- Loading branch information
Jason Yellick
committed
Aug 12, 2017
1 parent
9a4172e
commit fccd54d
Showing
4 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package resources | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/hyperledger/fabric/common/cauthdsl" | ||
"github.com/hyperledger/fabric/common/config" | ||
"github.com/hyperledger/fabric/common/configtx" | ||
configtxapi "github.com/hyperledger/fabric/common/configtx/api" | ||
"github.com/hyperledger/fabric/common/policies" | ||
"github.com/hyperledger/fabric/msp" | ||
cb "github.com/hyperledger/fabric/protos/common" | ||
|
||
"github.com/golang/protobuf/proto" | ||
) | ||
|
||
const RootGroupKey = "Resources" | ||
|
||
type policyProposerRoot struct { | ||
policyManager policies.Proposer | ||
} | ||
|
||
// BeginPolicyProposals is used to start a new config proposal | ||
func (p *policyProposerRoot) BeginPolicyProposals(tx interface{}, groups []string) ([]policies.Proposer, error) { | ||
if len(groups) != 1 { | ||
logger.Panicf("Initializer only supports having one root group") | ||
} | ||
return []policies.Proposer{p.policyManager}, nil | ||
} | ||
|
||
func (i *policyProposerRoot) ProposePolicy(tx interface{}, key string, policy *cb.ConfigPolicy) (proto.Message, error) { | ||
return nil, fmt.Errorf("Programming error, this should never be invoked") | ||
} | ||
|
||
// PreCommit is a no-op and returns nil | ||
func (i *policyProposerRoot) PreCommit(tx interface{}) error { | ||
return nil | ||
} | ||
|
||
// RollbackConfig is used to abandon a new config proposal | ||
func (i *policyProposerRoot) RollbackProposals(tx interface{}) {} | ||
|
||
// CommitConfig is used to commit a new config proposal | ||
func (i *policyProposerRoot) CommitProposals(tx interface{}) {} | ||
|
||
type Bundle struct { | ||
ppr *policyProposerRoot | ||
vpr *valueProposerRoot | ||
cm configtxapi.Manager | ||
pm policies.Manager | ||
} | ||
|
||
// New creates a new resources config bundle | ||
func New(envConfig *cb.Envelope, mspManager msp.MSPManager) (*Bundle, error) { | ||
policyProviderMap := make(map[int32]policies.Provider) | ||
for pType := range cb.Policy_PolicyType_name { | ||
rtype := cb.Policy_PolicyType(pType) | ||
switch rtype { | ||
case cb.Policy_UNKNOWN: | ||
// Do not register a handler | ||
case cb.Policy_SIGNATURE: | ||
policyProviderMap[pType] = cauthdsl.NewPolicyProvider(mspManager) | ||
case cb.Policy_MSP: | ||
// Add hook for MSP Handler here | ||
} | ||
} | ||
|
||
pm := policies.NewManagerImpl(RootGroupKey, policyProviderMap) | ||
|
||
b := &Bundle{ | ||
vpr: newValueProposerRoot(), | ||
ppr: &policyProposerRoot{ | ||
policyManager: pm, | ||
}, | ||
pm: pm, | ||
} | ||
var err error | ||
b.cm, err = configtx.NewManagerImpl(envConfig, b, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return b, err | ||
} | ||
|
||
func (b *Bundle) RootGroupKey() string { | ||
return RootGroupKey | ||
} | ||
|
||
func (b *Bundle) PolicyProposer() policies.Proposer { | ||
logger.Debug("Boo", b) | ||
logger.Debug("boo", b.ppr) | ||
return b.ppr | ||
} | ||
|
||
func (b *Bundle) ValueProposer() config.ValueProposer { | ||
return b.vpr | ||
} | ||
|
||
func (b *Bundle) ConfigtxManager() configtxapi.Manager { | ||
return b.cm | ||
} | ||
|
||
func (b *Bundle) PolicyManager() policies.Manager { | ||
return b.pm | ||
} | ||
|
||
func (b *Bundle) ResourcePolicyMapper() PolicyMapper { | ||
return b.vpr | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package resources | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/hyperledger/fabric/common/cauthdsl" | ||
cb "github.com/hyperledger/fabric/protos/common" | ||
pb "github.com/hyperledger/fabric/protos/peer" | ||
"github.com/hyperledger/fabric/protos/utils" | ||
|
||
logging "github.com/op/go-logging" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func init() { | ||
logging.SetLevel(logging.DEBUG, "") | ||
} | ||
|
||
var dummyPolicy = &cb.ConfigPolicy{ | ||
Policy: &cb.Policy{ | ||
Type: int32(cb.Policy_SIGNATURE), | ||
Value: utils.MarshalOrPanic(cauthdsl.AcceptAllPolicy), | ||
}, | ||
} | ||
|
||
func TestBundleGreenPath(t *testing.T) { | ||
env, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, "foo", nil, &cb.ConfigEnvelope{ | ||
Config: &cb.Config{ | ||
ChannelGroup: &cb.ConfigGroup{ | ||
Values: map[string]*cb.ConfigValue{ | ||
"Foo": &cb.ConfigValue{ | ||
Value: utils.MarshalOrPanic(&pb.Resource{ | ||
PolicyRef: "foo", | ||
}), | ||
}, | ||
"Bar": &cb.ConfigValue{ | ||
Value: utils.MarshalOrPanic(&pb.Resource{ | ||
PolicyRef: "/Channel/foo", | ||
}), | ||
}, | ||
}, | ||
Policies: map[string]*cb.ConfigPolicy{ | ||
"foo": dummyPolicy, | ||
"bar": dummyPolicy, | ||
}, | ||
Groups: map[string]*cb.ConfigGroup{ | ||
"subGroup": &cb.ConfigGroup{ | ||
Policies: map[string]*cb.ConfigPolicy{ | ||
"other": dummyPolicy, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, 0, 0) | ||
assert.NoError(t, err) | ||
|
||
b, err := New(env, nil) | ||
assert.NotNil(t, b) | ||
assert.NoError(t, err) | ||
assert.Equal(t, "/Resources/foo", b.ResourcePolicyMapper().PolicyRefForResource("Foo")) | ||
assert.Equal(t, "/Channel/foo", b.ResourcePolicyMapper().PolicyRefForResource("Bar")) | ||
|
||
t.Run("Code coverage nits", func(t *testing.T) { | ||
assert.Equal(t, b.RootGroupKey(), RootGroupKey) | ||
assert.NotNil(t, b.PolicyProposer()) | ||
assert.NotNil(t, b.ValueProposer()) | ||
assert.NotNil(t, b.ConfigtxManager()) | ||
assert.NotNil(t, b.PolicyManager()) | ||
}) | ||
} | ||
|
||
func TestBundleBadSubGroup(t *testing.T) { | ||
env, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, "foo", nil, &cb.ConfigEnvelope{ | ||
Config: &cb.Config{ | ||
ChannelGroup: &cb.ConfigGroup{ | ||
Groups: map[string]*cb.ConfigGroup{ | ||
"subGroup": &cb.ConfigGroup{ | ||
Values: map[string]*cb.ConfigValue{ | ||
"Foo": &cb.ConfigValue{ | ||
Value: utils.MarshalOrPanic(&pb.Resource{ | ||
PolicyRef: "foo", | ||
}), | ||
}, | ||
}, | ||
Policies: map[string]*cb.ConfigPolicy{ | ||
"other": dummyPolicy, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, 0, 0) | ||
assert.NoError(t, err) | ||
|
||
_, err = New(env, nil) | ||
assert.Error(t, err) | ||
assert.Regexp(t, "sub-groups not allowed to have values", err.Error()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
Copyright IBM Corp. 2017 All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package resources | ||
|
||
import ( | ||
"github.com/hyperledger/fabric/common/config" | ||
pb "github.com/hyperledger/fabric/protos/peer" | ||
|
||
"github.com/golang/protobuf/proto" | ||
) | ||
|
||
// ResourceGroup represents the ConfigGroup at the base of the resource configuration | ||
type resourceGroup struct { | ||
pendingResourceToPolicyRef map[string]string | ||
vpr *valueProposerRoot | ||
} | ||
|
||
func newResourceGroup(vpr *valueProposerRoot) *resourceGroup { | ||
return &resourceGroup{ | ||
vpr: vpr, | ||
pendingResourceToPolicyRef: make(map[string]string), | ||
} | ||
} | ||
|
||
// BeginValueProposals is invoked for each sub-group. These sub-groups are only for defining policies, not other config | ||
// so, an emptyGroup is returned to handle them | ||
func (rg *resourceGroup) BeginValueProposals(tx interface{}, groups []string) (config.ValueDeserializer, []config.ValueProposer, error) { | ||
subGroups := make([]config.ValueProposer, len(groups)) | ||
for i := range subGroups { | ||
subGroups[i] = emptyGroup{} | ||
} | ||
return &resourceGroupDeserializer{rg: rg}, subGroups, nil | ||
} | ||
|
||
// RollbackConfig a no-op | ||
func (rg *resourceGroup) RollbackProposals(tx interface{}) {} | ||
|
||
// PreCommit is a no-op | ||
func (rg *resourceGroup) PreCommit(tx interface{}) error { return nil } | ||
|
||
// CommitProposals writes the pendingResourceToPolicyRef map to the resource config root | ||
func (rg *resourceGroup) CommitProposals(tx interface{}) { | ||
rg.vpr.updatePolicyRefForResources(rg.pendingResourceToPolicyRef) | ||
} | ||
|
||
type resourceGroupDeserializer struct { | ||
rg *resourceGroup | ||
} | ||
|
||
// Deserialize unmarshals bytes to a pb.Resource | ||
func (rgd *resourceGroupDeserializer) Deserialize(key string, value []byte) (proto.Message, error) { | ||
resource := &pb.Resource{} | ||
if err := proto.Unmarshal(value, resource); err != nil { | ||
return nil, err | ||
} | ||
|
||
// If the policy is fully qualified, ie to /Channel/Application/Readers leave it alone | ||
// otherwise, make it fully qualified referring to /Resources/policyName | ||
if '/' != resource.PolicyRef[0] { | ||
rgd.rg.pendingResourceToPolicyRef[key] = "/" + RootGroupKey + "/" + resource.PolicyRef | ||
} else { | ||
rgd.rg.pendingResourceToPolicyRef[key] = resource.PolicyRef | ||
} | ||
|
||
return resource, nil | ||
} | ||
|
||
type emptyGroup struct{} | ||
|
||
func (eg emptyGroup) BeginValueProposals(tx interface{}, groups []string) (config.ValueDeserializer, []config.ValueProposer, error) { | ||
subGroups := make([]config.ValueProposer, len(groups)) | ||
for i := range subGroups { | ||
subGroups[i] = emptyGroup{} | ||
} | ||
return failDeserializer("sub-groups not allowed to have values"), subGroups, nil | ||
} | ||
|
||
// RollbackConfig a no-op | ||
func (eg emptyGroup) RollbackProposals(tx interface{}) {} | ||
|
||
// PreCommit is a no-op | ||
func (eg emptyGroup) PreCommit(tx interface{}) error { return nil } | ||
|
||
// CommitConfig is a no-op | ||
func (eg emptyGroup) CommitProposals(tx interface{}) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package resources | ||
|
||
import ( | ||
"fmt" | ||
"sync/atomic" | ||
|
||
"github.com/hyperledger/fabric/common/config" | ||
|
||
"github.com/golang/protobuf/proto" | ||
"github.com/hyperledger/fabric/common/flogging" | ||
) | ||
|
||
var logger = flogging.MustGetLogger("common/config/resource") | ||
|
||
// PolicyMapper is an interface for | ||
type PolicyMapper interface { | ||
// PolicyRefForResource takes the name of a resource, and returns the policy name for a resource | ||
// or the empty string is the resource is not found | ||
PolicyRefForResource(resourceName string) string | ||
} | ||
|
||
type valueProposerRoot struct { | ||
resourceToPolicyRefMap atomic.Value | ||
} | ||
|
||
// newValueProposerRoot creates a new instance of the resources config root | ||
func newValueProposerRoot() *valueProposerRoot { | ||
vpr := &valueProposerRoot{} | ||
vpr.resourceToPolicyRefMap.Store(map[string]string{}) | ||
return vpr | ||
} | ||
|
||
type failDeserializer string | ||
|
||
func (fd failDeserializer) Deserialize(key string, value []byte) (proto.Message, error) { | ||
return nil, fmt.Errorf(string(fd)) | ||
} | ||
|
||
// BeginValueProposals is used to start a new config proposal | ||
func (vpr *valueProposerRoot) BeginValueProposals(tx interface{}, groups []string) (config.ValueDeserializer, []config.ValueProposer, error) { | ||
if len(groups) != 1 { | ||
return nil, nil, fmt.Errorf("Root config only supports having one base group") | ||
} | ||
if groups[0] != RootGroupKey { | ||
return nil, nil, fmt.Errorf("Root group must be %s", RootGroupKey) | ||
} | ||
return failDeserializer("Programming error, this should never be invoked"), []config.ValueProposer{newResourceGroup(vpr)}, nil | ||
} | ||
|
||
// RollbackConfig a no-op | ||
func (vpr *valueProposerRoot) RollbackProposals(tx interface{}) {} | ||
|
||
// PreCommit is a no-op | ||
func (vpr *valueProposerRoot) PreCommit(tx interface{}) error { return nil } | ||
|
||
// CommitConfig a no-op | ||
func (vpr *valueProposerRoot) CommitProposals(tx interface{}) {} | ||
|
||
// PolicyRefForResources implements the PolicyMapper interface | ||
func (vpr *valueProposerRoot) PolicyRefForResource(resourceName string) string { | ||
return vpr.resourceToPolicyRefMap.Load().(map[string]string)[resourceName] | ||
} | ||
|
||
// updatePolicyRefForResources should be called to update the policyRefForResources map | ||
// it wraps the operation in the atomic package for thread safety. | ||
func (vpr *valueProposerRoot) updatePolicyRefForResources(newMap map[string]string) { | ||
logger.Debugf("Updating policy ref map for %p", vpr) | ||
vpr.resourceToPolicyRefMap.Store(newMap) | ||
} |