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
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"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/resourcetest"
|
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||
|
@ -138,7 +140,7 @@ func TestMutateFailoverPolicy(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"dest ref tenancy defaulting": {
|
||||
policyTenancy: newTestTenancy("foo.bar"),
|
||||
policyTenancy: resourcetest.Tenancy("foo.bar"),
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
Config: &pbcatalog.FailoverConfig{
|
||||
Mode: pbcatalog.FailoverMode_FAILOVER_MODE_SEQUENTIAL,
|
||||
|
@ -683,54 +685,149 @@ func TestFailoverPolicyACLs(t *testing.T) {
|
|||
registry := resource.NewRegistry()
|
||||
Register(registry)
|
||||
|
||||
failoverData := &pbcatalog.FailoverPolicy{
|
||||
Config: &pbcatalog.FailoverConfig{
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup")},
|
||||
},
|
||||
},
|
||||
newFailover := func(t *testing.T, name, tenancyStr string, destRefs []*pbresource.Reference) []*pbresource.Resource {
|
||||
var dr []*pbcatalog.FailoverDestination
|
||||
for _, destRef := range destRefs {
|
||||
dr = append(dr, &pbcatalog.FailoverDestination{Ref: destRef})
|
||||
}
|
||||
|
||||
res1 := resourcetest.Resource(pbcatalog.FailoverPolicyType, name).
|
||||
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||
WithData(t, &pbcatalog.FailoverPolicy{
|
||||
Config: &pbcatalog.FailoverConfig{Destinations: dr},
|
||||
}).
|
||||
Build()
|
||||
resourcetest.ValidateAndNormalize(t, registry, res1)
|
||||
|
||||
res2 := resourcetest.Resource(pbcatalog.FailoverPolicyType, name).
|
||||
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||
WithData(t, &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": {Destinations: dr},
|
||||
},
|
||||
}).
|
||||
Build()
|
||||
resourcetest.ValidateAndNormalize(t, registry, res2)
|
||||
|
||||
return []*pbresource.Resource{res1, res2}
|
||||
}
|
||||
|
||||
cases := map[string]resourcetest.ACLTestCase{
|
||||
"no rules": {
|
||||
Rules: ``,
|
||||
Data: failoverData,
|
||||
Typ: pbcatalog.FailoverPolicyType,
|
||||
ReadOK: resourcetest.DENY,
|
||||
WriteOK: resourcetest.DENY,
|
||||
ListOK: resourcetest.DEFAULT,
|
||||
},
|
||||
"service test read": {
|
||||
Rules: `service "test" { policy = "read" }`,
|
||||
Data: failoverData,
|
||||
Typ: pbcatalog.FailoverPolicyType,
|
||||
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,
|
||||
},
|
||||
type testcase struct {
|
||||
res *pbresource.Resource
|
||||
rules string
|
||||
check func(t *testing.T, authz acl.Authorizer, res *pbresource.Resource)
|
||||
readOK string
|
||||
writeOK string
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
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) {
|
||||
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 {
|
||||
|
@ -741,7 +838,7 @@ func newRef(typ *pbresource.Type, name string) *pbresource.Reference {
|
|||
|
||||
func newRefWithTenancy(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference {
|
||||
return resourcetest.Resource(typ, name).
|
||||
WithTenancy(newTestTenancy(tenancyStr)).
|
||||
WithTenancy(resourcetest.Tenancy(tenancyStr)).
|
||||
Reference("")
|
||||
}
|
||||
|
||||
|
@ -750,22 +847,3 @@ func newRefWithPeer(typ *pbresource.Type, name string, peer string) *pbresource.
|
|||
ref.Tenancy.PeerName = peer
|
||||
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