mirror of https://github.com/status-im/consul.git
Refactor xTP tests (#20597)
This commit is contained in:
parent
191561e44f
commit
7575b53737
|
@ -133,9 +133,7 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_NoReferencingTrafficPermis
|
|||
Write(suite.T(), suite.client)
|
||||
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
||||
require.NotNil(suite.T(), wi)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
require.NotNil(suite.T(), id)
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||
|
||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -151,12 +149,9 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
suite.Run("trafperms and namespace trafperms exist", func() {
|
||||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||
t := suite.T()
|
||||
// Workload identity ID == CTP ID
|
||||
wi1ID := &pbresource.ID{
|
||||
Name: "wi1",
|
||||
Type: pbauth.ComputedTrafficPermissionsType,
|
||||
Tenancy: tenancy,
|
||||
}
|
||||
// Create the workload identity to be referenced
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||
perm1 := &pbauth.Permission{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
|
@ -176,7 +171,7 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
WithTenancy(tenancy).
|
||||
Write(t, suite.client)
|
||||
|
||||
suite.requireTrafficPermissionsTracking(tp1, wi1ID)
|
||||
suite.requireTrafficPermissionsTracking(tp1, id)
|
||||
nsPerm1 := &pbauth.Permission{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
|
@ -194,10 +189,6 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
WithTenancy(tenancy).
|
||||
Write(t, suite.client)
|
||||
|
||||
// create the workload identity that they reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
|
||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -210,6 +201,10 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
suite.Run("only namespace trafperms exist", func() {
|
||||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||
t := suite.T()
|
||||
|
||||
// create the workload identity to reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||
// Write allow namespace trafperms
|
||||
nsPerm1 := &pbauth.Permission{
|
||||
Sources: []*pbauth.Source{
|
||||
|
@ -228,10 +223,6 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
WithTenancy(tenancy).
|
||||
Write(t, suite.client)
|
||||
|
||||
// create the workload identity that they reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
|
||||
require.NoError(t, suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id}))
|
||||
|
||||
// Ensure that the CTP was created
|
||||
|
@ -243,6 +234,10 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
suite.Run("only partition trafperms exist", func() {
|
||||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||
t := suite.T()
|
||||
// create the workload identity to reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||
|
||||
// Write allow partition trafperms
|
||||
ptPerm1 := &pbauth.Permission{
|
||||
Sources: []*pbauth.Source{
|
||||
|
@ -261,10 +256,6 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
WithTenancy(&pbresource.Tenancy{Partition: tenancy.GetPartition()}).
|
||||
Write(t, suite.client)
|
||||
|
||||
// create the workload identity that they reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
|
||||
require.NoError(t, suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id}))
|
||||
|
||||
// Ensure that the CTP was created
|
||||
|
@ -276,12 +267,11 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
suite.Run("trafperms and partition trafperms exist", func() {
|
||||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||
t := suite.T()
|
||||
// Workload identity ID == CTP ID
|
||||
wi1ID := &pbresource.ID{
|
||||
Name: "wi1",
|
||||
Type: pbauth.ComputedTrafficPermissionsType,
|
||||
Tenancy: tenancy,
|
||||
}
|
||||
|
||||
// create the workload identity to reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||
|
||||
perm1 := &pbauth.Permission{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
|
@ -301,7 +291,7 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
WithTenancy(tenancy).
|
||||
Write(t, suite.client)
|
||||
|
||||
suite.requireTrafficPermissionsTracking(tp1, wi1ID)
|
||||
suite.requireTrafficPermissionsTracking(tp1, id)
|
||||
ptPerm1 := &pbauth.Permission{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
|
@ -319,10 +309,6 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
WithTenancy(&pbresource.Tenancy{Partition: tenancy.GetPartition()}).
|
||||
Write(t, suite.client)
|
||||
|
||||
// create the workload identity that they reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
|
||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -335,12 +321,11 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
suite.Run("trafperms, partition and namespace trafperms exist", func() {
|
||||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||
t := suite.T()
|
||||
// Workload identity ID == CTP ID
|
||||
wi1ID := &pbresource.ID{
|
||||
Name: "wi1",
|
||||
Type: pbauth.ComputedTrafficPermissionsType,
|
||||
Tenancy: tenancy,
|
||||
}
|
||||
|
||||
// create the workload identity to reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||
|
||||
perm1 := &pbauth.Permission{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
|
@ -360,7 +345,7 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
WithTenancy(tenancy).
|
||||
Write(t, suite.client)
|
||||
|
||||
suite.requireTrafficPermissionsTracking(tp1, wi1ID)
|
||||
suite.requireTrafficPermissionsTracking(tp1, id)
|
||||
nsPerm1 := &pbauth.Permission{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
|
@ -395,10 +380,6 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
|||
WithTenancy(&pbresource.Tenancy{Partition: tenancy.GetPartition()}).
|
||||
Write(t, suite.client)
|
||||
|
||||
// create the workload identity that they reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
|
||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -458,8 +439,8 @@ func (suite *controllerSuite) TestReconcile_WorkloadIdentityDelete_ReferencingTr
|
|||
suite.requireTrafficPermissionsTracking(tp2, wi1ID)
|
||||
|
||||
// create the workload identity that they reference
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
wi := rtest.ResourceID(wi1ID).Write(suite.T(), suite.client)
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi1ID)
|
||||
|
||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -475,9 +456,9 @@ func (suite *controllerSuite) TestReconcile_WorkloadIdentityDelete_ReferencingTr
|
|||
|
||||
func (suite *controllerSuite) TestReconcile_WorkloadIdentityDelete_NoReferencingTrafficPermissionsExist() {
|
||||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||
// create the workload identity that they reference
|
||||
// create the workload identity to be referenced
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||
|
||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -499,7 +480,7 @@ func (suite *controllerSuite) TestReconcile_TrafficPermissionsCreate_Destination
|
|||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||
// create the workload identity to be referenced
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||
|
||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -600,7 +581,7 @@ func (suite *controllerSuite) TestReconcile_TrafficPermissionsDelete_Destination
|
|||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||
// create the workload identity to be referenced
|
||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
||||
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||
|
||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||
require.NoError(suite.T(), err)
|
||||
|
|
|
@ -41,7 +41,7 @@ var ValidateNamespaceTrafficPermissions = resource.DecodeAndValidate(validateNam
|
|||
func validateNamespaceTrafficPermissions(res *DecodedNamespaceTrafficPermissions) error {
|
||||
var merr error
|
||||
|
||||
if err := v.ValidateAction(res.Data); err != nil {
|
||||
if err := validateAction(res.Data); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
if err := validatePermissions(res.Id, res.Data); err != nil {
|
||||
|
|
|
@ -8,12 +8,9 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
"github.com/hashicorp/consul/proto/private/prototest"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
)
|
||||
|
@ -21,7 +18,7 @@ import (
|
|||
func TestValidateNamespaceTrafficPermissions_ParseError(t *testing.T) {
|
||||
data := &pbauth.ComputedTrafficPermissions{AllowPermissions: nil}
|
||||
|
||||
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "tp").
|
||||
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "ntp").
|
||||
WithData(t, data).
|
||||
Build()
|
||||
|
||||
|
@ -30,221 +27,6 @@ func TestValidateNamespaceTrafficPermissions_ParseError(t *testing.T) {
|
|||
require.ErrorAs(t, err, &resource.ErrDataParse{})
|
||||
}
|
||||
|
||||
// todo: this test is copy-pasted from traffic permissions tests.
|
||||
// would be nice to refator this to keep them in sync.
|
||||
func TestValidateNamespaceTrafficPermissions(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
id *pbresource.ID
|
||||
ntp *pbauth.NamespaceTrafficPermissions
|
||||
expectErr string
|
||||
}{
|
||||
"ok-minimal": {
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
},
|
||||
},
|
||||
"unspecified-action": {
|
||||
// Any type other than the TrafficPermissions type would work
|
||||
// to cause the error we are expecting
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_UNSPECIFIED,
|
||||
},
|
||||
expectErr: `invalid "data.action" field`,
|
||||
},
|
||||
"invalid-action": {
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action(50),
|
||||
},
|
||||
expectErr: `invalid "data.action" field`,
|
||||
},
|
||||
"source-tenancy": {
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "ap1",
|
||||
Peer: "cl1",
|
||||
SamenessGroup: "sg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": invalid element at index 0 of list "source": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||
},
|
||||
"source-has-same-tenancy-as-tp": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
Peer: resource.DefaultPeerName,
|
||||
SamenessGroup: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"source-has-partition-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "part",
|
||||
Peer: resource.DefaultPeerName,
|
||||
SamenessGroup: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"source-has-peer-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
Peer: "peer",
|
||||
SamenessGroup: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"source-has-sameness-group-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
Peer: resource.DefaultPeerName,
|
||||
SamenessGroup: "sg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"source-has-peer-and-partition-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "part",
|
||||
Peer: "peer",
|
||||
SamenessGroup: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": invalid element at index 0 of list "source": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||
},
|
||||
"source-has-sameness-group-and-partition-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "part",
|
||||
Peer: resource.DefaultPeerName,
|
||||
SamenessGroup: "sg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": invalid element at index 0 of list "source": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||
},
|
||||
"source-has-sameness-group-and-partition-peer-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ntp: &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "part",
|
||||
Peer: "peer",
|
||||
SamenessGroup: "sg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": invalid element at index 0 of list "source": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||
},
|
||||
}
|
||||
|
||||
for n, tc := range cases {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
resBuilder := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "ntp").
|
||||
WithData(t, tc.ntp)
|
||||
if tc.id != nil {
|
||||
resBuilder = resBuilder.WithTenancy(tc.id.Tenancy)
|
||||
}
|
||||
res := resBuilder.Build()
|
||||
|
||||
err := ValidateNamespaceTrafficPermissions(res)
|
||||
if tc.expectErr == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateNamespaceTrafficPermissions_Permissions(t *testing.T) {
|
||||
for n, tc := range permissionsTestCases() {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
|
@ -272,327 +54,32 @@ func TestValidateNamespaceTrafficPermissions_Permissions(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMutateNamespaceTrafficPermissions(t *testing.T) {
|
||||
type testcase struct {
|
||||
policyTenancy *pbresource.Tenancy
|
||||
tp *pbauth.NamespaceTrafficPermissions
|
||||
expect *pbauth.NamespaceTrafficPermissions
|
||||
}
|
||||
|
||||
run := func(t *testing.T, tc testcase) {
|
||||
tenancy := tc.policyTenancy
|
||||
run := func(t *testing.T, tc mutationTestCase) {
|
||||
tenancy := tc.tenancy
|
||||
if tenancy == nil {
|
||||
tenancy = resource.DefaultNamespacedTenancy()
|
||||
}
|
||||
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "ntp").
|
||||
WithTenancy(tenancy).
|
||||
WithData(t, tc.tp).
|
||||
WithData(t, &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: tc.permissions,
|
||||
}).
|
||||
Build()
|
||||
|
||||
err := MutateNamespaceTrafficPermissions(res)
|
||||
|
||||
got := resourcetest.MustDecode[*pbauth.NamespaceTrafficPermissions](t, res)
|
||||
require.NoError(t, err)
|
||||
prototest.AssertDeepEqual(t, tc.expect, got.Data)
|
||||
|
||||
if tc.expectErr == "" {
|
||||
require.NoError(t, err)
|
||||
prototest.AssertDeepEqual(t, tc.expect, got.Data.Permissions)
|
||||
} else {
|
||||
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||
}
|
||||
}
|
||||
|
||||
cases := map[string]testcase{
|
||||
"empty-1": {
|
||||
tp: &pbauth.NamespaceTrafficPermissions{},
|
||||
expect: &pbauth.NamespaceTrafficPermissions{},
|
||||
},
|
||||
"kitchen-sink-default-partition": {
|
||||
tp: &pbauth.NamespaceTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &pbauth.NamespaceTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"kitchen-sink-excludes-default-partition": {
|
||||
tp: &pbauth.NamespaceTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Exclude: []*pbauth.ExcludeSource{
|
||||
{},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &pbauth.NamespaceTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
Exclude: []*pbauth.ExcludeSource{
|
||||
{
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"kitchen-sink-non-default-partition": {
|
||||
policyTenancy: &pbresource.Tenancy{
|
||||
Partition: "ap1",
|
||||
Namespace: "ns3",
|
||||
},
|
||||
tp: &pbauth.NamespaceTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap5",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
Partition: "non-default",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &pbauth.NamespaceTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "ap1",
|
||||
Namespace: "",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap5",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
Namespace: "ns3",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
Namespace: "default",
|
||||
Partition: "non-default",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"kitchen-sink-excludes-non-default-partition": {
|
||||
policyTenancy: &pbresource.Tenancy{
|
||||
Partition: "ap1",
|
||||
Namespace: "ns3",
|
||||
},
|
||||
tp: &pbauth.NamespaceTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Exclude: []*pbauth.ExcludeSource{
|
||||
{},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap5",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &pbauth.NamespaceTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
Exclude: []*pbauth.ExcludeSource{
|
||||
{
|
||||
Partition: "ap1",
|
||||
Namespace: "",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap5",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
Namespace: "ns3",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
for name, tc := range mutationTestCases() {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
run(t, tc)
|
||||
})
|
||||
|
@ -600,110 +87,59 @@ func TestMutateNamespaceTrafficPermissions(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNamespaceTrafficPermissionsACLs(t *testing.T) {
|
||||
// Wire up a registry to generically invoke hooks
|
||||
registry := resource.NewRegistry()
|
||||
Register(registry)
|
||||
|
||||
type testcase struct {
|
||||
rules string
|
||||
readOK string
|
||||
writeOK string
|
||||
listOK string
|
||||
ntpData := &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
}
|
||||
|
||||
const (
|
||||
DENY = "deny"
|
||||
ALLOW = "allow"
|
||||
DEFAULT = "default"
|
||||
)
|
||||
|
||||
checkF := func(t *testing.T, expect string, got error) {
|
||||
switch expect {
|
||||
case ALLOW:
|
||||
if acl.IsErrPermissionDenied(got) {
|
||||
t.Fatal("should be allowed")
|
||||
}
|
||||
case DENY:
|
||||
if !acl.IsErrPermissionDenied(got) {
|
||||
t.Fatal("should be denied")
|
||||
}
|
||||
case DEFAULT:
|
||||
require.Nil(t, got, "expected fallthrough decision")
|
||||
default:
|
||||
t.Fatalf("unexpected expectation: %q", expect)
|
||||
}
|
||||
}
|
||||
|
||||
reg, ok := registry.Resolve(pbauth.NamespaceTrafficPermissionsType)
|
||||
require.True(t, ok)
|
||||
|
||||
run := func(t *testing.T, tc testcase) {
|
||||
tpData := &pbauth.NamespaceTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
}
|
||||
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "ntp1").
|
||||
WithTenancy(resource.DefaultNamespacedTenancy()).
|
||||
WithData(t, tpData).
|
||||
Build()
|
||||
resourcetest.ValidateAndNormalize(t, registry, res)
|
||||
|
||||
config := acl.Config{
|
||||
WildcardName: structs.WildcardSpecifier,
|
||||
}
|
||||
authz, err := acl.NewAuthorizerFromRules(tc.rules, &config, nil)
|
||||
require.NoError(t, err)
|
||||
authz = acl.NewChainedAuthorizer([]acl.Authorizer{authz, acl.DenyAll()})
|
||||
|
||||
t.Run("read", func(t *testing.T) {
|
||||
err := reg.ACLs.Read(authz, &acl.AuthorizerContext{}, res.Id, res)
|
||||
checkF(t, tc.readOK, err)
|
||||
})
|
||||
t.Run("write", func(t *testing.T) {
|
||||
err := reg.ACLs.Write(authz, &acl.AuthorizerContext{}, res)
|
||||
checkF(t, tc.writeOK, err)
|
||||
})
|
||||
t.Run("list", func(t *testing.T) {
|
||||
err := reg.ACLs.List(authz, &acl.AuthorizerContext{})
|
||||
checkF(t, tc.listOK, err)
|
||||
})
|
||||
}
|
||||
|
||||
cases := map[string]testcase{
|
||||
cases := map[string]resourcetest.ACLTestCase{
|
||||
"no rules": {
|
||||
rules: ``,
|
||||
readOK: DENY,
|
||||
writeOK: DENY,
|
||||
listOK: DEFAULT,
|
||||
Rules: ``,
|
||||
Data: ntpData,
|
||||
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||
ReadOK: resourcetest.DENY,
|
||||
WriteOK: resourcetest.DENY,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
"operator read": {
|
||||
rules: `operator = "read"`,
|
||||
readOK: ALLOW,
|
||||
writeOK: DENY,
|
||||
listOK: DEFAULT,
|
||||
Rules: `operator = "read"`,
|
||||
Data: ntpData,
|
||||
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||
ReadOK: resourcetest.ALLOW,
|
||||
WriteOK: resourcetest.DENY,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
"operator write": {
|
||||
rules: `operator = "write"`,
|
||||
readOK: ALLOW,
|
||||
writeOK: ALLOW,
|
||||
listOK: DEFAULT,
|
||||
Rules: `operator = "write"`,
|
||||
Data: ntpData,
|
||||
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||
ReadOK: resourcetest.ALLOW,
|
||||
WriteOK: resourcetest.ALLOW,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
"mesh read": {
|
||||
rules: `mesh = "read"`,
|
||||
readOK: ALLOW,
|
||||
writeOK: DENY,
|
||||
listOK: DEFAULT,
|
||||
Rules: `mesh = "read"`,
|
||||
Data: ntpData,
|
||||
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||
ReadOK: resourcetest.ALLOW,
|
||||
WriteOK: resourcetest.DENY,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
"namespace write": {
|
||||
rules: `mesh = "write"`,
|
||||
readOK: ALLOW,
|
||||
writeOK: ALLOW,
|
||||
listOK: DEFAULT,
|
||||
"mesh write": {
|
||||
Rules: `mesh = "write"`,
|
||||
Data: ntpData,
|
||||
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||
ReadOK: resourcetest.ALLOW,
|
||||
WriteOK: resourcetest.ALLOW,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
run(t, tc)
|
||||
resourcetest.RunACLTestCase(t, tc, registry)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
type DecodedPartitionTrafficPermissions = resource.DecodedResource[*pbauth.PartitionTrafficPermissions]
|
||||
|
@ -40,7 +41,7 @@ var ValidatePartitionTrafficPermissions = resource.DecodeAndValidate(validatePar
|
|||
func validatePartitionTrafficPermissions(res *DecodedPartitionTrafficPermissions) error {
|
||||
var merr error
|
||||
|
||||
if err := v.ValidateAction(res.Data); err != nil {
|
||||
if err := validateAction(res.Data); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
if err := validatePermissions(res.Id, res.Data); err != nil {
|
||||
|
|
|
@ -6,15 +6,13 @@ package types
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
"github.com/hashicorp/consul/proto/private/prototest"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestValidatePartitionTrafficPermissions_ParseError(t *testing.T) {
|
||||
|
@ -29,218 +27,6 @@ func TestValidatePartitionTrafficPermissions_ParseError(t *testing.T) {
|
|||
require.ErrorAs(t, err, &resource.ErrDataParse{})
|
||||
}
|
||||
|
||||
func TestValidatePartitionTrafficPermissions(t *testing.T) {
|
||||
// TODO: refactor test cases as these are similar to namespace traffic permissions
|
||||
cases := map[string]struct {
|
||||
id *pbresource.ID
|
||||
ptp *pbauth.PartitionTrafficPermissions
|
||||
expectErr string
|
||||
}{
|
||||
"ok-minimal": {
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
},
|
||||
},
|
||||
"unspecified-action": {
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_UNSPECIFIED,
|
||||
},
|
||||
expectErr: `invalid "data.action" field`,
|
||||
},
|
||||
"invalid-action": {
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action(50),
|
||||
},
|
||||
expectErr: `invalid "data.action" field`,
|
||||
},
|
||||
"source-tenancy": {
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "ap1",
|
||||
Peer: "cl1",
|
||||
SamenessGroup: "sg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": invalid element at index 0 of list "source": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||
},
|
||||
"source-has-same-tenancy-as-tp": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
Peer: resource.DefaultPeerName,
|
||||
SamenessGroup: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"source-has-partition-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "pt1",
|
||||
Peer: resource.DefaultPeerName,
|
||||
SamenessGroup: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"source-has-peer-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
Peer: "peer",
|
||||
SamenessGroup: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"source-has-sameness-group-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
Peer: resource.DefaultPeerName,
|
||||
SamenessGroup: "sg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"source-has-peer-and-partition-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "part",
|
||||
Peer: "peer",
|
||||
SamenessGroup: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": invalid element at index 0 of list "source": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||
},
|
||||
"source-has-sameness-group-and-partition-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "part",
|
||||
Peer: resource.DefaultPeerName,
|
||||
SamenessGroup: "sg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": invalid element at index 0 of list "source": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||
},
|
||||
"source-has-sameness-group-and-partition-peer-set": {
|
||||
id: &pbresource.ID{
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: resource.DefaultPartitionName,
|
||||
},
|
||||
},
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "part",
|
||||
Peer: "peer",
|
||||
SamenessGroup: "sg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": invalid element at index 0 of list "source": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||
},
|
||||
}
|
||||
|
||||
for n, tc := range cases {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
resBuilder := resourcetest.Resource(pbauth.PartitionTrafficPermissionsType, "ptp").
|
||||
WithData(t, tc.ptp)
|
||||
if tc.id != nil {
|
||||
resBuilder = resBuilder.WithTenancy(tc.id.Tenancy)
|
||||
}
|
||||
res := resBuilder.Build()
|
||||
|
||||
err := ValidatePartitionTrafficPermissions(res)
|
||||
if tc.expectErr == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePartitionTrafficPermissions_Permissions(t *testing.T) {
|
||||
for n, tc := range permissionsTestCases() {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
|
@ -268,327 +54,32 @@ func TestValidatePartitionTrafficPermissions_Permissions(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMutatePartitionTrafficPermissions(t *testing.T) {
|
||||
type testcase struct {
|
||||
policyTenancy *pbresource.Tenancy
|
||||
ptp *pbauth.PartitionTrafficPermissions
|
||||
expect *pbauth.PartitionTrafficPermissions
|
||||
}
|
||||
|
||||
run := func(t *testing.T, tc testcase) {
|
||||
tenancy := tc.policyTenancy
|
||||
run := func(t *testing.T, tc mutationTestCase) {
|
||||
tenancy := tc.tenancy
|
||||
if tenancy == nil {
|
||||
tenancy = resource.DefaultPartitionedTenancy()
|
||||
}
|
||||
res := resourcetest.Resource(pbauth.PartitionTrafficPermissionsType, "ptp").
|
||||
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "ntp").
|
||||
WithTenancy(tenancy).
|
||||
WithData(t, tc.ptp).
|
||||
WithData(t, &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
Permissions: tc.permissions,
|
||||
}).
|
||||
Build()
|
||||
|
||||
err := MutatePartitionTrafficPermissions(res)
|
||||
|
||||
got := resourcetest.MustDecode[*pbauth.PartitionTrafficPermissions](t, res)
|
||||
require.NoError(t, err)
|
||||
prototest.AssertDeepEqual(t, tc.expect, got.Data)
|
||||
|
||||
if tc.expectErr == "" {
|
||||
require.NoError(t, err)
|
||||
prototest.AssertDeepEqual(t, tc.expect, got.Data.Permissions)
|
||||
} else {
|
||||
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||
}
|
||||
}
|
||||
|
||||
cases := map[string]testcase{
|
||||
"empty-1": {
|
||||
ptp: &pbauth.PartitionTrafficPermissions{},
|
||||
expect: &pbauth.PartitionTrafficPermissions{},
|
||||
},
|
||||
"kitchen-sink-default-partition": {
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &pbauth.PartitionTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"kitchen-sink-excludes-default-partition": {
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Exclude: []*pbauth.ExcludeSource{
|
||||
{},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &pbauth.PartitionTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
Exclude: []*pbauth.ExcludeSource{
|
||||
{
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "default",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"kitchen-sink-non-default-partition": {
|
||||
policyTenancy: &pbresource.Tenancy{
|
||||
Partition: "ap1",
|
||||
Namespace: "ns3",
|
||||
},
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap5",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
Partition: "non-default",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &pbauth.PartitionTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "ap1",
|
||||
Namespace: "",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap5",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
Namespace: "ns3",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
Namespace: "default",
|
||||
Partition: "non-default",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"kitchen-sink-excludes-non-default-partition": {
|
||||
policyTenancy: &pbresource.Tenancy{
|
||||
Partition: "ap1",
|
||||
Namespace: "ns3",
|
||||
},
|
||||
ptp: &pbauth.PartitionTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Exclude: []*pbauth.ExcludeSource{
|
||||
{},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap5",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: &pbauth.PartitionTrafficPermissions{
|
||||
Permissions: []*pbauth.Permission{
|
||||
{
|
||||
Sources: []*pbauth.Source{
|
||||
{
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
Exclude: []*pbauth.ExcludeSource{
|
||||
{
|
||||
Partition: "ap1",
|
||||
Namespace: "",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
Peer: "not-default",
|
||||
},
|
||||
{
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap5",
|
||||
},
|
||||
{
|
||||
IdentityName: "i1",
|
||||
Namespace: "ns1",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
{
|
||||
IdentityName: "i2",
|
||||
Namespace: "ns3",
|
||||
Partition: "ap1",
|
||||
Peer: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
for name, tc := range mutationTestCases() {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
run(t, tc)
|
||||
})
|
||||
|
@ -596,110 +87,59 @@ func TestMutatePartitionTrafficPermissions(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPartitionTrafficPermissionsACLs(t *testing.T) {
|
||||
// Wire up a registry to generically invoke hooks
|
||||
registry := resource.NewRegistry()
|
||||
Register(registry)
|
||||
|
||||
type testcase struct {
|
||||
rules string
|
||||
readOK string
|
||||
writeOK string
|
||||
listOK string
|
||||
ptpData := &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
}
|
||||
|
||||
const (
|
||||
DENY = "deny"
|
||||
ALLOW = "allow"
|
||||
DEFAULT = "default"
|
||||
)
|
||||
|
||||
checkF := func(t *testing.T, expect string, got error) {
|
||||
switch expect {
|
||||
case ALLOW:
|
||||
if acl.IsErrPermissionDenied(got) {
|
||||
t.Fatal("should be allowed")
|
||||
}
|
||||
case DENY:
|
||||
if !acl.IsErrPermissionDenied(got) {
|
||||
t.Fatal("should be denied")
|
||||
}
|
||||
case DEFAULT:
|
||||
require.Nil(t, got, "expected fallthrough decision")
|
||||
default:
|
||||
t.Fatalf("unexpected expectation: %q", expect)
|
||||
}
|
||||
}
|
||||
|
||||
reg, ok := registry.Resolve(pbauth.PartitionTrafficPermissionsType)
|
||||
require.True(t, ok)
|
||||
|
||||
run := func(t *testing.T, tc testcase) {
|
||||
tpData := &pbauth.PartitionTrafficPermissions{
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
}
|
||||
res := resourcetest.Resource(pbauth.PartitionTrafficPermissionsType, "ptp1").
|
||||
WithTenancy(resource.DefaultPartitionedTenancy()).
|
||||
WithData(t, tpData).
|
||||
Build()
|
||||
resourcetest.ValidateAndNormalize(t, registry, res)
|
||||
|
||||
config := acl.Config{
|
||||
WildcardName: structs.WildcardSpecifier,
|
||||
}
|
||||
authz, err := acl.NewAuthorizerFromRules(tc.rules, &config, nil)
|
||||
require.NoError(t, err)
|
||||
authz = acl.NewChainedAuthorizer([]acl.Authorizer{authz, acl.DenyAll()})
|
||||
|
||||
t.Run("read", func(t *testing.T) {
|
||||
err := reg.ACLs.Read(authz, &acl.AuthorizerContext{}, res.Id, res)
|
||||
checkF(t, tc.readOK, err)
|
||||
})
|
||||
t.Run("write", func(t *testing.T) {
|
||||
err := reg.ACLs.Write(authz, &acl.AuthorizerContext{}, res)
|
||||
checkF(t, tc.writeOK, err)
|
||||
})
|
||||
t.Run("list", func(t *testing.T) {
|
||||
err := reg.ACLs.List(authz, &acl.AuthorizerContext{})
|
||||
checkF(t, tc.listOK, err)
|
||||
})
|
||||
}
|
||||
|
||||
cases := map[string]testcase{
|
||||
cases := map[string]resourcetest.ACLTestCase{
|
||||
"no rules": {
|
||||
rules: ``,
|
||||
readOK: DENY,
|
||||
writeOK: DENY,
|
||||
listOK: DEFAULT,
|
||||
Rules: ``,
|
||||
Data: ptpData,
|
||||
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||
ReadOK: resourcetest.DENY,
|
||||
WriteOK: resourcetest.DENY,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
"operator read": {
|
||||
rules: `operator = "read"`,
|
||||
readOK: ALLOW,
|
||||
writeOK: DENY,
|
||||
listOK: DEFAULT,
|
||||
Rules: `operator = "read"`,
|
||||
Data: ptpData,
|
||||
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||
ReadOK: resourcetest.ALLOW,
|
||||
WriteOK: resourcetest.DENY,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
"operator write": {
|
||||
rules: `operator = "write"`,
|
||||
readOK: ALLOW,
|
||||
writeOK: ALLOW,
|
||||
listOK: DEFAULT,
|
||||
Rules: `operator = "write"`,
|
||||
Data: ptpData,
|
||||
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||
ReadOK: resourcetest.ALLOW,
|
||||
WriteOK: resourcetest.ALLOW,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
"mesh read": {
|
||||
rules: `mesh = "read"`,
|
||||
readOK: ALLOW,
|
||||
writeOK: DENY,
|
||||
listOK: DEFAULT,
|
||||
Rules: `mesh = "read"`,
|
||||
Data: ptpData,
|
||||
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||
ReadOK: resourcetest.ALLOW,
|
||||
WriteOK: resourcetest.DENY,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
"partition write": {
|
||||
rules: `mesh = "write"`,
|
||||
readOK: ALLOW,
|
||||
writeOK: ALLOW,
|
||||
listOK: DEFAULT,
|
||||
"mesh write": {
|
||||
Rules: `mesh = "write"`,
|
||||
Data: ptpData,
|
||||
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||
ReadOK: resourcetest.ALLOW,
|
||||
WriteOK: resourcetest.ALLOW,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
run(t, tc)
|
||||
resourcetest.RunACLTestCase(t, tc, registry)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
|
@ -123,8 +121,7 @@ type validator interface {
|
|||
func validateTrafficPermissions(res *DecodedTrafficPermissions) error {
|
||||
var merr error
|
||||
|
||||
err := v.ValidateAction(res.Data)
|
||||
if err != nil {
|
||||
if err := validateAction(res.Data); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
|
||||
|
@ -142,202 +139,6 @@ func validateTrafficPermissions(res *DecodedTrafficPermissions) error {
|
|||
return merr
|
||||
}
|
||||
|
||||
func validatePermissions(id *pbresource.ID, data interface{ GetPermissions() []*pbauth.Permission }) error {
|
||||
var merr error
|
||||
for i, permission := range data.GetPermissions() {
|
||||
wrapErr := func(err error) error {
|
||||
return resource.ErrInvalidListElement{
|
||||
Name: "permissions",
|
||||
Index: i,
|
||||
Wrapped: err,
|
||||
}
|
||||
}
|
||||
if err := validatePermission(permission, id, wrapErr); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
}
|
||||
return merr
|
||||
}
|
||||
|
||||
func validatePermission(p *pbauth.Permission, id *pbresource.ID, wrapErr func(error) error) error {
|
||||
var merr error
|
||||
|
||||
if len(p.Sources) == 0 {
|
||||
merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{
|
||||
Name: "sources",
|
||||
Wrapped: resource.ErrEmpty,
|
||||
}))
|
||||
}
|
||||
|
||||
for s, src := range p.Sources {
|
||||
wrapSrcErr := func(err error) error {
|
||||
return wrapErr(resource.ErrInvalidListElement{
|
||||
Name: "sources",
|
||||
Index: s,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if sourceHasIncompatibleTenancies(src, id) {
|
||||
merr = multierror.Append(merr, wrapSrcErr(resource.ErrInvalidListElement{
|
||||
Name: "source",
|
||||
Wrapped: errSourcesTenancy,
|
||||
}))
|
||||
}
|
||||
|
||||
if src.Namespace == "" && src.IdentityName != "" {
|
||||
merr = multierror.Append(merr, wrapSrcErr(resource.ErrInvalidField{
|
||||
Name: "source",
|
||||
Wrapped: errSourceWildcards,
|
||||
}))
|
||||
}
|
||||
|
||||
// Excludes are only valid for wildcard sources.
|
||||
if src.IdentityName != "" && len(src.Exclude) > 0 {
|
||||
merr = multierror.Append(merr, wrapSrcErr(resource.ErrInvalidField{
|
||||
Name: "exclude_sources",
|
||||
Wrapped: errSourceExcludes,
|
||||
}))
|
||||
continue
|
||||
}
|
||||
|
||||
for e, d := range src.Exclude {
|
||||
wrapExclSrcErr := func(err error) error {
|
||||
return wrapErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_sources",
|
||||
Index: e,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if sourceHasIncompatibleTenancies(d, id) {
|
||||
merr = multierror.Append(merr, wrapExclSrcErr(resource.ErrInvalidField{
|
||||
Name: "exclude_source",
|
||||
Wrapped: errSourcesTenancy,
|
||||
}))
|
||||
}
|
||||
|
||||
if d.Namespace == "" && d.IdentityName != "" {
|
||||
merr = multierror.Append(merr, wrapExclSrcErr(resource.ErrInvalidField{
|
||||
Name: "source",
|
||||
Wrapped: errSourceWildcards,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
for d, dest := range p.DestinationRules {
|
||||
wrapDestRuleErr := func(err error) error {
|
||||
return wrapErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_rules",
|
||||
Index: d,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if (len(dest.PathExact) > 0 && len(dest.PathPrefix) > 0) ||
|
||||
(len(dest.PathRegex) > 0 && len(dest.PathExact) > 0) ||
|
||||
(len(dest.PathRegex) > 0 && len(dest.PathPrefix) > 0) {
|
||||
merr = multierror.Append(merr, wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_rule",
|
||||
Wrapped: errInvalidPrefixValues,
|
||||
}))
|
||||
}
|
||||
if len(dest.Headers) > 0 {
|
||||
for h, hdr := range dest.Headers {
|
||||
wrapHeaderErr := func(err error) error {
|
||||
return wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_header_rules",
|
||||
Index: h,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if len(hdr.Name) == 0 {
|
||||
merr = multierror.Append(merr, wrapHeaderErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_header_rule",
|
||||
Wrapped: errHeaderRulesInvalid,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
if dest.IsEmpty() {
|
||||
merr = multierror.Append(merr, wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_rule",
|
||||
Wrapped: errInvalidRule,
|
||||
}))
|
||||
}
|
||||
if len(dest.Exclude) > 0 {
|
||||
for e, excl := range dest.Exclude {
|
||||
wrapExclPermRuleErr := func(err error) error {
|
||||
return wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_rules",
|
||||
Index: e,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if (len(excl.PathExact) > 0 && len(excl.PathPrefix) > 0) ||
|
||||
(len(excl.PathRegex) > 0 && len(excl.PathExact) > 0) ||
|
||||
(len(excl.PathRegex) > 0 && len(excl.PathPrefix) > 0) {
|
||||
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_rule",
|
||||
Wrapped: errInvalidPrefixValues,
|
||||
}))
|
||||
}
|
||||
for eh, hdr := range excl.Headers {
|
||||
wrapExclHeaderErr := func(err error) error {
|
||||
return wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_header_rules",
|
||||
Index: eh,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if len(hdr.Name) == 0 {
|
||||
merr = multierror.Append(merr, wrapExclHeaderErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_header_rule",
|
||||
Wrapped: errHeaderRulesInvalid,
|
||||
}))
|
||||
}
|
||||
}
|
||||
for _, m := range excl.Methods {
|
||||
if len(dest.Methods) != 0 && !slices.Contains(dest.Methods, m) {
|
||||
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_header_rule",
|
||||
Wrapped: errExclValuesMustBeSubset,
|
||||
}))
|
||||
}
|
||||
}
|
||||
for _, port := range excl.PortNames {
|
||||
if len(dest.PortNames) != 0 && !slices.Contains(dest.PortNames, port) {
|
||||
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_header_rule",
|
||||
Wrapped: errExclValuesMustBeSubset,
|
||||
}))
|
||||
}
|
||||
}
|
||||
if excl.IsEmpty() {
|
||||
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_rule",
|
||||
Wrapped: errInvalidRule,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return merr
|
||||
}
|
||||
|
||||
func sourceHasIncompatibleTenancies(src pbauth.SourceToSpiffe, id *pbresource.ID) bool {
|
||||
if id.Tenancy == nil {
|
||||
id.Tenancy = &pbresource.Tenancy{}
|
||||
}
|
||||
peerSet := !isLocalPeer(src.GetPeer())
|
||||
apSet := src.GetPartition() != id.Tenancy.Partition
|
||||
sgSet := src.GetSamenessGroup() != ""
|
||||
|
||||
return (apSet && peerSet) || (apSet && sgSet) || (peerSet && sgSet)
|
||||
}
|
||||
|
||||
func isLocalPeer(p string) bool {
|
||||
return p == "local" || p == ""
|
||||
}
|
||||
|
||||
func aclReadHookTrafficPermissions(authorizer acl.Authorizer, authzContext *acl.AuthorizerContext, res *DecodedTrafficPermissions) error {
|
||||
return authorizer.ToAllowAuthorizer().TrafficPermissionsReadAllowed(res.Data.Destination.IdentityName, authzContext)
|
||||
}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
//go:build !consulent
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
)
|
||||
|
||||
func TestValidateTrafficPermissionsActionCE(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
tp *pbauth.TrafficPermissions
|
||||
expectErr string
|
||||
}{
|
||||
"ok-minimal": {
|
||||
tp: &pbauth.TrafficPermissions{
|
||||
Destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||
Action: pbauth.Action_ACTION_ALLOW,
|
||||
},
|
||||
},
|
||||
"unspecified-action": {
|
||||
// Any type other than the TrafficPermissions type would work
|
||||
// to cause the error we are expecting
|
||||
tp: &pbauth.TrafficPermissions{
|
||||
Destination: &pbauth.Destination{
|
||||
IdentityName: "wi1",
|
||||
},
|
||||
Action: pbauth.Action_ACTION_UNSPECIFIED,
|
||||
Permissions: nil,
|
||||
},
|
||||
expectErr: `invalid "data.action" field: action must be allow`,
|
||||
},
|
||||
"deny-action": {
|
||||
tp: &pbauth.TrafficPermissions{
|
||||
Destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||
Action: pbauth.Action_ACTION_DENY,
|
||||
},
|
||||
expectErr: `invalid "data.action" field: action must be allow`,
|
||||
},
|
||||
"invalid-action": {
|
||||
tp: &pbauth.TrafficPermissions{
|
||||
Destination: &pbauth.Destination{
|
||||
IdentityName: "wi1",
|
||||
},
|
||||
Action: pbauth.Action(50),
|
||||
Permissions: nil,
|
||||
},
|
||||
expectErr: `invalid "data.action" field: action must be allow`,
|
||||
},
|
||||
}
|
||||
|
||||
for n, tc := range cases {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
res := resourcetest.Resource(pbauth.TrafficPermissionsType, "tp").
|
||||
WithData(t, tc.tp).
|
||||
Build()
|
||||
|
||||
err := ValidateTrafficPermissions(res)
|
||||
if tc.expectErr == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,203 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
)
|
||||
|
||||
func validatePermissions(id *pbresource.ID, data interface{ GetPermissions() []*pbauth.Permission }) error {
|
||||
var merr error
|
||||
for i, permission := range data.GetPermissions() {
|
||||
wrapErr := func(err error) error {
|
||||
return resource.ErrInvalidListElement{
|
||||
Name: "permissions",
|
||||
Index: i,
|
||||
Wrapped: err,
|
||||
}
|
||||
}
|
||||
if err := validatePermission(permission, id, wrapErr); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
}
|
||||
return merr
|
||||
}
|
||||
|
||||
func validatePermission(p *pbauth.Permission, id *pbresource.ID, wrapErr func(error) error) error {
|
||||
var merr error
|
||||
|
||||
if len(p.Sources) == 0 {
|
||||
merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{
|
||||
Name: "sources",
|
||||
Wrapped: resource.ErrEmpty,
|
||||
}))
|
||||
}
|
||||
|
||||
for s, src := range p.Sources {
|
||||
wrapSrcErr := func(err error) error {
|
||||
return wrapErr(resource.ErrInvalidListElement{
|
||||
Name: "sources",
|
||||
Index: s,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if sourceHasIncompatibleTenancies(src, id) {
|
||||
merr = multierror.Append(merr, wrapSrcErr(errSourcesTenancy))
|
||||
}
|
||||
|
||||
if src.Namespace == "" && src.IdentityName != "" {
|
||||
merr = multierror.Append(merr, wrapSrcErr(errSourceWildcards))
|
||||
}
|
||||
|
||||
// Excludes are only valid for wildcard sources.
|
||||
if src.IdentityName != "" && len(src.Exclude) > 0 {
|
||||
merr = multierror.Append(merr, wrapSrcErr(resource.ErrInvalidField{
|
||||
Name: "exclude_sources",
|
||||
Wrapped: errSourceExcludes,
|
||||
}))
|
||||
continue
|
||||
}
|
||||
|
||||
for e, d := range src.Exclude {
|
||||
wrapExclSrcErr := func(err error) error {
|
||||
return wrapErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_sources",
|
||||
Index: e,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if sourceHasIncompatibleTenancies(d, id) {
|
||||
merr = multierror.Append(merr, wrapExclSrcErr(resource.ErrInvalidField{
|
||||
Name: "exclude_source",
|
||||
Wrapped: errSourcesTenancy,
|
||||
}))
|
||||
}
|
||||
|
||||
if d.Namespace == "" && d.IdentityName != "" {
|
||||
merr = multierror.Append(merr, wrapExclSrcErr(resource.ErrInvalidField{
|
||||
Name: "source",
|
||||
Wrapped: errSourceWildcards,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
for d, dest := range p.DestinationRules {
|
||||
wrapDestRuleErr := func(err error) error {
|
||||
return wrapErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_rules",
|
||||
Index: d,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if (len(dest.PathExact) > 0 && len(dest.PathPrefix) > 0) ||
|
||||
(len(dest.PathRegex) > 0 && len(dest.PathExact) > 0) ||
|
||||
(len(dest.PathRegex) > 0 && len(dest.PathPrefix) > 0) {
|
||||
merr = multierror.Append(merr, wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_rule",
|
||||
Wrapped: errInvalidPrefixValues,
|
||||
}))
|
||||
}
|
||||
if len(dest.Headers) > 0 {
|
||||
for h, hdr := range dest.Headers {
|
||||
wrapHeaderErr := func(err error) error {
|
||||
return wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_header_rules",
|
||||
Index: h,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if len(hdr.Name) == 0 {
|
||||
merr = multierror.Append(merr, wrapHeaderErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_header_rule",
|
||||
Wrapped: errHeaderRulesInvalid,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
if dest.IsEmpty() {
|
||||
merr = multierror.Append(merr, wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "destination_rule",
|
||||
Wrapped: errInvalidRule,
|
||||
}))
|
||||
}
|
||||
if len(dest.Exclude) > 0 {
|
||||
for e, excl := range dest.Exclude {
|
||||
wrapExclPermRuleErr := func(err error) error {
|
||||
return wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_rules",
|
||||
Index: e,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if (len(excl.PathExact) > 0 && len(excl.PathPrefix) > 0) ||
|
||||
(len(excl.PathRegex) > 0 && len(excl.PathExact) > 0) ||
|
||||
(len(excl.PathRegex) > 0 && len(excl.PathPrefix) > 0) {
|
||||
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_rule",
|
||||
Wrapped: errInvalidPrefixValues,
|
||||
}))
|
||||
}
|
||||
for eh, hdr := range excl.Headers {
|
||||
wrapExclHeaderErr := func(err error) error {
|
||||
return wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_header_rules",
|
||||
Index: eh,
|
||||
Wrapped: err,
|
||||
})
|
||||
}
|
||||
if len(hdr.Name) == 0 {
|
||||
merr = multierror.Append(merr, wrapExclHeaderErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_header_rule",
|
||||
Wrapped: errHeaderRulesInvalid,
|
||||
}))
|
||||
}
|
||||
}
|
||||
for _, m := range excl.Methods {
|
||||
if len(dest.Methods) != 0 && !slices.Contains(dest.Methods, m) {
|
||||
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_header_rule",
|
||||
Wrapped: errExclValuesMustBeSubset,
|
||||
}))
|
||||
}
|
||||
}
|
||||
for _, port := range excl.PortNames {
|
||||
if len(dest.PortNames) != 0 && !slices.Contains(dest.PortNames, port) {
|
||||
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_header_rule",
|
||||
Wrapped: errExclValuesMustBeSubset,
|
||||
}))
|
||||
}
|
||||
}
|
||||
if excl.IsEmpty() {
|
||||
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||
Name: "exclude_permission_rule",
|
||||
Wrapped: errInvalidRule,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return merr
|
||||
}
|
||||
|
||||
func sourceHasIncompatibleTenancies(src pbauth.SourceToSpiffe, id *pbresource.ID) bool {
|
||||
if id.Tenancy == nil {
|
||||
id.Tenancy = &pbresource.Tenancy{}
|
||||
}
|
||||
peerSet := !isLocalPeer(src.GetPeer())
|
||||
apSet := src.GetPartition() != id.Tenancy.Partition
|
||||
sgSet := src.GetSamenessGroup() != ""
|
||||
|
||||
return (apSet && peerSet) || (apSet && sgSet) || (peerSet && sgSet)
|
||||
}
|
||||
|
||||
func isLocalPeer(p string) bool {
|
||||
return p == "local" || p == ""
|
||||
}
|
|
@ -12,18 +12,9 @@ import (
|
|||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||
)
|
||||
|
||||
var v validator = &actionValidator{}
|
||||
|
||||
type actionValidator struct{}
|
||||
|
||||
func (v *actionValidator) ValidateAction(data interface{ GetAction() pbauth.Action }) error {
|
||||
// enumcover:pbauth.Action
|
||||
func validateAction(data interface{ GetAction() pbauth.Action }) error {
|
||||
switch data.GetAction() {
|
||||
case pbauth.Action_ACTION_ALLOW:
|
||||
case pbauth.Action_ACTION_UNSPECIFIED:
|
||||
fallthrough
|
||||
case pbauth.Action_ACTION_DENY:
|
||||
fallthrough
|
||||
default:
|
||||
return resource.ErrInvalidField{
|
||||
Name: "data.action",
|
||||
|
@ -32,5 +23,3 @@ func (v *actionValidator) ValidateAction(data interface{ GetAction() pbauth.Acti
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ validator = (*actionValidator)(nil)
|
Loading…
Reference in New Issue