2023-04-10 15:36:59 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2022-07-13 13:40:34 +00:00
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
2022-08-25 08:20:43 +00:00
|
|
|
"fmt"
|
2022-07-13 13:40:34 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
"github.com/hashicorp/go-memdb"
|
2022-07-13 13:40:34 +00:00
|
|
|
"github.com/hashicorp/nomad/ci"
|
|
|
|
"github.com/hashicorp/nomad/helper/pointer"
|
2022-08-11 07:43:50 +00:00
|
|
|
"github.com/hashicorp/nomad/helper/uuid"
|
2022-07-13 13:40:34 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/mock"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
2022-08-09 07:33:41 +00:00
|
|
|
"github.com/shoenig/test/must"
|
2022-07-13 13:40:34 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestStateStore_ACLTokensByExpired(t *testing.T) {
|
|
|
|
ci.Parallel(t)
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
// This function provides an easy way to get all tokens out of the
|
|
|
|
// iterator.
|
|
|
|
fromIteratorFunc := func(iter memdb.ResultIterator) []*structs.ACLToken {
|
|
|
|
var tokens []*structs.ACLToken
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
|
|
tokens = append(tokens, raw.(*structs.ACLToken))
|
|
|
|
}
|
|
|
|
return tokens
|
|
|
|
}
|
|
|
|
|
2022-07-13 13:40:34 +00:00
|
|
|
// This time is the threshold for all expiry calls to be based on. All
|
|
|
|
// tokens with expiry can use this as their base and use Add().
|
|
|
|
expiryTimeThreshold := time.Date(2022, time.April, 27, 14, 50, 0, 0, time.UTC)
|
|
|
|
|
|
|
|
// Generate two tokens without an expiry time. These tokens should never
|
|
|
|
// show up in calls to ACLTokensByExpired.
|
|
|
|
neverExpireLocalToken := mock.ACLToken()
|
|
|
|
neverExpireGlobalToken := mock.ACLToken()
|
|
|
|
neverExpireLocalToken.Global = true
|
|
|
|
|
|
|
|
// Upsert the tokens into state and perform a global and local read of
|
|
|
|
// the state.
|
|
|
|
err := testState.UpsertACLTokens(structs.MsgTypeTestSetup, 10, []*structs.ACLToken{
|
|
|
|
neverExpireLocalToken, neverExpireGlobalToken})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
iter, err := testState.ACLTokensByExpired(true)
|
2022-07-13 13:40:34 +00:00
|
|
|
require.NoError(t, err)
|
2022-07-19 13:37:46 +00:00
|
|
|
tokens := fromIteratorFunc(iter)
|
|
|
|
require.Len(t, tokens, 0)
|
2022-07-13 13:40:34 +00:00
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
iter, err = testState.ACLTokensByExpired(false)
|
2022-07-13 13:40:34 +00:00
|
|
|
require.NoError(t, err)
|
2022-07-19 13:37:46 +00:00
|
|
|
tokens = fromIteratorFunc(iter)
|
|
|
|
require.Len(t, tokens, 0)
|
2022-07-13 13:40:34 +00:00
|
|
|
|
|
|
|
// Generate, upsert, and test an expired local token. This token expired
|
|
|
|
// long ago and therefore before all others coming in the tests. It should
|
|
|
|
// therefore always be the first out.
|
|
|
|
expiredLocalToken := mock.ACLToken()
|
|
|
|
expiredLocalToken.ExpirationTime = pointer.Of(expiryTimeThreshold.Add(-48 * time.Hour))
|
|
|
|
|
|
|
|
err = testState.UpsertACLTokens(structs.MsgTypeTestSetup, 20, []*structs.ACLToken{expiredLocalToken})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
iter, err = testState.ACLTokensByExpired(false)
|
2022-07-13 13:40:34 +00:00
|
|
|
require.NoError(t, err)
|
2022-07-19 13:37:46 +00:00
|
|
|
tokens = fromIteratorFunc(iter)
|
|
|
|
require.Len(t, tokens, 1)
|
|
|
|
require.Equal(t, expiredLocalToken.AccessorID, tokens[0].AccessorID)
|
2022-07-13 13:40:34 +00:00
|
|
|
|
|
|
|
// Generate, upsert, and test an expired global token. This token expired
|
|
|
|
// long ago and therefore before all others coming in the tests. It should
|
|
|
|
// therefore always be the first out.
|
|
|
|
expiredGlobalToken := mock.ACLToken()
|
|
|
|
expiredGlobalToken.Global = true
|
|
|
|
expiredGlobalToken.ExpirationTime = pointer.Of(expiryTimeThreshold.Add(-48 * time.Hour))
|
|
|
|
|
|
|
|
err = testState.UpsertACLTokens(structs.MsgTypeTestSetup, 30, []*structs.ACLToken{expiredGlobalToken})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
iter, err = testState.ACLTokensByExpired(true)
|
2022-07-13 13:40:34 +00:00
|
|
|
require.NoError(t, err)
|
2022-07-19 13:37:46 +00:00
|
|
|
tokens = fromIteratorFunc(iter)
|
|
|
|
require.Len(t, tokens, 1)
|
|
|
|
require.Equal(t, expiredGlobalToken.AccessorID, tokens[0].AccessorID)
|
2022-07-13 13:40:34 +00:00
|
|
|
|
|
|
|
// This test function allows us to run the same test for local and global
|
|
|
|
// tokens.
|
2022-07-19 13:37:46 +00:00
|
|
|
testFn := func(oldToken *structs.ACLToken, global bool) {
|
2022-07-13 13:40:34 +00:00
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
// Track all the expected expired ACL tokens, including the long
|
2022-07-13 13:40:34 +00:00
|
|
|
// expired token.
|
2022-07-19 13:37:46 +00:00
|
|
|
var expiredTokens []*structs.ACLToken
|
|
|
|
expiredTokens = append(expiredTokens, oldToken)
|
2022-07-13 13:40:34 +00:00
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
// Generate and upsert a number of mixed expired, non-expired tokens.
|
|
|
|
mixedTokens := make([]*structs.ACLToken, 20)
|
2022-07-13 13:40:34 +00:00
|
|
|
for i := 0; i < 20; i++ {
|
|
|
|
mockedToken := mock.ACLToken()
|
|
|
|
mockedToken.Global = global
|
|
|
|
if i%2 == 0 {
|
2022-07-19 13:37:46 +00:00
|
|
|
expiredTokens = append(expiredTokens, mockedToken)
|
2022-07-13 13:40:34 +00:00
|
|
|
mockedToken.ExpirationTime = pointer.Of(expiryTimeThreshold.Add(-24 * time.Hour))
|
|
|
|
} else {
|
|
|
|
mockedToken.ExpirationTime = pointer.Of(expiryTimeThreshold.Add(24 * time.Hour))
|
|
|
|
}
|
2022-07-19 13:37:46 +00:00
|
|
|
mixedTokens[i] = mockedToken
|
2022-07-13 13:40:34 +00:00
|
|
|
}
|
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
err = testState.UpsertACLTokens(structs.MsgTypeTestSetup, 40, mixedTokens)
|
2022-07-13 13:40:34 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
// Check the full listing works as expected as the first 11 elements
|
|
|
|
// should all be our expired tokens. Ensure our oldest expired token is
|
|
|
|
// first in the list.
|
|
|
|
iter, err = testState.ACLTokensByExpired(global)
|
2022-07-13 13:40:34 +00:00
|
|
|
require.NoError(t, err)
|
2022-07-19 13:37:46 +00:00
|
|
|
tokens = fromIteratorFunc(iter)
|
|
|
|
require.ElementsMatch(t, expiredTokens, tokens[:11])
|
|
|
|
require.Equal(t, tokens[0], oldToken)
|
2022-07-13 13:40:34 +00:00
|
|
|
}
|
|
|
|
|
2022-07-19 13:37:46 +00:00
|
|
|
testFn(expiredLocalToken, false)
|
|
|
|
testFn(expiredGlobalToken, true)
|
2022-07-13 13:40:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func Test_expiresIndexName(t *testing.T) {
|
|
|
|
testCases := []struct {
|
|
|
|
globalInput bool
|
|
|
|
expectedOutput string
|
|
|
|
name string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
globalInput: false,
|
|
|
|
expectedOutput: indexExpiresLocal,
|
|
|
|
name: "local",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
globalInput: true,
|
|
|
|
expectedOutput: indexExpiresGlobal,
|
|
|
|
name: "global",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
actualOutput := expiresIndexName(tc.globalInput)
|
|
|
|
require.Equal(t, tc.expectedOutput, actualOutput)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2022-08-09 07:33:41 +00:00
|
|
|
|
|
|
|
func TestStateStore_UpsertACLRoles(t *testing.T) {
|
|
|
|
ci.Parallel(t)
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Generate a mocked ACL role for testing and attempt to upsert this
|
|
|
|
// straight into state. It should fail because the ACL policies do not
|
|
|
|
// exist.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole()}
|
2022-08-22 06:54:07 +00:00
|
|
|
err := testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false)
|
2022-08-09 07:33:41 +00:00
|
|
|
require.ErrorContains(t, err, "policy not found")
|
|
|
|
|
|
|
|
// Create the policies our ACL roles wants to link to and then try the
|
|
|
|
// upsert again.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(
|
|
|
|
structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false))
|
2022-08-09 07:33:41 +00:00
|
|
|
|
|
|
|
// Check that the index for the table was modified as expected.
|
|
|
|
initialIndex, err := testState.Index(TableACLRoles)
|
|
|
|
require.NoError(t, err)
|
|
|
|
must.Eq(t, 20, initialIndex)
|
|
|
|
|
|
|
|
// List all the ACL roles in the table, so we can perform a number of tests
|
|
|
|
// on the return array.
|
|
|
|
ws := memdb.NewWatchSet()
|
|
|
|
iter, err := testState.GetACLRoles(ws)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Count how many table entries we have, to ensure it is the expected
|
|
|
|
// number.
|
|
|
|
var count int
|
|
|
|
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
|
|
count++
|
|
|
|
|
|
|
|
// Ensure the create and modify indexes are populated correctly.
|
|
|
|
aclRole := raw.(*structs.ACLRole)
|
|
|
|
must.Eq(t, 20, aclRole.CreateIndex)
|
|
|
|
must.Eq(t, 20, aclRole.ModifyIndex)
|
|
|
|
}
|
|
|
|
require.Equal(t, 1, count, "incorrect number of ACL roles found")
|
|
|
|
|
|
|
|
// Try writing the same ACL roles to state which should not result in an
|
|
|
|
// update to the table index.
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 30, mockedACLRoles, false))
|
2022-08-09 07:33:41 +00:00
|
|
|
reInsertActualIndex, err := testState.Index(TableACLRoles)
|
|
|
|
require.NoError(t, err)
|
|
|
|
must.Eq(t, 20, reInsertActualIndex)
|
|
|
|
|
|
|
|
// Make a change to one of the ACL roles and ensure this update is accepted
|
|
|
|
// and the table index is updated.
|
|
|
|
updatedMockedACLRole := mockedACLRoles[0].Copy()
|
|
|
|
updatedMockedACLRole.Policies = []*structs.ACLRolePolicyLink{{Name: "mocked-test-policy-1"}}
|
|
|
|
updatedMockedACLRole.SetHash()
|
|
|
|
require.NoError(t, testState.UpsertACLRoles(
|
2022-08-22 06:54:07 +00:00
|
|
|
structs.MsgTypeTestSetup, 30, []*structs.ACLRole{updatedMockedACLRole}, false))
|
2022-08-09 07:33:41 +00:00
|
|
|
|
|
|
|
// Check that the index for the table was modified as expected.
|
|
|
|
updatedIndex, err := testState.Index(TableACLRoles)
|
|
|
|
require.NoError(t, err)
|
|
|
|
must.Eq(t, 30, updatedIndex)
|
|
|
|
|
|
|
|
// List the ACL roles in state.
|
|
|
|
iter, err = testState.GetACLRoles(ws)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Count how many table entries we have, to ensure it is the expected
|
|
|
|
// number.
|
|
|
|
count = 0
|
|
|
|
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
|
|
count++
|
|
|
|
|
|
|
|
// Ensure the create and modify indexes are populated correctly.
|
|
|
|
aclRole := raw.(*structs.ACLRole)
|
|
|
|
must.Eq(t, 20, aclRole.CreateIndex)
|
|
|
|
must.Eq(t, 30, aclRole.ModifyIndex)
|
|
|
|
}
|
|
|
|
require.Equal(t, 1, count, "incorrect number of ACL roles found")
|
2022-08-22 06:54:07 +00:00
|
|
|
|
|
|
|
// Now try inserting an ACL role using the missing policies' argument to
|
|
|
|
// simulate replication.
|
|
|
|
replicatedACLRole := mock.ACLRole()
|
|
|
|
replicatedACLRole.Policies = []*structs.ACLRolePolicyLink{{Name: "nope"}}
|
|
|
|
require.NoError(t, testState.UpsertACLRoles(
|
|
|
|
structs.MsgTypeTestSetup, 40, []*structs.ACLRole{replicatedACLRole}, true))
|
|
|
|
|
|
|
|
replicatedACLRoleResp, err := testState.GetACLRoleByName(ws, replicatedACLRole.Name)
|
|
|
|
require.NoError(t, err)
|
|
|
|
must.Eq(t, replicatedACLRole.Hash, replicatedACLRoleResp.Hash)
|
2022-08-25 08:20:43 +00:00
|
|
|
|
|
|
|
// Try adding a new ACL role, which has a name clash with an existing
|
|
|
|
// entry.
|
|
|
|
dupRoleName := mock.ACLRole()
|
|
|
|
dupRoleName.Name = mockedACLRoles[0].Name
|
|
|
|
|
|
|
|
err = testState.UpsertACLRoles(structs.MsgTypeTestSetup, 50,
|
|
|
|
[]*structs.ACLRole{dupRoleName}, false)
|
|
|
|
require.ErrorContains(t, err, fmt.Sprintf("ACL role with name %s already exists", dupRoleName.Name))
|
2022-08-09 07:33:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestStateStore_ValidateACLRolePolicyLinks(t *testing.T) {
|
|
|
|
ci.Parallel(t)
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Create our mocked role which includes two ACL policy links.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole()}
|
|
|
|
|
|
|
|
// This should error as no policies exist within state.
|
2022-08-22 06:54:07 +00:00
|
|
|
err := testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false)
|
2022-08-09 07:33:41 +00:00
|
|
|
require.ErrorContains(t, err, "ACL policy not found")
|
|
|
|
|
|
|
|
// Upsert one ACL policy and retry the role which should still fail.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1}))
|
2022-08-22 06:54:07 +00:00
|
|
|
err = testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false)
|
2022-08-09 07:33:41 +00:00
|
|
|
require.ErrorContains(t, err, "ACL policy not found")
|
|
|
|
|
|
|
|
// Upsert the second ACL policy. The ACL role should now upsert into state
|
|
|
|
// without error.
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(structs.MsgTypeTestSetup, 20, []*structs.ACLPolicy{policy2}))
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 30, mockedACLRoles, false))
|
2022-08-09 07:33:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestStateStore_DeleteACLRolesByID(t *testing.T) {
|
|
|
|
ci.Parallel(t)
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Create the policies our ACL roles wants to link to.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(
|
|
|
|
structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
|
|
|
|
|
|
|
|
// Generate a some mocked ACL roles for testing and upsert these straight
|
|
|
|
// into state.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
|
2022-08-09 07:33:41 +00:00
|
|
|
|
|
|
|
// Try and delete a role using a name that doesn't exist. This should
|
|
|
|
// return an error and not change the index for the table.
|
|
|
|
err := testState.DeleteACLRolesByID(structs.MsgTypeTestSetup, 20, []string{"not-a-role"})
|
|
|
|
require.ErrorContains(t, err, "ACL role not found")
|
|
|
|
|
|
|
|
tableIndex, err := testState.Index(TableACLRoles)
|
|
|
|
require.NoError(t, err)
|
|
|
|
must.Eq(t, 10, tableIndex)
|
|
|
|
|
|
|
|
// Delete one of the previously upserted ACL roles. This should succeed
|
|
|
|
// and modify the table index.
|
|
|
|
err = testState.DeleteACLRolesByID(structs.MsgTypeTestSetup, 20, []string{mockedACLRoles[0].ID})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
tableIndex, err = testState.Index(TableACLRoles)
|
|
|
|
require.NoError(t, err)
|
|
|
|
must.Eq(t, 20, tableIndex)
|
|
|
|
|
|
|
|
// List the ACL roles and ensure we now only have one present and that it
|
|
|
|
// is the one we expect.
|
|
|
|
ws := memdb.NewWatchSet()
|
|
|
|
iter, err := testState.GetACLRoles(ws)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var aclRoles []*structs.ACLRole
|
|
|
|
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
|
|
aclRoles = append(aclRoles, raw.(*structs.ACLRole))
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Len(t, aclRoles, 1, "incorrect number of ACL roles found")
|
2022-10-10 14:28:46 +00:00
|
|
|
require.True(t, aclRoles[0].Equal(mockedACLRoles[1]))
|
2022-08-09 07:33:41 +00:00
|
|
|
|
|
|
|
// Delete the final remaining ACL role. This should succeed and modify the
|
|
|
|
// table index.
|
|
|
|
err = testState.DeleteACLRolesByID(structs.MsgTypeTestSetup, 30, []string{mockedACLRoles[1].ID})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
tableIndex, err = testState.Index(TableACLRoles)
|
|
|
|
require.NoError(t, err)
|
|
|
|
must.Eq(t, 30, tableIndex)
|
|
|
|
|
|
|
|
// List the ACL roles and ensure we have zero entries.
|
|
|
|
iter, err = testState.GetACLRoles(ws)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
aclRoles = []*structs.ACLRole{}
|
|
|
|
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
|
|
aclRoles = append(aclRoles, raw.(*structs.ACLRole))
|
|
|
|
}
|
|
|
|
require.Len(t, aclRoles, 0, "incorrect number of ACL roles found")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStateStore_GetACLRoles(t *testing.T) {
|
|
|
|
ci.Parallel(t)
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Create the policies our ACL roles wants to link to.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(
|
|
|
|
structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
|
|
|
|
|
|
|
|
// Generate a some mocked ACL roles for testing and upsert these straight
|
|
|
|
// into state.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
|
2022-08-09 07:33:41 +00:00
|
|
|
|
|
|
|
// List the ACL roles and ensure they are exactly as we expect.
|
|
|
|
ws := memdb.NewWatchSet()
|
|
|
|
iter, err := testState.GetACLRoles(ws)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var aclRoles []*structs.ACLRole
|
|
|
|
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
|
|
aclRoles = append(aclRoles, raw.(*structs.ACLRole))
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := mockedACLRoles
|
|
|
|
for i := range expected {
|
|
|
|
expected[i].CreateIndex = 10
|
|
|
|
expected[i].ModifyIndex = 10
|
|
|
|
}
|
|
|
|
|
|
|
|
require.ElementsMatch(t, aclRoles, expected)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStateStore_GetACLRoleByID(t *testing.T) {
|
|
|
|
ci.Parallel(t)
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Create the policies our ACL roles wants to link to.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(
|
|
|
|
structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
|
|
|
|
|
|
|
|
// Generate a some mocked ACL roles for testing and upsert these straight
|
|
|
|
// into state.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
|
2022-08-09 07:33:41 +00:00
|
|
|
|
|
|
|
ws := memdb.NewWatchSet()
|
|
|
|
|
|
|
|
// Try reading an ACL role that does not exist.
|
|
|
|
aclRole, err := testState.GetACLRoleByID(ws, "not-a-role")
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Nil(t, aclRole)
|
|
|
|
|
|
|
|
// Read the two ACL roles that we should find.
|
|
|
|
aclRole, err = testState.GetACLRoleByID(ws, mockedACLRoles[0].ID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, mockedACLRoles[0], aclRole)
|
|
|
|
|
|
|
|
aclRole, err = testState.GetACLRoleByID(ws, mockedACLRoles[1].ID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, mockedACLRoles[1], aclRole)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStateStore_GetACLRoleByName(t *testing.T) {
|
|
|
|
ci.Parallel(t)
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Create the policies our ACL roles wants to link to.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(
|
|
|
|
structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
|
|
|
|
|
|
|
|
// Generate a some mocked ACL roles for testing and upsert these straight
|
|
|
|
// into state.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
|
2022-08-09 07:33:41 +00:00
|
|
|
|
|
|
|
ws := memdb.NewWatchSet()
|
|
|
|
|
|
|
|
// Try reading an ACL role that does not exist.
|
|
|
|
aclRole, err := testState.GetACLRoleByName(ws, "not-a-role")
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Nil(t, aclRole)
|
|
|
|
|
|
|
|
// Read the two ACL roles that we should find.
|
|
|
|
aclRole, err = testState.GetACLRoleByName(ws, mockedACLRoles[0].Name)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, mockedACLRoles[0], aclRole)
|
|
|
|
|
|
|
|
aclRole, err = testState.GetACLRoleByName(ws, mockedACLRoles[1].Name)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, mockedACLRoles[1], aclRole)
|
|
|
|
}
|
2022-08-11 07:43:50 +00:00
|
|
|
|
|
|
|
func TestStateStore_GetACLRoleByIDPrefix(t *testing.T) {
|
|
|
|
ci.Parallel(t)
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Create the policies our ACL roles wants to link to.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(
|
|
|
|
structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
|
|
|
|
|
|
|
|
// Generate a some mocked ACL roles for testing and upsert these straight
|
|
|
|
// into state. Set the ID to something with a prefix we know so it is easy
|
|
|
|
// to test.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
|
|
|
|
mockedACLRoles[0].ID = "test-prefix-" + uuid.Generate()
|
|
|
|
mockedACLRoles[1].ID = "test-prefix-" + uuid.Generate()
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
|
2022-08-11 07:43:50 +00:00
|
|
|
|
|
|
|
ws := memdb.NewWatchSet()
|
|
|
|
|
|
|
|
// Try using a prefix that doesn't match any entries.
|
|
|
|
iter, err := testState.GetACLRoleByIDPrefix(ws, "nope")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var aclRoles []*structs.ACLRole
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
|
|
aclRoles = append(aclRoles, raw.(*structs.ACLRole))
|
|
|
|
}
|
|
|
|
require.Len(t, aclRoles, 0)
|
|
|
|
|
|
|
|
// Use a prefix which should match two entries in state.
|
|
|
|
iter, err = testState.GetACLRoleByIDPrefix(ws, "test-prefix-")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
aclRoles = []*structs.ACLRole{}
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
|
|
aclRoles = append(aclRoles, raw.(*structs.ACLRole))
|
|
|
|
}
|
|
|
|
require.Len(t, aclRoles, 2)
|
|
|
|
}
|
2022-08-17 13:45:01 +00:00
|
|
|
|
|
|
|
func TestStateStore_fixTokenRoleLinks(t *testing.T) {
|
|
|
|
ci.Parallel(t)
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
name string
|
|
|
|
testFn func()
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "no fix needed",
|
|
|
|
testFn: func() {
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Create the policies our ACL roles wants to link to.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(
|
|
|
|
structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
|
|
|
|
|
|
|
|
// Generate a some mocked ACL roles for testing and upsert these straight
|
|
|
|
// into state.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false))
|
2022-08-17 13:45:01 +00:00
|
|
|
|
|
|
|
// Create an ACL token linking to the ACL role.
|
|
|
|
token1 := mock.ACLToken()
|
|
|
|
token1.Roles = []*structs.ACLTokenRoleLink{{ID: mockedACLRoles[0].ID}}
|
|
|
|
require.NoError(t, testState.UpsertACLTokens(
|
|
|
|
structs.MsgTypeTestSetup, 20, []*structs.ACLToken{token1}))
|
|
|
|
|
|
|
|
// Perform the fix and check the returned token contains the
|
|
|
|
// correct roles.
|
|
|
|
readTxn := testState.db.ReadTxn()
|
|
|
|
outputToken, err := testState.fixTokenRoleLinks(readTxn, token1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, outputToken.Roles, []*structs.ACLTokenRoleLink{{
|
|
|
|
Name: mockedACLRoles[0].Name, ID: mockedACLRoles[0].ID,
|
|
|
|
}})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "acl role from link deleted",
|
|
|
|
testFn: func() {
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Create the policies our ACL roles wants to link to.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(
|
|
|
|
structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
|
|
|
|
|
|
|
|
// Generate a some mocked ACL roles for testing and upsert these straight
|
|
|
|
// into state.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false))
|
2022-08-17 13:45:01 +00:00
|
|
|
|
|
|
|
// Create an ACL token linking to the ACL roles.
|
|
|
|
token1 := mock.ACLToken()
|
|
|
|
token1.Roles = []*structs.ACLTokenRoleLink{{ID: mockedACLRoles[0].ID}, {ID: mockedACLRoles[1].ID}}
|
|
|
|
require.NoError(t, testState.UpsertACLTokens(
|
|
|
|
structs.MsgTypeTestSetup, 30, []*structs.ACLToken{token1}))
|
|
|
|
|
|
|
|
// Now delete one of the ACL roles from state.
|
|
|
|
require.NoError(t, testState.DeleteACLRolesByID(
|
|
|
|
structs.MsgTypeTestSetup, 40, []string{mockedACLRoles[0].ID}))
|
|
|
|
|
|
|
|
// Perform the fix and check the returned token contains the
|
|
|
|
// correct roles.
|
|
|
|
readTxn := testState.db.ReadTxn()
|
|
|
|
outputToken, err := testState.fixTokenRoleLinks(readTxn, token1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, outputToken.Roles, 1)
|
|
|
|
require.Equal(t, outputToken.Roles, []*structs.ACLTokenRoleLink{{
|
|
|
|
Name: mockedACLRoles[1].Name, ID: mockedACLRoles[1].ID,
|
|
|
|
}})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "acl role from link name changed",
|
|
|
|
testFn: func() {
|
|
|
|
testState := testStateStore(t)
|
|
|
|
|
|
|
|
// Create the policies our ACL roles wants to link to.
|
|
|
|
policy1 := mock.ACLPolicy()
|
|
|
|
policy1.Name = "mocked-test-policy-1"
|
|
|
|
policy2 := mock.ACLPolicy()
|
|
|
|
policy2.Name = "mocked-test-policy-2"
|
|
|
|
|
|
|
|
require.NoError(t, testState.UpsertACLPolicies(
|
|
|
|
structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
|
|
|
|
|
|
|
|
// Generate a some mocked ACL roles for testing and upsert these straight
|
|
|
|
// into state.
|
|
|
|
mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false))
|
2022-08-17 13:45:01 +00:00
|
|
|
|
|
|
|
// Create an ACL token linking to the ACL roles.
|
|
|
|
token1 := mock.ACLToken()
|
|
|
|
token1.Roles = []*structs.ACLTokenRoleLink{{ID: mockedACLRoles[0].ID}, {ID: mockedACLRoles[1].ID}}
|
|
|
|
require.NoError(t, testState.UpsertACLTokens(
|
|
|
|
structs.MsgTypeTestSetup, 30, []*structs.ACLToken{token1}))
|
|
|
|
|
|
|
|
// Now change the name of one of the ACL roles.
|
|
|
|
mockedACLRoles[0].Name = "badger-badger-badger"
|
2022-08-22 06:54:07 +00:00
|
|
|
require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 40, mockedACLRoles, false))
|
2022-08-17 13:45:01 +00:00
|
|
|
|
|
|
|
// Perform the fix and check the returned token contains the
|
|
|
|
// correct roles.
|
|
|
|
readTxn := testState.db.ReadTxn()
|
|
|
|
outputToken, err := testState.fixTokenRoleLinks(readTxn, token1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, outputToken.Roles, 2)
|
|
|
|
require.ElementsMatch(t, outputToken.Roles, []*structs.ACLTokenRoleLink{
|
|
|
|
{Name: mockedACLRoles[0].Name, ID: mockedACLRoles[0].ID},
|
|
|
|
{Name: mockedACLRoles[1].Name, ID: mockedACLRoles[1].ID},
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
tc.testFn()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|