open-consul/agent/consul/state/acl_test.go

1635 lines
48 KiB
Go

package state
import (
"testing"
"time"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs"
"github.com/stretchr/testify/require"
)
func setupGlobalManagement(t *testing.T, s *Store) {
policy := structs.ACLPolicy{
ID: structs.ACLPolicyGlobalManagementID,
Name: "global-management",
Description: "Builtin Policy that grants unlimited access",
Rules: structs.ACLPolicyGlobalManagement,
Syntax: acl.SyntaxCurrent,
}
policy.SetHash(true)
require.NoError(t, s.ACLPolicySet(1, &policy))
}
func setupAnonymous(t *testing.T, s *Store) {
token := structs.ACLToken{
AccessorID: structs.ACLTokenAnonymousID,
SecretID: "anonymous",
Description: "Anonymous Token",
}
token.SetHash(true)
require.NoError(t, s.ACLTokenSet(1, &token, false))
}
func testACLStateStore(t *testing.T) *Store {
s := testStateStore(t)
setupGlobalManagement(t, s)
setupAnonymous(t, s)
return s
}
func setupExtraPolicies(t *testing.T, s *Store) {
policies := structs.ACLPolicies{
&structs.ACLPolicy{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
Name: "node-read",
Description: "Allows reading all node information",
Rules: `node_prefix "" { policy = "read" }`,
Syntax: acl.SyntaxCurrent,
},
}
for _, policy := range policies {
policy.SetHash(true)
}
require.NoError(t, s.ACLPolicyBatchSet(2, policies))
}
func testACLTokensStateStore(t *testing.T) *Store {
s := testACLStateStore(t)
setupExtraPolicies(t, s)
return s
}
func TestStateStore_ACLBootstrap(t *testing.T) {
t.Parallel()
token1 := &structs.ACLToken{
AccessorID: "30fca056-9fbb-4455-b94a-bf0e2bc575d6",
SecretID: "cbe1c6fd-d865-4034-9d6d-64fef7fb46a9",
Description: "Bootstrap Token (Global Management)",
Policies: []structs.ACLTokenPolicyLink{
{
ID: structs.ACLPolicyGlobalManagementID,
},
},
CreateTime: time.Now(),
Local: false,
// DEPRECATED (ACL-Legacy-Compat) - This is used so that the bootstrap token is still visible via the v1 acl APIs
Type: structs.ACLTokenTypeManagement,
}
token2 := &structs.ACLToken{
AccessorID: "fd5c17fa-1503-4422-a424-dd44cdf35919",
SecretID: "7fd776b1-ded1-4d15-931b-db4770fc2317",
Description: "Bootstrap Token (Global Management)",
Policies: []structs.ACLTokenPolicyLink{
{
ID: structs.ACLPolicyGlobalManagementID,
},
},
CreateTime: time.Now(),
Local: false,
// DEPRECATED (ACL-Legacy-Compat) - This is used so that the bootstrap token is still visible via the v1 acl APIs
Type: structs.ACLTokenTypeManagement,
}
stripIrrelevantFields := func(token *structs.ACLToken) *structs.ACLToken {
tokenCopy := token.Clone()
// When comparing the tokens disregard the policy link names. This
// data is not cleanly updated in a variety of scenarios and should not
// be relied upon.
for i, _ := range tokenCopy.Policies {
tokenCopy.Policies[i].Name = ""
}
// The raft indexes won't match either because the requester will not
// have access to that.
tokenCopy.RaftIndex = structs.RaftIndex{}
return tokenCopy
}
compareTokens := func(expected, actual *structs.ACLToken) {
require.Equal(t, stripIrrelevantFields(expected), stripIrrelevantFields(actual))
}
s := testStateStore(t)
setupGlobalManagement(t, s)
canBootstrap, index, err := s.CanBootstrapACLToken()
require.NoError(t, err)
require.True(t, canBootstrap)
require.Equal(t, uint64(0), index)
// Perform a regular bootstrap.
require.NoError(t, s.ACLBootstrap(3, 0, token1.Clone(), false))
// Make sure we can't bootstrap again
canBootstrap, index, err = s.CanBootstrapACLToken()
require.NoError(t, err)
require.False(t, canBootstrap)
require.Equal(t, uint64(3), index)
// Make sure another attempt fails.
err = s.ACLBootstrap(4, 0, token2.Clone(), false)
require.Error(t, err)
require.Equal(t, structs.ACLBootstrapNotAllowedErr, err)
// Check that the bootstrap state remains the same.
canBootstrap, index, err = s.CanBootstrapACLToken()
require.NoError(t, err)
require.False(t, canBootstrap)
require.Equal(t, uint64(3), index)
// Make sure the ACLs are in an expected state.
_, tokens, err := s.ACLTokenList(nil, true, true, "")
require.NoError(t, err)
require.Len(t, tokens, 1)
compareTokens(token1, tokens[0])
// bootstrap reset
err = s.ACLBootstrap(32, index-1, token2.Clone(), false)
require.Error(t, err)
require.Equal(t, structs.ACLBootstrapInvalidResetIndexErr, err)
// bootstrap reset
err = s.ACLBootstrap(32, index, token2.Clone(), false)
require.NoError(t, err)
_, tokens, err = s.ACLTokenList(nil, true, true, "")
require.NoError(t, err)
require.Len(t, tokens, 2)
}
func TestStateStore_ACLToken_SetGet_Legacy(t *testing.T) {
t.Parallel()
t.Run("Legacy - Existing With Policies", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
AccessorID: "c8d0378c-566a-4535-8fc9-c883a8cc9849",
SecretID: "6d48ce91-2558-4098-bdab-8737e4e57d5f",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
},
},
}
require.NoError(t, s.ACLTokenSet(2, token, false))
// legacy flag is set so it should disallow setting this token
err := s.ACLTokenSet(3, token, true)
require.Error(t, err)
})
t.Run("Legacy - Empty Type", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
AccessorID: "271cd056-0038-4fd3-90e5-f97f50fb3ac8",
SecretID: "c0056225-5785-43b3-9b77-3954f06d6aee",
}
require.NoError(t, s.ACLTokenSet(2, token, false))
// legacy flag is set so it should disallow setting this token
err := s.ACLTokenSet(3, token, true)
require.Error(t, err)
})
t.Run("Legacy - New", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
SecretID: "2989e271-6169-4f34-8fec-4618d70008fb",
Type: structs.ACLTokenTypeClient,
Rules: `service "" { policy = "read" }`,
}
require.NoError(t, s.ACLTokenSet(2, token, true))
idx, rtoken, err := s.ACLTokenGetBySecret(nil, token.SecretID)
require.NoError(t, err)
require.Equal(t, uint64(2), idx)
require.NotNil(t, rtoken)
require.Equal(t, "", rtoken.AccessorID)
require.Equal(t, "2989e271-6169-4f34-8fec-4618d70008fb", rtoken.SecretID)
require.Equal(t, "", rtoken.Description)
require.Len(t, rtoken.Policies, 0)
require.Equal(t, structs.ACLTokenTypeClient, rtoken.Type)
require.Equal(t, uint64(2), rtoken.CreateIndex)
require.Equal(t, uint64(2), rtoken.ModifyIndex)
})
t.Run("Legacy - Update", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
original := &structs.ACLToken{
SecretID: "2989e271-6169-4f34-8fec-4618d70008fb",
Type: structs.ACLTokenTypeClient,
Rules: `service "" { policy = "read" }`,
}
require.NoError(t, s.ACLTokenSet(2, original, true))
updatedRules := `service "" { policy = "read" } service "foo" { policy = "deny"}`
update := &structs.ACLToken{
SecretID: "2989e271-6169-4f34-8fec-4618d70008fb",
Type: structs.ACLTokenTypeClient,
Rules: updatedRules,
}
require.NoError(t, s.ACLTokenSet(3, update, true))
idx, rtoken, err := s.ACLTokenGetBySecret(nil, original.SecretID)
require.NoError(t, err)
require.Equal(t, uint64(3), idx)
require.NotNil(t, rtoken)
require.Equal(t, "", rtoken.AccessorID)
require.Equal(t, "2989e271-6169-4f34-8fec-4618d70008fb", rtoken.SecretID)
require.Equal(t, "", rtoken.Description)
require.Len(t, rtoken.Policies, 0)
require.Equal(t, structs.ACLTokenTypeClient, rtoken.Type)
require.Equal(t, updatedRules, rtoken.Rules)
require.Equal(t, uint64(2), rtoken.CreateIndex)
require.Equal(t, uint64(3), rtoken.ModifyIndex)
})
}
func TestStateStore_ACLToken_SetGet(t *testing.T) {
t.Parallel()
t.Run("Missing Secret", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
AccessorID: "39171632-6f34-4411-827f-9416403687f4",
}
err := s.ACLTokenSet(2, token, false)
require.Error(t, err)
require.Equal(t, ErrMissingACLTokenSecret, err)
})
t.Run("Missing Accessor", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
SecretID: "39171632-6f34-4411-827f-9416403687f4",
}
err := s.ACLTokenSet(2, token, false)
require.Error(t, err)
require.Equal(t, ErrMissingACLTokenAccessor, err)
})
t.Run("Missing Policy ID", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
AccessorID: "daf37c07-d04d-4fd5-9678-a8206a57d61a",
SecretID: "39171632-6f34-4411-827f-9416403687f4",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
Name: "no-id",
},
},
}
err := s.ACLTokenSet(2, token, false)
require.Error(t, err)
})
t.Run("Unresolvable Policy ID", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
AccessorID: "daf37c07-d04d-4fd5-9678-a8206a57d61a",
SecretID: "39171632-6f34-4411-827f-9416403687f4",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "4f20e379-b496-4b99-9599-19a197126490",
},
},
}
err := s.ACLTokenSet(2, token, false)
require.Error(t, err)
})
t.Run("New", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
AccessorID: "daf37c07-d04d-4fd5-9678-a8206a57d61a",
SecretID: "39171632-6f34-4411-827f-9416403687f4",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
},
},
}
require.NoError(t, s.ACLTokenSet(2, token, false))
idx, rtoken, err := s.ACLTokenGetByAccessor(nil, "daf37c07-d04d-4fd5-9678-a8206a57d61a")
require.NoError(t, err)
require.Equal(t, uint64(2), idx)
// pointer equality
require.True(t, rtoken == token)
require.Equal(t, uint64(2), rtoken.CreateIndex)
require.Equal(t, uint64(2), rtoken.ModifyIndex)
require.Len(t, rtoken.Policies, 1)
require.Equal(t, "node-read", rtoken.Policies[0].Name)
})
t.Run("Update", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
AccessorID: "daf37c07-d04d-4fd5-9678-a8206a57d61a",
SecretID: "39171632-6f34-4411-827f-9416403687f4",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
},
},
}
require.NoError(t, s.ACLTokenSet(2, token, false))
updated := &structs.ACLToken{
AccessorID: "daf37c07-d04d-4fd5-9678-a8206a57d61a",
SecretID: "39171632-6f34-4411-827f-9416403687f4",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
}
require.NoError(t, s.ACLTokenSet(3, updated, false))
idx, rtoken, err := s.ACLTokenGetByAccessor(nil, "daf37c07-d04d-4fd5-9678-a8206a57d61a")
require.NoError(t, err)
require.Equal(t, uint64(3), idx)
// pointer equality
require.True(t, rtoken == updated)
require.Equal(t, uint64(2), rtoken.CreateIndex)
require.Equal(t, uint64(3), rtoken.ModifyIndex)
require.Len(t, rtoken.Policies, 1)
require.Equal(t, structs.ACLPolicyGlobalManagementID, rtoken.Policies[0].ID)
require.Equal(t, "global-management", rtoken.Policies[0].Name)
})
}
func TestStateStore_ACLTokens_UpsertBatchRead(t *testing.T) {
t.Parallel()
t.Run("CAS - Deleted", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
// CAS op + nonexistent token should not work. This prevents modifying
// deleted tokens
tokens := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
SecretID: "00ff4564-dd96-4d1b-8ad6-578a08279f79",
RaftIndex: structs.RaftIndex{CreateIndex: 2, ModifyIndex: 3},
},
}
require.NoError(t, s.ACLTokenBatchSet(2, tokens, true))
_, token, err := s.ACLTokenGetByAccessor(nil, tokens[0].AccessorID)
require.NoError(t, err)
require.Nil(t, token)
})
t.Run("CAS - Updated", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
// CAS op + nonexistent token should not work. This prevents modifying
// deleted tokens
tokens := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
SecretID: "00ff4564-dd96-4d1b-8ad6-578a08279f79",
},
}
require.NoError(t, s.ACLTokenBatchSet(5, tokens, true))
updated := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
SecretID: "00ff4564-dd96-4d1b-8ad6-578a08279f79",
Description: "wont update",
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 4},
},
}
require.NoError(t, s.ACLTokenBatchSet(6, updated, true))
_, token, err := s.ACLTokenGetByAccessor(nil, tokens[0].AccessorID)
require.NoError(t, err)
require.NotNil(t, token)
require.Equal(t, "", token.Description)
})
t.Run("CAS - Already Exists", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
tokens := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
SecretID: "00ff4564-dd96-4d1b-8ad6-578a08279f79",
},
}
require.NoError(t, s.ACLTokenBatchSet(5, tokens, true))
updated := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
SecretID: "00ff4564-dd96-4d1b-8ad6-578a08279f79",
Description: "wont update",
},
}
require.NoError(t, s.ACLTokenBatchSet(6, updated, true))
_, token, err := s.ACLTokenGetByAccessor(nil, tokens[0].AccessorID)
require.NoError(t, err)
require.NotNil(t, token)
require.Equal(t, "", token.Description)
})
t.Run("Normal", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
tokens := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
SecretID: "00ff4564-dd96-4d1b-8ad6-578a08279f79",
},
&structs.ACLToken{
AccessorID: "a2719052-40b3-4a4b-baeb-f3df1831a217",
SecretID: "ff826eaf-4b88-4881-aaef-52b1089e5d5d",
},
}
require.NoError(t, s.ACLTokenBatchSet(2, tokens, false))
idx, rtokens, err := s.ACLTokenBatchGet(nil, []string{
"a4f68bd6-3af5-4f56-b764-3c6f20247879",
"a2719052-40b3-4a4b-baeb-f3df1831a217"})
require.NoError(t, err)
require.Equal(t, uint64(2), idx)
require.Len(t, rtokens, 2)
require.ElementsMatch(t, tokens, rtokens)
require.Equal(t, uint64(2), rtokens[0].CreateIndex)
require.Equal(t, uint64(2), rtokens[0].ModifyIndex)
require.Equal(t, uint64(2), rtokens[1].CreateIndex)
require.Equal(t, uint64(2), rtokens[1].ModifyIndex)
})
t.Run("Update", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
tokens := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
SecretID: "00ff4564-dd96-4d1b-8ad6-578a08279f79",
},
&structs.ACLToken{
AccessorID: "a2719052-40b3-4a4b-baeb-f3df1831a217",
SecretID: "ff826eaf-4b88-4881-aaef-52b1089e5d5d",
},
}
require.NoError(t, s.ACLTokenBatchSet(2, tokens, false))
updates := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
SecretID: "00ff4564-dd96-4d1b-8ad6-578a08279f79",
Description: "first token",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
},
},
},
&structs.ACLToken{
AccessorID: "a2719052-40b3-4a4b-baeb-f3df1831a217",
SecretID: "ff826eaf-4b88-4881-aaef-52b1089e5d5d",
Description: "second token",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
},
}
require.NoError(t, s.ACLTokenBatchSet(3, updates, false))
idx, rtokens, err := s.ACLTokenBatchGet(nil, []string{
"a4f68bd6-3af5-4f56-b764-3c6f20247879",
"a2719052-40b3-4a4b-baeb-f3df1831a217"})
require.NoError(t, err)
require.Equal(t, uint64(3), idx)
require.Len(t, rtokens, 2)
rtokens.Sort()
require.Equal(t, "a2719052-40b3-4a4b-baeb-f3df1831a217", rtokens[0].AccessorID)
require.Equal(t, "ff826eaf-4b88-4881-aaef-52b1089e5d5d", rtokens[0].SecretID)
require.Equal(t, "second token", rtokens[0].Description)
require.Len(t, rtokens[0].Policies, 1)
require.Equal(t, structs.ACLPolicyGlobalManagementID, rtokens[0].Policies[0].ID)
require.Equal(t, "global-management", rtokens[0].Policies[0].Name)
require.Equal(t, uint64(2), rtokens[0].CreateIndex)
require.Equal(t, uint64(3), rtokens[0].ModifyIndex)
require.Equal(t, "a4f68bd6-3af5-4f56-b764-3c6f20247879", rtokens[1].AccessorID)
require.Equal(t, "00ff4564-dd96-4d1b-8ad6-578a08279f79", rtokens[1].SecretID)
require.Equal(t, "first token", rtokens[1].Description)
require.Len(t, rtokens[1].Policies, 1)
require.Equal(t, "a0625e95-9b3e-42de-a8d6-ceef5b6f3286", rtokens[1].Policies[0].ID)
require.Equal(t, "node-read", rtokens[1].Policies[0].Name)
require.Equal(t, uint64(2), rtokens[1].CreateIndex)
require.Equal(t, uint64(3), rtokens[1].ModifyIndex)
})
}
func TestStateStore_ACLTokens_ListUpgradeable(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
require.NoError(t, s.ACLTokenSet(2, &structs.ACLToken{
SecretID: "34ec8eb3-095d-417a-a937-b439af7a8e8b",
Type: structs.ACLTokenTypeManagement,
}, true))
require.NoError(t, s.ACLTokenSet(3, &structs.ACLToken{
SecretID: "8de2dd39-134d-4cb1-950b-b7ab96ea20ba",
Type: structs.ACLTokenTypeManagement,
}, true))
require.NoError(t, s.ACLTokenSet(4, &structs.ACLToken{
SecretID: "548bdb8e-c0d6-477b-bcc4-67fb836e9e61",
Type: structs.ACLTokenTypeManagement,
}, true))
require.NoError(t, s.ACLTokenSet(5, &structs.ACLToken{
SecretID: "3ee33676-d9b8-4144-bf0b-92618cff438b",
Type: structs.ACLTokenTypeManagement,
}, true))
require.NoError(t, s.ACLTokenSet(6, &structs.ACLToken{
SecretID: "fa9d658a-6e26-42ab-a5f0-1ea05c893dee",
Type: structs.ACLTokenTypeManagement,
}, true))
tokens, _, err := s.ACLTokenListUpgradeable(3)
require.NoError(t, err)
require.Len(t, tokens, 3)
tokens, _, err = s.ACLTokenListUpgradeable(10)
require.NoError(t, err)
require.Len(t, tokens, 5)
updates := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "f1093997-b6c7-496d-bfb8-6b1b1895641b",
SecretID: "34ec8eb3-095d-417a-a937-b439af7a8e8b",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
},
&structs.ACLToken{
AccessorID: "54866514-3cf2-4fec-8a8a-710583831834",
SecretID: "8de2dd39-134d-4cb1-950b-b7ab96ea20ba",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
},
&structs.ACLToken{
AccessorID: "47eea4da-bda1-48a6-901c-3e36d2d9262f",
SecretID: "548bdb8e-c0d6-477b-bcc4-67fb836e9e61",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
},
&structs.ACLToken{
AccessorID: "af1dffe5-8ac2-4282-9336-aeed9f7d951a",
SecretID: "3ee33676-d9b8-4144-bf0b-92618cff438b",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
},
&structs.ACLToken{
AccessorID: "511df589-3316-4784-b503-6e25ead4d4e1",
SecretID: "fa9d658a-6e26-42ab-a5f0-1ea05c893dee",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
},
}
require.NoError(t, s.ACLTokenBatchSet(7, updates, false))
tokens, _, err = s.ACLTokenListUpgradeable(10)
require.NoError(t, err)
require.Len(t, tokens, 0)
}
func TestStateStore_ACLToken_List(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
tokens := structs.ACLTokens{
// the local token
&structs.ACLToken{
AccessorID: "f1093997-b6c7-496d-bfb8-6b1b1895641b",
SecretID: "34ec8eb3-095d-417a-a937-b439af7a8e8b",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
Local: true,
},
// the global token
&structs.ACLToken{
AccessorID: "54866514-3cf2-4fec-8a8a-710583831834",
SecretID: "8de2dd39-134d-4cb1-950b-b7ab96ea20ba",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
},
// the policy specific token
&structs.ACLToken{
AccessorID: "47eea4da-bda1-48a6-901c-3e36d2d9262f",
SecretID: "548bdb8e-c0d6-477b-bcc4-67fb836e9e61",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
},
},
},
// the policy specific token and local
&structs.ACLToken{
AccessorID: "4915fc9d-3726-4171-b588-6c271f45eecd",
SecretID: "f6998577-fd9b-4e6c-b202-cc3820513d32",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
},
},
Local: true,
},
}
require.NoError(t, s.ACLTokenBatchSet(2, tokens, false))
type testCase struct {
name string
local bool
global bool
policy string
accessors []string
}
cases := []testCase{
{
name: "Global",
local: false,
global: true,
policy: "",
accessors: []string{
structs.ACLTokenAnonymousID,
"47eea4da-bda1-48a6-901c-3e36d2d9262f",
"54866514-3cf2-4fec-8a8a-710583831834",
},
},
{
name: "Local",
local: true,
global: false,
policy: "",
accessors: []string{
"4915fc9d-3726-4171-b588-6c271f45eecd",
"f1093997-b6c7-496d-bfb8-6b1b1895641b",
},
},
{
name: "Policy",
local: true,
global: true,
policy: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
accessors: []string{
"47eea4da-bda1-48a6-901c-3e36d2d9262f",
"4915fc9d-3726-4171-b588-6c271f45eecd",
},
},
{
name: "Policy - Local",
local: true,
global: false,
policy: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
accessors: []string{
"4915fc9d-3726-4171-b588-6c271f45eecd",
},
},
{
name: "Policy - Global",
local: false,
global: true,
policy: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
accessors: []string{
"47eea4da-bda1-48a6-901c-3e36d2d9262f",
"4915fc9d-3726-4171-b588-6c271f45eecd",
},
},
{
name: "All",
local: true,
global: true,
policy: "",
accessors: []string{
structs.ACLTokenAnonymousID,
"47eea4da-bda1-48a6-901c-3e36d2d9262f",
"4915fc9d-3726-4171-b588-6c271f45eecd",
"54866514-3cf2-4fec-8a8a-710583831834",
"f1093997-b6c7-496d-bfb8-6b1b1895641b",
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
_, tokens, err := s.ACLTokenList(nil, tc.local, tc.global, tc.policy)
require.NoError(t, err)
require.Len(t, tokens, len(tc.accessors))
tokens.Sort()
for i, token := range tokens {
require.Equal(t, tc.accessors[i], token.AccessorID)
}
})
}
}
func TestStateStore_ACLToken_FixupPolicyLinks(t *testing.T) {
// This test wants to ensure a couple of things.
//
// 1. Doing a token list/get should never modify the data
// tracked by memdb
// 2. Token list/get operations should return an accurate set
// of policy links
t.Parallel()
s := testACLTokensStateStore(t)
// the policy specific token
token := &structs.ACLToken{
AccessorID: "47eea4da-bda1-48a6-901c-3e36d2d9262f",
SecretID: "548bdb8e-c0d6-477b-bcc4-67fb836e9e61",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
},
},
}
require.NoError(t, s.ACLTokenSet(2, token, false))
_, retrieved, err := s.ACLTokenGetByAccessor(nil, token.AccessorID)
require.NoError(t, err)
// pointer equality check these should be identical
require.True(t, token == retrieved)
require.Len(t, retrieved.Policies, 1)
require.Equal(t, "node-read", retrieved.Policies[0].Name)
// rename the policy
renamed := &structs.ACLPolicy{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
Name: "node-read-renamed",
Description: "Allows reading all node information",
Rules: `node_prefix "" { policy = "read" }`,
Syntax: acl.SyntaxCurrent,
}
renamed.SetHash(true)
require.NoError(t, s.ACLPolicySet(3, renamed))
// retrieve the token again
_, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID)
require.NoError(t, err)
// pointer equality check these should be different if we cloned things appropriately
require.True(t, token != retrieved)
require.Len(t, retrieved.Policies, 1)
require.Equal(t, "node-read-renamed", retrieved.Policies[0].Name)
// list tokens without stale links
_, tokens, err := s.ACLTokenList(nil, true, true, "")
require.NoError(t, err)
found := false
for _, tok := range tokens {
if tok.AccessorID == token.AccessorID {
// these pointers shouldn't be equal because the link should have been fixed
require.True(t, tok != token)
require.Len(t, tok.Policies, 1)
require.Equal(t, "node-read-renamed", tok.Policies[0].Name)
found = true
break
}
}
require.True(t, found)
// batch get without stale links
_, tokens, err = s.ACLTokenBatchGet(nil, []string{token.AccessorID})
require.NoError(t, err)
found = false
for _, tok := range tokens {
if tok.AccessorID == token.AccessorID {
// these pointers shouldn't be equal because the link should have been fixed
require.True(t, tok != token)
require.Len(t, tok.Policies, 1)
require.Equal(t, "node-read-renamed", tok.Policies[0].Name)
found = true
break
}
}
require.True(t, found)
// delete the policy
require.NoError(t, s.ACLPolicyDeleteByID(4, "a0625e95-9b3e-42de-a8d6-ceef5b6f3286"))
// retrieve the token again
_, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID)
require.NoError(t, err)
// pointer equality check these should be different if we cloned things appropriately
require.True(t, token != retrieved)
require.Len(t, retrieved.Policies, 0)
// list tokens without stale links
_, tokens, err = s.ACLTokenList(nil, true, true, "")
require.NoError(t, err)
found = false
for _, tok := range tokens {
if tok.AccessorID == token.AccessorID {
// these pointers shouldn't be equal because the link should have been fixed
require.True(t, tok != token)
require.Len(t, tok.Policies, 0)
found = true
break
}
}
require.True(t, found)
// batch get without stale links
_, tokens, err = s.ACLTokenBatchGet(nil, []string{token.AccessorID})
require.NoError(t, err)
found = false
for _, tok := range tokens {
if tok.AccessorID == token.AccessorID {
// these pointers shouldn't be equal because the link should have been fixed
require.True(t, tok != token)
require.Len(t, tok.Policies, 0)
found = true
break
}
}
require.True(t, found)
}
func TestStateStore_ACLToken_Delete(t *testing.T) {
t.Parallel()
t.Run("Accessor", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
AccessorID: "f1093997-b6c7-496d-bfb8-6b1b1895641b",
SecretID: "34ec8eb3-095d-417a-a937-b439af7a8e8b",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
Local: true,
}
require.NoError(t, s.ACLTokenSet(2, token, false))
_, rtoken, err := s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.NotNil(t, rtoken)
require.NoError(t, s.ACLTokenDeleteByAccessor(3, "f1093997-b6c7-496d-bfb8-6b1b1895641b"))
_, rtoken, err = s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.Nil(t, rtoken)
})
t.Run("Secret", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
token := &structs.ACLToken{
AccessorID: "f1093997-b6c7-496d-bfb8-6b1b1895641b",
SecretID: "34ec8eb3-095d-417a-a937-b439af7a8e8b",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
Local: true,
}
require.NoError(t, s.ACLTokenSet(2, token, false))
_, rtoken, err := s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.NotNil(t, rtoken)
require.NoError(t, s.ACLTokenDeleteBySecret(3, "34ec8eb3-095d-417a-a937-b439af7a8e8b"))
_, rtoken, err = s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.Nil(t, rtoken)
})
t.Run("Multiple", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
tokens := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "f1093997-b6c7-496d-bfb8-6b1b1895641b",
SecretID: "34ec8eb3-095d-417a-a937-b439af7a8e8b",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
Local: true,
},
&structs.ACLToken{
AccessorID: "a0bfe8d4-b2f3-4b48-b387-f28afb820eab",
SecretID: "be444e46-fb95-4ccc-80d5-c873f34e6fa6",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: structs.ACLPolicyGlobalManagementID,
},
},
Local: true,
},
}
require.NoError(t, s.ACLTokenBatchSet(2, tokens, false))
_, rtoken, err := s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.NotNil(t, rtoken)
_, rtoken, err = s.ACLTokenGetByAccessor(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab")
require.NoError(t, err)
require.NotNil(t, rtoken)
require.NoError(t, s.ACLTokenBatchDelete(2, []string{
"f1093997-b6c7-496d-bfb8-6b1b1895641b",
"a0bfe8d4-b2f3-4b48-b387-f28afb820eab"}))
_, rtoken, err = s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.Nil(t, rtoken)
_, rtoken, err = s.ACLTokenGetByAccessor(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab")
require.NoError(t, err)
require.Nil(t, rtoken)
})
t.Run("Anonymous", func(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
require.Error(t, s.ACLTokenDeleteByAccessor(3, structs.ACLTokenAnonymousID))
require.Error(t, s.ACLTokenDeleteBySecret(3, "anonymous"))
})
t.Run("Not Found", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
// deletion of non-existent policies is not an error
require.NoError(t, s.ACLTokenDeleteByAccessor(3, "ea58a09c-2100-4aef-816b-8ee0ade77dcd"))
require.NoError(t, s.ACLTokenDeleteBySecret(3, "376d0cae-dd50-4213-9668-2c7797a7fb2d"))
})
}
func TestStateStore_ACLPolicy_SetGet(t *testing.T) {
t.Parallel()
t.Run("Missing ID", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
policy := structs.ACLPolicy{
Name: "test-policy",
Description: "test",
Rules: `keyring = "write"`,
}
require.Error(t, s.ACLPolicySet(3, &policy))
})
t.Run("Missing Name", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
policy := structs.ACLPolicy{
ID: "2c74a9b8-271c-4a21-b727-200db397c01c",
Description: "test",
Rules: `keyring = "write"`,
}
require.Error(t, s.ACLPolicySet(3, &policy))
})
t.Run("Global Management", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
t.Run("Rules", func(t *testing.T) {
t.Parallel()
policy := structs.ACLPolicy{
ID: structs.ACLPolicyGlobalManagementID,
Name: "global-management",
Description: "Global Management",
Rules: `acl = "write"`,
}
require.Error(t, s.ACLPolicySet(3, &policy))
})
t.Run("Datacenters", func(t *testing.T) {
t.Parallel()
policy := structs.ACLPolicy{
ID: structs.ACLPolicyGlobalManagementID,
Name: "global-management",
Description: "Global Management",
Rules: structs.ACLPolicyGlobalManagement,
Datacenters: []string{"dc1"},
}
require.Error(t, s.ACLPolicySet(3, &policy))
})
t.Run("Change", func(t *testing.T) {
t.Parallel()
policy := structs.ACLPolicy{
ID: structs.ACLPolicyGlobalManagementID,
Name: "management",
Description: "Modified",
Rules: structs.ACLPolicyGlobalManagement,
}
require.NoError(t, s.ACLPolicySet(3, &policy))
_, rpolicy, err := s.ACLPolicyGetByName(nil, "management")
require.NoError(t, err)
require.NotNil(t, rpolicy)
require.Equal(t, structs.ACLPolicyGlobalManagementID, rpolicy.ID)
require.Equal(t, "management", rpolicy.Name)
require.Equal(t, "Modified", rpolicy.Description)
require.Equal(t, uint64(1), rpolicy.CreateIndex)
require.Equal(t, uint64(3), rpolicy.ModifyIndex)
})
})
t.Run("New", func(t *testing.T) {
t.Parallel()
// this actually creates a new policy - we just need to verify it.
s := testACLStateStore(t)
policy := structs.ACLPolicy{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
Name: "node-read",
Description: "Allows reading all node information",
Rules: `node_prefix "" { policy = "read" }`,
Syntax: acl.SyntaxCurrent,
Datacenters: []string{"dc1"},
}
require.NoError(t, s.ACLPolicySet(3, &policy))
idx, rpolicy, err := s.ACLPolicyGetByID(nil, "a0625e95-9b3e-42de-a8d6-ceef5b6f3286")
require.Equal(t, uint64(3), idx)
require.NoError(t, err)
require.NotNil(t, rpolicy)
require.Equal(t, "node-read", rpolicy.Name)
require.Equal(t, "Allows reading all node information", rpolicy.Description)
require.Equal(t, `node_prefix "" { policy = "read" }`, rpolicy.Rules)
require.Equal(t, acl.SyntaxCurrent, rpolicy.Syntax)
require.Len(t, rpolicy.Datacenters, 1)
require.Equal(t, "dc1", rpolicy.Datacenters[0])
require.Equal(t, uint64(3), rpolicy.CreateIndex)
require.Equal(t, uint64(3), rpolicy.ModifyIndex)
// also verify the global management policy that testACLStateStore Set while we are at it.
idx, rpolicy, err = s.ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID)
require.Equal(t, uint64(3), idx)
require.NoError(t, err)
require.NotNil(t, rpolicy)
require.Equal(t, "global-management", rpolicy.Name)
require.Equal(t, "Builtin Policy that grants unlimited access", rpolicy.Description)
require.Equal(t, structs.ACLPolicyGlobalManagement, rpolicy.Rules)
require.Equal(t, acl.SyntaxCurrent, rpolicy.Syntax)
require.Len(t, rpolicy.Datacenters, 0)
require.Equal(t, uint64(1), rpolicy.CreateIndex)
require.Equal(t, uint64(1), rpolicy.ModifyIndex)
})
t.Run("Update", func(t *testing.T) {
t.Parallel()
// this creates the node read policy which we can update
s := testACLTokensStateStore(t)
update := structs.ACLPolicy{
ID: "a0625e95-9b3e-42de-a8d6-ceef5b6f3286",
Name: "node-read-modified",
Description: "Modified",
Rules: `node_prefix "" { policy = "read" } node "secret" { policy = "deny" }`,
Syntax: acl.SyntaxCurrent,
Datacenters: []string{"dc1", "dc2"},
}
require.NoError(t, s.ACLPolicySet(3, &update))
idx, rpolicy, err := s.ACLPolicyGetByID(nil, "a0625e95-9b3e-42de-a8d6-ceef5b6f3286")
require.Equal(t, uint64(3), idx)
require.NoError(t, err)
require.NotNil(t, rpolicy)
require.Equal(t, "node-read-modified", rpolicy.Name)
require.Equal(t, "Modified", rpolicy.Description)
require.Equal(t, `node_prefix "" { policy = "read" } node "secret" { policy = "deny" }`, rpolicy.Rules)
require.Equal(t, acl.SyntaxCurrent, rpolicy.Syntax)
require.ElementsMatch(t, []string{"dc1", "dc2"}, rpolicy.Datacenters)
require.Equal(t, uint64(2), rpolicy.CreateIndex)
require.Equal(t, uint64(3), rpolicy.ModifyIndex)
})
}
func TestStateStore_ACLPolicy_UpsertBatchRead(t *testing.T) {
t.Parallel()
t.Run("Normal", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
policies := structs.ACLPolicies{
&structs.ACLPolicy{
ID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
Name: "service-read",
Rules: `service_prefix "" { policy = "read" }`,
},
&structs.ACLPolicy{
ID: "a2719052-40b3-4a4b-baeb-f3df1831a217",
Name: "acl-write-dc3",
Description: "Can manage ACLs in dc3",
Datacenters: []string{"dc3"},
Rules: `acl = "write"`,
},
}
require.NoError(t, s.ACLPolicyBatchSet(2, policies))
idx, rpolicies, err := s.ACLPolicyBatchGet(nil, []string{
"a4f68bd6-3af5-4f56-b764-3c6f20247879",
"a2719052-40b3-4a4b-baeb-f3df1831a217"})
require.NoError(t, err)
require.Equal(t, uint64(2), idx)
require.Len(t, rpolicies, 2)
require.ElementsMatch(t, policies, rpolicies)
require.Equal(t, uint64(2), rpolicies[0].CreateIndex)
require.Equal(t, uint64(2), rpolicies[0].ModifyIndex)
require.Equal(t, uint64(2), rpolicies[1].CreateIndex)
require.Equal(t, uint64(2), rpolicies[1].ModifyIndex)
})
t.Run("Update", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
policies := structs.ACLPolicies{
&structs.ACLPolicy{
ID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
Name: "service-read",
Rules: `service_prefix "" { policy = "read" }`,
},
&structs.ACLPolicy{
ID: "a2719052-40b3-4a4b-baeb-f3df1831a217",
Name: "acl-write-dc3",
Description: "Can manage ACLs in dc3",
Datacenters: []string{"dc3"},
Rules: `acl = "write"`,
},
}
require.NoError(t, s.ACLPolicyBatchSet(2, policies))
updates := structs.ACLPolicies{
&structs.ACLPolicy{
ID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
Name: "service-write",
Rules: `service_prefix "" { policy = "write" }`,
Datacenters: []string{"dc1"},
},
&structs.ACLPolicy{
ID: "a2719052-40b3-4a4b-baeb-f3df1831a217",
Name: "acl-write",
Description: "Modified",
Rules: `acl = "write"`,
},
}
require.NoError(t, s.ACLPolicyBatchSet(3, updates))
idx, rpolicies, err := s.ACLPolicyBatchGet(nil, []string{
"a4f68bd6-3af5-4f56-b764-3c6f20247879",
"a2719052-40b3-4a4b-baeb-f3df1831a217"})
require.NoError(t, err)
require.Equal(t, uint64(3), idx)
require.Len(t, rpolicies, 2)
rpolicies.Sort()
require.Equal(t, "a2719052-40b3-4a4b-baeb-f3df1831a217", rpolicies[0].ID)
require.Equal(t, "acl-write", rpolicies[0].Name)
require.Equal(t, "Modified", rpolicies[0].Description)
require.Equal(t, `acl = "write"`, rpolicies[0].Rules)
require.Empty(t, rpolicies[0].Datacenters)
require.Equal(t, uint64(2), rpolicies[0].CreateIndex)
require.Equal(t, uint64(3), rpolicies[0].ModifyIndex)
require.Equal(t, "a4f68bd6-3af5-4f56-b764-3c6f20247879", rpolicies[1].ID)
require.Equal(t, "service-write", rpolicies[1].Name)
require.Equal(t, "", rpolicies[1].Description)
require.Equal(t, `service_prefix "" { policy = "write" }`, rpolicies[1].Rules)
require.ElementsMatch(t, []string{"dc1"}, rpolicies[1].Datacenters)
require.Equal(t, uint64(2), rpolicies[1].CreateIndex)
require.Equal(t, uint64(3), rpolicies[1].ModifyIndex)
})
}
func TestStateStore_ACLPolicy_List(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
policies := structs.ACLPolicies{
&structs.ACLPolicy{
ID: "a4f68bd6-3af5-4f56-b764-3c6f20247879",
Name: "service-read",
Rules: `service_prefix "" { policy = "read" }`,
},
&structs.ACLPolicy{
ID: "a2719052-40b3-4a4b-baeb-f3df1831a217",
Name: "acl-write-dc3",
Description: "Can manage ACLs in dc3",
Datacenters: []string{"dc3"},
Rules: `acl = "write"`,
},
}
require.NoError(t, s.ACLPolicyBatchSet(2, policies))
_, policies, err := s.ACLPolicyList(nil)
require.NoError(t, err)
require.Len(t, policies, 3)
policies.Sort()
require.Equal(t, structs.ACLPolicyGlobalManagementID, policies[0].ID)
require.Equal(t, "global-management", policies[0].Name)
require.Equal(t, "Builtin Policy that grants unlimited access", policies[0].Description)
require.Empty(t, policies[0].Datacenters)
require.NotEqual(t, []byte{}, policies[0].Hash)
require.Equal(t, uint64(1), policies[0].CreateIndex)
require.Equal(t, uint64(1), policies[0].ModifyIndex)
require.Equal(t, "a2719052-40b3-4a4b-baeb-f3df1831a217", policies[1].ID)
require.Equal(t, "acl-write-dc3", policies[1].Name)
require.Equal(t, "Can manage ACLs in dc3", policies[1].Description)
require.ElementsMatch(t, []string{"dc3"}, policies[1].Datacenters)
require.Nil(t, policies[1].Hash)
require.Equal(t, uint64(2), policies[1].CreateIndex)
require.Equal(t, uint64(2), policies[1].ModifyIndex)
require.Equal(t, "a4f68bd6-3af5-4f56-b764-3c6f20247879", policies[2].ID)
require.Equal(t, "service-read", policies[2].Name)
require.Equal(t, "", policies[2].Description)
require.Empty(t, policies[2].Datacenters)
require.Nil(t, policies[2].Hash)
require.Equal(t, uint64(2), policies[2].CreateIndex)
require.Equal(t, uint64(2), policies[2].ModifyIndex)
}
func TestStateStore_ACLPolicy_Delete(t *testing.T) {
t.Parallel()
t.Run("ID", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
policy := &structs.ACLPolicy{
ID: "f1093997-b6c7-496d-bfb8-6b1b1895641b",
Name: "test-policy",
Rules: `acl = "read"`,
}
require.NoError(t, s.ACLPolicySet(2, policy))
_, rpolicy, err := s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.NotNil(t, rpolicy)
require.NoError(t, s.ACLPolicyDeleteByID(3, "f1093997-b6c7-496d-bfb8-6b1b1895641b"))
require.NoError(t, err)
_, rpolicy, err = s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.Nil(t, rpolicy)
})
t.Run("Name", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
policy := &structs.ACLPolicy{
ID: "f1093997-b6c7-496d-bfb8-6b1b1895641b",
Name: "test-policy",
Rules: `acl = "read"`,
}
require.NoError(t, s.ACLPolicySet(2, policy))
_, rpolicy, err := s.ACLPolicyGetByName(nil, "test-policy")
require.NoError(t, err)
require.NotNil(t, rpolicy)
require.NoError(t, s.ACLPolicyDeleteByName(3, "test-policy"))
require.NoError(t, err)
_, rpolicy, err = s.ACLPolicyGetByName(nil, "test-policy")
require.NoError(t, err)
require.Nil(t, rpolicy)
})
t.Run("Multiple", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
policies := structs.ACLPolicies{
&structs.ACLPolicy{
ID: "f1093997-b6c7-496d-bfb8-6b1b1895641b",
Name: "34ec8eb3-095d-417a-a937-b439af7a8e8b",
Rules: `acl = "read"`,
},
&structs.ACLPolicy{
ID: "a0bfe8d4-b2f3-4b48-b387-f28afb820eab",
Name: "be444e46-fb95-4ccc-80d5-c873f34e6fa6",
Rules: `acl = "write"`,
},
}
require.NoError(t, s.ACLPolicyBatchSet(2, policies))
_, rpolicy, err := s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.NotNil(t, rpolicy)
_, rpolicy, err = s.ACLPolicyGetByID(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab")
require.NoError(t, err)
require.NotNil(t, rpolicy)
require.NoError(t, s.ACLPolicyBatchDelete(3, []string{
"f1093997-b6c7-496d-bfb8-6b1b1895641b",
"a0bfe8d4-b2f3-4b48-b387-f28afb820eab"}))
_, rpolicy, err = s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b")
require.NoError(t, err)
require.Nil(t, rpolicy)
_, rpolicy, err = s.ACLPolicyGetByID(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab")
require.NoError(t, err)
require.Nil(t, rpolicy)
})
t.Run("Global-Management", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
require.Error(t, s.ACLPolicyDeleteByID(5, structs.ACLPolicyGlobalManagementID))
require.Error(t, s.ACLPolicyDeleteByName(5, "global-management"))
})
t.Run("Not Found", func(t *testing.T) {
t.Parallel()
s := testACLStateStore(t)
// deletion of non-existent policies is not an error
require.NoError(t, s.ACLPolicyDeleteByName(3, "not-found"))
require.NoError(t, s.ACLPolicyDeleteByID(3, "376d0cae-dd50-4213-9668-2c7797a7fb2d"))
})
}
func TestStateStore_ACLTokens_Snapshot_Restore(t *testing.T) {
s := testStateStore(t)
policies := structs.ACLPolicies{
&structs.ACLPolicy{
ID: "ca1fc52c-3676-4050-82ed-ca223e38b2c9",
Name: "policy1",
Description: "policy1",
Rules: `node_prefix "" { policy = "read" }`,
Syntax: acl.SyntaxCurrent,
},
&structs.ACLPolicy{
ID: "7b70fa0f-58cd-412d-93c3-a0f17bb19a3e",
Name: "policy2",
Description: "policy2",
Rules: `acl = "read"`,
Syntax: acl.SyntaxCurrent,
},
}
for _, policy := range policies {
policy.SetHash(true)
}
require.NoError(t, s.ACLPolicyBatchSet(2, policies))
tokens := structs.ACLTokens{
&structs.ACLToken{
AccessorID: "68016c3d-835b-450c-a6f9-75db9ba740be",
SecretID: "838f72b5-5c15-4a9e-aa6d-31734c3a0286",
Description: "token1",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "ca1fc52c-3676-4050-82ed-ca223e38b2c9",
Name: "policy1",
},
structs.ACLTokenPolicyLink{
ID: "7b70fa0f-58cd-412d-93c3-a0f17bb19a3e",
Name: "policy2",
},
},
Hash: []byte{1, 2, 3, 4},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
&structs.ACLToken{
AccessorID: "b2125a1b-2a52-41d4-88f3-c58761998a46",
SecretID: "ba5d9239-a4ab-49b9-ae09-1f19eed92204",
Description: "token2",
Policies: []structs.ACLTokenPolicyLink{
structs.ACLTokenPolicyLink{
ID: "ca1fc52c-3676-4050-82ed-ca223e38b2c9",
Name: "policy1",
},
structs.ACLTokenPolicyLink{
ID: "7b70fa0f-58cd-412d-93c3-a0f17bb19a3e",
Name: "policy2",
},
},
Hash: []byte{1, 2, 3, 4},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
}
require.NoError(t, s.ACLTokenBatchSet(2, tokens, false))
// Snapshot the ACLs.
snap := s.Snapshot()
defer snap.Close()
// Alter the real state store.
require.NoError(t, s.ACLTokenDeleteByAccessor(3, tokens[0].AccessorID))
// Verify the snapshot.
require.Equal(t, uint64(2), snap.LastIndex())
iter, err := snap.ACLTokens()
require.NoError(t, err)
var dump structs.ACLTokens
for token := iter.Next(); token != nil; token = iter.Next() {
dump = append(dump, token.(*structs.ACLToken))
}
require.ElementsMatch(t, dump, tokens)
// Restore the values into a new state store.
func() {
s := testStateStore(t)
restore := s.Restore()
for _, token := range dump {
require.NoError(t, restore.ACLToken(token))
}
restore.Commit()
// need to ensure we have the policies or else the links will be removed
require.NoError(t, s.ACLPolicyBatchSet(2, policies))
// Read the restored ACLs back out and verify that they match.
idx, res, err := s.ACLTokenList(nil, true, true, "")
require.NoError(t, err)
require.Equal(t, uint64(2), idx)
require.ElementsMatch(t, tokens, res)
require.Equal(t, uint64(2), s.maxIndex("acl-tokens"))
}()
}
func TestStateStore_ACLPolicies_Snapshot_Restore(t *testing.T) {
s := testStateStore(t)
policies := structs.ACLPolicies{
&structs.ACLPolicy{
ID: "68016c3d-835b-450c-a6f9-75db9ba740be",
Name: "838f72b5-5c15-4a9e-aa6d-31734c3a0286",
Description: "policy1",
Rules: `acl = "read"`,
Hash: []byte{1, 2, 3, 4},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
&structs.ACLPolicy{
ID: "b2125a1b-2a52-41d4-88f3-c58761998a46",
Name: "ba5d9239-a4ab-49b9-ae09-1f19eed92204",
Description: "policy2",
Rules: `operator = "read"`,
Hash: []byte{1, 2, 3, 4},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
}
require.NoError(t, s.ACLPolicyBatchSet(2, policies))
// Snapshot the ACLs.
snap := s.Snapshot()
defer snap.Close()
// Alter the real state store.
require.NoError(t, s.ACLPolicyDeleteByID(3, policies[0].ID))
// Verify the snapshot.
require.Equal(t, uint64(2), snap.LastIndex())
iter, err := snap.ACLPolicies()
require.NoError(t, err)
var dump structs.ACLPolicies
for policy := iter.Next(); policy != nil; policy = iter.Next() {
dump = append(dump, policy.(*structs.ACLPolicy))
}
require.ElementsMatch(t, dump, policies)
// Restore the values into a new state store.
func() {
s := testStateStore(t)
restore := s.Restore()
for _, policy := range dump {
require.NoError(t, restore.ACLPolicy(policy))
}
restore.Commit()
// Read the restored ACLs back out and verify that they match.
idx, res, err := s.ACLPolicyList(nil)
require.NoError(t, err)
require.Equal(t, uint64(2), idx)
require.ElementsMatch(t, policies, res)
require.Equal(t, uint64(2), s.maxIndex("acl-policies"))
}()
}