mirror of https://github.com/status-im/consul.git
acl: remove ACL.GetPolicy endpoint and resolve legacy acls
And all code that was no longer used once those two were removed.
This commit is contained in:
parent
d12dd48c61
commit
8e9773e20b
|
@ -453,54 +453,6 @@ func NewPolicyFromSource(id string, revision uint64, rules string, syntax Syntax
|
||||||
return policy, err
|
return policy, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(ACL-Legacy): remove this
|
|
||||||
func (policy *Policy) ConvertToLegacy() *Policy {
|
|
||||||
converted := &Policy{
|
|
||||||
ID: policy.ID,
|
|
||||||
Revision: policy.Revision,
|
|
||||||
PolicyRules: PolicyRules{
|
|
||||||
ACL: policy.ACL,
|
|
||||||
Keyring: policy.Keyring,
|
|
||||||
Operator: policy.Operator,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
converted.Agents = append(converted.Agents, policy.Agents...)
|
|
||||||
converted.Agents = append(converted.Agents, policy.AgentPrefixes...)
|
|
||||||
converted.Keys = append(converted.Keys, policy.Keys...)
|
|
||||||
converted.Keys = append(converted.Keys, policy.KeyPrefixes...)
|
|
||||||
converted.Nodes = append(converted.Nodes, policy.Nodes...)
|
|
||||||
converted.Nodes = append(converted.Nodes, policy.NodePrefixes...)
|
|
||||||
converted.Services = append(converted.Services, policy.Services...)
|
|
||||||
converted.Services = append(converted.Services, policy.ServicePrefixes...)
|
|
||||||
converted.Sessions = append(converted.Sessions, policy.Sessions...)
|
|
||||||
converted.Sessions = append(converted.Sessions, policy.SessionPrefixes...)
|
|
||||||
converted.Events = append(converted.Events, policy.Events...)
|
|
||||||
converted.Events = append(converted.Events, policy.EventPrefixes...)
|
|
||||||
converted.PreparedQueries = append(converted.PreparedQueries, policy.PreparedQueries...)
|
|
||||||
converted.PreparedQueries = append(converted.PreparedQueries, policy.PreparedQueryPrefixes...)
|
|
||||||
return converted
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(ACL-Legacy): remove this
|
|
||||||
func (policy *Policy) ConvertFromLegacy() *Policy {
|
|
||||||
return &Policy{
|
|
||||||
ID: policy.ID,
|
|
||||||
Revision: policy.Revision,
|
|
||||||
PolicyRules: PolicyRules{
|
|
||||||
AgentPrefixes: policy.Agents,
|
|
||||||
KeyPrefixes: policy.Keys,
|
|
||||||
NodePrefixes: policy.Nodes,
|
|
||||||
ServicePrefixes: policy.Services,
|
|
||||||
SessionPrefixes: policy.Sessions,
|
|
||||||
EventPrefixes: policy.Events,
|
|
||||||
PreparedQueryPrefixes: policy.PreparedQueries,
|
|
||||||
Keyring: policy.Keyring,
|
|
||||||
Operator: policy.Operator,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// takesPrecedenceOver returns true when permission a
|
// takesPrecedenceOver returns true when permission a
|
||||||
// should take precedence over permission b
|
// should take precedence over permission b
|
||||||
func takesPrecedenceOver(a, b string) bool {
|
func takesPrecedenceOver(a, b string) bool {
|
||||||
|
|
|
@ -383,135 +383,6 @@ func (r *ACLResolver) Close() {
|
||||||
r.aclConf.Close()
|
r.aclConf.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ACLResolver) fetchAndCacheTokenLegacy(token string, cached *structs.AuthorizerCacheEntry) (acl.Authorizer, error) {
|
|
||||||
req := structs.ACLPolicyResolveLegacyRequest{
|
|
||||||
Datacenter: r.delegate.ACLDatacenter(true),
|
|
||||||
ACL: token,
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheTTL := r.config.ACLTokenTTL
|
|
||||||
if cached != nil {
|
|
||||||
cacheTTL = cached.TTL
|
|
||||||
}
|
|
||||||
|
|
||||||
var reply structs.ACLPolicyResolveLegacyResponse
|
|
||||||
err := r.delegate.RPC("ACL.GetPolicy", &req, &reply)
|
|
||||||
if err == nil {
|
|
||||||
parent := acl.RootAuthorizer(reply.Parent)
|
|
||||||
if parent == nil {
|
|
||||||
var authorizer acl.Authorizer
|
|
||||||
if cached != nil {
|
|
||||||
authorizer = cached.Authorizer
|
|
||||||
}
|
|
||||||
r.cache.PutAuthorizerWithTTL(token, authorizer, cacheTTL)
|
|
||||||
return authorizer, acl.ErrInvalidParent
|
|
||||||
}
|
|
||||||
|
|
||||||
var policies []*acl.Policy
|
|
||||||
policy := reply.Policy
|
|
||||||
if policy != nil {
|
|
||||||
policies = append(policies, policy.ConvertFromLegacy())
|
|
||||||
}
|
|
||||||
|
|
||||||
authorizer, err := acl.NewPolicyAuthorizerWithDefaults(parent, policies, r.aclConf)
|
|
||||||
|
|
||||||
r.cache.PutAuthorizerWithTTL(token, authorizer, reply.TTL)
|
|
||||||
return authorizer, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if acl.IsErrNotFound(err) {
|
|
||||||
// Make sure to remove from the cache if it was deleted
|
|
||||||
r.cache.PutAuthorizerWithTTL(token, nil, cacheTTL)
|
|
||||||
return nil, acl.ErrNotFound
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// some other RPC error
|
|
||||||
switch r.config.ACLDownPolicy {
|
|
||||||
case "allow":
|
|
||||||
r.cache.PutAuthorizerWithTTL(token, acl.AllowAll(), cacheTTL)
|
|
||||||
return acl.AllowAll(), nil
|
|
||||||
case "async-cache", "extend-cache":
|
|
||||||
if cached != nil {
|
|
||||||
r.cache.PutAuthorizerWithTTL(token, cached.Authorizer, cacheTTL)
|
|
||||||
return cached.Authorizer, nil
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
r.cache.PutAuthorizerWithTTL(token, acl.DenyAll(), cacheTTL)
|
|
||||||
return acl.DenyAll(), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
func (r *ACLResolver) resolveTokenLegacy(token string) (structs.ACLIdentity, acl.Authorizer, error) {
|
|
||||||
defer metrics.MeasureSince([]string{"acl", "resolveTokenLegacy"}, time.Now())
|
|
||||||
|
|
||||||
// Attempt to resolve locally first (local results are not cached)
|
|
||||||
// This is only useful for servers where either legacy replication is being
|
|
||||||
// done or the server is within the primary datacenter.
|
|
||||||
if done, identity, err := r.delegate.ResolveIdentityFromToken(token); done {
|
|
||||||
if err == nil && identity != nil {
|
|
||||||
policies, err := r.resolvePoliciesForIdentity(identity)
|
|
||||||
if err != nil {
|
|
||||||
return identity, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
authz, err := policies.Compile(r.cache, r.aclConf)
|
|
||||||
if err != nil {
|
|
||||||
return identity, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return identity, acl.NewChainedAuthorizer([]acl.Authorizer{authz, acl.RootAuthorizer(r.config.ACLDefaultPolicy)}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
identity := &missingIdentity{
|
|
||||||
reason: "legacy-token",
|
|
||||||
token: token,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look in the cache prior to making a RPC request
|
|
||||||
entry := r.cache.GetAuthorizer(token)
|
|
||||||
|
|
||||||
if entry != nil && entry.Age() <= minTTL(entry.TTL, r.config.ACLTokenTTL) {
|
|
||||||
metrics.IncrCounter([]string{"acl", "token", "cache_hit"}, 1)
|
|
||||||
if entry.Authorizer != nil {
|
|
||||||
return identity, entry.Authorizer, nil
|
|
||||||
}
|
|
||||||
return identity, nil, acl.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
metrics.IncrCounter([]string{"acl", "token", "cache_miss"}, 1)
|
|
||||||
|
|
||||||
// Resolve the token in the background and wait on the result if we must
|
|
||||||
waitChan := r.legacyGroup.DoChan(token, func() (interface{}, error) {
|
|
||||||
authorizer, err := r.fetchAndCacheTokenLegacy(token, entry)
|
|
||||||
return authorizer, err
|
|
||||||
})
|
|
||||||
|
|
||||||
waitForResult := entry == nil || r.config.ACLDownPolicy != "async-cache"
|
|
||||||
if !waitForResult {
|
|
||||||
// waitForResult being false requires the cacheEntry to not be nil
|
|
||||||
if entry.Authorizer != nil {
|
|
||||||
return identity, entry.Authorizer, nil
|
|
||||||
}
|
|
||||||
return identity, nil, acl.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// block waiting for the async RPC to finish.
|
|
||||||
res := <-waitChan
|
|
||||||
|
|
||||||
var authorizer acl.Authorizer
|
|
||||||
if res.Val != nil { // avoid a nil-not-nil bug
|
|
||||||
authorizer = res.Val.(acl.Authorizer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return identity, authorizer, res.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ACLResolver) fetchAndCacheIdentityFromToken(token string, cached *structs.IdentityCacheEntry) (structs.ACLIdentity, error) {
|
func (r *ACLResolver) fetchAndCacheIdentityFromToken(token string, cached *structs.IdentityCacheEntry) (structs.ACLIdentity, error) {
|
||||||
cacheID := tokenSecretCacheID(token)
|
cacheID := tokenSecretCacheID(token)
|
||||||
|
|
||||||
|
@ -1326,19 +1197,6 @@ func (r *ACLResolver) ACLsEnabled() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ACLResolver) GetMergedPolicyForToken(token string) (structs.ACLIdentity, *acl.Policy, error) {
|
|
||||||
ident, policies, err := r.resolveTokenToIdentityAndPolicies(token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if len(policies) == 0 {
|
|
||||||
return nil, nil, acl.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
policy, err := policies.Merge(r.cache, r.aclConf)
|
|
||||||
return ident, policy, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// aclFilter is used to filter results from our state store based on ACL rules
|
// aclFilter is used to filter results from our state store based on ACL rules
|
||||||
// configured for the provided token.
|
// configured for the provided token.
|
||||||
type aclFilter struct {
|
type aclFilter struct {
|
||||||
|
|
|
@ -1372,56 +1372,6 @@ func (a *ACL) PolicyResolve(args *structs.ACLPolicyBatchGetRequest, reply *struc
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeACLETag returns an ETag for the given parent and policy.
|
|
||||||
func makeACLETag(parent string, policy *acl.Policy) string {
|
|
||||||
return fmt.Sprintf("%s:%s", parent, policy.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPolicy is used to retrieve a compiled policy object with a TTL. Does not
|
|
||||||
// support a blocking query.
|
|
||||||
//
|
|
||||||
// TODO(ACL-Legacy): remove this
|
|
||||||
func (a *ACL) GetPolicy(args *structs.ACLPolicyResolveLegacyRequest, reply *structs.ACLPolicyResolveLegacyResponse) error {
|
|
||||||
if done, err := a.srv.ForwardRPC("ACL.GetPolicy", args, reply); done {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify we are allowed to serve this request
|
|
||||||
if a.srv.config.PrimaryDatacenter != a.srv.config.Datacenter {
|
|
||||||
return acl.ErrDisabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the policy via the cache
|
|
||||||
parent := a.srv.config.ACLResolverSettings.ACLDefaultPolicy
|
|
||||||
|
|
||||||
ident, policy, err := a.srv.acls.GetMergedPolicyForToken(args.ACL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if token, ok := ident.(*structs.ACLToken); ok && token.Type == structs.ACLTokenTypeManagement {
|
|
||||||
parent = "manage"
|
|
||||||
}
|
|
||||||
|
|
||||||
// translates the structures internals to most closely match what could be expressed in the original rule language
|
|
||||||
policy = policy.ConvertToLegacy()
|
|
||||||
|
|
||||||
// Generate an ETag
|
|
||||||
etag := makeACLETag(parent, policy)
|
|
||||||
|
|
||||||
// Setup the response
|
|
||||||
reply.ETag = etag
|
|
||||||
reply.TTL = a.srv.config.ACLResolverSettings.ACLTokenTTL
|
|
||||||
a.srv.setQueryMeta(&reply.QueryMeta)
|
|
||||||
|
|
||||||
// Only send the policy on an Etag mis-match
|
|
||||||
if args.ETag != etag {
|
|
||||||
reply.Parent = parent
|
|
||||||
reply.Policy = policy
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplicationStatus is used to retrieve the current ACL replication status.
|
// ReplicationStatus is used to retrieve the current ACL replication status.
|
||||||
func (a *ACL) ReplicationStatus(args *structs.DCSpecificRequest,
|
func (a *ACL) ReplicationStatus(args *structs.DCSpecificRequest,
|
||||||
reply *structs.ACLReplicationStatus) error {
|
reply *structs.ACLReplicationStatus) error {
|
||||||
|
|
|
@ -6,6 +6,12 @@ import (
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type LegacyACLGetPolicy struct{}
|
||||||
|
|
||||||
|
func (a *ACL) GetPolicy(*LegacyACLGetPolicy, *LegacyACLGetPolicy) error {
|
||||||
|
return fmt.Errorf("ACL.GetPolicy: the legacy ACL system has been removed")
|
||||||
|
}
|
||||||
|
|
||||||
func (a *ACL) Bootstrap(*structs.DCSpecificRequest, *structs.ACL) error {
|
func (a *ACL) Bootstrap(*structs.DCSpecificRequest, *structs.ACL) error {
|
||||||
return fmt.Errorf("ACL.Bootstrap: the legacy ACL system has been removed")
|
return fmt.Errorf("ACL.Bootstrap: the legacy ACL system has been removed")
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,30 +74,6 @@ func TestACLEndpoint_BootstrapTokens(t *testing.T) {
|
||||||
require.Equal(t, out.CreateIndex, out.ModifyIndex)
|
require.Equal(t, out.CreateIndex, out.ModifyIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestACLEndpoint_GetPolicy_Management(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("too slow for testing.Short")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Parallel()
|
|
||||||
_, srv, codec := testACLServerWithConfig(t, nil, false)
|
|
||||||
|
|
||||||
// wait for leader election and leader establishment to finish.
|
|
||||||
// after this the global management policy, master token and
|
|
||||||
// anonymous token will have been injected into the state store
|
|
||||||
// and we will be ready to resolve the master token
|
|
||||||
waitForLeaderEstablishment(t, srv)
|
|
||||||
|
|
||||||
req := structs.ACLPolicyResolveLegacyRequest{
|
|
||||||
Datacenter: srv.config.Datacenter,
|
|
||||||
ACL: TestDefaultMasterToken,
|
|
||||||
}
|
|
||||||
|
|
||||||
var resp structs.ACLPolicyResolveLegacyResponse
|
|
||||||
require.NoError(t, msgpackrpc.CallWithCodec(codec, "ACL.GetPolicy", &req, &resp))
|
|
||||||
require.Equal(t, "manage", resp.Parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestACLEndpoint_ReplicationStatus(t *testing.T) {
|
func TestACLEndpoint_ReplicationStatus(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("too slow for testing.Short")
|
t.Skip("too slow for testing.Short")
|
||||||
|
|
|
@ -434,7 +434,6 @@ type ACLResolverTestDelegate struct {
|
||||||
localTokens bool
|
localTokens bool
|
||||||
localPolicies bool
|
localPolicies bool
|
||||||
localRoles bool
|
localRoles bool
|
||||||
getPolicyFn func(*structs.ACLPolicyResolveLegacyRequest, *structs.ACLPolicyResolveLegacyResponse) error
|
|
||||||
tokenReadFn func(*structs.ACLTokenGetRequest, *structs.ACLTokenResponse) error
|
tokenReadFn func(*structs.ACLTokenGetRequest, *structs.ACLTokenResponse) error
|
||||||
policyResolveFn func(*structs.ACLPolicyBatchGetRequest, *structs.ACLPolicyBatchResponse) error
|
policyResolveFn func(*structs.ACLPolicyBatchGetRequest, *structs.ACLPolicyBatchResponse) error
|
||||||
roleResolveFn func(*structs.ACLRoleBatchGetRequest, *structs.ACLRoleBatchResponse) 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 {
|
func (d *ACLResolverTestDelegate) RPC(method string, args interface{}, reply interface{}) error {
|
||||||
switch method {
|
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":
|
case "ACL.TokenRead":
|
||||||
atomic.AddInt32(&d.remoteTokenResolutions, 1)
|
atomic.AddInt32(&d.remoteTokenResolutions, 1)
|
||||||
if d.tokenReadFn != nil {
|
if d.tokenReadFn != nil {
|
||||||
|
@ -1571,49 +1564,6 @@ func TestACLResolver_Client(t *testing.T) {
|
||||||
require.EqualValues(t, 0, delegate.remoteLegacyResolutions)
|
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.Run("Concurrent-Token-Resolve", func(t *testing.T) {
|
||||||
t.Parallel()
|
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) {
|
func TestACL_filterHealthChecks(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Create some health checks.
|
// Create some health checks.
|
||||||
|
|
|
@ -811,15 +811,6 @@ func (policies ACLPolicies) Compile(cache *ACLCaches, entConf *acl.Config) (acl.
|
||||||
return authorizer, nil
|
return authorizer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (policies ACLPolicies) Merge(cache *ACLCaches, entConf *acl.Config) (*acl.Policy, error) {
|
|
||||||
parsed, err := policies.resolveWithCache(cache, entConf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return acl.MergePolicies(parsed), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ACLRoles []*ACLRole
|
type ACLRoles []*ACLRole
|
||||||
|
|
||||||
// HashKey returns a consistent hash for a set of roles.
|
// HashKey returns a consistent hash for a set of roles.
|
||||||
|
|
|
@ -3,8 +3,9 @@ package structs
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ACLCachesConfig struct {
|
type ACLCachesConfig struct {
|
||||||
|
@ -218,14 +219,6 @@ func (c *ACLCaches) PutAuthorizer(id string, authorizer acl.Authorizer) {
|
||||||
c.authorizers.Add(id, &AuthorizerCacheEntry{Authorizer: authorizer, CacheTime: time.Now()})
|
c.authorizers.Add(id, &AuthorizerCacheEntry{Authorizer: authorizer, CacheTime: time.Now()})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ACLCaches) PutAuthorizerWithTTL(id string, authorizer acl.Authorizer, ttl time.Duration) {
|
|
||||||
if c == nil || c.authorizers == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.authorizers.Add(id, &AuthorizerCacheEntry{Authorizer: authorizer, CacheTime: time.Now(), TTL: ttl})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ACLCaches) PutRole(roleID string, role *ACLRole) {
|
func (c *ACLCaches) PutRole(roleID string, role *ACLRole) {
|
||||||
if c == nil || c.roles == nil {
|
if c == nil || c.roles == nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -8,9 +8,6 @@ package structs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -114,25 +111,3 @@ type ACLBootstrap struct {
|
||||||
|
|
||||||
RaftIndex
|
RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// ACLPolicyResolveLegacyRequest is used to request an ACL by Token SecretID, conditionally
|
|
||||||
// filtering on an ID
|
|
||||||
type ACLPolicyResolveLegacyRequest struct {
|
|
||||||
Datacenter string // The Datacenter the RPC may be sent to
|
|
||||||
ACL string // The Tokens Secret ID
|
|
||||||
ETag string // Caching ETag to prevent resending the policy when not needed
|
|
||||||
QueryOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// RequestDatacenter returns the DC this request is targeted to.
|
|
||||||
func (r *ACLPolicyResolveLegacyRequest) RequestDatacenter() string {
|
|
||||||
return r.Datacenter
|
|
||||||
}
|
|
||||||
|
|
||||||
type ACLPolicyResolveLegacyResponse struct {
|
|
||||||
ETag string
|
|
||||||
Parent string
|
|
||||||
Policy *acl.Policy
|
|
||||||
TTL time.Duration
|
|
||||||
QueryMeta
|
|
||||||
}
|
|
||||||
|
|
|
@ -64,7 +64,6 @@ func TestStructs_Implements(t *testing.T) {
|
||||||
_ RPCInfo = &SessionRequest{}
|
_ RPCInfo = &SessionRequest{}
|
||||||
_ RPCInfo = &SessionSpecificRequest{}
|
_ RPCInfo = &SessionSpecificRequest{}
|
||||||
_ RPCInfo = &EventFireRequest{}
|
_ RPCInfo = &EventFireRequest{}
|
||||||
_ RPCInfo = &ACLPolicyResolveLegacyRequest{}
|
|
||||||
_ RPCInfo = &ACLPolicyBatchGetRequest{}
|
_ RPCInfo = &ACLPolicyBatchGetRequest{}
|
||||||
_ RPCInfo = &ACLPolicyGetRequest{}
|
_ RPCInfo = &ACLPolicyGetRequest{}
|
||||||
_ RPCInfo = &ACLTokenGetRequest{}
|
_ RPCInfo = &ACLTokenGetRequest{}
|
||||||
|
|
Loading…
Reference in New Issue