acl: small resolver changes to account for partitions (#11052)
Also refactoring the enterprise side of a test to make it easier to reason about.
This commit is contained in:
parent
08b222cfc3
commit
494764ee2d
|
@ -757,10 +757,12 @@ func (r *ACLResolver) filterPoliciesByScope(policies structs.ACLPolicies) struct
|
|||
}
|
||||
|
||||
func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) (structs.ACLPolicies, error) {
|
||||
policyIDs := identity.PolicyIDs()
|
||||
roleIDs := identity.RoleIDs()
|
||||
serviceIdentities := identity.ServiceIdentityList()
|
||||
nodeIdentities := identity.NodeIdentityList()
|
||||
var (
|
||||
policyIDs = identity.PolicyIDs()
|
||||
roleIDs = identity.RoleIDs()
|
||||
serviceIdentities = identity.ServiceIdentityList()
|
||||
nodeIdentities = identity.NodeIdentityList()
|
||||
)
|
||||
|
||||
if len(policyIDs) == 0 && len(serviceIdentities) == 0 && len(roleIDs) == 0 && len(nodeIdentities) == 0 {
|
||||
policy := identity.EmbeddedPolicy()
|
||||
|
@ -794,7 +796,7 @@ func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) (
|
|||
|
||||
// Generate synthetic policies for all service identities in effect.
|
||||
syntheticPolicies := r.synthesizePoliciesForServiceIdentities(serviceIdentities, identity.EnterpriseMetadata())
|
||||
syntheticPolicies = append(syntheticPolicies, r.synthesizePoliciesForNodeIdentities(nodeIdentities)...)
|
||||
syntheticPolicies = append(syntheticPolicies, r.synthesizePoliciesForNodeIdentities(nodeIdentities, identity.EnterpriseMetadata())...)
|
||||
|
||||
// For the new ACLs policy replication is mandatory for correct operation on servers. Therefore
|
||||
// we only attempt to resolve policies locally
|
||||
|
@ -805,6 +807,7 @@ func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) (
|
|||
|
||||
policies = append(policies, syntheticPolicies...)
|
||||
filtered := r.filterPoliciesByScope(policies)
|
||||
// TODO(partitions,acls): filter these by the partition/namespace of the token trying to use them?
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
|
@ -821,14 +824,14 @@ func (r *ACLResolver) synthesizePoliciesForServiceIdentities(serviceIdentities [
|
|||
return syntheticPolicies
|
||||
}
|
||||
|
||||
func (r *ACLResolver) synthesizePoliciesForNodeIdentities(nodeIdentities []*structs.ACLNodeIdentity) []*structs.ACLPolicy {
|
||||
func (r *ACLResolver) synthesizePoliciesForNodeIdentities(nodeIdentities []*structs.ACLNodeIdentity, entMeta *structs.EnterpriseMeta) []*structs.ACLPolicy {
|
||||
if len(nodeIdentities) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
syntheticPolicies := make([]*structs.ACLPolicy, 0, len(nodeIdentities))
|
||||
for _, n := range nodeIdentities {
|
||||
syntheticPolicies = append(syntheticPolicies, n.SyntheticPolicy())
|
||||
syntheticPolicies = append(syntheticPolicies, n.SyntheticPolicy(entMeta))
|
||||
}
|
||||
|
||||
return syntheticPolicies
|
||||
|
@ -1242,6 +1245,7 @@ func (r *ACLResolver) ResolveTokenToIdentityAndAuthorizer(token string) (structs
|
|||
}
|
||||
|
||||
if r.delegate.UseLegacyACLs() {
|
||||
// TODO(partitions,acls): do we have to care about legacy acls?
|
||||
identity, authorizer, err := r.resolveTokenLegacy(token)
|
||||
r.handleACLDisabledError(err)
|
||||
return identity, authorizer, err
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
)
|
||||
|
@ -26,3 +28,10 @@ type EnterpriseACLResolverTestDelegate struct{}
|
|||
func (d *EnterpriseACLResolverTestDelegate) RPC(string, interface{}, interface{}) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (d *EnterpriseACLResolverTestDelegate) UseTestLocalData(data []interface{}) {
|
||||
if len(data) > 0 {
|
||||
panic(fmt.Sprintf("unexpected data type: %T", data[0]))
|
||||
}
|
||||
}
|
||||
func (d *EnterpriseACLResolverTestDelegate) UseDefaultData() {}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -81,19 +82,6 @@ func resolveToken(t *testing.T, r *ACLResolver, token string) acl.Authorizer {
|
|||
|
||||
func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
|
||||
switch token {
|
||||
case "missing-policy":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
|
||||
SecretID: "b1b6be70-ed2e-4c80-8495-bdb3db110b1e",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{
|
||||
ID: "not-found",
|
||||
},
|
||||
{
|
||||
ID: "acl-ro",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case "missing-role":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
|
||||
|
@ -107,29 +95,6 @@ func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
|
|||
},
|
||||
},
|
||||
}, nil
|
||||
case "missing-policy-on-role":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
|
||||
SecretID: "b1b6be70-ed2e-4c80-8495-bdb3db110b1e",
|
||||
Roles: []structs.ACLTokenRoleLink{
|
||||
{
|
||||
ID: "missing-policy",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case "legacy-management":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "d109a033-99d1-47e2-a711-d6593373a973",
|
||||
SecretID: "415cd1e1-1493-4fb4-827d-d762ed9cfe7c",
|
||||
Type: structs.ACLTokenTypeManagement,
|
||||
}, nil
|
||||
case "legacy-client":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "b7375838-b104-4a25-b457-329d939bf257",
|
||||
SecretID: "03f49328-c23c-4b26-92a2-3b898332400d",
|
||||
Type: structs.ACLTokenTypeClient,
|
||||
Rules: `service "" { policy = "read" }`,
|
||||
}, nil
|
||||
case "found":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df",
|
||||
|
@ -173,58 +138,6 @@ func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
|
|||
},
|
||||
},
|
||||
}, nil
|
||||
case "found-synthetic-policy-1":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "f6c5a5fb-4da4-422b-9abf-2c942813fc71",
|
||||
SecretID: "55cb7d69-2bea-42c3-a68f-2a1443d2abbc",
|
||||
ServiceIdentities: []*structs.ACLServiceIdentity{
|
||||
{
|
||||
ServiceName: "service1",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case "found-synthetic-policy-2":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "7c87dfad-be37-446e-8305-299585677cb5",
|
||||
SecretID: "dfca9676-ac80-453a-837b-4c0cf923473c",
|
||||
ServiceIdentities: []*structs.ACLServiceIdentity{
|
||||
{
|
||||
ServiceName: "service2",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case "found-synthetic-policy-3":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "bebccc92-3987-489d-84c2-ffd00d93ef93",
|
||||
SecretID: "de70f2e2-69d9-4e88-9815-f91c03c6bcb1",
|
||||
NodeIdentities: []*structs.ACLNodeIdentity{
|
||||
{
|
||||
NodeName: "test-node1",
|
||||
Datacenter: "dc1",
|
||||
},
|
||||
// as the resolver is in dc1 this identity should be ignored
|
||||
{
|
||||
NodeName: "test-node-dc2",
|
||||
Datacenter: "dc2",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case "found-synthetic-policy-4":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "359b9927-25fd-46b9-bd14-3470f848ec65",
|
||||
SecretID: "83c4d500-847d-49f7-8c08-0483f6b4156e",
|
||||
NodeIdentities: []*structs.ACLNodeIdentity{
|
||||
{
|
||||
NodeName: "test-node2",
|
||||
Datacenter: "dc1",
|
||||
},
|
||||
// as the resolver is in dc1 this identity should be ignored
|
||||
{
|
||||
NodeName: "test-node-dc2",
|
||||
Datacenter: "dc2",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case "found-role-node-identity":
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "f3f47a09-de29-4c57-8f54-b65a9be79641",
|
||||
|
@ -291,18 +204,8 @@ func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
|
|||
},
|
||||
},
|
||||
}, nil
|
||||
case anonymousToken:
|
||||
return true, &structs.ACLToken{
|
||||
AccessorID: "00000000-0000-0000-0000-000000000002",
|
||||
SecretID: anonymousToken,
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{
|
||||
ID: "node-wr",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return testIdentityForTokenEnterprise(token)
|
||||
return true, nil, acl.ErrNotFound
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,7 +280,7 @@ func testPolicyForID(policyID string) (bool, *structs.ACLPolicy, error) {
|
|||
p.SetHash(false)
|
||||
return true, p, nil
|
||||
default:
|
||||
return testPolicyForIDEnterprise(policyID)
|
||||
return true, nil, acl.ErrNotFound
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -407,21 +310,6 @@ func testRoleForID(roleID string) (bool, *structs.ACLRole, error) {
|
|||
},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
}, nil
|
||||
case "missing-policy":
|
||||
return true, &structs.ACLRole{
|
||||
ID: "missing-policy",
|
||||
Name: "missing-policy",
|
||||
Description: "missing-policy",
|
||||
Policies: []structs.ACLRolePolicyLink{
|
||||
{
|
||||
ID: "not-found",
|
||||
},
|
||||
{
|
||||
ID: "acl-ro",
|
||||
},
|
||||
},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
}, nil
|
||||
case "found":
|
||||
return true, &structs.ACLRole{
|
||||
ID: "found",
|
||||
|
@ -528,7 +416,7 @@ func testRoleForID(roleID string) (bool, *structs.ACLRole, error) {
|
|||
},
|
||||
}, nil
|
||||
default:
|
||||
return testRoleForIDEnterprise(roleID)
|
||||
return true, nil, acl.ErrNotFound
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -549,6 +437,13 @@ type ACLResolverTestDelegate struct {
|
|||
policyResolveFn func(*structs.ACLPolicyBatchGetRequest, *structs.ACLPolicyBatchResponse) error
|
||||
roleResolveFn func(*structs.ACLRoleBatchGetRequest, *structs.ACLRoleBatchResponse) error
|
||||
|
||||
// testTokens is used by plainTokenReadFn if not nil
|
||||
testTokens map[string]*structs.ACLToken
|
||||
// testPolicies is used by plainPolicyResolveFn if not nil
|
||||
testPolicies map[string]*structs.ACLPolicy
|
||||
// testRoles is used by plainRoleResolveFn if not nil
|
||||
testRoles map[string]*structs.ACLRole
|
||||
|
||||
localTokenResolutions int32
|
||||
remoteTokenResolutions int32
|
||||
localPolicyResolutions int32
|
||||
|
@ -567,6 +462,51 @@ type ACLResolverTestDelegate struct {
|
|||
EnterpriseACLResolverTestDelegate
|
||||
}
|
||||
|
||||
// UseTestLocalData will force delegate-local maps to be used in lieu of the
|
||||
// global factory functions.
|
||||
func (d *ACLResolverTestDelegate) UseTestLocalData(data []interface{}) {
|
||||
d.testTokens = make(map[string]*structs.ACLToken)
|
||||
d.testPolicies = make(map[string]*structs.ACLPolicy)
|
||||
d.testRoles = make(map[string]*structs.ACLRole)
|
||||
|
||||
var rest []interface{}
|
||||
for _, item := range data {
|
||||
switch x := item.(type) {
|
||||
case *structs.ACLToken:
|
||||
d.testTokens[x.SecretID] = x
|
||||
case *structs.ACLPolicy:
|
||||
d.testPolicies[x.ID] = x
|
||||
case *structs.ACLRole:
|
||||
d.testRoles[x.ID] = x
|
||||
case string:
|
||||
parts := strings.SplitN(x, ":", 2)
|
||||
switch parts[0] {
|
||||
case "token-not-found":
|
||||
d.testTokens[parts[1]] = nil
|
||||
case "policy-not-found":
|
||||
d.testPolicies[parts[1]] = nil
|
||||
case "role-not-found":
|
||||
d.testRoles[parts[1]] = nil
|
||||
default:
|
||||
rest = append(rest, item)
|
||||
}
|
||||
default:
|
||||
rest = append(rest, item)
|
||||
}
|
||||
}
|
||||
|
||||
d.EnterpriseACLResolverTestDelegate.UseTestLocalData(rest)
|
||||
}
|
||||
|
||||
// UseDefaultData will force the global factory functions to be used instead of
|
||||
// delegate-local maps.
|
||||
func (d *ACLResolverTestDelegate) UseDefaultData() {
|
||||
d.testTokens = nil
|
||||
d.testPolicies = nil
|
||||
d.testRoles = nil
|
||||
d.EnterpriseACLResolverTestDelegate.UseDefaultData()
|
||||
}
|
||||
|
||||
func (d *ACLResolverTestDelegate) Reset() {
|
||||
d.tokenCached = false
|
||||
d.policyCached = false
|
||||
|
@ -587,6 +527,17 @@ func (d *ACLResolverTestDelegate) defaultTokenReadFn(errAfterCached error) func(
|
|||
}
|
||||
|
||||
func (d *ACLResolverTestDelegate) plainTokenReadFn(args *structs.ACLTokenGetRequest, reply *structs.ACLTokenResponse) error {
|
||||
if d.testTokens != nil {
|
||||
token, ok := d.testTokens[args.TokenID]
|
||||
if ok {
|
||||
if token == nil {
|
||||
return acl.ErrNotFound
|
||||
}
|
||||
reply.Token = token
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_, token, err := testIdentityForToken(args.TokenID)
|
||||
if token != nil {
|
||||
reply.Token = token.(*structs.ACLToken)
|
||||
|
@ -611,9 +562,15 @@ func (d *ACLResolverTestDelegate) plainPolicyResolveFn(args *structs.ACLPolicyBa
|
|||
// TODO: and possibly return a not-found or permission-denied here
|
||||
|
||||
for _, policyID := range args.PolicyIDs {
|
||||
_, policy, _ := testPolicyForID(policyID)
|
||||
if policy != nil {
|
||||
reply.Policies = append(reply.Policies, policy)
|
||||
if d.testPolicies != nil {
|
||||
if policy := d.testPolicies[policyID]; policy != nil {
|
||||
reply.Policies = append(reply.Policies, policy)
|
||||
}
|
||||
} else {
|
||||
_, policy, _ := testPolicyForID(policyID)
|
||||
if policy != nil {
|
||||
reply.Policies = append(reply.Policies, policy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,9 +596,15 @@ func (d *ACLResolverTestDelegate) plainRoleResolveFn(args *structs.ACLRoleBatchG
|
|||
// TODO: and possibly return a not-found or permission-denied here
|
||||
|
||||
for _, roleID := range args.RoleIDs {
|
||||
_, role, _ := testRoleForID(roleID)
|
||||
if role != nil {
|
||||
reply.Roles = append(reply.Roles, role)
|
||||
if d.testRoles != nil {
|
||||
if role := d.testRoles[roleID]; role != nil {
|
||||
reply.Roles = append(reply.Roles, role)
|
||||
}
|
||||
} else {
|
||||
_, role, _ := testRoleForID(roleID)
|
||||
if role != nil {
|
||||
reply.Roles = append(reply.Roles, role)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -662,6 +625,14 @@ func (d *ACLResolverTestDelegate) ResolveIdentityFromToken(token string) (bool,
|
|||
}
|
||||
|
||||
atomic.AddInt32(&d.localTokenResolutions, 1)
|
||||
if d.testTokens != nil {
|
||||
if token, ok := d.testTokens[token]; ok {
|
||||
if token != nil {
|
||||
return true, token, nil
|
||||
}
|
||||
}
|
||||
return true, nil, acl.ErrNotFound
|
||||
}
|
||||
return testIdentityForToken(token)
|
||||
}
|
||||
|
||||
|
@ -671,6 +642,14 @@ func (d *ACLResolverTestDelegate) ResolvePolicyFromID(policyID string) (bool, *s
|
|||
}
|
||||
|
||||
atomic.AddInt32(&d.localPolicyResolutions, 1)
|
||||
if d.testPolicies != nil {
|
||||
if policy, ok := d.testPolicies[policyID]; ok {
|
||||
if policy != nil {
|
||||
return true, policy, nil
|
||||
}
|
||||
}
|
||||
return true, nil, acl.ErrNotFound
|
||||
}
|
||||
return testPolicyForID(policyID)
|
||||
}
|
||||
|
||||
|
@ -680,6 +659,14 @@ func (d *ACLResolverTestDelegate) ResolveRoleFromID(roleID string) (bool, *struc
|
|||
}
|
||||
|
||||
atomic.AddInt32(&d.localRoleResolutions, 1)
|
||||
if d.testRoles != nil {
|
||||
if role, ok := d.testRoles[roleID]; ok {
|
||||
if role != nil {
|
||||
return true, role, nil
|
||||
}
|
||||
}
|
||||
return true, nil, acl.ErrNotFound
|
||||
}
|
||||
return testRoleForID(roleID)
|
||||
}
|
||||
|
||||
|
@ -1762,6 +1749,7 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
}
|
||||
|
||||
runTwiceAndReset("Missing Identity", func(t *testing.T) {
|
||||
delegate.UseTestLocalData(nil)
|
||||
authz, err := r.ResolveToken("doesn't exist")
|
||||
require.Nil(t, authz)
|
||||
require.Error(t, err)
|
||||
|
@ -1769,6 +1757,25 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("Missing Policy", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
|
||||
SecretID: "missing-policy",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{ID: "not-found"},
|
||||
{ID: "acl-ro"},
|
||||
},
|
||||
},
|
||||
"policy-not-found:not-found",
|
||||
&structs.ACLPolicy{
|
||||
ID: "acl-ro",
|
||||
Name: "acl-ro",
|
||||
Description: "acl-ro",
|
||||
Rules: `acl = "read"`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
})
|
||||
authz := resolveToken(t, r, "missing-policy")
|
||||
require.NotNil(t, authz)
|
||||
require.Equal(t, acl.Allow, authz.ACLRead(nil))
|
||||
|
@ -1776,6 +1783,33 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("Missing Role", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
|
||||
SecretID: "missing-role",
|
||||
Roles: []structs.ACLTokenRoleLink{
|
||||
{ID: "not-found"},
|
||||
{ID: "acl-ro"},
|
||||
},
|
||||
},
|
||||
"role-not-found:not-found",
|
||||
&structs.ACLRole{
|
||||
ID: "acl-ro",
|
||||
Name: "acl-ro",
|
||||
Description: "acl-ro",
|
||||
Policies: []structs.ACLRolePolicyLink{
|
||||
{ID: "acl-ro"},
|
||||
},
|
||||
},
|
||||
&structs.ACLPolicy{
|
||||
ID: "acl-ro",
|
||||
Name: "acl-ro",
|
||||
Description: "acl-ro",
|
||||
Rules: `acl = "read"`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
})
|
||||
authz := resolveToken(t, r, "missing-role")
|
||||
require.NotNil(t, authz)
|
||||
require.Equal(t, acl.Allow, authz.ACLRead(nil))
|
||||
|
@ -1783,6 +1817,34 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("Missing Policy on Role", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
|
||||
SecretID: "missing-policy-on-role",
|
||||
Roles: []structs.ACLTokenRoleLink{
|
||||
{ID: "missing-policy"},
|
||||
},
|
||||
},
|
||||
&structs.ACLRole{
|
||||
ID: "missing-policy",
|
||||
Name: "missing-policy",
|
||||
Description: "missing-policy",
|
||||
Policies: []structs.ACLRolePolicyLink{
|
||||
{ID: "not-found"},
|
||||
{ID: "acl-ro"},
|
||||
},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
"policy-not-found:not-found",
|
||||
&structs.ACLPolicy{
|
||||
ID: "acl-ro",
|
||||
Name: "acl-ro",
|
||||
Description: "acl-ro",
|
||||
Rules: `acl = "read"`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
})
|
||||
authz := resolveToken(t, r, "missing-policy-on-role")
|
||||
require.NotNil(t, authz)
|
||||
require.Equal(t, acl.Allow, authz.ACLRead(nil))
|
||||
|
@ -1790,6 +1852,34 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("Normal with Policy", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df",
|
||||
SecretID: "found",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{ID: "node-wr"},
|
||||
{ID: "dc2-key-wr"},
|
||||
},
|
||||
},
|
||||
&structs.ACLPolicy{
|
||||
ID: "node-wr",
|
||||
Name: "node-wr",
|
||||
Description: "node-wr",
|
||||
Rules: `node_prefix "" { policy = "write"}`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
Datacenters: []string{"dc1"},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
&structs.ACLPolicy{
|
||||
ID: "dc2-key-wr",
|
||||
Name: "dc2-key-wr",
|
||||
Description: "dc2-key-wr",
|
||||
Rules: `key_prefix "" { policy = "write"}`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
Datacenters: []string{"dc2"},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
})
|
||||
authz := resolveToken(t, r, "found")
|
||||
require.NotNil(t, authz)
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
|
@ -1797,6 +1887,42 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("Normal with Role", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df",
|
||||
SecretID: "found-role",
|
||||
Roles: []structs.ACLTokenRoleLink{
|
||||
{ID: "found"},
|
||||
},
|
||||
},
|
||||
&structs.ACLRole{
|
||||
ID: "found",
|
||||
Name: "found",
|
||||
Description: "found",
|
||||
Policies: []structs.ACLRolePolicyLink{
|
||||
{ID: "node-wr"},
|
||||
{ID: "dc2-key-wr"},
|
||||
},
|
||||
},
|
||||
&structs.ACLPolicy{
|
||||
ID: "node-wr",
|
||||
Name: "node-wr",
|
||||
Description: "node-wr",
|
||||
Rules: `node_prefix "" { policy = "write"}`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
Datacenters: []string{"dc1"},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
&structs.ACLPolicy{
|
||||
ID: "dc2-key-wr",
|
||||
Name: "dc2-key-wr",
|
||||
Description: "dc2-key-wr",
|
||||
Rules: `key_prefix "" { policy = "write"}`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
Datacenters: []string{"dc2"},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
})
|
||||
authz := resolveToken(t, r, "found-role")
|
||||
require.NotNil(t, authz)
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
|
@ -1804,6 +1930,54 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("Normal with Policy and Role", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df",
|
||||
SecretID: "found-policy-and-role",
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{ID: "node-wr"},
|
||||
{ID: "dc2-key-wr"},
|
||||
},
|
||||
Roles: []structs.ACLTokenRoleLink{
|
||||
{ID: "service-ro"},
|
||||
},
|
||||
},
|
||||
&structs.ACLPolicy{
|
||||
ID: "node-wr",
|
||||
Name: "node-wr",
|
||||
Description: "node-wr",
|
||||
Rules: `node_prefix "" { policy = "write"}`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
Datacenters: []string{"dc1"},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
&structs.ACLPolicy{
|
||||
ID: "dc2-key-wr",
|
||||
Name: "dc2-key-wr",
|
||||
Description: "dc2-key-wr",
|
||||
Rules: `key_prefix "" { policy = "write"}`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
Datacenters: []string{"dc2"},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
&structs.ACLRole{
|
||||
ID: "service-ro",
|
||||
Name: "service-ro",
|
||||
Description: "service-ro",
|
||||
Policies: []structs.ACLRolePolicyLink{
|
||||
{ID: "service-ro"},
|
||||
},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
&structs.ACLPolicy{
|
||||
ID: "service-ro",
|
||||
Name: "service-ro",
|
||||
Description: "service-ro",
|
||||
Rules: `service_prefix "" { policy = "read" }`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
})
|
||||
authz := resolveToken(t, r, "found-policy-and-role")
|
||||
require.NotNil(t, authz)
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
|
@ -1812,6 +1986,30 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("Role With Node Identity", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "f3f47a09-de29-4c57-8f54-b65a9be79641",
|
||||
SecretID: "found-role-node-identity",
|
||||
Roles: []structs.ACLTokenRoleLink{
|
||||
{ID: "node-identity"},
|
||||
},
|
||||
},
|
||||
&structs.ACLRole{
|
||||
ID: "node-identity",
|
||||
Name: "node-identity",
|
||||
Description: "node-identity",
|
||||
NodeIdentities: []*structs.ACLNodeIdentity{
|
||||
{
|
||||
NodeName: "test-node",
|
||||
Datacenter: "dc1",
|
||||
},
|
||||
{
|
||||
NodeName: "test-node-dc2",
|
||||
Datacenter: "dc2",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
authz := resolveToken(t, r, "found-role-node-identity")
|
||||
require.NotNil(t, authz)
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("test-node", nil))
|
||||
|
@ -1821,10 +2019,57 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("Synthetic Policies Independently Cache", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "f6c5a5fb-4da4-422b-9abf-2c942813fc71",
|
||||
SecretID: "found-synthetic-policy-1",
|
||||
ServiceIdentities: []*structs.ACLServiceIdentity{
|
||||
{ServiceName: "service1"},
|
||||
},
|
||||
},
|
||||
&structs.ACLToken{
|
||||
AccessorID: "7c87dfad-be37-446e-8305-299585677cb5",
|
||||
SecretID: "found-synthetic-policy-2",
|
||||
ServiceIdentities: []*structs.ACLServiceIdentity{
|
||||
{ServiceName: "service2"},
|
||||
},
|
||||
},
|
||||
&structs.ACLToken{
|
||||
AccessorID: "bebccc92-3987-489d-84c2-ffd00d93ef93",
|
||||
SecretID: "found-synthetic-policy-3",
|
||||
NodeIdentities: []*structs.ACLNodeIdentity{
|
||||
{
|
||||
NodeName: "test-node1",
|
||||
Datacenter: "dc1",
|
||||
},
|
||||
// as the resolver is in dc1 this identity should be ignored
|
||||
{
|
||||
NodeName: "test-node-dc2",
|
||||
Datacenter: "dc2",
|
||||
},
|
||||
},
|
||||
},
|
||||
&structs.ACLToken{
|
||||
AccessorID: "359b9927-25fd-46b9-bd14-3470f848ec65",
|
||||
SecretID: "found-synthetic-policy-4",
|
||||
NodeIdentities: []*structs.ACLNodeIdentity{
|
||||
{
|
||||
NodeName: "test-node2",
|
||||
Datacenter: "dc1",
|
||||
},
|
||||
// as the resolver is in dc1 this identity should be ignored
|
||||
{
|
||||
NodeName: "test-node-dc2",
|
||||
Datacenter: "dc2",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// We resolve these tokens in the same cache session
|
||||
// to verify that the keys for caching synthetic policies don't bleed
|
||||
// over between each other.
|
||||
{
|
||||
t.Run("synthetic-policy-1", func(t *testing.T) { // service identity
|
||||
authz, err := r.ResolveToken("found-synthetic-policy-1")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
|
@ -1837,8 +2082,8 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
require.Equal(t, acl.Allow, authz.ServiceWrite("service1", nil))
|
||||
require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil))
|
||||
}
|
||||
{
|
||||
})
|
||||
t.Run("synthetic-policy-2", func(t *testing.T) { // service identity
|
||||
authz, err := r.ResolveToken("found-synthetic-policy-2")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
|
@ -1851,8 +2096,8 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
require.Equal(t, acl.Allow, authz.ServiceWrite("service2", nil))
|
||||
require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil))
|
||||
}
|
||||
{
|
||||
})
|
||||
t.Run("synthetic-policy-3", func(t *testing.T) { // node identity
|
||||
authz, err := r.ResolveToken("found-synthetic-policy-3")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
|
@ -1867,8 +2112,8 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
require.Equal(t, acl.Allow, authz.NodeWrite("test-node1", nil))
|
||||
// ensure node identity for other DC is ignored
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("test-node-dc2", nil))
|
||||
}
|
||||
{
|
||||
})
|
||||
t.Run("synthetic-policy-4", func(t *testing.T) { // node identity
|
||||
authz, err := r.ResolveToken("found-synthetic-policy-4")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
|
@ -1883,10 +2128,28 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
require.Equal(t, acl.Allow, authz.NodeWrite("test-node2", nil))
|
||||
// ensure node identity for other DC is ignored
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("test-node-dc2", nil))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
runTwiceAndReset("Anonymous", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "00000000-0000-0000-0000-000000000002",
|
||||
SecretID: anonymousToken,
|
||||
Policies: []structs.ACLTokenPolicyLink{
|
||||
{ID: "node-wr"},
|
||||
},
|
||||
},
|
||||
&structs.ACLPolicy{
|
||||
ID: "node-wr",
|
||||
Name: "node-wr",
|
||||
Description: "node-wr",
|
||||
Rules: `node_prefix "" { policy = "write"}`,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
Datacenters: []string{"dc1"},
|
||||
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
|
||||
},
|
||||
})
|
||||
authz, err := r.ResolveToken("")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
|
@ -1895,6 +2158,13 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("legacy-management", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "d109a033-99d1-47e2-a711-d6593373a973",
|
||||
SecretID: "legacy-management",
|
||||
Type: structs.ACLTokenTypeManagement,
|
||||
},
|
||||
})
|
||||
authz, err := r.ResolveToken("legacy-management")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
|
@ -1903,6 +2173,14 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
})
|
||||
|
||||
runTwiceAndReset("legacy-client", func(t *testing.T) {
|
||||
delegate.UseTestLocalData([]interface{}{
|
||||
&structs.ACLToken{
|
||||
AccessorID: "b7375838-b104-4a25-b457-329d939bf257",
|
||||
SecretID: "legacy-client",
|
||||
Type: structs.ACLTokenTypeClient,
|
||||
Rules: `service "" { policy = "read" }`,
|
||||
},
|
||||
})
|
||||
authz, err := r.ResolveToken("legacy-client")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
|
|
|
@ -216,10 +216,10 @@ func (s *ACLNodeIdentity) EstimateSize() int {
|
|||
return len(s.NodeName) + len(s.Datacenter)
|
||||
}
|
||||
|
||||
func (s *ACLNodeIdentity) SyntheticPolicy() *ACLPolicy {
|
||||
func (s *ACLNodeIdentity) SyntheticPolicy(entMeta *EnterpriseMeta) *ACLPolicy {
|
||||
// Given that we validate this string name before persisting, we do not
|
||||
// have to escape it before doing the following interpolation.
|
||||
rules := fmt.Sprintf(aclPolicyTemplateNodeIdentity, s.NodeName)
|
||||
rules := aclNodeIdentityRules(s.NodeName, entMeta)
|
||||
|
||||
hasher := fnv.New128a()
|
||||
hashID := fmt.Sprintf("%x", hasher.Sum([]byte(rules)))
|
||||
|
@ -231,8 +231,7 @@ func (s *ACLNodeIdentity) SyntheticPolicy() *ACLPolicy {
|
|||
policy.Rules = rules
|
||||
policy.Syntax = acl.SyntaxCurrent
|
||||
policy.Datacenters = []string{s.Datacenter}
|
||||
// TODO(partitions,acls): this needs to be fed the correct partition
|
||||
policy.EnterpriseMeta = *DefaultEnterpriseMetaInDefaultPartition()
|
||||
policy.EnterpriseMeta.Merge(entMeta)
|
||||
policy.SetHash(true)
|
||||
return policy
|
||||
}
|
||||
|
|
|
@ -62,6 +62,10 @@ func aclServiceIdentityRules(svc string, _ *EnterpriseMeta) string {
|
|||
return fmt.Sprintf(aclPolicyTemplateServiceIdentity, svc)
|
||||
}
|
||||
|
||||
func aclNodeIdentityRules(node string, _ *EnterpriseMeta) string {
|
||||
return fmt.Sprintf(aclPolicyTemplateNodeIdentity, node)
|
||||
}
|
||||
|
||||
func (p *ACLPolicy) EnterprisePolicyMeta() *acl.EnterprisePolicyMeta {
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue