github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/state/state_store_acl_test.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hashicorp/go-memdb"
     9  	"github.com/hashicorp/nomad/ci"
    10  	"github.com/hashicorp/nomad/helper/pointer"
    11  	"github.com/hashicorp/nomad/helper/uuid"
    12  	"github.com/hashicorp/nomad/nomad/mock"
    13  	"github.com/hashicorp/nomad/nomad/structs"
    14  	"github.com/shoenig/test/must"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestStateStore_ACLTokensByExpired(t *testing.T) {
    19  	ci.Parallel(t)
    20  	testState := testStateStore(t)
    21  
    22  	// This function provides an easy way to get all tokens out of the
    23  	// iterator.
    24  	fromIteratorFunc := func(iter memdb.ResultIterator) []*structs.ACLToken {
    25  		var tokens []*structs.ACLToken
    26  		for raw := iter.Next(); raw != nil; raw = iter.Next() {
    27  			tokens = append(tokens, raw.(*structs.ACLToken))
    28  		}
    29  		return tokens
    30  	}
    31  
    32  	// This time is the threshold for all expiry calls to be based on. All
    33  	// tokens with expiry can use this as their base and use Add().
    34  	expiryTimeThreshold := time.Date(2022, time.April, 27, 14, 50, 0, 0, time.UTC)
    35  
    36  	// Generate two tokens without an expiry time. These tokens should never
    37  	// show up in calls to ACLTokensByExpired.
    38  	neverExpireLocalToken := mock.ACLToken()
    39  	neverExpireGlobalToken := mock.ACLToken()
    40  	neverExpireLocalToken.Global = true
    41  
    42  	// Upsert the tokens into state and perform a global and local read of
    43  	// the state.
    44  	err := testState.UpsertACLTokens(structs.MsgTypeTestSetup, 10, []*structs.ACLToken{
    45  		neverExpireLocalToken, neverExpireGlobalToken})
    46  	require.NoError(t, err)
    47  
    48  	iter, err := testState.ACLTokensByExpired(true)
    49  	require.NoError(t, err)
    50  	tokens := fromIteratorFunc(iter)
    51  	require.Len(t, tokens, 0)
    52  
    53  	iter, err = testState.ACLTokensByExpired(false)
    54  	require.NoError(t, err)
    55  	tokens = fromIteratorFunc(iter)
    56  	require.Len(t, tokens, 0)
    57  
    58  	// Generate, upsert, and test an expired local token. This token expired
    59  	// long ago and therefore before all others coming in the tests. It should
    60  	// therefore always be the first out.
    61  	expiredLocalToken := mock.ACLToken()
    62  	expiredLocalToken.ExpirationTime = pointer.Of(expiryTimeThreshold.Add(-48 * time.Hour))
    63  
    64  	err = testState.UpsertACLTokens(structs.MsgTypeTestSetup, 20, []*structs.ACLToken{expiredLocalToken})
    65  	require.NoError(t, err)
    66  
    67  	iter, err = testState.ACLTokensByExpired(false)
    68  	require.NoError(t, err)
    69  	tokens = fromIteratorFunc(iter)
    70  	require.Len(t, tokens, 1)
    71  	require.Equal(t, expiredLocalToken.AccessorID, tokens[0].AccessorID)
    72  
    73  	// Generate, upsert, and test an expired global token. This token expired
    74  	// long ago and therefore before all others coming in the tests. It should
    75  	// therefore always be the first out.
    76  	expiredGlobalToken := mock.ACLToken()
    77  	expiredGlobalToken.Global = true
    78  	expiredGlobalToken.ExpirationTime = pointer.Of(expiryTimeThreshold.Add(-48 * time.Hour))
    79  
    80  	err = testState.UpsertACLTokens(structs.MsgTypeTestSetup, 30, []*structs.ACLToken{expiredGlobalToken})
    81  	require.NoError(t, err)
    82  
    83  	iter, err = testState.ACLTokensByExpired(true)
    84  	require.NoError(t, err)
    85  	tokens = fromIteratorFunc(iter)
    86  	require.Len(t, tokens, 1)
    87  	require.Equal(t, expiredGlobalToken.AccessorID, tokens[0].AccessorID)
    88  
    89  	// This test function allows us to run the same test for local and global
    90  	// tokens.
    91  	testFn := func(oldToken *structs.ACLToken, global bool) {
    92  
    93  		// Track all the expected expired ACL tokens, including the long
    94  		// expired token.
    95  		var expiredTokens []*structs.ACLToken
    96  		expiredTokens = append(expiredTokens, oldToken)
    97  
    98  		// Generate and upsert a number of mixed expired, non-expired tokens.
    99  		mixedTokens := make([]*structs.ACLToken, 20)
   100  		for i := 0; i < 20; i++ {
   101  			mockedToken := mock.ACLToken()
   102  			mockedToken.Global = global
   103  			if i%2 == 0 {
   104  				expiredTokens = append(expiredTokens, mockedToken)
   105  				mockedToken.ExpirationTime = pointer.Of(expiryTimeThreshold.Add(-24 * time.Hour))
   106  			} else {
   107  				mockedToken.ExpirationTime = pointer.Of(expiryTimeThreshold.Add(24 * time.Hour))
   108  			}
   109  			mixedTokens[i] = mockedToken
   110  		}
   111  
   112  		err = testState.UpsertACLTokens(structs.MsgTypeTestSetup, 40, mixedTokens)
   113  		require.NoError(t, err)
   114  
   115  		// Check the full listing works as expected as the first 11 elements
   116  		// should all be our expired tokens. Ensure our oldest expired token is
   117  		// first in the list.
   118  		iter, err = testState.ACLTokensByExpired(global)
   119  		require.NoError(t, err)
   120  		tokens = fromIteratorFunc(iter)
   121  		require.ElementsMatch(t, expiredTokens, tokens[:11])
   122  		require.Equal(t, tokens[0], oldToken)
   123  	}
   124  
   125  	testFn(expiredLocalToken, false)
   126  	testFn(expiredGlobalToken, true)
   127  }
   128  
   129  func Test_expiresIndexName(t *testing.T) {
   130  	testCases := []struct {
   131  		globalInput    bool
   132  		expectedOutput string
   133  		name           string
   134  	}{
   135  		{
   136  			globalInput:    false,
   137  			expectedOutput: indexExpiresLocal,
   138  			name:           "local",
   139  		},
   140  		{
   141  			globalInput:    true,
   142  			expectedOutput: indexExpiresGlobal,
   143  			name:           "global",
   144  		},
   145  	}
   146  
   147  	for _, tc := range testCases {
   148  		t.Run(tc.name, func(t *testing.T) {
   149  			actualOutput := expiresIndexName(tc.globalInput)
   150  			require.Equal(t, tc.expectedOutput, actualOutput)
   151  		})
   152  	}
   153  }
   154  
   155  func TestStateStore_UpsertACLRoles(t *testing.T) {
   156  	ci.Parallel(t)
   157  	testState := testStateStore(t)
   158  
   159  	// Generate a mocked ACL role for testing and attempt to upsert this
   160  	// straight into state. It should fail because the ACL policies do not
   161  	// exist.
   162  	mockedACLRoles := []*structs.ACLRole{mock.ACLRole()}
   163  	err := testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false)
   164  	require.ErrorContains(t, err, "policy not found")
   165  
   166  	// Create the policies our ACL roles wants to link to and then try the
   167  	// upsert again.
   168  	policy1 := mock.ACLPolicy()
   169  	policy1.Name = "mocked-test-policy-1"
   170  	policy2 := mock.ACLPolicy()
   171  	policy2.Name = "mocked-test-policy-2"
   172  
   173  	require.NoError(t, testState.UpsertACLPolicies(
   174  		structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   175  	require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false))
   176  
   177  	// Check that the index for the table was modified as expected.
   178  	initialIndex, err := testState.Index(TableACLRoles)
   179  	require.NoError(t, err)
   180  	must.Eq(t, 20, initialIndex)
   181  
   182  	// List all the ACL roles in the table, so we can perform a number of tests
   183  	// on the return array.
   184  	ws := memdb.NewWatchSet()
   185  	iter, err := testState.GetACLRoles(ws)
   186  	require.NoError(t, err)
   187  
   188  	// Count how many table entries we have, to ensure it is the expected
   189  	// number.
   190  	var count int
   191  
   192  	for raw := iter.Next(); raw != nil; raw = iter.Next() {
   193  		count++
   194  
   195  		// Ensure the create and modify indexes are populated correctly.
   196  		aclRole := raw.(*structs.ACLRole)
   197  		must.Eq(t, 20, aclRole.CreateIndex)
   198  		must.Eq(t, 20, aclRole.ModifyIndex)
   199  	}
   200  	require.Equal(t, 1, count, "incorrect number of ACL roles found")
   201  
   202  	// Try writing the same ACL roles to state which should not result in an
   203  	// update to the table index.
   204  	require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 30, mockedACLRoles, false))
   205  	reInsertActualIndex, err := testState.Index(TableACLRoles)
   206  	require.NoError(t, err)
   207  	must.Eq(t, 20, reInsertActualIndex)
   208  
   209  	// Make a change to one of the ACL roles and ensure this update is accepted
   210  	// and the table index is updated.
   211  	updatedMockedACLRole := mockedACLRoles[0].Copy()
   212  	updatedMockedACLRole.Policies = []*structs.ACLRolePolicyLink{{Name: "mocked-test-policy-1"}}
   213  	updatedMockedACLRole.SetHash()
   214  	require.NoError(t, testState.UpsertACLRoles(
   215  		structs.MsgTypeTestSetup, 30, []*structs.ACLRole{updatedMockedACLRole}, false))
   216  
   217  	// Check that the index for the table was modified as expected.
   218  	updatedIndex, err := testState.Index(TableACLRoles)
   219  	require.NoError(t, err)
   220  	must.Eq(t, 30, updatedIndex)
   221  
   222  	// List the ACL roles in state.
   223  	iter, err = testState.GetACLRoles(ws)
   224  	require.NoError(t, err)
   225  
   226  	// Count how many table entries we have, to ensure it is the expected
   227  	// number.
   228  	count = 0
   229  
   230  	for raw := iter.Next(); raw != nil; raw = iter.Next() {
   231  		count++
   232  
   233  		// Ensure the create and modify indexes are populated correctly.
   234  		aclRole := raw.(*structs.ACLRole)
   235  		must.Eq(t, 20, aclRole.CreateIndex)
   236  		must.Eq(t, 30, aclRole.ModifyIndex)
   237  	}
   238  	require.Equal(t, 1, count, "incorrect number of ACL roles found")
   239  
   240  	// Now try inserting an ACL role using the missing policies' argument to
   241  	// simulate replication.
   242  	replicatedACLRole := mock.ACLRole()
   243  	replicatedACLRole.Policies = []*structs.ACLRolePolicyLink{{Name: "nope"}}
   244  	require.NoError(t, testState.UpsertACLRoles(
   245  		structs.MsgTypeTestSetup, 40, []*structs.ACLRole{replicatedACLRole}, true))
   246  
   247  	replicatedACLRoleResp, err := testState.GetACLRoleByName(ws, replicatedACLRole.Name)
   248  	require.NoError(t, err)
   249  	must.Eq(t, replicatedACLRole.Hash, replicatedACLRoleResp.Hash)
   250  
   251  	// Try adding a new ACL role, which has a name clash with an existing
   252  	// entry.
   253  	dupRoleName := mock.ACLRole()
   254  	dupRoleName.Name = mockedACLRoles[0].Name
   255  
   256  	err = testState.UpsertACLRoles(structs.MsgTypeTestSetup, 50,
   257  		[]*structs.ACLRole{dupRoleName}, false)
   258  	require.ErrorContains(t, err, fmt.Sprintf("ACL role with name %s already exists", dupRoleName.Name))
   259  }
   260  
   261  func TestStateStore_ValidateACLRolePolicyLinks(t *testing.T) {
   262  	ci.Parallel(t)
   263  	testState := testStateStore(t)
   264  
   265  	// Create our mocked role which includes two ACL policy links.
   266  	mockedACLRoles := []*structs.ACLRole{mock.ACLRole()}
   267  
   268  	// This should error as no policies exist within state.
   269  	err := testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false)
   270  	require.ErrorContains(t, err, "ACL policy not found")
   271  
   272  	// Upsert one ACL policy and retry the role which should still fail.
   273  	policy1 := mock.ACLPolicy()
   274  	policy1.Name = "mocked-test-policy-1"
   275  
   276  	require.NoError(t, testState.UpsertACLPolicies(structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1}))
   277  	err = testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false)
   278  	require.ErrorContains(t, err, "ACL policy not found")
   279  
   280  	// Upsert the second ACL policy. The ACL role should now upsert into state
   281  	// without error.
   282  	policy2 := mock.ACLPolicy()
   283  	policy2.Name = "mocked-test-policy-2"
   284  
   285  	require.NoError(t, testState.UpsertACLPolicies(structs.MsgTypeTestSetup, 20, []*structs.ACLPolicy{policy2}))
   286  	require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 30, mockedACLRoles, false))
   287  }
   288  
   289  func TestStateStore_DeleteACLRolesByID(t *testing.T) {
   290  	ci.Parallel(t)
   291  	testState := testStateStore(t)
   292  
   293  	// Create the policies our ACL roles wants to link to.
   294  	policy1 := mock.ACLPolicy()
   295  	policy1.Name = "mocked-test-policy-1"
   296  	policy2 := mock.ACLPolicy()
   297  	policy2.Name = "mocked-test-policy-2"
   298  
   299  	require.NoError(t, testState.UpsertACLPolicies(
   300  		structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   301  
   302  	// Generate a some mocked ACL roles for testing and upsert these straight
   303  	// into state.
   304  	mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   305  	require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
   306  
   307  	// Try and delete a role using a name that doesn't exist. This should
   308  	// return an error and not change the index for the table.
   309  	err := testState.DeleteACLRolesByID(structs.MsgTypeTestSetup, 20, []string{"not-a-role"})
   310  	require.ErrorContains(t, err, "ACL role not found")
   311  
   312  	tableIndex, err := testState.Index(TableACLRoles)
   313  	require.NoError(t, err)
   314  	must.Eq(t, 10, tableIndex)
   315  
   316  	// Delete one of the previously upserted ACL roles. This should succeed
   317  	// and modify the table index.
   318  	err = testState.DeleteACLRolesByID(structs.MsgTypeTestSetup, 20, []string{mockedACLRoles[0].ID})
   319  	require.NoError(t, err)
   320  
   321  	tableIndex, err = testState.Index(TableACLRoles)
   322  	require.NoError(t, err)
   323  	must.Eq(t, 20, tableIndex)
   324  
   325  	// List the ACL roles and ensure we now only have one present and that it
   326  	// is the one we expect.
   327  	ws := memdb.NewWatchSet()
   328  	iter, err := testState.GetACLRoles(ws)
   329  	require.NoError(t, err)
   330  
   331  	var aclRoles []*structs.ACLRole
   332  
   333  	for raw := iter.Next(); raw != nil; raw = iter.Next() {
   334  		aclRoles = append(aclRoles, raw.(*structs.ACLRole))
   335  	}
   336  
   337  	require.Len(t, aclRoles, 1, "incorrect number of ACL roles found")
   338  	require.True(t, aclRoles[0].Equal(mockedACLRoles[1]))
   339  
   340  	// Delete the final remaining ACL role. This should succeed and modify the
   341  	// table index.
   342  	err = testState.DeleteACLRolesByID(structs.MsgTypeTestSetup, 30, []string{mockedACLRoles[1].ID})
   343  	require.NoError(t, err)
   344  
   345  	tableIndex, err = testState.Index(TableACLRoles)
   346  	require.NoError(t, err)
   347  	must.Eq(t, 30, tableIndex)
   348  
   349  	// List the ACL roles and ensure we have zero entries.
   350  	iter, err = testState.GetACLRoles(ws)
   351  	require.NoError(t, err)
   352  
   353  	aclRoles = []*structs.ACLRole{}
   354  
   355  	for raw := iter.Next(); raw != nil; raw = iter.Next() {
   356  		aclRoles = append(aclRoles, raw.(*structs.ACLRole))
   357  	}
   358  	require.Len(t, aclRoles, 0, "incorrect number of ACL roles found")
   359  }
   360  
   361  func TestStateStore_GetACLRoles(t *testing.T) {
   362  	ci.Parallel(t)
   363  	testState := testStateStore(t)
   364  
   365  	// Create the policies our ACL roles wants to link to.
   366  	policy1 := mock.ACLPolicy()
   367  	policy1.Name = "mocked-test-policy-1"
   368  	policy2 := mock.ACLPolicy()
   369  	policy2.Name = "mocked-test-policy-2"
   370  
   371  	require.NoError(t, testState.UpsertACLPolicies(
   372  		structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   373  
   374  	// Generate a some mocked ACL roles for testing and upsert these straight
   375  	// into state.
   376  	mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   377  	require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
   378  
   379  	// List the ACL roles and ensure they are exactly as we expect.
   380  	ws := memdb.NewWatchSet()
   381  	iter, err := testState.GetACLRoles(ws)
   382  	require.NoError(t, err)
   383  
   384  	var aclRoles []*structs.ACLRole
   385  
   386  	for raw := iter.Next(); raw != nil; raw = iter.Next() {
   387  		aclRoles = append(aclRoles, raw.(*structs.ACLRole))
   388  	}
   389  
   390  	expected := mockedACLRoles
   391  	for i := range expected {
   392  		expected[i].CreateIndex = 10
   393  		expected[i].ModifyIndex = 10
   394  	}
   395  
   396  	require.ElementsMatch(t, aclRoles, expected)
   397  }
   398  
   399  func TestStateStore_GetACLRoleByID(t *testing.T) {
   400  	ci.Parallel(t)
   401  	testState := testStateStore(t)
   402  
   403  	// Create the policies our ACL roles wants to link to.
   404  	policy1 := mock.ACLPolicy()
   405  	policy1.Name = "mocked-test-policy-1"
   406  	policy2 := mock.ACLPolicy()
   407  	policy2.Name = "mocked-test-policy-2"
   408  
   409  	require.NoError(t, testState.UpsertACLPolicies(
   410  		structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   411  
   412  	// Generate a some mocked ACL roles for testing and upsert these straight
   413  	// into state.
   414  	mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   415  	require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
   416  
   417  	ws := memdb.NewWatchSet()
   418  
   419  	// Try reading an ACL role that does not exist.
   420  	aclRole, err := testState.GetACLRoleByID(ws, "not-a-role")
   421  	require.NoError(t, err)
   422  	require.Nil(t, aclRole)
   423  
   424  	// Read the two ACL roles that we should find.
   425  	aclRole, err = testState.GetACLRoleByID(ws, mockedACLRoles[0].ID)
   426  	require.NoError(t, err)
   427  	require.Equal(t, mockedACLRoles[0], aclRole)
   428  
   429  	aclRole, err = testState.GetACLRoleByID(ws, mockedACLRoles[1].ID)
   430  	require.NoError(t, err)
   431  	require.Equal(t, mockedACLRoles[1], aclRole)
   432  }
   433  
   434  func TestStateStore_GetACLRoleByName(t *testing.T) {
   435  	ci.Parallel(t)
   436  	testState := testStateStore(t)
   437  
   438  	// Create the policies our ACL roles wants to link to.
   439  	policy1 := mock.ACLPolicy()
   440  	policy1.Name = "mocked-test-policy-1"
   441  	policy2 := mock.ACLPolicy()
   442  	policy2.Name = "mocked-test-policy-2"
   443  
   444  	require.NoError(t, testState.UpsertACLPolicies(
   445  		structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   446  
   447  	// Generate a some mocked ACL roles for testing and upsert these straight
   448  	// into state.
   449  	mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   450  	require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
   451  
   452  	ws := memdb.NewWatchSet()
   453  
   454  	// Try reading an ACL role that does not exist.
   455  	aclRole, err := testState.GetACLRoleByName(ws, "not-a-role")
   456  	require.NoError(t, err)
   457  	require.Nil(t, aclRole)
   458  
   459  	// Read the two ACL roles that we should find.
   460  	aclRole, err = testState.GetACLRoleByName(ws, mockedACLRoles[0].Name)
   461  	require.NoError(t, err)
   462  	require.Equal(t, mockedACLRoles[0], aclRole)
   463  
   464  	aclRole, err = testState.GetACLRoleByName(ws, mockedACLRoles[1].Name)
   465  	require.NoError(t, err)
   466  	require.Equal(t, mockedACLRoles[1], aclRole)
   467  }
   468  
   469  func TestStateStore_GetACLRoleByIDPrefix(t *testing.T) {
   470  	ci.Parallel(t)
   471  	testState := testStateStore(t)
   472  
   473  	// Create the policies our ACL roles wants to link to.
   474  	policy1 := mock.ACLPolicy()
   475  	policy1.Name = "mocked-test-policy-1"
   476  	policy2 := mock.ACLPolicy()
   477  	policy2.Name = "mocked-test-policy-2"
   478  
   479  	require.NoError(t, testState.UpsertACLPolicies(
   480  		structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   481  
   482  	// Generate a some mocked ACL roles for testing and upsert these straight
   483  	// into state. Set the ID to something with a prefix we know so it is easy
   484  	// to test.
   485  	mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   486  	mockedACLRoles[0].ID = "test-prefix-" + uuid.Generate()
   487  	mockedACLRoles[1].ID = "test-prefix-" + uuid.Generate()
   488  	require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, mockedACLRoles, false))
   489  
   490  	ws := memdb.NewWatchSet()
   491  
   492  	// Try using a prefix that doesn't match any entries.
   493  	iter, err := testState.GetACLRoleByIDPrefix(ws, "nope")
   494  	require.NoError(t, err)
   495  
   496  	var aclRoles []*structs.ACLRole
   497  	for raw := iter.Next(); raw != nil; raw = iter.Next() {
   498  		aclRoles = append(aclRoles, raw.(*structs.ACLRole))
   499  	}
   500  	require.Len(t, aclRoles, 0)
   501  
   502  	// Use a prefix which should match two entries in state.
   503  	iter, err = testState.GetACLRoleByIDPrefix(ws, "test-prefix-")
   504  	require.NoError(t, err)
   505  
   506  	aclRoles = []*structs.ACLRole{}
   507  	for raw := iter.Next(); raw != nil; raw = iter.Next() {
   508  		aclRoles = append(aclRoles, raw.(*structs.ACLRole))
   509  	}
   510  	require.Len(t, aclRoles, 2)
   511  }
   512  
   513  func TestStateStore_fixTokenRoleLinks(t *testing.T) {
   514  	ci.Parallel(t)
   515  
   516  	testCases := []struct {
   517  		name   string
   518  		testFn func()
   519  	}{
   520  		{
   521  			name: "no fix needed",
   522  			testFn: func() {
   523  				testState := testStateStore(t)
   524  
   525  				// Create the policies our ACL roles wants to link to.
   526  				policy1 := mock.ACLPolicy()
   527  				policy1.Name = "mocked-test-policy-1"
   528  				policy2 := mock.ACLPolicy()
   529  				policy2.Name = "mocked-test-policy-2"
   530  
   531  				require.NoError(t, testState.UpsertACLPolicies(
   532  					structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   533  
   534  				// Generate a some mocked ACL roles for testing and upsert these straight
   535  				// into state.
   536  				mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   537  				require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false))
   538  
   539  				// Create an ACL token linking to the ACL role.
   540  				token1 := mock.ACLToken()
   541  				token1.Roles = []*structs.ACLTokenRoleLink{{ID: mockedACLRoles[0].ID}}
   542  				require.NoError(t, testState.UpsertACLTokens(
   543  					structs.MsgTypeTestSetup, 20, []*structs.ACLToken{token1}))
   544  
   545  				// Perform the fix and check the returned token contains the
   546  				// correct roles.
   547  				readTxn := testState.db.ReadTxn()
   548  				outputToken, err := testState.fixTokenRoleLinks(readTxn, token1)
   549  				require.NoError(t, err)
   550  				require.Equal(t, outputToken.Roles, []*structs.ACLTokenRoleLink{{
   551  					Name: mockedACLRoles[0].Name, ID: mockedACLRoles[0].ID,
   552  				}})
   553  			},
   554  		},
   555  		{
   556  			name: "acl role from link deleted",
   557  			testFn: func() {
   558  				testState := testStateStore(t)
   559  
   560  				// Create the policies our ACL roles wants to link to.
   561  				policy1 := mock.ACLPolicy()
   562  				policy1.Name = "mocked-test-policy-1"
   563  				policy2 := mock.ACLPolicy()
   564  				policy2.Name = "mocked-test-policy-2"
   565  
   566  				require.NoError(t, testState.UpsertACLPolicies(
   567  					structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   568  
   569  				// Generate a some mocked ACL roles for testing and upsert these straight
   570  				// into state.
   571  				mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   572  				require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false))
   573  
   574  				// Create an ACL token linking to the ACL roles.
   575  				token1 := mock.ACLToken()
   576  				token1.Roles = []*structs.ACLTokenRoleLink{{ID: mockedACLRoles[0].ID}, {ID: mockedACLRoles[1].ID}}
   577  				require.NoError(t, testState.UpsertACLTokens(
   578  					structs.MsgTypeTestSetup, 30, []*structs.ACLToken{token1}))
   579  
   580  				// Now delete one of the ACL roles from state.
   581  				require.NoError(t, testState.DeleteACLRolesByID(
   582  					structs.MsgTypeTestSetup, 40, []string{mockedACLRoles[0].ID}))
   583  
   584  				// Perform the fix and check the returned token contains the
   585  				// correct roles.
   586  				readTxn := testState.db.ReadTxn()
   587  				outputToken, err := testState.fixTokenRoleLinks(readTxn, token1)
   588  				require.NoError(t, err)
   589  				require.Len(t, outputToken.Roles, 1)
   590  				require.Equal(t, outputToken.Roles, []*structs.ACLTokenRoleLink{{
   591  					Name: mockedACLRoles[1].Name, ID: mockedACLRoles[1].ID,
   592  				}})
   593  			},
   594  		},
   595  		{
   596  			name: "acl role from link name changed",
   597  			testFn: func() {
   598  				testState := testStateStore(t)
   599  
   600  				// Create the policies our ACL roles wants to link to.
   601  				policy1 := mock.ACLPolicy()
   602  				policy1.Name = "mocked-test-policy-1"
   603  				policy2 := mock.ACLPolicy()
   604  				policy2.Name = "mocked-test-policy-2"
   605  
   606  				require.NoError(t, testState.UpsertACLPolicies(
   607  					structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}))
   608  
   609  				// Generate a some mocked ACL roles for testing and upsert these straight
   610  				// into state.
   611  				mockedACLRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}
   612  				require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 20, mockedACLRoles, false))
   613  
   614  				// Create an ACL token linking to the ACL roles.
   615  				token1 := mock.ACLToken()
   616  				token1.Roles = []*structs.ACLTokenRoleLink{{ID: mockedACLRoles[0].ID}, {ID: mockedACLRoles[1].ID}}
   617  				require.NoError(t, testState.UpsertACLTokens(
   618  					structs.MsgTypeTestSetup, 30, []*structs.ACLToken{token1}))
   619  
   620  				// Now change the name of one of the ACL roles.
   621  				mockedACLRoles[0].Name = "badger-badger-badger"
   622  				require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 40, mockedACLRoles, false))
   623  
   624  				// Perform the fix and check the returned token contains the
   625  				// correct roles.
   626  				readTxn := testState.db.ReadTxn()
   627  				outputToken, err := testState.fixTokenRoleLinks(readTxn, token1)
   628  				require.NoError(t, err)
   629  				require.Len(t, outputToken.Roles, 2)
   630  				require.ElementsMatch(t, outputToken.Roles, []*structs.ACLTokenRoleLink{
   631  					{Name: mockedACLRoles[0].Name, ID: mockedACLRoles[0].ID},
   632  					{Name: mockedACLRoles[1].Name, ID: mockedACLRoles[1].ID},
   633  				})
   634  			},
   635  		},
   636  	}
   637  
   638  	for _, tc := range testCases {
   639  		t.Run(tc.name, func(t *testing.T) {
   640  			tc.testFn()
   641  		})
   642  	}
   643  }