mirror of https://github.com/status-im/consul.git
catalog: add FailoverPolicy ACL hook tenancy test (#19179)
This commit is contained in:
parent
df8ea430c6
commit
6741392a4f
|
@ -4,12 +4,14 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"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"
|
||||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||||
|
@ -138,7 +140,7 @@ func TestMutateFailoverPolicy(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"dest ref tenancy defaulting": {
|
"dest ref tenancy defaulting": {
|
||||||
policyTenancy: newTestTenancy("foo.bar"),
|
policyTenancy: resourcetest.Tenancy("foo.bar"),
|
||||||
failover: &pbcatalog.FailoverPolicy{
|
failover: &pbcatalog.FailoverPolicy{
|
||||||
Config: &pbcatalog.FailoverConfig{
|
Config: &pbcatalog.FailoverConfig{
|
||||||
Mode: pbcatalog.FailoverMode_FAILOVER_MODE_SEQUENTIAL,
|
Mode: pbcatalog.FailoverMode_FAILOVER_MODE_SEQUENTIAL,
|
||||||
|
@ -683,54 +685,149 @@ func TestFailoverPolicyACLs(t *testing.T) {
|
||||||
registry := resource.NewRegistry()
|
registry := resource.NewRegistry()
|
||||||
Register(registry)
|
Register(registry)
|
||||||
|
|
||||||
failoverData := &pbcatalog.FailoverPolicy{
|
newFailover := func(t *testing.T, name, tenancyStr string, destRefs []*pbresource.Reference) []*pbresource.Resource {
|
||||||
Config: &pbcatalog.FailoverConfig{
|
var dr []*pbcatalog.FailoverDestination
|
||||||
Destinations: []*pbcatalog.FailoverDestination{
|
for _, destRef := range destRefs {
|
||||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup")},
|
dr = append(dr, &pbcatalog.FailoverDestination{Ref: destRef})
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cases := map[string]resourcetest.ACLTestCase{
|
res1 := resourcetest.Resource(pbcatalog.FailoverPolicyType, name).
|
||||||
"no rules": {
|
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||||
Rules: ``,
|
WithData(t, &pbcatalog.FailoverPolicy{
|
||||||
Data: failoverData,
|
Config: &pbcatalog.FailoverConfig{Destinations: dr},
|
||||||
Typ: pbcatalog.FailoverPolicyType,
|
}).
|
||||||
ReadOK: resourcetest.DENY,
|
Build()
|
||||||
WriteOK: resourcetest.DENY,
|
resourcetest.ValidateAndNormalize(t, registry, res1)
|
||||||
ListOK: resourcetest.DEFAULT,
|
|
||||||
},
|
res2 := resourcetest.Resource(pbcatalog.FailoverPolicyType, name).
|
||||||
"service test read": {
|
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||||
Rules: `service "test" { policy = "read" }`,
|
WithData(t, &pbcatalog.FailoverPolicy{
|
||||||
Data: failoverData,
|
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||||
Typ: pbcatalog.FailoverPolicyType,
|
"http": {Destinations: dr},
|
||||||
ReadOK: resourcetest.ALLOW,
|
|
||||||
WriteOK: resourcetest.DENY,
|
|
||||||
ListOK: resourcetest.DEFAULT,
|
|
||||||
},
|
|
||||||
"service test write": {
|
|
||||||
Rules: `service "test" { policy = "write" }`,
|
|
||||||
Data: failoverData,
|
|
||||||
Typ: pbcatalog.FailoverPolicyType,
|
|
||||||
ReadOK: resourcetest.ALLOW,
|
|
||||||
WriteOK: resourcetest.DENY,
|
|
||||||
ListOK: resourcetest.DEFAULT,
|
|
||||||
},
|
|
||||||
"service test write and api-backup read": {
|
|
||||||
Rules: `service "test" { policy = "write" } service "api-backup" { policy = "read" }`,
|
|
||||||
Data: failoverData,
|
|
||||||
Typ: pbcatalog.FailoverPolicyType,
|
|
||||||
ReadOK: resourcetest.ALLOW,
|
|
||||||
WriteOK: resourcetest.ALLOW,
|
|
||||||
ListOK: resourcetest.DEFAULT,
|
|
||||||
},
|
},
|
||||||
|
}).
|
||||||
|
Build()
|
||||||
|
resourcetest.ValidateAndNormalize(t, registry, res2)
|
||||||
|
|
||||||
|
return []*pbresource.Resource{res1, res2}
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
type testcase struct {
|
||||||
|
res *pbresource.Resource
|
||||||
|
rules string
|
||||||
|
check func(t *testing.T, authz acl.Authorizer, res *pbresource.Resource)
|
||||||
|
readOK string
|
||||||
|
writeOK string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
DENY = resourcetest.DENY
|
||||||
|
ALLOW = resourcetest.ALLOW
|
||||||
|
DEFAULT = resourcetest.DEFAULT
|
||||||
|
)
|
||||||
|
|
||||||
|
serviceRef := func(tenancy, name string) *pbresource.Reference {
|
||||||
|
return newRefWithTenancy(pbcatalog.ServiceType, tenancy, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
resOneDest := func(tenancy, destTenancy string) []*pbresource.Resource {
|
||||||
|
return newFailover(t, "api", tenancy, []*pbresource.Reference{
|
||||||
|
serviceRef(destTenancy, "dest1"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resTwoDests := func(tenancy, destTenancy string) []*pbresource.Resource {
|
||||||
|
return newFailover(t, "api", tenancy, []*pbresource.Reference{
|
||||||
|
serviceRef(destTenancy, "dest1"),
|
||||||
|
serviceRef(destTenancy, "dest2"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
run := func(t *testing.T, name string, tc resourcetest.ACLTestCase) {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
resourcetest.RunACLTestCase(t, tc, registry)
|
resourcetest.RunACLTestCase(t, tc, registry)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isEnterprise := (structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty() == "default")
|
||||||
|
|
||||||
|
serviceRead := func(partition, namespace, name string) string {
|
||||||
|
if isEnterprise {
|
||||||
|
return fmt.Sprintf(` partition %q { namespace %q { service %q { policy = "read" } } }`, partition, namespace, name)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(` service %q { policy = "read" } `, name)
|
||||||
|
}
|
||||||
|
serviceWrite := func(partition, namespace, name string) string {
|
||||||
|
if isEnterprise {
|
||||||
|
return fmt.Sprintf(` partition %q { namespace %q { service %q { policy = "write" } } }`, partition, namespace, name)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(` service %q { policy = "write" } `, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert := func(t *testing.T, name string, rules string, resList []*pbresource.Resource, readOK, writeOK string) {
|
||||||
|
for i, res := range resList {
|
||||||
|
tc := resourcetest.ACLTestCase{
|
||||||
|
AuthCtx: resource.AuthorizerContext(res.Id.Tenancy),
|
||||||
|
Res: res,
|
||||||
|
Rules: rules,
|
||||||
|
ReadOK: readOK,
|
||||||
|
WriteOK: writeOK,
|
||||||
|
ListOK: DEFAULT,
|
||||||
|
}
|
||||||
|
run(t, fmt.Sprintf("%s-%d", name, i), tc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tenancies := []string{"default.default"}
|
||||||
|
if isEnterprise {
|
||||||
|
tenancies = append(tenancies, "default.foo", "alpha.default", "alpha.foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, policyTenancyStr := range tenancies {
|
||||||
|
t.Run("policy tenancy: "+policyTenancyStr, func(t *testing.T) {
|
||||||
|
for _, destTenancyStr := range tenancies {
|
||||||
|
t.Run("dest tenancy: "+destTenancyStr, func(t *testing.T) {
|
||||||
|
for _, aclTenancyStr := range tenancies {
|
||||||
|
t.Run("acl tenancy: "+aclTenancyStr, func(t *testing.T) {
|
||||||
|
aclTenancy := resourcetest.Tenancy(aclTenancyStr)
|
||||||
|
|
||||||
|
maybe := func(match string, parentOnly bool) string {
|
||||||
|
if policyTenancyStr != aclTenancyStr {
|
||||||
|
return DENY
|
||||||
|
}
|
||||||
|
if !parentOnly && destTenancyStr != aclTenancyStr {
|
||||||
|
return DENY
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("no rules", func(t *testing.T) {
|
||||||
|
rules := ``
|
||||||
|
assert(t, "1dest", rules, resOneDest(policyTenancyStr, destTenancyStr), DENY, DENY)
|
||||||
|
assert(t, "2dests", rules, resTwoDests(policyTenancyStr, destTenancyStr), DENY, DENY)
|
||||||
|
})
|
||||||
|
t.Run("api:read", func(t *testing.T) {
|
||||||
|
rules := serviceRead(aclTenancy.Partition, aclTenancy.Namespace, "api")
|
||||||
|
assert(t, "1dest", rules, resOneDest(policyTenancyStr, destTenancyStr), maybe(ALLOW, true), DENY)
|
||||||
|
assert(t, "2dests", rules, resTwoDests(policyTenancyStr, destTenancyStr), maybe(ALLOW, true), DENY)
|
||||||
|
})
|
||||||
|
t.Run("api:write", func(t *testing.T) {
|
||||||
|
rules := serviceWrite(aclTenancy.Partition, aclTenancy.Namespace, "api")
|
||||||
|
assert(t, "1dest", rules, resOneDest(policyTenancyStr, destTenancyStr), maybe(ALLOW, true), DENY)
|
||||||
|
assert(t, "2dests", rules, resTwoDests(policyTenancyStr, destTenancyStr), maybe(ALLOW, true), DENY)
|
||||||
|
})
|
||||||
|
t.Run("api:write dest1:read", func(t *testing.T) {
|
||||||
|
rules := serviceWrite(aclTenancy.Partition, aclTenancy.Namespace, "api") +
|
||||||
|
serviceRead(aclTenancy.Partition, aclTenancy.Namespace, "dest1")
|
||||||
|
assert(t, "1dest", rules, resOneDest(policyTenancyStr, destTenancyStr), maybe(ALLOW, true), maybe(ALLOW, false))
|
||||||
|
assert(t, "2dests", rules, resTwoDests(policyTenancyStr, destTenancyStr), maybe(ALLOW, true), DENY)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRef(typ *pbresource.Type, name string) *pbresource.Reference {
|
func newRef(typ *pbresource.Type, name string) *pbresource.Reference {
|
||||||
|
@ -741,7 +838,7 @@ func newRef(typ *pbresource.Type, name string) *pbresource.Reference {
|
||||||
|
|
||||||
func newRefWithTenancy(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
func newRefWithTenancy(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
||||||
return resourcetest.Resource(typ, name).
|
return resourcetest.Resource(typ, name).
|
||||||
WithTenancy(newTestTenancy(tenancyStr)).
|
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||||
Reference("")
|
Reference("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,22 +847,3 @@ func newRefWithPeer(typ *pbresource.Type, name string, peer string) *pbresource.
|
||||||
ref.Tenancy.PeerName = peer
|
ref.Tenancy.PeerName = peer
|
||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestTenancy(s string) *pbresource.Tenancy {
|
|
||||||
parts := strings.Split(s, ".")
|
|
||||||
switch len(parts) {
|
|
||||||
case 0:
|
|
||||||
return resource.DefaultClusteredTenancy()
|
|
||||||
case 1:
|
|
||||||
v := resource.DefaultPartitionedTenancy()
|
|
||||||
v.Partition = parts[0]
|
|
||||||
return v
|
|
||||||
case 2:
|
|
||||||
v := resource.DefaultNamespacedTenancy()
|
|
||||||
v.Partition = parts[0]
|
|
||||||
v.Namespace = parts[1]
|
|
||||||
return v
|
|
||||||
default:
|
|
||||||
return &pbresource.Tenancy{Partition: "BAD", Namespace: "BAD", PeerName: "BAD"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue