open-nomad/nomad/structs/acl_test.go

765 lines
20 KiB
Go

package structs
import (
"fmt"
"testing"
"time"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/helper/pointer"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/stretchr/testify/require"
)
func TestACLToken_Canonicalize(t *testing.T) {
testCases := []struct {
name string
testFn func()
}{
{
name: "token with accessor",
testFn: func() {
mockToken := &ACLToken{
AccessorID: uuid.Generate(),
SecretID: uuid.Generate(),
Name: "my cool token " + uuid.Generate(),
Type: "client",
Policies: []string{"foo", "bar"},
Roles: []*ACLTokenRoleLink{},
Global: false,
CreateTime: time.Now().UTC(),
CreateIndex: 10,
ModifyIndex: 20,
}
mockToken.SetHash()
copiedMockToken := mockToken.Copy()
mockToken.Canonicalize()
require.Equal(t, copiedMockToken, mockToken)
},
},
{
name: "token without accessor",
testFn: func() {
mockToken := &ACLToken{
Name: "my cool token " + uuid.Generate(),
Type: "client",
Policies: []string{"foo", "bar"},
Global: false,
}
mockToken.Canonicalize()
require.NotEmpty(t, mockToken.AccessorID)
require.NotEmpty(t, mockToken.SecretID)
require.NotEmpty(t, mockToken.CreateTime)
},
},
{
name: "token with ttl without accessor",
testFn: func() {
mockToken := &ACLToken{
Name: "my cool token " + uuid.Generate(),
Type: "client",
Policies: []string{"foo", "bar"},
Global: false,
ExpirationTTL: 10 * time.Hour,
}
mockToken.Canonicalize()
require.NotEmpty(t, mockToken.AccessorID)
require.NotEmpty(t, mockToken.SecretID)
require.NotEmpty(t, mockToken.CreateTime)
require.NotEmpty(t, mockToken.ExpirationTime)
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.testFn()
})
}
}
func TestACLTokenValidate(t *testing.T) {
ci.Parallel(t)
testCases := []struct {
name string
inputACLToken *ACLToken
inputExistingACLToken *ACLToken
expectedErrorContains string
}{
{
name: "missing type",
inputACLToken: &ACLToken{},
inputExistingACLToken: nil,
expectedErrorContains: "client or management",
},
{
name: "missing policies or roles",
inputACLToken: &ACLToken{
Type: ACLClientToken,
},
inputExistingACLToken: nil,
expectedErrorContains: "missing policies or roles",
},
{
name: "invalid policies",
inputACLToken: &ACLToken{
Type: ACLManagementToken,
Policies: []string{"foo"},
},
inputExistingACLToken: nil,
expectedErrorContains: "associated with policies or roles",
},
{
name: "invalid roles",
inputACLToken: &ACLToken{
Type: ACLManagementToken,
Roles: []*ACLTokenRoleLink{{Name: "foo"}},
},
inputExistingACLToken: nil,
expectedErrorContains: "associated with policies or roles",
},
{
name: "name too long",
inputACLToken: &ACLToken{
Type: ACLManagementToken,
Name: uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate() +
uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate(),
},
inputExistingACLToken: nil,
expectedErrorContains: "name too long",
},
{
name: "negative TTL",
inputACLToken: &ACLToken{
Type: ACLManagementToken,
Name: "foo",
ExpirationTTL: -1 * time.Hour,
},
inputExistingACLToken: nil,
expectedErrorContains: "should not be negative",
},
{
name: "TTL too small",
inputACLToken: &ACLToken{
Type: ACLManagementToken,
Name: "foo",
CreateTime: time.Date(2022, time.July, 11, 16, 23, 0, 0, time.UTC),
ExpirationTime: pointer.Of(time.Date(2022, time.July, 11, 16, 23, 10, 0, time.UTC)),
},
inputExistingACLToken: nil,
expectedErrorContains: "expiration time cannot be less than",
},
{
name: "TTL too large",
inputACLToken: &ACLToken{
Type: ACLManagementToken,
Name: "foo",
CreateTime: time.Date(2022, time.July, 11, 16, 23, 0, 0, time.UTC),
ExpirationTime: pointer.Of(time.Date(2042, time.July, 11, 16, 23, 0, 0, time.UTC)),
},
inputExistingACLToken: nil,
expectedErrorContains: "expiration time cannot be more than",
},
{
name: "valid management",
inputACLToken: &ACLToken{
Type: ACLManagementToken,
Name: "foo",
},
inputExistingACLToken: nil,
expectedErrorContains: "",
},
{
name: "valid client",
inputACLToken: &ACLToken{
Type: ACLClientToken,
Name: "foo",
Policies: []string{"foo"},
},
inputExistingACLToken: nil,
expectedErrorContains: "",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actualOutputError := tc.inputACLToken.Validate(1*time.Minute, 24*time.Hour, tc.inputExistingACLToken)
if tc.expectedErrorContains != "" {
require.ErrorContains(t, actualOutputError, tc.expectedErrorContains)
} else {
require.NoError(t, actualOutputError)
}
})
}
}
func TestACLToken_HasExpirationTime(t *testing.T) {
testCases := []struct {
name string
inputACLToken *ACLToken
expectedOutput bool ``
}{
{
name: "nil acl token",
inputACLToken: nil,
expectedOutput: false,
},
{
name: "default empty value",
inputACLToken: &ACLToken{},
expectedOutput: false,
},
{
name: "expiration set to now",
inputACLToken: &ACLToken{
ExpirationTime: pointer.Of(time.Now().UTC()),
},
expectedOutput: true,
},
{
name: "expiration set to past",
inputACLToken: &ACLToken{
ExpirationTime: pointer.Of(time.Date(2022, time.February, 21, 19, 35, 0, 0, time.UTC)),
},
expectedOutput: true,
},
{
name: "expiration set to future",
inputACLToken: &ACLToken{
ExpirationTime: pointer.Of(time.Date(2087, time.April, 25, 12, 0, 0, 0, time.UTC)),
},
expectedOutput: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actualOutput := tc.inputACLToken.HasExpirationTime()
require.Equal(t, tc.expectedOutput, actualOutput)
})
}
}
func TestACLToken_IsExpired(t *testing.T) {
testCases := []struct {
name string
inputACLToken *ACLToken
inputTime time.Time
expectedOutput bool
}{
{
name: "token without expiry",
inputACLToken: &ACLToken{},
inputTime: time.Now().UTC(),
expectedOutput: false,
},
{
name: "empty input time",
inputACLToken: &ACLToken{},
inputTime: time.Time{},
expectedOutput: false,
},
{
name: "token not expired",
inputACLToken: &ACLToken{
ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)),
},
inputTime: time.Date(2022, time.May, 9, 10, 26, 0, 0, time.UTC),
expectedOutput: false,
},
{
name: "token expired",
inputACLToken: &ACLToken{
ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)),
},
inputTime: time.Date(2022, time.May, 9, 10, 28, 0, 0, time.UTC),
expectedOutput: true,
},
{
name: "empty input time",
inputACLToken: &ACLToken{
ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)),
},
inputTime: time.Time{},
expectedOutput: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actualOutput := tc.inputACLToken.IsExpired(tc.inputTime)
require.Equal(t, tc.expectedOutput, actualOutput)
})
}
}
func TestACLRole_SetHash(t *testing.T) {
testCases := []struct {
name string
inputACLRole *ACLRole
expectedOutput []byte
}{
{
name: "no hash set",
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{},
},
expectedOutput: []byte{
122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
},
},
{
name: "hash set with change",
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{
137, 147, 2, 29, 53, 94, 78, 13, 45, 51, 127, 193, 21, 248, 230, 126, 34,
106, 216, 73, 248, 219, 209, 146, 204, 107, 185, 2, 89, 255, 198, 5,
},
},
expectedOutput: []byte{
122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actualOutput := tc.inputACLRole.SetHash()
require.Equal(t, tc.expectedOutput, actualOutput)
require.Equal(t, tc.inputACLRole.Hash, actualOutput)
})
}
}
func TestACLRole_Validate(t *testing.T) {
testCases := []struct {
name string
inputACLRole *ACLRole
expectedError bool
expectedErrorContains string
}{
{
name: "role name too long",
inputACLRole: &ACLRole{
Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
expectedError: true,
expectedErrorContains: "invalid name",
},
{
name: "role name too short",
inputACLRole: &ACLRole{
Name: "",
},
expectedError: true,
expectedErrorContains: "invalid name",
},
{
name: "role name with invalid characters",
inputACLRole: &ACLRole{
Name: "--#$%$^%_%%_?>",
},
expectedError: true,
expectedErrorContains: "invalid name",
},
{
name: "description too long",
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
expectedError: true,
expectedErrorContains: "description longer than",
},
{
name: "no policies",
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "",
},
expectedError: true,
expectedErrorContains: "at least one policy should be specified",
},
{
name: "valid",
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "",
Policies: []*ACLRolePolicyLink{
{Name: "policy-1"},
},
},
expectedError: false,
expectedErrorContains: "",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actualOutput := tc.inputACLRole.Validate()
if tc.expectedError {
require.ErrorContains(t, actualOutput, tc.expectedErrorContains)
} else {
require.NoError(t, actualOutput)
}
})
}
}
func TestACLRole_Canonicalize(t *testing.T) {
testCases := []struct {
name string
inputACLRole *ACLRole
}{
{
name: "no ID set",
inputACLRole: &ACLRole{},
},
{
name: "id set",
inputACLRole: &ACLRole{ID: "some-random-uuid"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
existing := tc.inputACLRole.Copy()
tc.inputACLRole.Canonicalize()
if existing.ID == "" {
require.NotEmpty(t, tc.inputACLRole.ID)
} else {
require.Equal(t, existing.ID, tc.inputACLRole.ID)
}
})
}
}
func TestACLRole_Equals(t *testing.T) {
testCases := []struct {
name string
composedACLRole *ACLRole
inputACLRole *ACLRole
expectedOutput bool
}{
{
name: "equal with hash set",
composedACLRole: &ACLRole{
Name: "acl-role-",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{
122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
},
},
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{
122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
},
},
expectedOutput: true,
},
{
name: "equal without hash set",
composedACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{},
},
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{},
},
expectedOutput: true,
},
{
name: "both nil",
composedACLRole: nil,
inputACLRole: nil,
expectedOutput: true,
},
{
name: "not equal composed nil",
composedACLRole: nil,
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{
122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
},
},
expectedOutput: false,
},
{
name: "not equal input nil",
composedACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{
122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
},
},
inputACLRole: nil,
expectedOutput: false,
},
{
name: "not equal with hash set",
composedACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{
122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
},
},
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{
137, 147, 2, 29, 53, 94, 78, 13, 45, 51, 127, 193, 21, 248, 230, 126, 34,
106, 216, 73, 248, 219, 209, 146, 204, 107, 185, 2, 89, 255, 198, 5,
},
},
expectedOutput: false,
},
{
name: "not equal without hash set",
composedACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{},
},
inputACLRole: &ACLRole{
Name: "acl-role",
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{},
},
expectedOutput: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actualOutput := tc.composedACLRole.Equals(tc.inputACLRole)
require.Equal(t, tc.expectedOutput, actualOutput)
})
}
}
func TestACLRole_Copy(t *testing.T) {
testCases := []struct {
name string
inputACLRole *ACLRole
}{
{
name: "nil input",
inputACLRole: nil,
},
{
name: "general 1",
inputACLRole: &ACLRole{
Name: fmt.Sprintf("acl-role"),
Description: "mocked-test-acl-role",
Policies: []*ACLRolePolicyLink{
{Name: "mocked-test-policy-1"},
{Name: "mocked-test-policy-2"},
},
CreateIndex: 10,
ModifyIndex: 10,
Hash: []byte{
122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actualOutput := tc.inputACLRole.Copy()
require.Equal(t, tc.inputACLRole, actualOutput)
})
}
}
func TestACLRole_Stub(t *testing.T) {
testCases := []struct {
name string
inputACLRole *ACLRole
expectedOutput *ACLRoleListStub
}{
{
name: "partially hydrated",
inputACLRole: &ACLRole{
ID: "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
Name: "my-lovely-role",
Description: "",
Policies: []*ACLRolePolicyLink{
{Name: "my-lovely-policy"},
},
Hash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
CreateIndex: 24,
ModifyIndex: 24,
},
expectedOutput: &ACLRoleListStub{
ID: "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
Name: "my-lovely-role",
Description: "",
Policies: []*ACLRolePolicyLink{
{Name: "my-lovely-policy"},
},
Hash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
CreateIndex: 24,
ModifyIndex: 24,
},
},
{
name: "hully hydrated",
inputACLRole: &ACLRole{
ID: "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
Name: "my-lovely-role",
Description: "this-is-my-lovely-role",
Policies: []*ACLRolePolicyLink{
{Name: "my-lovely-policy"},
},
Hash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
CreateIndex: 24,
ModifyIndex: 24,
},
expectedOutput: &ACLRoleListStub{
ID: "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
Name: "my-lovely-role",
Description: "this-is-my-lovely-role",
Policies: []*ACLRolePolicyLink{
{Name: "my-lovely-policy"},
},
Hash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
CreateIndex: 24,
ModifyIndex: 24,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actualOutput := tc.inputACLRole.Stub()
require.Equal(t, tc.expectedOutput, actualOutput)
})
}
}
func Test_ACLRolesUpsertRequest(t *testing.T) {
req := ACLRolesUpsertRequest{}
require.False(t, req.IsRead())
}
func Test_ACLRolesDeleteByIDRequest(t *testing.T) {
req := ACLRolesDeleteByIDRequest{}
require.False(t, req.IsRead())
}
func Test_ACLRolesListRequest(t *testing.T) {
req := ACLRolesListRequest{}
require.True(t, req.IsRead())
}
func Test_ACLRolesByIDRequest(t *testing.T) {
req := ACLRolesByIDRequest{}
require.True(t, req.IsRead())
}
func Test_ACLRoleByIDRequest(t *testing.T) {
req := ACLRoleByIDRequest{}
require.True(t, req.IsRead())
}
func Test_ACLRoleByNameRequest(t *testing.T) {
req := ACLRoleByNameRequest{}
require.True(t, req.IsRead())
}