open-nomad/e2e/acl/acl_role_test.go

157 lines
5.8 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package acl
import (
"fmt"
"testing"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/e2e/e2eutil"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/stretchr/testify/require"
)
// testACLRole tests basic functionality of ACL roles when used for
// authorization. It also performs some basic token and policy tests due to the
// coupling between the ACL objects.
func testACLRole(t *testing.T) {
nomadClient := e2eutil.NomadClient(t)
// Create and defer the Cleanup process. This is used to remove all
// resources created by this test and covers situations where the test
// fails or during normal running.
cleanUpProcess := NewCleanup()
defer cleanUpProcess.Run(t, nomadClient)
// An ACL role must reference an ACL policy that is stored in state. Ensure
// this behaviour by attempting to create a role that links to a policy
// that does not exist.
invalidRole := api.ACLRole{
Name: "e2e-acl-" + uuid.Short(),
Description: "E2E ACL Role Testing",
Policies: []*api.ACLRolePolicyLink{{Name: "404-not-found"}},
}
aclRoleCreateResp, _, err := nomadClient.ACLRoles().Create(&invalidRole, nil)
require.ErrorContains(t, err, "cannot find policy 404-not-found")
require.Nil(t, aclRoleCreateResp)
// Create a custom namespace to test along with the default.
ns := api.Namespace{
Name: "e2e-acl-" + uuid.Short(),
Description: "E2E ACL Role Testing",
}
_, err = nomadClient.Namespaces().Register(&ns, nil)
require.NoError(t, err)
cleanUpProcess.Add(ns.Name, NamespaceTestResourceType)
// Create an ACL policy which will be used to link from the role. This
// policy grants read access to our custom namespace.
customNamespacePolicy := api.ACLPolicy{
Name: "e2e-acl-" + uuid.Short(),
Description: "E2E ACL Role Testing",
Rules: fmt.Sprintf(`namespace %q {policy = "read"}`, ns.Name),
}
_, err = nomadClient.ACLPolicies().Upsert(&customNamespacePolicy, nil)
require.NoError(t, err)
cleanUpProcess.Add(customNamespacePolicy.Name, ACLPolicyTestResourceType)
// Create a valid role with a link to the previously created policy.
validRole := api.ACLRole{
Name: "e2e-acl-" + uuid.Short(),
Description: "E2E ACL Role Testing",
Policies: []*api.ACLRolePolicyLink{{Name: customNamespacePolicy.Name}},
}
aclRoleCreateResp, _, err = nomadClient.ACLRoles().Create(&validRole, nil)
require.NoError(t, err)
require.NotNil(t, aclRoleCreateResp)
require.NotEmpty(t, aclRoleCreateResp.ID)
require.Equal(t, validRole.Name, aclRoleCreateResp.Name)
cleanUpProcess.Add(aclRoleCreateResp.ID, ACLRoleTestResourceType)
// Perform a role listing and check we have the expected entries.
aclRoleListResp, _, err := nomadClient.ACLRoles().List(nil)
require.NoError(t, err)
require.Len(t, aclRoleListResp, 1)
require.Equal(t, aclRoleCreateResp.ID, aclRoleListResp[0].ID)
// Create our ACL token which is linked to the created ACL role.
token := api.ACLToken{
Name: "e2e-acl-" + uuid.Short(),
Type: "client",
Roles: []*api.ACLTokenRoleLink{{ID: aclRoleCreateResp.ID}},
}
aclTokenCreateResp, _, err := nomadClient.ACLTokens().Create(&token, nil)
require.NoError(t, err)
require.NotNil(t, aclTokenCreateResp)
cleanUpProcess.Add(aclTokenCreateResp.AccessorID, ACLTokenTestResourceType)
// Attempt two job listings against the two available namespaces. The token
// only has access to the custom namespace, so the default should return an
// error.
customNSQueryMeta := api.QueryOptions{Namespace: ns.Name, AuthToken: aclTokenCreateResp.SecretID}
defaultNSQueryMeta := api.QueryOptions{Namespace: "default", AuthToken: aclTokenCreateResp.SecretID}
jobListResp, _, err := nomadClient.Jobs().List(&customNSQueryMeta)
require.NoError(t, err)
require.Empty(t, jobListResp)
jobListResp, _, err = nomadClient.Jobs().List(&defaultNSQueryMeta)
require.ErrorContains(t, err, "Permission denied")
// Create an ACL policy which grants read access to the default namespace.
defaultNamespacePolicy := api.ACLPolicy{
Name: "e2e-acl-" + uuid.Short(),
Description: "E2E ACL Role Testing",
Rules: `namespace "default" {policy = "read"}`,
}
_, err = nomadClient.ACLPolicies().Upsert(&defaultNamespacePolicy, nil)
require.NoError(t, err)
cleanUpProcess.Add(defaultNamespacePolicy.Name, ACLPolicyTestResourceType)
// Update the ACL role to include the new ACL policy that allows read
// access to the default namespace.
aclRoleCreateResp.Policies = append(aclRoleCreateResp.Policies, &api.ACLRolePolicyLink{
Name: defaultNamespacePolicy.Name,
})
aclRoleUpdateResp, _, err := nomadClient.ACLRoles().Update(aclRoleCreateResp, nil)
require.NoError(t, err)
require.Equal(t, aclRoleCreateResp.ID, aclRoleUpdateResp.ID)
require.Len(t, aclRoleUpdateResp.Policies, 2)
// Try listing the jobs in the default namespace again to ensure we now
// have permission due to the updated role.
jobListResp, _, err = nomadClient.Jobs().List(&defaultNSQueryMeta)
require.NoError(t, err)
require.Empty(t, jobListResp)
// Delete a policy from under the role.
_, err = nomadClient.ACLPolicies().Delete(defaultNamespacePolicy.Name, nil)
require.NoError(t, err)
cleanUpProcess.Remove(defaultNamespacePolicy.Name, ACLPolicyTestResourceType)
// The permission to list the job in the default namespace should now be
// revoked.
jobListResp, _, err = nomadClient.Jobs().List(&defaultNSQueryMeta)
require.ErrorContains(t, err, "Permission denied")
// Delete the ACL role.
_, err = nomadClient.ACLRoles().Delete(aclRoleUpdateResp.ID, nil)
require.NoError(t, err)
cleanUpProcess.Remove(aclRoleUpdateResp.ID, ACLRoleTestResourceType)
// We should now not be able to list jobs in the custom namespace either as
// the token does not have any permissions.
jobListResp, _, err = nomadClient.Jobs().List(&customNSQueryMeta)
require.ErrorContains(t, err, "Permission denied")
}