consul/agent/proxycfg/config_snapshot_glue_test.go

316 lines
11 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package proxycfg
import (
"strings"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs"
)
func TestConfigSnapshot_AllowEmptyClusters(t *testing.T) {
type testCase struct {
description string
cfgSnapshot *ConfigSnapshot
expectedResult bool
}
testsCases := []testCase{
{
description: "Mesh proxies are not allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindConnectProxy},
expectedResult: false,
},
{
description: "Ingress gateways are allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindIngressGateway},
expectedResult: true,
},
{
description: "Terminating gateways are allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindTerminatingGateway},
expectedResult: true,
},
{
description: "API Gateways are allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindAPIGateway},
expectedResult: true,
},
{
description: "Mesh Gateways are allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindMeshGateway},
expectedResult: true,
},
}
for _, tc := range testsCases {
t.Run(tc.description, func(t *testing.T) {
require.Equal(t, tc.expectedResult, tc.cfgSnapshot.AllowEmptyClusters())
})
}
}
func TestConfigSnapshot_AllowEmptyListeners(t *testing.T) {
type testCase struct {
description string
cfgSnapshot *ConfigSnapshot
expectedResult bool
}
testsCases := []testCase{
{
description: "Mesh proxies are not allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindConnectProxy},
expectedResult: false,
},
{
description: "Ingress gateways are allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindIngressGateway},
expectedResult: true,
},
{
description: "Terminating gateways are not allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindTerminatingGateway},
expectedResult: false,
},
{
description: "API Gateways are allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindAPIGateway},
expectedResult: true,
},
{
description: "Mesh Gateways are not allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindMeshGateway},
expectedResult: false,
},
}
for _, tc := range testsCases {
t.Run(tc.description, func(t *testing.T) {
require.Equal(t, tc.expectedResult, tc.cfgSnapshot.AllowEmptyListeners())
})
}
}
func TestConfigSnapshot_AllowEmptyRoutes(t *testing.T) {
type testCase struct {
description string
cfgSnapshot *ConfigSnapshot
expectedResult bool
}
testsCases := []testCase{
{
description: "Mesh proxies are not allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindConnectProxy},
expectedResult: false,
},
{
description: "Ingress gateways are allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindIngressGateway},
expectedResult: true,
},
{
description: "Terminating gateways are not allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindTerminatingGateway},
expectedResult: false,
},
{
description: "API Gateways are allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindAPIGateway},
expectedResult: true,
},
{
description: "Mesh Gateways are not allowed",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindMeshGateway},
expectedResult: false,
},
}
for _, tc := range testsCases {
t.Run(tc.description, func(t *testing.T) {
require.Equal(t, tc.expectedResult, tc.cfgSnapshot.AllowEmptyRoutes())
})
}
}
func TestConfigSnapshot_LoggerName(t *testing.T) {
type testCase struct {
description string
cfgSnapshot *ConfigSnapshot
expectedResult string
}
testsCases := []testCase{
{
description: "Mesh proxies have a logger named ''",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindConnectProxy},
expectedResult: "",
},
{
description: "Ingress gateways have a logger named 'ingress_gateway'",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindIngressGateway},
expectedResult: "ingress_gateway",
},
{
description: "Terminating gateways have a logger named 'terminating_gateway'",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindTerminatingGateway},
expectedResult: "terminating_gateway",
},
{
description: "API Gateways have a logger named ''",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindAPIGateway},
expectedResult: "",
},
{
description: "Mesh Gateways have a logger named 'mesh_gateway'",
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindMeshGateway},
expectedResult: "mesh_gateway",
},
}
for _, tc := range testsCases {
t.Run(tc.description, func(t *testing.T) {
require.Equal(t, tc.expectedResult, tc.cfgSnapshot.LoggerName())
})
}
}
func TestConfigSnapshot_Authorize(t *testing.T) {
type testCase struct {
description string
cfgSnapshot *ConfigSnapshot
configureAuthorizer func(authorizer *acl.MockAuthorizer)
expectedErrorMessage string
}
testsCases := []testCase{
{
description: "ConnectProxy - if service write is allowed for the DestinationService then allow.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindConnectProxy,
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "DestinationServiceName",
},
},
expectedErrorMessage: "",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "DestinationServiceName", mock.Anything).Return(acl.Allow)
},
},
{
description: "ConnectProxy - if service write is not allowed for the DestinationService then deny.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindConnectProxy,
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "DestinationServiceName",
},
},
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"DestinationServiceName\"",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "DestinationServiceName", mock.Anything).Return(acl.Deny)
},
},
{
description: "Mesh Gateway - if service write is allowed for the Service then allow.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindMeshGateway,
Service: "Service",
},
expectedErrorMessage: "",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Allow)
},
},
{
description: "Mesh Gateway - if service write is not allowed for the Service then deny.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindMeshGateway,
Service: "Service",
},
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
},
},
{
description: "Terminating Gateway - if service write is allowed for the Service then allow.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindTerminatingGateway,
Service: "Service",
},
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
},
},
{
description: "Terminating Gateway - if service write is not allowed for the Service then deny.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindTerminatingGateway,
Service: "Service",
},
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
},
},
{
description: "Ingress Gateway - if service write is allowed for the Service then allow.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindIngressGateway,
Service: "Service",
},
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
},
},
{
description: "Ingress Gateway - if service write is not allowed for the Service then deny.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindIngressGateway,
Service: "Service",
},
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
},
},
{
description: "API Gateway - if service write is allowed for the Service then allow.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindAPIGateway,
Service: "Service",
},
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
},
},
{
description: "API Gateway - if service write is not allowed for the Service then deny.",
cfgSnapshot: &ConfigSnapshot{
Kind: structs.ServiceKindAPIGateway,
Service: "Service",
},
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
configureAuthorizer: func(authz *acl.MockAuthorizer) {
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
},
},
}
for _, tc := range testsCases {
t.Run(tc.description, func(t *testing.T) {
authz := &acl.MockAuthorizer{}
authz.On("ToAllow").Return(acl.AllowAuthorizer{Authorizer: authz})
tc.configureAuthorizer(authz)
err := tc.cfgSnapshot.Authorize(authz)
errMsg := ""
if err != nil {
errMsg = err.Error()
}
// using contains because Enterprise tests append the parition and namespace
// information to the message.
require.True(t, strings.Contains(errMsg, tc.expectedErrorMessage))
})
}
}