diff --git a/common/resourcesconfig/apis.go b/common/resourcesconfig/apis.go new file mode 100644 index 00000000000..815f82d4583 --- /dev/null +++ b/common/resourcesconfig/apis.go @@ -0,0 +1,51 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package resourcesconfig + +import ( + cb "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" + + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +// apisGroup represents the ConfigGroup names APIs off the resources group +type apisGroup struct { + apiPolicyRefs map[string]string +} + +func (ag *apisGroup) PolicyRefForAPI(apiName string) string { + return ag.apiPolicyRefs[apiName] +} + +func newAPIsGroup(group *cb.ConfigGroup) (*apisGroup, error) { + if len(group.Groups) > 0 { + return nil, errors.New("apis group does not support sub-groups") + } + + apiPolicyRefs := make(map[string]string) + + for key, value := range group.Values { + api := &pb.Resource{} + if err := proto.Unmarshal(value.Value, api); 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/APIs/policyName + if '/' != api.PolicyRef[0] { + apiPolicyRefs[key] = "/" + RootGroupKey + "/" + APIsGroupKey + "/" + api.PolicyRef + } else { + apiPolicyRefs[key] = api.PolicyRef + } + } + + return &apisGroup{ + apiPolicyRefs: apiPolicyRefs, + }, nil +} diff --git a/common/resourcesconfig/bundle.go b/common/resourcesconfig/bundle.go index e5dfbaac4da..f05149f3070 100644 --- a/common/resourcesconfig/bundle.go +++ b/common/resourcesconfig/bundle.go @@ -25,9 +25,9 @@ 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 + // PolicyRefForAPI takes the name of an API, and returns the policy name + // or the empty string if the API is not found + PolicyRefForAPI(apiName string) string } type Bundle struct { @@ -107,6 +107,6 @@ func (b *Bundle) PolicyManager() policies.Manager { return b.pm } -func (b *Bundle) ResourcePolicyMapper() PolicyMapper { - return b.rg +func (b *Bundle) APIPolicyMapper() PolicyMapper { + return b.rg.apisGroup } diff --git a/common/resourcesconfig/bundle_test.go b/common/resourcesconfig/bundle_test.go index 5a1c8ac7df1..79219d2eb65 100644 --- a/common/resourcesconfig/bundle_test.go +++ b/common/resourcesconfig/bundle_test.go @@ -33,26 +33,32 @@ 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{ + APIsGroupKey: &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", + }), + }, + }, + }, + PeerPoliciesGroupKey: &cb.ConfigGroup{ Policies: map[string]*cb.ConfigPolicy{ - "other": dummyPolicy, + "foo": dummyPolicy, + "bar": dummyPolicy, + }, + Groups: map[string]*cb.ConfigGroup{ + "subGroup": &cb.ConfigGroup{ + Policies: map[string]*cb.ConfigPolicy{ + "other": dummyPolicy, + }, + }, }, }, }, @@ -64,8 +70,8 @@ func TestBundleGreenPath(t *testing.T) { b, err := New(env, nil, nil) assert.NoError(t, err) assert.NotNil(t, b) - assert.Equal(t, "/Resources/foo", b.ResourcePolicyMapper().PolicyRefForResource("Foo")) - assert.Equal(t, "/Channel/foo", b.ResourcePolicyMapper().PolicyRefForResource("Bar")) + assert.Equal(t, "/Resources/APIs/foo", b.APIPolicyMapper().PolicyRefForAPI("Foo")) + assert.Equal(t, "/Channel/foo", b.APIPolicyMapper().PolicyRefForAPI("Bar")) t.Run("Code coverage nits", func(t *testing.T) { assert.Equal(t, b.RootGroupKey(), RootGroupKey) @@ -79,7 +85,7 @@ func TestBundleBadSubGroup(t *testing.T) { Config: &cb.Config{ ChannelGroup: &cb.ConfigGroup{ Groups: map[string]*cb.ConfigGroup{ - "subGroup": &cb.ConfigGroup{ + PeerPoliciesGroupKey: &cb.ConfigGroup{ Values: map[string]*cb.ConfigValue{ "Foo": &cb.ConfigValue{ Value: utils.MarshalOrPanic(&pb.Resource{ diff --git a/common/resourcesconfig/peerpolicies.go b/common/resourcesconfig/peerpolicies.go new file mode 100644 index 00000000000..8d796942279 --- /dev/null +++ b/common/resourcesconfig/peerpolicies.go @@ -0,0 +1,32 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package resourcesconfig + +import ( + cb "github.com/hyperledger/fabric/protos/common" + + "github.com/pkg/errors" +) + +// peerPoliciesGroup is a free-form group, which supports only policies +type peerPoliciesGroup struct{} + +func newPeerPoliciesGroup(group *cb.ConfigGroup) (*peerPoliciesGroup, error) { + return &peerPoliciesGroup{}, verifyNoMoreValues(group) +} + +func verifyNoMoreValues(subGroup *cb.ConfigGroup) error { + if len(subGroup.Values) > 0 { + return errors.Errorf("sub-groups not allowed to have values") + } + for _, subGroup := range subGroup.Groups { + if err := verifyNoMoreValues(subGroup); err != nil { + return err + } + } + return nil +} diff --git a/common/resourcesconfig/resources.go b/common/resourcesconfig/resources.go index 45927743cf0..a4c994f26d1 100644 --- a/common/resourcesconfig/resources.go +++ b/common/resourcesconfig/resources.go @@ -7,60 +7,47 @@ SPDX-License-Identifier: Apache-2.0 package resourcesconfig import ( - "fmt" - cb "github.com/hyperledger/fabric/protos/common" - pb "github.com/hyperledger/fabric/protos/peer" - "github.com/golang/protobuf/proto" + "github.com/pkg/errors" ) -// ResourceGroup represents the ConfigGroup at the base of the resource configuration -type resourceGroup struct { - resourcePolicyRefs map[string]string -} +const ( + PeerPoliciesGroupKey = "PeerPolicies" + APIsGroupKey = "APIs" +) -func (rg *resourceGroup) PolicyRefForResource(resourceName string) string { - return rg.resourcePolicyRefs[resourceName] +// resourceGroup represents the ConfigGroup at the base of the resource configuration. +type resourceGroup struct { + apisGroup *apisGroup + peerPoliciesGroup *peerPoliciesGroup } func newResourceGroup(root *cb.ConfigGroup) (*resourceGroup, error) { - resourcePolicyRefs := make(map[string]string) - - for key, value := range root.Values { - resource := &pb.Resource{} - if err := proto.Unmarshal(value.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] { - resourcePolicyRefs[key] = "/" + RootGroupKey + "/" + resource.PolicyRef - } else { - resourcePolicyRefs[key] = resource.PolicyRef - } + if len(root.Values) > 0 { + return nil, errors.New("/Resources group does not support any values") } - for _, subGroup := range root.Groups { - if err := verifyNoMoreValues(subGroup); err != nil { - return nil, err - } + // initialize the elements with empty implementations, override if actually set + rg := &resourceGroup{ + apisGroup: &apisGroup{}, + peerPoliciesGroup: &peerPoliciesGroup{}, } - return &resourceGroup{ - resourcePolicyRefs: resourcePolicyRefs, - }, nil -} - -func verifyNoMoreValues(subGroup *cb.ConfigGroup) error { - if len(subGroup.Values) > 0 { - return fmt.Errorf("sub-groups not allowed to have values") - } - for _, subGroup := range subGroup.Groups { - if err := verifyNoMoreValues(subGroup); err != nil { - return err + for subGroupName, subGroup := range root.Groups { + var err error + switch subGroupName { + case APIsGroupKey: + rg.apisGroup, err = newAPIsGroup(subGroup) + case PeerPoliciesGroupKey: + rg.peerPoliciesGroup, err = newPeerPoliciesGroup(subGroup) + default: + err = errors.New("unknown sub-group") + } + if err != nil { + return nil, errors.Wrapf(err, "error processing group %s", subGroupName) } } - return nil + + return rg, nil } diff --git a/core/scc/rscc/rscc_test.go b/core/scc/rscc/rscc_test.go index 3046c908217..7e7cbdc6257 100644 --- a/core/scc/rscc/rscc_test.go +++ b/core/scc/rscc/rscc_test.go @@ -91,22 +91,26 @@ func createConfig() []byte { b := utils.MarshalOrPanic(&common.Config{ Type: int32(common.ConfigType_RESOURCE), ChannelGroup: &common.ConfigGroup{ - // All of the default seed data values would inside this ConfigGroup - Values: map[string]*common.ConfigValue{ - "res": &common.ConfigValue{ - Value: utils.MarshalOrPanic(&pb.Resource{ - PolicyRef: "respol", - }), - ModPolicy: "resmodpol", - }, - }, - Policies: map[string]*common.ConfigPolicy{ - "respol": &common.ConfigPolicy{ - Policy: &common.Policy{ - Type: int32(common.Policy_SIGNATURE), - Value: utils.MarshalOrPanic(cauthdsl.AcceptAllPolicy), + Groups: map[string]*common.ConfigGroup{ + "APIs": &common.ConfigGroup{ + // All of the default seed data values would inside this ConfigGroup + Values: map[string]*common.ConfigValue{ + "res": &common.ConfigValue{ + Value: utils.MarshalOrPanic(&pb.Resource{ + PolicyRef: "respol", + }), + ModPolicy: "resmodpol", + }, + }, + Policies: map[string]*common.ConfigPolicy{ + "respol": &common.ConfigPolicy{ + Policy: &common.Policy{ + Type: int32(common.Policy_SIGNATURE), + Value: utils.MarshalOrPanic(cauthdsl.AcceptAllPolicy), + }, + ModPolicy: "Example", + }, }, - ModPolicy: "Example", }, }, ModPolicy: "adminpol", diff --git a/core/scc/rscc/rsccpolicy.go b/core/scc/rscc/rsccpolicy.go index 58977e205e5..b0cf5e52309 100644 --- a/core/scc/rscc/rsccpolicy.go +++ b/core/scc/rscc/rsccpolicy.go @@ -35,7 +35,7 @@ func (e InvalidIdInfo) Error() string { //policyEvalutor interface provides the interfaces for policy evaluation type policyEvaluator interface { - PolicyRefForResource(resName string) string + PolicyRefForAPI(resName string) string Evaluate(polName string, id []*common.SignedData) error } @@ -44,13 +44,13 @@ type policyEvaluatorImpl struct { bundle *resourcesconfig.Bundle } -func (pe *policyEvaluatorImpl) PolicyRefForResource(resName string) string { - pm := pe.bundle.ResourcePolicyMapper() +func (pe *policyEvaluatorImpl) PolicyRefForAPI(resName string) string { + pm := pe.bundle.APIPolicyMapper() if pm == nil { return "" } - return pm.PolicyRefForResource(resName) + return pm.PolicyRefForAPI(resName) } func (pe *policyEvaluatorImpl) Evaluate(polName string, sd []*common.SignedData) error { @@ -80,7 +80,7 @@ type rsccPolicyProviderImpl struct { //GetPolicyName returns the policy name given the resource string func (rp *rsccPolicyProviderImpl) GetPolicyName(resName string) string { - return rp.pEvaluator.PolicyRefForResource(resName) + return rp.pEvaluator.PolicyRefForAPI(resName) } func newRsccPolicyProvider(channel string, pEvaluator policyEvaluator) rsccPolicyProvider { diff --git a/core/scc/rscc/rsccpolicy_test.go b/core/scc/rscc/rsccpolicy_test.go index 5caf9cdb564..eb8f75634d8 100644 --- a/core/scc/rscc/rsccpolicy_test.go +++ b/core/scc/rscc/rsccpolicy_test.go @@ -33,7 +33,7 @@ type mockPolicyEvaluatorImpl struct { peval map[string]error } -func (pe *mockPolicyEvaluatorImpl) PolicyRefForResource(resName string) string { +func (pe *mockPolicyEvaluatorImpl) PolicyRefForAPI(resName string) string { return pe.pmap[resName] }