github.com/hernad/nomad@v1.6.112/nomad/state/state_store_acl_test.go (about)

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