package nomad import ( "testing" "time" "github.com/hashicorp/nomad/acl" "github.com/hashicorp/nomad/ci" "github.com/hashicorp/nomad/helper/pointer" "github.com/hashicorp/nomad/helper/uuid" "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/testutil" "github.com/stretchr/testify/require" ) func TestResolveACLToken(t *testing.T) { ci.Parallel(t) testServer, _, testServerCleanup := TestACLServer(t, nil) defer testServerCleanup() testutil.WaitForLeader(t, testServer.RPC) testCases := []struct { name string testFn func(testServer *Server) }{ { name: "leader token", testFn: func(testServer *Server) { // Check the leader ACL token is correctly set. leaderACL := testServer.getLeaderAcl() require.NotEmpty(t, leaderACL) // Resolve the token and ensure it's a management token. aclResp, err := testServer.ResolveToken(leaderACL) require.NoError(t, err) require.NotNil(t, aclResp) require.True(t, aclResp.IsManagement()) }, }, { name: "anonymous token", testFn: func(testServer *Server) { // Call the function with an empty input secret ID which is // classed as representing anonymous access in clusters with // ACLs enabled. aclResp, err := testServer.ResolveToken("") require.NoError(t, err) require.NotNil(t, aclResp) require.False(t, aclResp.IsManagement()) }, }, { name: "token not found", testFn: func(testServer *Server) { // Call the function with randomly generated secret ID which // does not exist within state. aclResp, err := testServer.ResolveToken(uuid.Generate()) require.Equal(t, structs.ErrTokenNotFound, err) require.Nil(t, aclResp) }, }, { name: "token expired", testFn: func(testServer *Server) { // Create a mock token with an expiration time long in the // past, and upsert. token := mock.ACLToken() token.ExpirationTime = pointer.Of(time.Date( 1970, time.January, 1, 0, 0, 0, 0, time.UTC)) err := testServer.State().UpsertACLTokens( structs.MsgTypeTestSetup, 10, []*structs.ACLToken{token}) require.NoError(t, err) // Perform the function call which should result in finding the // token has expired. aclResp, err := testServer.ResolveToken(uuid.Generate()) require.Equal(t, structs.ErrTokenNotFound, err) require.Nil(t, aclResp) }, }, { name: "management token", testFn: func(testServer *Server) { // Generate a management token and upsert this. managementToken := mock.ACLToken() managementToken.Type = structs.ACLManagementToken managementToken.Policies = nil err := testServer.State().UpsertACLTokens( structs.MsgTypeTestSetup, 10, []*structs.ACLToken{managementToken}) require.NoError(t, err) // Resolve the token and check that we received a management // ACL. aclResp, err := testServer.ResolveToken(managementToken.SecretID) require.Nil(t, err) require.NotNil(t, aclResp) require.True(t, aclResp.IsManagement()) require.Equal(t, acl.ManagementACL, aclResp) }, }, { name: "client token", testFn: func(testServer *Server) { // Generate a client token with associated policies and upsert // these. policy1 := mock.ACLPolicy() policy2 := mock.ACLPolicy() err := testServer.State().UpsertACLPolicies( structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2}) clientToken := mock.ACLToken() clientToken.Policies = []string{policy1.Name, policy2.Name} err = testServer.State().UpsertACLTokens( structs.MsgTypeTestSetup, 20, []*structs.ACLToken{clientToken}) require.NoError(t, err) // Resolve the token and check that we received a client // ACL with appropriate permissions. aclResp, err := testServer.ResolveToken(clientToken.SecretID) require.Nil(t, err) require.NotNil(t, aclResp) require.False(t, aclResp.IsManagement()) allowed := aclResp.AllowNamespaceOperation("default", acl.NamespaceCapabilityListJobs) require.True(t, allowed) allowed = aclResp.AllowNamespaceOperation("other", acl.NamespaceCapabilityListJobs) require.False(t, allowed) // Resolve the same token again and ensure we get the same // result. aclResp2, err := testServer.ResolveToken(clientToken.SecretID) require.Nil(t, err) require.NotNil(t, aclResp2) require.Equal(t, aclResp, aclResp2) // Bust the cache by upserting the policy err = testServer.State().UpsertACLPolicies( structs.MsgTypeTestSetup, 30, []*structs.ACLPolicy{policy1}) require.Nil(t, err) // Resolve the same token again, should get different value aclResp3, err := testServer.ResolveToken(clientToken.SecretID) require.Nil(t, err) require.NotNil(t, aclResp3) require.NotEqual(t, aclResp2, aclResp3) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tc.testFn(testServer) }) } } func TestResolveSecretToken(t *testing.T) { ci.Parallel(t) testServer, _, testServerCleanup := TestACLServer(t, nil) defer testServerCleanup() testutil.WaitForLeader(t, testServer.RPC) testCases := []struct { name string testFn func(testServer *Server) }{ { name: "valid token", testFn: func(testServer *Server) { // Generate and upsert a token. token := mock.ACLToken() err := testServer.State().UpsertACLTokens( structs.MsgTypeTestSetup, 10, []*structs.ACLToken{token}) require.NoError(t, err) // Attempt to look up the token and perform checks. tokenResp, err := testServer.ResolveSecretToken(token.SecretID) require.NoError(t, err) require.NotNil(t, tokenResp) require.Equal(t, token, tokenResp) }, }, { name: "anonymous token", testFn: func(testServer *Server) { // Call the function with an empty input secret ID which is // classed as representing anonymous access in clusters with // ACLs enabled. tokenResp, err := testServer.ResolveSecretToken("") require.NoError(t, err) require.NotNil(t, tokenResp) require.Equal(t, structs.AnonymousACLToken, tokenResp) }, }, { name: "token not found", testFn: func(testServer *Server) { // Call the function with randomly generated secret ID which // does not exist within state. tokenResp, err := testServer.ResolveSecretToken(uuid.Generate()) require.Equal(t, structs.ErrTokenNotFound, err) require.Nil(t, tokenResp) }, }, { name: "token expired", testFn: func(testServer *Server) { // Create a mock token with an expiration time long in the // past, and upsert. token := mock.ACLToken() token.ExpirationTime = pointer.Of(time.Date( 1970, time.January, 1, 0, 0, 0, 0, time.UTC)) err := testServer.State().UpsertACLTokens( structs.MsgTypeTestSetup, 10, []*structs.ACLToken{token}) require.NoError(t, err) // Perform the function call which should result in finding the // token has expired. tokenResp, err := testServer.ResolveSecretToken(uuid.Generate()) require.Equal(t, structs.ErrTokenNotFound, err) require.Nil(t, tokenResp) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tc.testFn(testServer) }) } }