2020-06-02 22:37:10 +00:00
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2020-06-15 22:49:00 +00:00
|
|
|
"github.com/hashicorp/consul/agent/consul/stream"
|
2020-06-02 22:37:10 +00:00
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestACLEventsFromChanges(t *testing.T) {
|
|
|
|
cases := []struct {
|
2020-06-15 22:49:00 +00:00
|
|
|
Name string
|
|
|
|
Setup func(s *Store, tx *txn) error
|
|
|
|
Mutate func(s *Store, tx *txn) error
|
|
|
|
expected stream.Event
|
2020-06-02 22:37:10 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
Name: "token create",
|
|
|
|
Mutate: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclTokenSetTxn(tx, tx.Index, newACLToken(1), false, false, false, false)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
2020-06-15 22:49:00 +00:00
|
|
|
expected: newACLTokenEvent(100, 1),
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "token update",
|
|
|
|
Setup: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclTokenSetTxn(tx, tx.Index, newACLToken(1), false, false, false, false)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
Mutate: func(s *Store, tx *txn) error {
|
|
|
|
// Add a policy to the token (never mind it doesn't exist for now) we
|
|
|
|
// allow it in the set command below.
|
2020-06-15 22:49:00 +00:00
|
|
|
token := newACLToken(1)
|
2020-06-02 22:37:10 +00:00
|
|
|
token.Policies = []structs.ACLTokenPolicyLink{{ID: "33333333-1111-1111-1111-111111111111"}}
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclTokenSetTxn(tx, tx.Index, token, false, true, false, false)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
2020-06-15 22:49:00 +00:00
|
|
|
expected: newACLTokenEvent(100, 1, structs.ACLTokenPolicyLink{ID: "33333333-1111-1111-1111-111111111111"}),
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "token delete",
|
|
|
|
Setup: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclTokenSetTxn(tx, tx.Index, newACLToken(1), false, false, false, false)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
Mutate: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
token := newACLToken(1)
|
|
|
|
return s.aclTokenDeleteTxn(tx, tx.Index, token.AccessorID, "id", nil)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
2020-06-15 22:49:00 +00:00
|
|
|
expected: newACLTokenEvent(100, 1),
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "policy create",
|
|
|
|
Mutate: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclPolicySetTxn(tx, tx.Index, newACLPolicy(1))
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
2020-06-15 22:49:00 +00:00
|
|
|
expected: newACLPolicyEvent(100, 1),
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "policy update",
|
|
|
|
Setup: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclPolicySetTxn(tx, tx.Index, newACLPolicy(1))
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
Mutate: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
policy := newACLPolicy(1)
|
2020-06-02 22:37:10 +00:00
|
|
|
policy.Rules = `operator = "write"`
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclPolicySetTxn(tx, tx.Index, policy)
|
|
|
|
},
|
|
|
|
expected: stream.Event{
|
|
|
|
Topic: stream.Topic_ACLPolicies,
|
|
|
|
Index: 100,
|
|
|
|
Payload: &structs.ACLPolicy{
|
|
|
|
ID: "22222222-1111-1111-1111-111111111111",
|
|
|
|
Name: "test_policy_1",
|
|
|
|
Rules: `operator = "write"`,
|
|
|
|
},
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "policy delete",
|
|
|
|
Setup: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclPolicySetTxn(tx, tx.Index, newACLPolicy(1))
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
Mutate: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
policy := newACLPolicy(1)
|
|
|
|
return s.aclPolicyDeleteTxn(tx, tx.Index, policy.ID, s.aclPolicyGetByID, nil)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
2020-06-15 22:49:00 +00:00
|
|
|
expected: newACLPolicyEvent(100, 1),
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "role create",
|
|
|
|
Mutate: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclRoleSetTxn(tx, tx.Index, newACLRole(1, newACLRolePolicyLink(1)), true)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
2020-06-15 22:49:00 +00:00
|
|
|
expected: newACLRoleEvent(100, 1, newACLRolePolicyLink(1)),
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "role update",
|
|
|
|
Setup: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclRoleSetTxn(tx, tx.Index, newACLRole(1, newACLRolePolicyLink(1)), true)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
Mutate: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
role := newACLRole(1, newACLRolePolicyLink(1))
|
|
|
|
policy2 := newACLPolicy(2)
|
2020-06-02 22:37:10 +00:00
|
|
|
role.Policies = append(role.Policies, structs.ACLRolePolicyLink{
|
|
|
|
ID: policy2.ID,
|
|
|
|
Name: policy2.Name,
|
|
|
|
})
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclRoleSetTxn(tx, tx.Index, role, true)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
2020-06-15 22:49:00 +00:00
|
|
|
expected: newACLRoleEvent(100, 1, newACLRolePolicyLink(1), newACLRolePolicyLink(2)),
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "role delete",
|
|
|
|
Setup: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
return s.aclRoleSetTxn(tx, tx.Index, newACLRole(1, newACLRolePolicyLink(1)), true)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
Mutate: func(s *Store, tx *txn) error {
|
2020-06-15 22:49:00 +00:00
|
|
|
role := newACLRole(1, newACLRolePolicyLink(1))
|
|
|
|
return s.aclRoleDeleteTxn(tx, tx.Index, role.ID, s.aclRoleGetByID, nil)
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
2020-06-15 22:49:00 +00:00
|
|
|
expected: newACLRoleEvent(100, 1, newACLRolePolicyLink(1)),
|
2020-06-02 22:37:10 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
|
|
s := testStateStore(t)
|
|
|
|
|
|
|
|
if tc.Setup != nil {
|
|
|
|
// Bypass the publish mechanism for this test or we get into odd
|
|
|
|
// recursive stuff...
|
|
|
|
setupTx := s.db.WriteTxn(10)
|
|
|
|
require.NoError(t, tc.Setup(s, setupTx))
|
|
|
|
// Commit the underlying transaction without using wrapped Commit so we
|
|
|
|
// avoid the whole event publishing system for setup here. It _should_
|
|
|
|
// work but it makes debugging test hard as it will call the function
|
|
|
|
// under test for the setup data...
|
|
|
|
setupTx.Txn.Commit()
|
|
|
|
}
|
|
|
|
|
|
|
|
tx := s.db.WriteTxn(100)
|
|
|
|
require.NoError(t, tc.Mutate(s, tx))
|
|
|
|
|
|
|
|
// Note we call the func under test directly rather than publishChanges so
|
|
|
|
// we can test this in isolation.
|
2020-06-15 22:49:00 +00:00
|
|
|
events, err := aclEventsFromChanges(tx, tx.Changes())
|
2020-06-02 22:37:10 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-06-15 22:49:00 +00:00
|
|
|
require.Len(t, events, 1)
|
|
|
|
actual := events[0]
|
|
|
|
// ignore modified and created index because we don't set them in our expected values
|
|
|
|
// TODO: gotest.tools/assert would make this easier
|
|
|
|
normalizePayload(&actual)
|
|
|
|
require.Equal(t, tc.expected, actual)
|
2020-06-02 22:37:10 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2020-06-15 22:49:00 +00:00
|
|
|
|
|
|
|
func normalizePayload(s *stream.Event) {
|
|
|
|
switch s := s.Payload.(type) {
|
|
|
|
case *structs.ACLToken:
|
|
|
|
s.ModifyIndex = 0
|
|
|
|
s.CreateIndex = 0
|
|
|
|
s.Hash = nil
|
|
|
|
case *structs.ACLPolicy:
|
|
|
|
s.ModifyIndex = 0
|
|
|
|
s.CreateIndex = 0
|
|
|
|
case *structs.ACLRole:
|
|
|
|
s.ModifyIndex = 0
|
|
|
|
s.CreateIndex = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newACLTokenEvent(idx uint64, n int, policies ...structs.ACLTokenPolicyLink) stream.Event {
|
|
|
|
uuid := strings.ReplaceAll("11111111-????-????-????-????????????", "?", strconv.Itoa(n))
|
|
|
|
return stream.Event{
|
|
|
|
Topic: stream.Topic_ACLTokens,
|
|
|
|
Index: idx,
|
|
|
|
Payload: &structs.ACLToken{
|
|
|
|
AccessorID: uuid,
|
|
|
|
SecretID: uuid,
|
|
|
|
Policies: policies,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newACLPolicyEvent(idx uint64, n int) stream.Event {
|
|
|
|
return stream.Event{
|
|
|
|
Topic: stream.Topic_ACLPolicies,
|
|
|
|
Index: idx,
|
|
|
|
Payload: newACLPolicy(n),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newACLRoleEvent(idx uint64, n int, policies ...structs.ACLRolePolicyLink) stream.Event {
|
|
|
|
return stream.Event{
|
|
|
|
Topic: stream.Topic_ACLRoles,
|
|
|
|
Index: idx,
|
|
|
|
Payload: newACLRole(n, policies...),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newACLToken(n int) *structs.ACLToken {
|
|
|
|
uuid := strings.ReplaceAll("11111111-????-????-????-????????????", "?", strconv.Itoa(n))
|
|
|
|
return &structs.ACLToken{
|
|
|
|
AccessorID: uuid,
|
|
|
|
SecretID: uuid,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newACLPolicy(n int) *structs.ACLPolicy {
|
|
|
|
numStr := strconv.Itoa(n)
|
|
|
|
uuid := strings.ReplaceAll("22222222-????-????-????-????????????", "?", numStr)
|
|
|
|
return &structs.ACLPolicy{
|
|
|
|
ID: uuid,
|
|
|
|
Name: "test_policy_" + numStr,
|
|
|
|
Rules: `operator = "read"`,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newACLRole(n int, policies ...structs.ACLRolePolicyLink) *structs.ACLRole {
|
|
|
|
numStr := strconv.Itoa(n)
|
|
|
|
uuid := strings.ReplaceAll("33333333-????-????-????-????????????", "?", numStr)
|
|
|
|
return &structs.ACLRole{
|
|
|
|
ID: uuid,
|
|
|
|
Name: "test_role_" + numStr,
|
|
|
|
Policies: policies,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newACLRolePolicyLink(n int) structs.ACLRolePolicyLink {
|
|
|
|
policy := newACLPolicy(n)
|
|
|
|
return structs.ACLRolePolicyLink{
|
|
|
|
ID: policy.ID,
|
|
|
|
Name: policy.Name,
|
|
|
|
}
|
|
|
|
}
|