mirror of https://github.com/hashicorp/consul
Refactor xTP tests
parent
671c436415
commit
e484c3c7dc
|
@ -133,9 +133,7 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_NoReferencingTrafficPermis
|
||||||
Write(suite.T(), suite.client)
|
Write(suite.T(), suite.client)
|
||||||
|
|
||||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
||||||
require.NotNil(suite.T(), wi)
|
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
|
||||||
require.NotNil(suite.T(), id)
|
|
||||||
|
|
||||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
@ -194,9 +192,9 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
||||||
WithTenancy(tenancy).
|
WithTenancy(tenancy).
|
||||||
Write(t, suite.client)
|
Write(t, suite.client)
|
||||||
|
|
||||||
// create the workload identity that they reference
|
// create the workload identity to reference
|
||||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
wi := rtest.ResourceID(wi1ID).Write(t, suite.client)
|
||||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi1ID)
|
||||||
|
|
||||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -228,9 +226,9 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
||||||
WithTenancy(tenancy).
|
WithTenancy(tenancy).
|
||||||
Write(t, suite.client)
|
Write(t, suite.client)
|
||||||
|
|
||||||
// create the workload identity that they reference
|
// create the workload identity to reference
|
||||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
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()
|
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||||
|
|
||||||
require.NoError(t, suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id}))
|
require.NoError(t, suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id}))
|
||||||
|
|
||||||
|
@ -261,9 +259,9 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
||||||
WithTenancy(&pbresource.Tenancy{Partition: tenancy.GetPartition()}).
|
WithTenancy(&pbresource.Tenancy{Partition: tenancy.GetPartition()}).
|
||||||
Write(t, suite.client)
|
Write(t, suite.client)
|
||||||
|
|
||||||
// create the workload identity that they reference
|
// create the workload identity to reference
|
||||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
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()
|
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi.Id)
|
||||||
|
|
||||||
require.NoError(t, suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id}))
|
require.NoError(t, suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id}))
|
||||||
|
|
||||||
|
@ -320,8 +318,8 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
||||||
Write(t, suite.client)
|
Write(t, suite.client)
|
||||||
|
|
||||||
// create the workload identity that they reference
|
// create the workload identity that they reference
|
||||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
wi := rtest.ResourceID(wi1ID).Write(t, suite.client)
|
||||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi1ID)
|
||||||
|
|
||||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -396,8 +394,8 @@ func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissi
|
||||||
Write(t, suite.client)
|
Write(t, suite.client)
|
||||||
|
|
||||||
// create the workload identity that they reference
|
// create the workload identity that they reference
|
||||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(t, suite.client)
|
wi := rtest.ResourceID(wi1ID).Write(t, suite.client)
|
||||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi1ID)
|
||||||
|
|
||||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -458,8 +456,8 @@ func (suite *controllerSuite) TestReconcile_WorkloadIdentityDelete_ReferencingTr
|
||||||
suite.requireTrafficPermissionsTracking(tp2, wi1ID)
|
suite.requireTrafficPermissionsTracking(tp2, wi1ID)
|
||||||
|
|
||||||
// create the workload identity that they reference
|
// create the workload identity that they reference
|
||||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
wi := rtest.ResourceID(wi1ID).Write(suite.T(), suite.client)
|
||||||
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(tenancy).WithOwner(wi.Id).ID()
|
id := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, wi1ID)
|
||||||
|
|
||||||
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
@ -475,9 +473,9 @@ func (suite *controllerSuite) TestReconcile_WorkloadIdentityDelete_ReferencingTr
|
||||||
|
|
||||||
func (suite *controllerSuite) TestReconcile_WorkloadIdentityDelete_NoReferencingTrafficPermissionsExist() {
|
func (suite *controllerSuite) TestReconcile_WorkloadIdentityDelete_NoReferencingTrafficPermissionsExist() {
|
||||||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
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)
|
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})
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
@ -499,7 +497,7 @@ func (suite *controllerSuite) TestReconcile_TrafficPermissionsCreate_Destination
|
||||||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||||
// create the workload identity to be referenced
|
// create the workload identity to be referenced
|
||||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
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})
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
@ -600,7 +598,7 @@ func (suite *controllerSuite) TestReconcile_TrafficPermissionsDelete_Destination
|
||||||
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||||
// create the workload identity to be referenced
|
// create the workload identity to be referenced
|
||||||
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").WithTenancy(tenancy).Write(suite.T(), suite.client)
|
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})
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
|
|
@ -24,7 +24,7 @@ func RegisterNamespaceTrafficPermissions(r resource.Registry) {
|
||||||
},
|
},
|
||||||
Validate: ValidateNamespaceTrafficPermissions,
|
Validate: ValidateNamespaceTrafficPermissions,
|
||||||
Mutate: MutateNamespaceTrafficPermissions,
|
Mutate: MutateNamespaceTrafficPermissions,
|
||||||
Scope: resource.ScopeNamespace,
|
Scope: resource.ScopePartition,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ var ValidateNamespaceTrafficPermissions = resource.DecodeAndValidate(validateNam
|
||||||
func validateNamespaceTrafficPermissions(res *DecodedNamespaceTrafficPermissions) error {
|
func validateNamespaceTrafficPermissions(res *DecodedNamespaceTrafficPermissions) error {
|
||||||
var merr error
|
var merr error
|
||||||
|
|
||||||
if err := v.ValidateAction(res.Data); err != nil {
|
if err := validateAction(res.Data); err != nil {
|
||||||
merr = multierror.Append(merr, err)
|
merr = multierror.Append(merr, err)
|
||||||
}
|
}
|
||||||
if err := validatePermissions(res.Id, res.Data); err != nil {
|
if err := validatePermissions(res.Id, res.Data); err != nil {
|
||||||
|
|
|
@ -8,12 +8,9 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"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"
|
||||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
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/proto/private/prototest"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
)
|
)
|
||||||
|
@ -21,7 +18,7 @@ import (
|
||||||
func TestValidateNamespaceTrafficPermissions_ParseError(t *testing.T) {
|
func TestValidateNamespaceTrafficPermissions_ParseError(t *testing.T) {
|
||||||
data := &pbauth.ComputedTrafficPermissions{AllowPermissions: nil}
|
data := &pbauth.ComputedTrafficPermissions{AllowPermissions: nil}
|
||||||
|
|
||||||
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "tp").
|
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "ntp").
|
||||||
WithData(t, data).
|
WithData(t, data).
|
||||||
Build()
|
Build()
|
||||||
|
|
||||||
|
@ -30,221 +27,6 @@ func TestValidateNamespaceTrafficPermissions_ParseError(t *testing.T) {
|
||||||
require.ErrorAs(t, err, &resource.ErrDataParse{})
|
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) {
|
func TestValidateNamespaceTrafficPermissions_Permissions(t *testing.T) {
|
||||||
for n, tc := range permissionsTestCases() {
|
for n, tc := range permissionsTestCases() {
|
||||||
t.Run(n, func(t *testing.T) {
|
t.Run(n, func(t *testing.T) {
|
||||||
|
@ -254,7 +36,7 @@ func TestValidateNamespaceTrafficPermissions_Permissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "tp").
|
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "tp").
|
||||||
WithTenancy(resource.DefaultNamespacedTenancy()).
|
WithTenancy(resource.DefaultPartitionedTenancy()).
|
||||||
WithData(t, tp).
|
WithData(t, tp).
|
||||||
Build()
|
Build()
|
||||||
|
|
||||||
|
@ -272,327 +54,32 @@ func TestValidateNamespaceTrafficPermissions_Permissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMutateNamespaceTrafficPermissions(t *testing.T) {
|
func TestMutateNamespaceTrafficPermissions(t *testing.T) {
|
||||||
type testcase struct {
|
run := func(t *testing.T, tc mutationTestCase) {
|
||||||
policyTenancy *pbresource.Tenancy
|
tenancy := tc.tenancy
|
||||||
tp *pbauth.NamespaceTrafficPermissions
|
|
||||||
expect *pbauth.NamespaceTrafficPermissions
|
|
||||||
}
|
|
||||||
|
|
||||||
run := func(t *testing.T, tc testcase) {
|
|
||||||
tenancy := tc.policyTenancy
|
|
||||||
if tenancy == nil {
|
if tenancy == nil {
|
||||||
tenancy = resource.DefaultNamespacedTenancy()
|
tenancy = resource.DefaultPartitionedTenancy()
|
||||||
}
|
}
|
||||||
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "ntp").
|
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "ntp").
|
||||||
WithTenancy(tenancy).
|
WithTenancy(tenancy).
|
||||||
WithData(t, tc.tp).
|
WithData(t, &pbauth.NamespaceTrafficPermissions{
|
||||||
|
Action: pbauth.Action_ACTION_ALLOW,
|
||||||
|
Permissions: tc.permissions,
|
||||||
|
}).
|
||||||
Build()
|
Build()
|
||||||
|
|
||||||
err := MutateNamespaceTrafficPermissions(res)
|
err := MutateNamespaceTrafficPermissions(res)
|
||||||
|
|
||||||
got := resourcetest.MustDecode[*pbauth.NamespaceTrafficPermissions](t, res)
|
got := resourcetest.MustDecode[*pbauth.NamespaceTrafficPermissions](t, res)
|
||||||
|
|
||||||
|
if tc.expectErr == "" {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prototest.AssertDeepEqual(t, tc.expect, got.Data)
|
prototest.AssertDeepEqual(t, tc.expect, got.Data.Permissions)
|
||||||
|
} else {
|
||||||
|
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cases := map[string]testcase{
|
for name, tc := range mutationTestCases() {
|
||||||
"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 {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
run(t, tc)
|
run(t, tc)
|
||||||
})
|
})
|
||||||
|
@ -600,110 +87,59 @@ func TestMutateNamespaceTrafficPermissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNamespaceTrafficPermissionsACLs(t *testing.T) {
|
func TestNamespaceTrafficPermissionsACLs(t *testing.T) {
|
||||||
// Wire up a registry to generically invoke hooks
|
|
||||||
registry := resource.NewRegistry()
|
registry := resource.NewRegistry()
|
||||||
Register(registry)
|
Register(registry)
|
||||||
|
|
||||||
type testcase struct {
|
ntpData := &pbauth.NamespaceTrafficPermissions{
|
||||||
rules string
|
|
||||||
readOK string
|
|
||||||
writeOK string
|
|
||||||
listOK string
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
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{
|
cases := map[string]resourcetest.ACLTestCase{
|
||||||
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{
|
|
||||||
"no rules": {
|
"no rules": {
|
||||||
rules: ``,
|
Rules: ``,
|
||||||
readOK: DENY,
|
Data: ntpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.DENY,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"operator read": {
|
"operator read": {
|
||||||
rules: `operator = "read"`,
|
Rules: `operator = "read"`,
|
||||||
readOK: ALLOW,
|
Data: ntpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"operator write": {
|
"operator write": {
|
||||||
rules: `operator = "write"`,
|
Rules: `operator = "write"`,
|
||||||
readOK: ALLOW,
|
Data: ntpData,
|
||||||
writeOK: ALLOW,
|
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.ALLOW,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"mesh read": {
|
"mesh read": {
|
||||||
rules: `mesh = "read"`,
|
Rules: `mesh = "read"`,
|
||||||
readOK: ALLOW,
|
Data: ntpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"namespace write": {
|
"mesh write": {
|
||||||
rules: `mesh = "write"`,
|
Rules: `mesh = "write"`,
|
||||||
readOK: ALLOW,
|
Data: ntpData,
|
||||||
writeOK: ALLOW,
|
Typ: pbauth.NamespaceTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.ALLOW,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
run(t, tc)
|
resourcetest.RunACLTestCase(t, tc, registry)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||||
"github.com/hashicorp/go-multierror"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DecodedPartitionTrafficPermissions = resource.DecodedResource[*pbauth.PartitionTrafficPermissions]
|
type DecodedPartitionTrafficPermissions = resource.DecodedResource[*pbauth.PartitionTrafficPermissions]
|
||||||
|
@ -23,7 +24,7 @@ func RegisterPartitionTrafficPermissions(r resource.Registry) {
|
||||||
},
|
},
|
||||||
Validate: ValidatePartitionTrafficPermissions,
|
Validate: ValidatePartitionTrafficPermissions,
|
||||||
Mutate: MutatePartitionTrafficPermissions,
|
Mutate: MutatePartitionTrafficPermissions,
|
||||||
Scope: resource.ScopePartition,
|
Scope: resource.ScopeCluster,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ var ValidatePartitionTrafficPermissions = resource.DecodeAndValidate(validatePar
|
||||||
func validatePartitionTrafficPermissions(res *DecodedPartitionTrafficPermissions) error {
|
func validatePartitionTrafficPermissions(res *DecodedPartitionTrafficPermissions) error {
|
||||||
var merr error
|
var merr error
|
||||||
|
|
||||||
if err := v.ValidateAction(res.Data); err != nil {
|
if err := validateAction(res.Data); err != nil {
|
||||||
merr = multierror.Append(merr, err)
|
merr = multierror.Append(merr, err)
|
||||||
}
|
}
|
||||||
if err := validatePermissions(res.Id, res.Data); err != nil {
|
if err := validatePermissions(res.Id, res.Data); err != nil {
|
||||||
|
|
|
@ -6,15 +6,13 @@ package types
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
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/proto/private/prototest"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestValidatePartitionTrafficPermissions_ParseError(t *testing.T) {
|
func TestValidatePartitionTrafficPermissions_ParseError(t *testing.T) {
|
||||||
|
@ -29,218 +27,6 @@ func TestValidatePartitionTrafficPermissions_ParseError(t *testing.T) {
|
||||||
require.ErrorAs(t, err, &resource.ErrDataParse{})
|
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) {
|
func TestValidatePartitionTrafficPermissions_Permissions(t *testing.T) {
|
||||||
for n, tc := range permissionsTestCases() {
|
for n, tc := range permissionsTestCases() {
|
||||||
t.Run(n, func(t *testing.T) {
|
t.Run(n, func(t *testing.T) {
|
||||||
|
@ -250,7 +36,7 @@ func TestValidatePartitionTrafficPermissions_Permissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res := resourcetest.Resource(pbauth.PartitionTrafficPermissionsType, "ptp").
|
res := resourcetest.Resource(pbauth.PartitionTrafficPermissionsType, "ptp").
|
||||||
WithTenancy(resource.DefaultPartitionedTenancy()).
|
WithTenancy(resource.DefaultClusteredTenancy()).
|
||||||
WithData(t, tp).
|
WithData(t, tp).
|
||||||
Build()
|
Build()
|
||||||
|
|
||||||
|
@ -268,327 +54,32 @@ func TestValidatePartitionTrafficPermissions_Permissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMutatePartitionTrafficPermissions(t *testing.T) {
|
func TestMutatePartitionTrafficPermissions(t *testing.T) {
|
||||||
type testcase struct {
|
run := func(t *testing.T, tc mutationTestCase) {
|
||||||
policyTenancy *pbresource.Tenancy
|
tenancy := tc.tenancy
|
||||||
ptp *pbauth.PartitionTrafficPermissions
|
|
||||||
expect *pbauth.PartitionTrafficPermissions
|
|
||||||
}
|
|
||||||
|
|
||||||
run := func(t *testing.T, tc testcase) {
|
|
||||||
tenancy := tc.policyTenancy
|
|
||||||
if tenancy == nil {
|
if tenancy == nil {
|
||||||
tenancy = resource.DefaultPartitionedTenancy()
|
tenancy = resource.DefaultClusteredTenancy()
|
||||||
}
|
}
|
||||||
res := resourcetest.Resource(pbauth.PartitionTrafficPermissionsType, "ptp").
|
res := resourcetest.Resource(pbauth.NamespaceTrafficPermissionsType, "ntp").
|
||||||
WithTenancy(tenancy).
|
WithTenancy(tenancy).
|
||||||
WithData(t, tc.ptp).
|
WithData(t, &pbauth.PartitionTrafficPermissions{
|
||||||
|
Action: pbauth.Action_ACTION_ALLOW,
|
||||||
|
Permissions: tc.permissions,
|
||||||
|
}).
|
||||||
Build()
|
Build()
|
||||||
|
|
||||||
err := MutatePartitionTrafficPermissions(res)
|
err := MutatePartitionTrafficPermissions(res)
|
||||||
|
|
||||||
got := resourcetest.MustDecode[*pbauth.PartitionTrafficPermissions](t, res)
|
got := resourcetest.MustDecode[*pbauth.PartitionTrafficPermissions](t, res)
|
||||||
|
|
||||||
|
if tc.expectErr == "" {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prototest.AssertDeepEqual(t, tc.expect, got.Data)
|
prototest.AssertDeepEqual(t, tc.expect, got.Data.Permissions)
|
||||||
|
} else {
|
||||||
|
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cases := map[string]testcase{
|
for name, tc := range mutationTestCases() {
|
||||||
"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 {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
run(t, tc)
|
run(t, tc)
|
||||||
})
|
})
|
||||||
|
@ -596,110 +87,59 @@ func TestMutatePartitionTrafficPermissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPartitionTrafficPermissionsACLs(t *testing.T) {
|
func TestPartitionTrafficPermissionsACLs(t *testing.T) {
|
||||||
// Wire up a registry to generically invoke hooks
|
|
||||||
registry := resource.NewRegistry()
|
registry := resource.NewRegistry()
|
||||||
Register(registry)
|
Register(registry)
|
||||||
|
|
||||||
type testcase struct {
|
ptpData := &pbauth.PartitionTrafficPermissions{
|
||||||
rules string
|
|
||||||
readOK string
|
|
||||||
writeOK string
|
|
||||||
listOK string
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
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{
|
cases := map[string]resourcetest.ACLTestCase{
|
||||||
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{
|
|
||||||
"no rules": {
|
"no rules": {
|
||||||
rules: ``,
|
Rules: ``,
|
||||||
readOK: DENY,
|
Data: ptpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.DENY,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"operator read": {
|
"operator read": {
|
||||||
rules: `operator = "read"`,
|
Rules: `operator = "read"`,
|
||||||
readOK: ALLOW,
|
Data: ptpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"operator write": {
|
"operator write": {
|
||||||
rules: `operator = "write"`,
|
Rules: `operator = "write"`,
|
||||||
readOK: ALLOW,
|
Data: ptpData,
|
||||||
writeOK: ALLOW,
|
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.ALLOW,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"mesh read": {
|
"mesh read": {
|
||||||
rules: `mesh = "read"`,
|
Rules: `mesh = "read"`,
|
||||||
readOK: ALLOW,
|
Data: ptpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"partition write": {
|
"mesh write": {
|
||||||
rules: `mesh = "write"`,
|
Rules: `mesh = "write"`,
|
||||||
readOK: ALLOW,
|
Data: ptpData,
|
||||||
writeOK: ALLOW,
|
Typ: pbauth.PartitionTrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.ALLOW,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
run(t, tc)
|
resourcetest.RunACLTestCase(t, tc, registry)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
|
@ -123,8 +121,7 @@ type validator interface {
|
||||||
func validateTrafficPermissions(res *DecodedTrafficPermissions) error {
|
func validateTrafficPermissions(res *DecodedTrafficPermissions) error {
|
||||||
var merr error
|
var merr error
|
||||||
|
|
||||||
err := v.ValidateAction(res.Data)
|
if err := validateAction(res.Data); err != nil {
|
||||||
if err != nil {
|
|
||||||
merr = multierror.Append(merr, err)
|
merr = multierror.Append(merr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,202 +139,6 @@ func validateTrafficPermissions(res *DecodedTrafficPermissions) error {
|
||||||
return merr
|
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 {
|
func aclReadHookTrafficPermissions(authorizer acl.Authorizer, authzContext *acl.AuthorizerContext, res *DecodedTrafficPermissions) error {
|
||||||
return authorizer.ToAllowAuthorizer().TrafficPermissionsReadAllowed(res.Data.Destination.IdentityName, authzContext)
|
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,15 +7,15 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
|
"github.com/hashicorp/consul/version/versiontest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestValidateTrafficPermissions_ParseError(t *testing.T) {
|
func TestValidateTrafficPermissions_ParseError(t *testing.T) {
|
||||||
|
@ -31,22 +31,36 @@ func TestValidateTrafficPermissions_ParseError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateTrafficPermissions(t *testing.T) {
|
func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
|
const (
|
||||||
|
TrafficPermissions = 1 << iota
|
||||||
|
NamespaceTrafficPermissions
|
||||||
|
PartitionTrafficPermissions
|
||||||
|
)
|
||||||
|
all := TrafficPermissions | NamespaceTrafficPermissions | PartitionTrafficPermissions
|
||||||
|
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
tp *pbauth.TrafficPermissions
|
// bitmask of what xTrafficPermissions to test
|
||||||
|
xTPs int
|
||||||
|
|
||||||
|
// following fields will be used to construct all the xTrafficPermissions
|
||||||
|
destination *pbauth.Destination // used only by TrafficPermissions
|
||||||
|
action pbauth.Action
|
||||||
|
permissions []*pbauth.Permission
|
||||||
|
|
||||||
id *pbresource.ID
|
id *pbresource.ID
|
||||||
expectErr string
|
expectErr string
|
||||||
|
enterprise bool
|
||||||
}{
|
}{
|
||||||
"ok-minimal": {
|
"ok-minimal": {
|
||||||
tp: &pbauth.TrafficPermissions{
|
xTPs: all,
|
||||||
Destination: &pbauth.Destination{IdentityName: "wi-1"},
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"ok-permissions": {
|
"ok-permissions": {
|
||||||
tp: &pbauth.TrafficPermissions{
|
xTPs: all,
|
||||||
Destination: &pbauth.Destination{IdentityName: "wi-1"},
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
Permissions: []*pbauth.Permission{
|
permissions: []*pbauth.Permission{
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -72,43 +86,36 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"unspecified-action": {
|
"unspecified-action": {
|
||||||
// Any type other than the TrafficPermissions type would work
|
xTPs: all,
|
||||||
// to cause the error we are expecting
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
tp: &pbauth.TrafficPermissions{
|
action: pbauth.Action_ACTION_UNSPECIFIED,
|
||||||
Destination: &pbauth.Destination{
|
|
||||||
IdentityName: "wi1",
|
|
||||||
},
|
|
||||||
Action: pbauth.Action_ACTION_UNSPECIFIED,
|
|
||||||
Permissions: nil,
|
|
||||||
},
|
|
||||||
expectErr: `invalid "data.action" field`,
|
expectErr: `invalid "data.action" field`,
|
||||||
},
|
},
|
||||||
"invalid-action": {
|
"invalid-action": {
|
||||||
tp: &pbauth.TrafficPermissions{
|
xTPs: all,
|
||||||
Destination: &pbauth.Destination{
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
IdentityName: "wi1",
|
action: pbauth.Action(50),
|
||||||
},
|
|
||||||
Action: pbauth.Action(50),
|
|
||||||
Permissions: nil,
|
|
||||||
},
|
|
||||||
expectErr: `invalid "data.action" field`,
|
expectErr: `invalid "data.action" field`,
|
||||||
},
|
},
|
||||||
"no-destination": {
|
"deny-action": {
|
||||||
tp: &pbauth.TrafficPermissions{
|
xTPs: all,
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Permissions: nil,
|
action: pbauth.Action_ACTION_DENY,
|
||||||
|
expectErr: `invalid "data.action" field`,
|
||||||
|
enterprise: true,
|
||||||
},
|
},
|
||||||
|
"no-destination": {
|
||||||
|
xTPs: TrafficPermissions,
|
||||||
|
destination: nil,
|
||||||
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
expectErr: `invalid "data.destination" field: cannot be empty`,
|
expectErr: `invalid "data.destination" field: cannot be empty`,
|
||||||
},
|
},
|
||||||
"source-tenancy": {
|
"source-tenancy": {
|
||||||
tp: &pbauth.TrafficPermissions{
|
xTPs: all,
|
||||||
Destination: &pbauth.Destination{
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
IdentityName: "w1",
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
},
|
permissions: []*pbauth.Permission{
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -120,21 +127,18 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
DestinationRules: nil,
|
DestinationRules: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||||
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": {
|
"source-has-same-tenancy-as-tp": {
|
||||||
|
xTPs: all,
|
||||||
id: &pbresource.ID{
|
id: &pbresource.ID{
|
||||||
Tenancy: &pbresource.Tenancy{
|
Tenancy: &pbresource.Tenancy{
|
||||||
Partition: resource.DefaultPartitionName,
|
Partition: resource.DefaultPartitionName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tp: &pbauth.TrafficPermissions{
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Destination: &pbauth.Destination{
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
IdentityName: "w1",
|
permissions: []*pbauth.Permission{
|
||||||
},
|
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -146,19 +150,16 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"source-has-partition-set": {
|
"source-has-partition-set": {
|
||||||
|
xTPs: all,
|
||||||
id: &pbresource.ID{
|
id: &pbresource.ID{
|
||||||
Tenancy: &pbresource.Tenancy{
|
Tenancy: &pbresource.Tenancy{
|
||||||
Partition: resource.DefaultPartitionName,
|
Partition: resource.DefaultPartitionName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tp: &pbauth.TrafficPermissions{
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Destination: &pbauth.Destination{
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
IdentityName: "w1",
|
permissions: []*pbauth.Permission{
|
||||||
},
|
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -170,23 +171,20 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"source-has-peer-set": {
|
"source-has-peer-set": {
|
||||||
|
xTPs: all,
|
||||||
id: &pbresource.ID{
|
id: &pbresource.ID{
|
||||||
Tenancy: &pbresource.Tenancy{
|
Tenancy: &pbresource.Tenancy{
|
||||||
Partition: resource.DefaultPartitionName,
|
Partition: resource.DefaultPartitionName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tp: &pbauth.TrafficPermissions{
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Destination: &pbauth.Destination{
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
IdentityName: "w1",
|
permissions: []*pbauth.Permission{
|
||||||
},
|
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
Partition: resource.DefaultNamespaceName,
|
Partition: resource.DefaultPartitionName,
|
||||||
Peer: "peer",
|
Peer: "peer",
|
||||||
SamenessGroup: "",
|
SamenessGroup: "",
|
||||||
},
|
},
|
||||||
|
@ -194,23 +192,20 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"source-has-sameness-group-set": {
|
"source-has-sameness-group-set": {
|
||||||
|
xTPs: all,
|
||||||
id: &pbresource.ID{
|
id: &pbresource.ID{
|
||||||
Tenancy: &pbresource.Tenancy{
|
Tenancy: &pbresource.Tenancy{
|
||||||
Partition: resource.DefaultPartitionName,
|
Partition: resource.DefaultPartitionName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tp: &pbauth.TrafficPermissions{
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Destination: &pbauth.Destination{
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
IdentityName: "w1",
|
permissions: []*pbauth.Permission{
|
||||||
},
|
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
Partition: resource.DefaultNamespaceName,
|
Partition: resource.DefaultPartitionName,
|
||||||
Peer: resource.DefaultPeerName,
|
Peer: resource.DefaultPeerName,
|
||||||
SamenessGroup: "sg1",
|
SamenessGroup: "sg1",
|
||||||
},
|
},
|
||||||
|
@ -218,19 +213,16 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"source-has-peer-and-partition-set": {
|
"source-has-peer-and-partition-set": {
|
||||||
|
xTPs: all,
|
||||||
id: &pbresource.ID{
|
id: &pbresource.ID{
|
||||||
Tenancy: &pbresource.Tenancy{
|
Tenancy: &pbresource.Tenancy{
|
||||||
Partition: resource.DefaultPartitionName,
|
Partition: resource.DefaultPartitionName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tp: &pbauth.TrafficPermissions{
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Destination: &pbauth.Destination{
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
IdentityName: "w1",
|
permissions: []*pbauth.Permission{
|
||||||
},
|
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -241,21 +233,18 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||||
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": {
|
"source-has-sameness-group-and-partition-set": {
|
||||||
|
xTPs: all,
|
||||||
id: &pbresource.ID{
|
id: &pbresource.ID{
|
||||||
Tenancy: &pbresource.Tenancy{
|
Tenancy: &pbresource.Tenancy{
|
||||||
Partition: resource.DefaultPartitionName,
|
Partition: resource.DefaultPartitionName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tp: &pbauth.TrafficPermissions{
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Destination: &pbauth.Destination{
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
IdentityName: "w1",
|
permissions: []*pbauth.Permission{
|
||||||
},
|
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -266,21 +255,18 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||||
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": {
|
"source-has-sameness-group-and-partition-peer-set": {
|
||||||
|
xTPs: all,
|
||||||
id: &pbresource.ID{
|
id: &pbresource.ID{
|
||||||
Tenancy: &pbresource.Tenancy{
|
Tenancy: &pbresource.Tenancy{
|
||||||
Partition: resource.DefaultPartitionName,
|
Partition: resource.DefaultPartitionName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tp: &pbauth.TrafficPermissions{
|
destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
Destination: &pbauth.Destination{
|
action: pbauth.Action_ACTION_ALLOW,
|
||||||
IdentityName: "w1",
|
permissions: []*pbauth.Permission{
|
||||||
},
|
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -291,26 +277,57 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
expectErr: `invalid element at index 0 of list "permissions": invalid element at index 0 of list "sources": permissions sources may not specify partitions, peers, and sameness_groups together`,
|
||||||
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 {
|
for n, tc := range cases {
|
||||||
t.Run(n, func(t *testing.T) {
|
t.Run(n, func(t *testing.T) {
|
||||||
resBuilder := resourcetest.Resource(pbauth.TrafficPermissionsType, "tp").
|
check := func(t *testing.T, typ *pbresource.Type, data protoreflect.ProtoMessage, validateFunc resource.ValidationHook) {
|
||||||
WithData(t, tc.tp)
|
resBuilder := resourcetest.Resource(typ, "tp").
|
||||||
|
WithData(t, data)
|
||||||
if tc.id != nil {
|
if tc.id != nil {
|
||||||
resBuilder = resBuilder.WithTenancy(tc.id.Tenancy)
|
resBuilder = resBuilder.WithTenancy(tc.id.Tenancy)
|
||||||
}
|
}
|
||||||
res := resBuilder.Build()
|
res := resBuilder.Build()
|
||||||
|
err := validateFunc(res)
|
||||||
err := ValidateTrafficPermissions(res)
|
|
||||||
if tc.expectErr == "" {
|
if tc.expectErr == "" {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
} else if tc.enterprise && versiontest.IsEnterprise() {
|
||||||
|
require.NoError(t, err)
|
||||||
} else {
|
} else {
|
||||||
|
// Expect error in CE, not ENT
|
||||||
testutil.RequireErrorContains(t, err, tc.expectErr)
|
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if tc.xTPs&TrafficPermissions != 0 {
|
||||||
|
t.Run("TrafficPermissions", func(t *testing.T) {
|
||||||
|
tp := &pbauth.TrafficPermissions{
|
||||||
|
Destination: tc.destination,
|
||||||
|
Action: tc.action,
|
||||||
|
Permissions: tc.permissions,
|
||||||
|
}
|
||||||
|
check(t, pbauth.TrafficPermissionsType, tp, ValidateTrafficPermissions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if tc.xTPs&NamespaceTrafficPermissions != 0 {
|
||||||
|
t.Run("NamespaceTrafficPermissions", func(t *testing.T) {
|
||||||
|
ntp := &pbauth.NamespaceTrafficPermissions{
|
||||||
|
Action: tc.action,
|
||||||
|
Permissions: tc.permissions,
|
||||||
|
}
|
||||||
|
check(t, pbauth.NamespaceTrafficPermissionsType, ntp, ValidateNamespaceTrafficPermissions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if tc.xTPs&PartitionTrafficPermissions != 0 {
|
||||||
|
t.Run("PartitionTrafficPermissions", func(t *testing.T) {
|
||||||
|
ptp := &pbauth.PartitionTrafficPermissions{
|
||||||
|
Action: tc.action,
|
||||||
|
Permissions: tc.permissions,
|
||||||
|
}
|
||||||
|
check(t, pbauth.PartitionTrafficPermissionsType, ptp, ValidatePartitionTrafficPermissions)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -580,44 +597,21 @@ func TestValidateTrafficPermissions_Permissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMutateTrafficPermissions(t *testing.T) {
|
type mutationTestCase struct {
|
||||||
type testcase struct {
|
tenancy *pbresource.Tenancy
|
||||||
policyTenancy *pbresource.Tenancy
|
permissions []*pbauth.Permission
|
||||||
tp *pbauth.TrafficPermissions
|
expect []*pbauth.Permission
|
||||||
expect *pbauth.TrafficPermissions
|
|
||||||
expectErr string
|
expectErr string
|
||||||
}
|
}
|
||||||
|
|
||||||
run := func(t *testing.T, tc testcase) {
|
func mutationTestCases() map[string]mutationTestCase {
|
||||||
tenancy := tc.policyTenancy
|
return map[string]mutationTestCase{
|
||||||
if tenancy == nil {
|
"empty": {
|
||||||
tenancy = resource.DefaultNamespacedTenancy()
|
permissions: nil,
|
||||||
}
|
expect: nil,
|
||||||
res := resourcetest.Resource(pbauth.TrafficPermissionsType, "api").
|
|
||||||
WithTenancy(tenancy).
|
|
||||||
WithData(t, tc.tp).
|
|
||||||
Build()
|
|
||||||
|
|
||||||
err := MutateTrafficPermissions(res)
|
|
||||||
|
|
||||||
got := resourcetest.MustDecode[*pbauth.TrafficPermissions](t, res)
|
|
||||||
|
|
||||||
if tc.expectErr == "" {
|
|
||||||
require.NoError(t, err)
|
|
||||||
prototest.AssertDeepEqual(t, tc.expect, got.Data)
|
|
||||||
} else {
|
|
||||||
testutil.RequireErrorContains(t, err, tc.expectErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cases := map[string]testcase{
|
|
||||||
"empty-1": {
|
|
||||||
tp: &pbauth.TrafficPermissions{},
|
|
||||||
expect: &pbauth.TrafficPermissions{},
|
|
||||||
},
|
},
|
||||||
"kitchen-sink-default-partition": {
|
"kitchen-sink-default-partition": {
|
||||||
tp: &pbauth.TrafficPermissions{
|
permissions: []*pbauth.Permission{
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{},
|
{},
|
||||||
|
@ -640,9 +634,7 @@ func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
expect: []*pbauth.Permission{
|
||||||
expect: &pbauth.TrafficPermissions{
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -674,10 +666,8 @@ func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"kitchen-sink-excludes-default-partition": {
|
"kitchen-sink-excludes-default-partition": {
|
||||||
tp: &pbauth.TrafficPermissions{
|
permissions: []*pbauth.Permission{
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -704,9 +694,7 @@ func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
expect: []*pbauth.Permission{
|
||||||
expect: &pbauth.TrafficPermissions{
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -744,14 +732,12 @@ func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"kitchen-sink-non-default-partition": {
|
"kitchen-sink-non-default-partition": {
|
||||||
policyTenancy: &pbresource.Tenancy{
|
tenancy: &pbresource.Tenancy{
|
||||||
Partition: "ap1",
|
Partition: "ap1",
|
||||||
Namespace: "ns3",
|
Namespace: "ns3",
|
||||||
},
|
},
|
||||||
tp: &pbauth.TrafficPermissions{
|
permissions: []*pbauth.Permission{
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{},
|
{},
|
||||||
|
@ -781,9 +767,7 @@ func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
expect: []*pbauth.Permission{
|
||||||
expect: &pbauth.TrafficPermissions{
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -828,14 +812,12 @@ func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"kitchen-sink-excludes-non-default-partition": {
|
"kitchen-sink-excludes-non-default-partition": {
|
||||||
policyTenancy: &pbresource.Tenancy{
|
tenancy: &pbresource.Tenancy{
|
||||||
Partition: "ap1",
|
Partition: "ap1",
|
||||||
Namespace: "ns3",
|
Namespace: "ns3",
|
||||||
},
|
},
|
||||||
tp: &pbauth.TrafficPermissions{
|
permissions: []*pbauth.Permission{
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -865,9 +847,7 @@ func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
expect: []*pbauth.Permission{
|
||||||
expect: &pbauth.TrafficPermissions{
|
|
||||||
Permissions: []*pbauth.Permission{
|
|
||||||
{
|
{
|
||||||
Sources: []*pbauth.Source{
|
Sources: []*pbauth.Source{
|
||||||
{
|
{
|
||||||
|
@ -912,10 +892,37 @@ func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
|
run := func(t *testing.T, tc mutationTestCase) {
|
||||||
|
tenancy := tc.tenancy
|
||||||
|
if tenancy == nil {
|
||||||
|
tenancy = resource.DefaultNamespacedTenancy()
|
||||||
|
}
|
||||||
|
res := resourcetest.Resource(pbauth.TrafficPermissionsType, "api").
|
||||||
|
WithTenancy(tenancy).
|
||||||
|
WithData(t, &pbauth.TrafficPermissions{
|
||||||
|
Destination: &pbauth.Destination{IdentityName: "wi1"},
|
||||||
|
Action: pbauth.Action_ACTION_ALLOW,
|
||||||
|
Permissions: tc.permissions,
|
||||||
|
}).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
err := MutateTrafficPermissions(res)
|
||||||
|
|
||||||
|
got := resourcetest.MustDecode[*pbauth.TrafficPermissions](t, res)
|
||||||
|
|
||||||
|
if tc.expectErr == "" {
|
||||||
|
require.NoError(t, err)
|
||||||
|
prototest.AssertDeepEqual(t, tc.expect, got.Data.Permissions)
|
||||||
|
} else {
|
||||||
|
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range mutationTestCases() {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
run(t, tc)
|
run(t, tc)
|
||||||
})
|
})
|
||||||
|
@ -923,130 +930,83 @@ func TestMutateTrafficPermissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTrafficPermissionsACLs(t *testing.T) {
|
func TestTrafficPermissionsACLs(t *testing.T) {
|
||||||
// Wire up a registry to generically invoke hooks
|
|
||||||
registry := resource.NewRegistry()
|
registry := resource.NewRegistry()
|
||||||
Register(registry)
|
Register(registry)
|
||||||
|
|
||||||
type testcase struct {
|
|
||||||
rules string
|
|
||||||
check func(t *testing.T, authz acl.Authorizer, res *pbresource.Resource)
|
|
||||||
readOK string
|
|
||||||
writeOK string
|
|
||||||
listOK string
|
|
||||||
}
|
|
||||||
|
|
||||||
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.TrafficPermissionsType)
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
run := func(t *testing.T, tc testcase) {
|
|
||||||
tpData := &pbauth.TrafficPermissions{
|
tpData := &pbauth.TrafficPermissions{
|
||||||
Destination: &pbauth.Destination{IdentityName: "wi1"},
|
Destination: &pbauth.Destination{IdentityName: "wi1"},
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
Action: pbauth.Action_ACTION_ALLOW,
|
||||||
}
|
}
|
||||||
res := resourcetest.Resource(pbauth.TrafficPermissionsType, "tp1").
|
cases := map[string]resourcetest.ACLTestCase{
|
||||||
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{
|
|
||||||
"no rules": {
|
"no rules": {
|
||||||
rules: ``,
|
Rules: ``,
|
||||||
readOK: DENY,
|
Data: tpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.TrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.DENY,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"workload identity w1 read, no intentions": {
|
"workload identity w1 read, no intentions": {
|
||||||
rules: `identity "wi1" { policy = "read" }`,
|
Rules: `identity "wi1" { policy = "read" }`,
|
||||||
readOK: ALLOW,
|
Data: tpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.TrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"workload identity w1 read, deny intentions": {
|
"workload identity w1 read, deny intentions": {
|
||||||
rules: `identity "wi1" { policy = "read", intentions = "deny" }`,
|
Rules: `identity "wi1" { policy = "read", intentions = "deny" }`,
|
||||||
readOK: DENY,
|
Data: tpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.TrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.DENY,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"workload identity w1 read, intentions read": {
|
"workload identity w1 read, intentions read": {
|
||||||
rules: `identity "wi1" { policy = "read", intentions = "read" }`,
|
Rules: `identity "wi1" { policy = "read", intentions = "read" }`,
|
||||||
readOK: ALLOW,
|
Data: tpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.TrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"workload identity w1 write, write intentions": {
|
"workload identity w1 read, intentions write": {
|
||||||
rules: `identity "wi1" { policy = "read", intentions = "write" }`,
|
Rules: `identity "wi1" { policy = "read", intentions = "write" }`,
|
||||||
readOK: ALLOW,
|
Data: tpData,
|
||||||
writeOK: ALLOW,
|
Typ: pbauth.TrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.ALLOW,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"workload identity w1 write, deny intentions": {
|
"workload identity w1 write, deny intentions": {
|
||||||
rules: `identity "wi1" { policy = "write", intentions = "deny" }`,
|
Rules: `identity "wi1" { policy = "write", intentions = "deny" }`,
|
||||||
readOK: DENY,
|
Data: tpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.TrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.DENY,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"workload identity w1 write, intentions read": {
|
"workload identity w1 write, intentions read": {
|
||||||
rules: `identity "wi1" { policy = "write", intentions = "read" }`,
|
Rules: `identity "wi1" { policy = "write", intentions = "read" }`,
|
||||||
readOK: ALLOW,
|
Data: tpData,
|
||||||
writeOK: DENY,
|
Typ: pbauth.TrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.DENY,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
"workload identity w1 write, intentions write": {
|
"workload identity w1 write, intentions write": {
|
||||||
rules: `identity "wi1" { policy = "write", intentions = "write" }`,
|
Rules: `identity "wi1" { policy = "write", intentions = "write" }`,
|
||||||
readOK: ALLOW,
|
Data: tpData,
|
||||||
writeOK: ALLOW,
|
Typ: pbauth.TrafficPermissionsType,
|
||||||
listOK: DEFAULT,
|
ReadOK: resourcetest.ALLOW,
|
||||||
|
WriteOK: resourcetest.ALLOW,
|
||||||
|
ListOK: resourcetest.DEFAULT,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
run(t, tc)
|
resourcetest.RunACLTestCase(t, tc, registry)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
// Copyright (c) HashiCorp, Inc.
|
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
|
||||||
|
|
||||||
//go:build !consulent
|
|
||||||
|
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
|
||||||
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
|
|
||||||
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",
|
|
||||||
Wrapped: errors.New("action must be allow"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ validator = (*actionValidator)(nil)
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
|
"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 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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 == ""
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
//go:build !consulent
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
|
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateAction(data interface{ GetAction() pbauth.Action }) error {
|
||||||
|
switch data.GetAction() {
|
||||||
|
case pbauth.Action_ACTION_ALLOW:
|
||||||
|
default:
|
||||||
|
return resource.ErrInvalidField{
|
||||||
|
Name: "data.action",
|
||||||
|
Wrapped: errors.New("action must be allow"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue