|
|
|
@ -434,7 +434,6 @@ type ACLResolverTestDelegate struct {
|
|
|
|
|
localTokens bool
|
|
|
|
|
localPolicies bool
|
|
|
|
|
localRoles bool
|
|
|
|
|
getPolicyFn func(*structs.ACLPolicyResolveLegacyRequest, *structs.ACLPolicyResolveLegacyResponse) error
|
|
|
|
|
tokenReadFn func(*structs.ACLTokenGetRequest, *structs.ACLTokenResponse) error
|
|
|
|
|
policyResolveFn func(*structs.ACLPolicyBatchGetRequest, *structs.ACLPolicyBatchResponse) error
|
|
|
|
|
roleResolveFn func(*structs.ACLRoleBatchGetRequest, *structs.ACLRoleBatchResponse) error
|
|
|
|
@ -675,12 +674,6 @@ func (d *ACLResolverTestDelegate) ResolveRoleFromID(roleID string) (bool, *struc
|
|
|
|
|
|
|
|
|
|
func (d *ACLResolverTestDelegate) RPC(method string, args interface{}, reply interface{}) error {
|
|
|
|
|
switch method {
|
|
|
|
|
case "ACL.GetPolicy":
|
|
|
|
|
atomic.AddInt32(&d.remoteLegacyResolutions, 1)
|
|
|
|
|
if d.getPolicyFn != nil {
|
|
|
|
|
return d.getPolicyFn(args.(*structs.ACLPolicyResolveLegacyRequest), reply.(*structs.ACLPolicyResolveLegacyResponse))
|
|
|
|
|
}
|
|
|
|
|
panic("Bad Test Implementation: should provide a getPolicyFn to the ACLResolverTestDelegate")
|
|
|
|
|
case "ACL.TokenRead":
|
|
|
|
|
atomic.AddInt32(&d.remoteTokenResolutions, 1)
|
|
|
|
|
if d.tokenReadFn != nil {
|
|
|
|
@ -1571,49 +1564,6 @@ func TestACLResolver_Client(t *testing.T) {
|
|
|
|
|
require.EqualValues(t, 0, delegate.remoteLegacyResolutions)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("Resolve-Identity-Legacy", func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
delegate := &ACLResolverTestDelegate{
|
|
|
|
|
enabled: true,
|
|
|
|
|
datacenter: "dc1",
|
|
|
|
|
legacy: true,
|
|
|
|
|
localTokens: false,
|
|
|
|
|
localPolicies: false,
|
|
|
|
|
getPolicyFn: func(_ *structs.ACLPolicyResolveLegacyRequest, reply *structs.ACLPolicyResolveLegacyResponse) error {
|
|
|
|
|
reply.Parent = "deny"
|
|
|
|
|
reply.TTL = 30
|
|
|
|
|
reply.ETag = "nothing"
|
|
|
|
|
reply.Policy = &acl.Policy{
|
|
|
|
|
ID: "not-needed",
|
|
|
|
|
PolicyRules: acl.PolicyRules{
|
|
|
|
|
Nodes: []*acl.NodeRule{
|
|
|
|
|
{
|
|
|
|
|
Name: "foo",
|
|
|
|
|
Policy: acl.PolicyWrite,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r := newTestACLResolver(t, delegate, nil)
|
|
|
|
|
|
|
|
|
|
ident, err := r.ResolveTokenToIdentity("found-policy-and-role")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, ident)
|
|
|
|
|
require.Equal(t, "legacy-token", ident.ID())
|
|
|
|
|
require.EqualValues(t, 0, delegate.localTokenResolutions)
|
|
|
|
|
require.EqualValues(t, 0, delegate.remoteTokenResolutions)
|
|
|
|
|
require.EqualValues(t, 0, delegate.localPolicyResolutions)
|
|
|
|
|
require.EqualValues(t, 0, delegate.remotePolicyResolutions)
|
|
|
|
|
require.EqualValues(t, 0, delegate.localRoleResolutions)
|
|
|
|
|
require.EqualValues(t, 0, delegate.remoteRoleResolutions)
|
|
|
|
|
require.EqualValues(t, 1, delegate.remoteLegacyResolutions)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("Concurrent-Token-Resolve", func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
@ -2231,280 +2181,6 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestACLResolver_Legacy(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
t.Run("Cached", func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
cached := false
|
|
|
|
|
delegate := &ACLResolverTestDelegate{
|
|
|
|
|
enabled: true,
|
|
|
|
|
datacenter: "dc1",
|
|
|
|
|
legacy: true,
|
|
|
|
|
localTokens: false,
|
|
|
|
|
localPolicies: false,
|
|
|
|
|
getPolicyFn: func(_ *structs.ACLPolicyResolveLegacyRequest, reply *structs.ACLPolicyResolveLegacyResponse) error {
|
|
|
|
|
if !cached {
|
|
|
|
|
reply.Parent = "deny"
|
|
|
|
|
reply.TTL = 30
|
|
|
|
|
reply.ETag = "nothing"
|
|
|
|
|
reply.Policy = &acl.Policy{
|
|
|
|
|
ID: "not-needed",
|
|
|
|
|
PolicyRules: acl.PolicyRules{
|
|
|
|
|
Nodes: []*acl.NodeRule{
|
|
|
|
|
{
|
|
|
|
|
Name: "foo",
|
|
|
|
|
Policy: acl.PolicyWrite,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
cached = true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return errRPC
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
r := newTestACLResolver(t, delegate, nil)
|
|
|
|
|
|
|
|
|
|
authz, err := r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
// there is a bit of translation that happens
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
|
|
|
|
|
|
|
|
|
// this should be from the cache
|
|
|
|
|
authz, err = r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
// there is a bit of translation that happens
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("Cache-Expiry-Extend", func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
cached := false
|
|
|
|
|
delegate := &ACLResolverTestDelegate{
|
|
|
|
|
enabled: true,
|
|
|
|
|
datacenter: "dc1",
|
|
|
|
|
legacy: true,
|
|
|
|
|
localTokens: false,
|
|
|
|
|
localPolicies: false,
|
|
|
|
|
getPolicyFn: func(_ *structs.ACLPolicyResolveLegacyRequest, reply *structs.ACLPolicyResolveLegacyResponse) error {
|
|
|
|
|
if !cached {
|
|
|
|
|
reply.Parent = "deny"
|
|
|
|
|
reply.TTL = 0
|
|
|
|
|
reply.ETag = "nothing"
|
|
|
|
|
reply.Policy = &acl.Policy{
|
|
|
|
|
ID: "not-needed",
|
|
|
|
|
PolicyRules: acl.PolicyRules{
|
|
|
|
|
Nodes: []*acl.NodeRule{
|
|
|
|
|
{
|
|
|
|
|
Name: "foo",
|
|
|
|
|
Policy: acl.PolicyWrite,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
cached = true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return errRPC
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
r := newTestACLResolver(t, delegate, func(config *ACLResolverConfig) {
|
|
|
|
|
config.Config.ACLTokenTTL = 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authz, err := r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
// there is a bit of translation that happens
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
|
|
|
|
|
|
|
|
|
// this should be from the cache
|
|
|
|
|
authz, err = r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
// there is a bit of translation that happens
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("Cache-Expiry-Allow", func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
cached := false
|
|
|
|
|
delegate := &ACLResolverTestDelegate{
|
|
|
|
|
enabled: true,
|
|
|
|
|
datacenter: "dc1",
|
|
|
|
|
legacy: true,
|
|
|
|
|
localTokens: false,
|
|
|
|
|
localPolicies: false,
|
|
|
|
|
getPolicyFn: func(_ *structs.ACLPolicyResolveLegacyRequest, reply *structs.ACLPolicyResolveLegacyResponse) error {
|
|
|
|
|
if !cached {
|
|
|
|
|
reply.Parent = "deny"
|
|
|
|
|
reply.TTL = 0
|
|
|
|
|
reply.ETag = "nothing"
|
|
|
|
|
reply.Policy = &acl.Policy{
|
|
|
|
|
ID: "not-needed",
|
|
|
|
|
PolicyRules: acl.PolicyRules{
|
|
|
|
|
Nodes: []*acl.NodeRule{
|
|
|
|
|
{
|
|
|
|
|
Name: "foo",
|
|
|
|
|
Policy: acl.PolicyWrite,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
cached = true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return errRPC
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
r := newTestACLResolver(t, delegate, func(config *ACLResolverConfig) {
|
|
|
|
|
config.Config.ACLDownPolicy = "allow"
|
|
|
|
|
config.Config.ACLTokenTTL = 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authz, err := r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
// there is a bit of translation that happens
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
|
|
|
|
|
|
|
|
|
// this should be from the cache
|
|
|
|
|
authz, err = r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
// there is a bit of translation that happens
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("fo", nil))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("Cache-Expiry-Deny", func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
cached := false
|
|
|
|
|
delegate := &ACLResolverTestDelegate{
|
|
|
|
|
enabled: true,
|
|
|
|
|
datacenter: "dc1",
|
|
|
|
|
legacy: true,
|
|
|
|
|
localTokens: false,
|
|
|
|
|
localPolicies: false,
|
|
|
|
|
getPolicyFn: func(_ *structs.ACLPolicyResolveLegacyRequest, reply *structs.ACLPolicyResolveLegacyResponse) error {
|
|
|
|
|
if !cached {
|
|
|
|
|
reply.Parent = "deny"
|
|
|
|
|
reply.TTL = 0
|
|
|
|
|
reply.ETag = "nothing"
|
|
|
|
|
reply.Policy = &acl.Policy{
|
|
|
|
|
ID: "not-needed",
|
|
|
|
|
PolicyRules: acl.PolicyRules{
|
|
|
|
|
Nodes: []*acl.NodeRule{
|
|
|
|
|
{
|
|
|
|
|
Name: "foo",
|
|
|
|
|
Policy: acl.PolicyWrite,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
cached = true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return errRPC
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
r := newTestACLResolver(t, delegate, func(config *ACLResolverConfig) {
|
|
|
|
|
config.Config.ACLDownPolicy = "deny"
|
|
|
|
|
config.Config.ACLTokenTTL = 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authz, err := r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
// there is a bit of translation that happens
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
|
|
|
|
|
|
|
|
|
// this should be from the cache
|
|
|
|
|
authz, err = r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
// there is a bit of translation that happens
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo/bar", nil))
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("Cache-Expiry-Async-Cache", func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
cached := false
|
|
|
|
|
delegate := &ACLResolverTestDelegate{
|
|
|
|
|
enabled: true,
|
|
|
|
|
datacenter: "dc1",
|
|
|
|
|
legacy: true,
|
|
|
|
|
localTokens: false,
|
|
|
|
|
localPolicies: false,
|
|
|
|
|
getPolicyFn: func(_ *structs.ACLPolicyResolveLegacyRequest, reply *structs.ACLPolicyResolveLegacyResponse) error {
|
|
|
|
|
if !cached {
|
|
|
|
|
reply.Parent = "deny"
|
|
|
|
|
reply.TTL = 0
|
|
|
|
|
reply.ETag = "nothing"
|
|
|
|
|
reply.Policy = &acl.Policy{
|
|
|
|
|
ID: "not-needed",
|
|
|
|
|
PolicyRules: acl.PolicyRules{
|
|
|
|
|
Nodes: []*acl.NodeRule{
|
|
|
|
|
{
|
|
|
|
|
Name: "foo",
|
|
|
|
|
Policy: acl.PolicyWrite,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
cached = true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return acl.ErrNotFound
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
r := newTestACLResolver(t, delegate, func(config *ACLResolverConfig) {
|
|
|
|
|
config.Config.ACLDownPolicy = "async-cache"
|
|
|
|
|
config.Config.ACLTokenTTL = 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authz, err := r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
// there is a bit of translation that happens
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
|
|
|
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
|
|
|
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
|
|
|
|
|
|
|
|
|
// delivered from the cache
|
|
|
|
|
authz2, err := r.ResolveToken("foo")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotNil(t, authz)
|
|
|
|
|
require.True(t, authz == authz2)
|
|
|
|
|
|
|
|
|
|
// the go routine spawned will eventually return and this will be a not found error
|
|
|
|
|
retry.Run(t, func(t *retry.R) {
|
|
|
|
|
authz3, err := r.ResolveToken("foo")
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
assert.True(t, acl.IsErrNotFound(err))
|
|
|
|
|
assert.Nil(t, authz3)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
func TestACL_filterHealthChecks(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
// Create some health checks.
|
|
|
|
|