Skip to content

Commit d5d01e4

Browse files
author
Jason Yellick
committed
Add a Policy Manager
In order to enforce policy for configuration there must be a policy manager which tracks the policies and evaluates them. This policy manager is designed to be updated by the config manager, which itself depends on the policy manager. This loop is broken because the policy manager stands on its own, and validates configuration changes first, and then is atomically updated to the new policy configuration if the new config (and policy) is admitted by the existing policy. Note that the mechanism for updating policy seems somewhat strange, namely Begin, Propose, ..., Propose, Commit/Rollback, rather than simply supplying a new set of policy. This is done in order to accomodate the design of the configuration manager (which comes next in this patch series). This resolves: https://jira.hyperledger.org/browse/FAB-705 Change-Id: Ie4b85aed2622d34e9b29d10c46f39b266ac9a936 Signed-off-by: Jason Yellick <[email protected]>
1 parent 4bead68 commit d5d01e4

File tree

3 files changed

+299
-0
lines changed

3 files changed

+299
-0
lines changed

orderer/common/cauthdsl/cauthdsl_builder.go

+30
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,38 @@ package cauthdsl
1818

1919
import (
2020
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
21+
22+
"github.com/golang/protobuf/proto"
2123
)
2224

25+
// AcceptAllPolicy always evaluates to true
26+
var AcceptAllPolicy *ab.SignaturePolicyEnvelope
27+
28+
// MarshaledAcceptAllPolicy is the Marshaled version of AcceptAllPolicy
29+
var MarshaledAcceptAllPolicy []byte
30+
31+
// RejectAllPolicy always evaluates to false
32+
var RejectAllPolicy *ab.SignaturePolicyEnvelope
33+
34+
// MarshaledRejectAllPolicy is the Marshaled version of RejectAllPolicy
35+
var MarshaledRejectAllPolicy []byte
36+
37+
func init() {
38+
var err error
39+
40+
AcceptAllPolicy = Envelope(NOutOf(0, []*ab.SignaturePolicy{}), [][]byte{})
41+
MarshaledAcceptAllPolicy, err = proto.Marshal(AcceptAllPolicy)
42+
if err != nil {
43+
panic("Error marshaling trueEnvelope")
44+
}
45+
46+
RejectAllPolicy = Envelope(NOutOf(1, []*ab.SignaturePolicy{}), [][]byte{})
47+
MarshaledRejectAllPolicy, err = proto.Marshal(RejectAllPolicy)
48+
if err != nil {
49+
panic("Error marshaling falseEnvelope")
50+
}
51+
}
52+
2353
// Envelope builds an envelope message embedding a SignaturePolicy
2454
func Envelope(policy *ab.SignaturePolicy, identities [][]byte) *ab.SignaturePolicyEnvelope {
2555
return &ab.SignaturePolicyEnvelope{

orderer/common/policies/policy.go

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package policies
18+
19+
import (
20+
"fmt"
21+
22+
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
23+
"github.com/hyperledger/fabric/orderer/common/cauthdsl"
24+
25+
"github.com/golang/protobuf/proto"
26+
)
27+
28+
// Policy is used to determine if a signature is valid
29+
type Policy interface {
30+
// Evaluate returns nil if a msg is properly signed by sigs, or an error indicating why it failed
31+
Evaluate(msg []byte, sigs []*ab.SignedData) error
32+
}
33+
34+
// Manager is intended to be the primary accessor of ManagerImpl
35+
// It is intended to discourage use of the other exported ManagerImpl methods
36+
// which are used for updating policy by the ConfigManager
37+
type Manager interface {
38+
// GetPolicy returns a policy and true if it was the policy requested, or false if it is the default policy
39+
GetPolicy(id string) (Policy, bool)
40+
}
41+
42+
type policy struct {
43+
source *ab.Policy
44+
evaluator *cauthdsl.SignaturePolicyEvaluator
45+
}
46+
47+
func newPolicy(policySource *ab.Policy, ch cauthdsl.CryptoHelper) (*policy, error) {
48+
envelopeWrapper, ok := policySource.Type.(*ab.Policy_SignaturePolicy)
49+
50+
if !ok {
51+
return nil, fmt.Errorf("Unknown policy type: %T", policySource.Type)
52+
}
53+
54+
if envelopeWrapper.SignaturePolicy == nil {
55+
return nil, fmt.Errorf("Nil signature policy received")
56+
}
57+
58+
sigPolicy := envelopeWrapper.SignaturePolicy
59+
60+
evaluator, err := cauthdsl.NewSignaturePolicyEvaluator(sigPolicy, ch)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
return &policy{
66+
evaluator: evaluator,
67+
source: policySource,
68+
}, nil
69+
}
70+
71+
// Evaluate returns nil if a msg is properly signed by sigs, or an error indicating why it failed
72+
func (p *policy) Evaluate(msg []byte, sigs []*ab.SignedData) error {
73+
if p == nil {
74+
return fmt.Errorf("Evaluated default policy, results in reject")
75+
}
76+
77+
identities := make([][]byte, len(sigs))
78+
signatures := make([][]byte, len(sigs))
79+
for i, sigpair := range sigs {
80+
envelope := &ab.PayloadEnvelope{}
81+
if err := proto.Unmarshal(sigpair.PayloadEnvelope, envelope); err != nil {
82+
return fmt.Errorf("Failed to unmarshal the payload envelope to extract the signatures")
83+
}
84+
identities[i] = envelope.Signer
85+
signatures[i] = sigpair.Signature
86+
}
87+
// XXX This is wrong, as the signatures are over the payload envelope, not the message, fix either here, or in cauthdsl once transaction is finalized
88+
if !p.evaluator.Authenticate(msg, identities, signatures) {
89+
return fmt.Errorf("Failed to authenticate policy")
90+
}
91+
return nil
92+
}
93+
94+
// ManagerImpl is an implementation of Manager and configtx.ConfigHandler
95+
// In general, it should only be referenced as an Impl for the configtx.ConfigManager
96+
type ManagerImpl struct {
97+
policies map[string]*policy
98+
pendingPolicies map[string]*policy
99+
ch cauthdsl.CryptoHelper
100+
}
101+
102+
// NewManagerImpl creates a new ManagerImpl with the given CryptoHelper
103+
func NewManagerImpl(ch cauthdsl.CryptoHelper) *ManagerImpl {
104+
return &ManagerImpl{
105+
ch: ch,
106+
policies: make(map[string]*policy),
107+
}
108+
}
109+
110+
// GetPolicy returns a policy and true if it was the policy requested, or false if it is the default policy
111+
func (pm *ManagerImpl) GetPolicy(id string) (Policy, bool) {
112+
policy, ok := pm.policies[id]
113+
// Note the nil policy evaluates fine
114+
return policy, ok
115+
}
116+
117+
// BeginConfig is used to start a new configuration proposal
118+
func (pm *ManagerImpl) BeginConfig() {
119+
if pm.pendingPolicies != nil {
120+
panic("Programming error, cannot call begin in the middle of a proposal")
121+
}
122+
pm.pendingPolicies = make(map[string]*policy)
123+
}
124+
125+
// RollbackConfig is used to abandon a new configuration proposal
126+
func (pm *ManagerImpl) RollbackConfig() {
127+
pm.pendingPolicies = nil
128+
}
129+
130+
// CommitConfig is used to commit a new configuration proposal
131+
func (pm *ManagerImpl) CommitConfig() {
132+
if pm.pendingPolicies == nil {
133+
panic("Programming error, cannot call commit without an existing proposal")
134+
}
135+
pm.policies = pm.pendingPolicies
136+
pm.pendingPolicies = nil
137+
}
138+
139+
// ProposeConfig is used to add new configuration to the configuration proposal
140+
func (pm *ManagerImpl) ProposeConfig(configItem *ab.Configuration) error {
141+
if configItem.Type != ab.Configuration_Policy {
142+
return fmt.Errorf("Expected type of Configuration_Policy, got %v", configItem.Type)
143+
}
144+
145+
policy := &ab.Policy{}
146+
err := proto.Unmarshal(configItem.Data, policy)
147+
if err != nil {
148+
return err
149+
}
150+
151+
cPolicy, err := newPolicy(policy, pm.ch)
152+
if err != nil {
153+
return err
154+
}
155+
156+
pm.pendingPolicies[configItem.ID] = cPolicy
157+
return nil
158+
}
+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package policies
18+
19+
import (
20+
"testing"
21+
22+
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
23+
"github.com/hyperledger/fabric/orderer/common/cauthdsl"
24+
25+
"github.com/golang/protobuf/proto"
26+
)
27+
28+
type mockCryptoHelper struct{}
29+
30+
func (mch *mockCryptoHelper) VerifySignature(msg []byte, identity []byte, signature []byte) bool {
31+
return true
32+
}
33+
34+
var acceptAllPolicy []byte
35+
var rejectAllPolicy []byte
36+
37+
func init() {
38+
acceptAllPolicy = makePolicySource(true)
39+
rejectAllPolicy = makePolicySource(false)
40+
}
41+
42+
func makePolicySource(policyResult bool) []byte {
43+
var policyData *ab.SignaturePolicyEnvelope
44+
if policyResult {
45+
policyData = cauthdsl.AcceptAllPolicy
46+
} else {
47+
policyData = cauthdsl.RejectAllPolicy
48+
}
49+
marshaledPolicy, err := proto.Marshal(&ab.Policy{
50+
Type: &ab.Policy_SignaturePolicy{
51+
SignaturePolicy: policyData,
52+
},
53+
})
54+
if err != nil {
55+
panic("Error marshaling policy")
56+
}
57+
return marshaledPolicy
58+
}
59+
60+
func addPolicy(manager *ManagerImpl, id string, policy []byte) {
61+
manager.BeginConfig()
62+
err := manager.ProposeConfig(&ab.Configuration{
63+
ID: id,
64+
Type: ab.Configuration_Policy,
65+
Data: policy,
66+
})
67+
if err != nil {
68+
panic(err)
69+
}
70+
manager.CommitConfig()
71+
}
72+
73+
func TestAccept(t *testing.T) {
74+
policyID := "policyID"
75+
m := NewManagerImpl(&mockCryptoHelper{})
76+
addPolicy(m, policyID, acceptAllPolicy)
77+
policy, ok := m.GetPolicy(policyID)
78+
if !ok {
79+
t.Errorf("Should have found policy which was just added, but did not")
80+
}
81+
err := policy.Evaluate(nil, nil)
82+
if err != nil {
83+
t.Fatalf("Should not have errored evaluating an acceptAll policy: %s", err)
84+
}
85+
}
86+
87+
func TestReject(t *testing.T) {
88+
policyID := "policyID"
89+
m := NewManagerImpl(&mockCryptoHelper{})
90+
addPolicy(m, policyID, rejectAllPolicy)
91+
policy, ok := m.GetPolicy(policyID)
92+
if !ok {
93+
t.Errorf("Should have found policy which was just added, but did not")
94+
}
95+
err := policy.Evaluate(nil, nil)
96+
if err == nil {
97+
t.Fatalf("Should have errored evaluating the rejectAll policy")
98+
}
99+
}
100+
101+
func TestRejectOnUnknown(t *testing.T) {
102+
m := NewManagerImpl(&mockCryptoHelper{})
103+
policy, ok := m.GetPolicy("FakePolicyID")
104+
if ok {
105+
t.Errorf("Should not have found policy which was never added, but did")
106+
}
107+
err := policy.Evaluate(nil, nil)
108+
if err == nil {
109+
t.Fatalf("Should have errored evaluating the default policy")
110+
}
111+
}

0 commit comments

Comments
 (0)