// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package structs import ( "time" lru "github.com/hashicorp/golang-lru" "github.com/hashicorp/consul/acl" ) type ACLCachesConfig struct { Identities int Policies int ParsedPolicies int Authorizers int Roles int } type ACLCaches struct { identities *lru.TwoQueueCache // identity id -> structs.ACLIdentity parsedPolicies *lru.TwoQueueCache // policy content hash -> acl.Policy policies *lru.TwoQueueCache // policy ID -> ACLPolicy authorizers *lru.TwoQueueCache // token secret -> acl.Authorizer roles *lru.TwoQueueCache // role ID -> ACLRole } type IdentityCacheEntry struct { Identity ACLIdentity CacheTime time.Time } func (e *IdentityCacheEntry) Age() time.Duration { return time.Since(e.CacheTime) } type ParsedPolicyCacheEntry struct { Policy *acl.Policy CacheTime time.Time } func (e *ParsedPolicyCacheEntry) Age() time.Duration { return time.Since(e.CacheTime) } type PolicyCacheEntry struct { Policy *ACLPolicy CacheTime time.Time } func (e *PolicyCacheEntry) Age() time.Duration { return time.Since(e.CacheTime) } type AuthorizerCacheEntry struct { Authorizer acl.Authorizer CacheTime time.Time TTL time.Duration } func (e *AuthorizerCacheEntry) Age() time.Duration { return time.Since(e.CacheTime) } type RoleCacheEntry struct { Role *ACLRole CacheTime time.Time } func (e *RoleCacheEntry) Age() time.Duration { return time.Since(e.CacheTime) } func NewACLCaches(config *ACLCachesConfig) (*ACLCaches, error) { cache := &ACLCaches{} if config != nil && config.Identities > 0 { identCache, err := lru.New2Q(config.Identities) if err != nil { return nil, err } cache.identities = identCache } if config != nil && config.Policies > 0 { policyCache, err := lru.New2Q(config.Policies) if err != nil { return nil, err } cache.policies = policyCache } if config != nil && config.ParsedPolicies > 0 { parsedCache, err := lru.New2Q(config.ParsedPolicies) if err != nil { return nil, err } cache.parsedPolicies = parsedCache } if config != nil && config.Authorizers > 0 { authCache, err := lru.New2Q(config.Authorizers) if err != nil { return nil, err } cache.authorizers = authCache } if config != nil && config.Roles > 0 { roleCache, err := lru.New2Q(config.Roles) if err != nil { return nil, err } cache.roles = roleCache } return cache, nil } // GetIdentity fetches an identity from the cache and returns it func (c *ACLCaches) GetIdentity(id string) *IdentityCacheEntry { if c == nil || c.identities == nil { return nil } if raw, ok := c.identities.Get(id); ok { return raw.(*IdentityCacheEntry) } return nil } // GetIdentityWithSecretToken fetches the identity with the given secret token // from the cache. func (c *ACLCaches) GetIdentityWithSecretToken(secretToken string) *IdentityCacheEntry { return c.GetIdentity(cacheIDSecretToken(secretToken)) } // GetPolicy fetches a policy from the cache and returns it func (c *ACLCaches) GetPolicy(policyID string) *PolicyCacheEntry { if c == nil || c.policies == nil { return nil } if raw, ok := c.policies.Get(policyID); ok { return raw.(*PolicyCacheEntry) } return nil } // GetPolicy fetches a policy from the cache and returns it func (c *ACLCaches) GetParsedPolicy(id string) *ParsedPolicyCacheEntry { if c == nil || c.parsedPolicies == nil { return nil } if raw, ok := c.parsedPolicies.Get(id); ok { return raw.(*ParsedPolicyCacheEntry) } return nil } // GetAuthorizer fetches a acl from the cache and returns it func (c *ACLCaches) GetAuthorizer(id string) *AuthorizerCacheEntry { if c == nil || c.authorizers == nil { return nil } if raw, ok := c.authorizers.Get(id); ok { return raw.(*AuthorizerCacheEntry) } return nil } // GetRole fetches a role from the cache by id and returns it func (c *ACLCaches) GetRole(roleID string) *RoleCacheEntry { if c == nil || c.roles == nil { return nil } if raw, ok := c.roles.Get(roleID); ok { return raw.(*RoleCacheEntry) } return nil } // PutIdentity adds a new identity to the cache func (c *ACLCaches) PutIdentity(id string, ident ACLIdentity) { if c == nil || c.identities == nil { return } c.identities.Add(id, &IdentityCacheEntry{Identity: ident, CacheTime: time.Now()}) } // PutIdentityWithSecretToken adds a new identity to the cache, keyed by the // given secret token (with a prefix to prevent collisions). func (c *ACLCaches) PutIdentityWithSecretToken(secretToken string, identity ACLIdentity) { c.PutIdentity(cacheIDSecretToken(secretToken), identity) } // RemoveIdentityWithSecretToken removes the identity from the cache with the // given secret token. func (c *ACLCaches) RemoveIdentityWithSecretToken(secretToken string) { if c == nil || c.identities == nil { return } c.identities.Remove(cacheIDSecretToken(secretToken)) } func (c *ACLCaches) PutPolicy(policyId string, policy *ACLPolicy) { if c == nil || c.policies == nil { return } c.policies.Add(policyId, &PolicyCacheEntry{Policy: policy, CacheTime: time.Now()}) } func (c *ACLCaches) PutParsedPolicy(id string, policy *acl.Policy) { if c == nil || c.parsedPolicies == nil { return } c.parsedPolicies.Add(id, &ParsedPolicyCacheEntry{Policy: policy, CacheTime: time.Now()}) } func (c *ACLCaches) PutAuthorizer(id string, authorizer acl.Authorizer) { if c == nil || c.authorizers == nil { return } c.authorizers.Add(id, &AuthorizerCacheEntry{Authorizer: authorizer, CacheTime: time.Now()}) } func (c *ACLCaches) PutRole(roleID string, role *ACLRole) { if c == nil || c.roles == nil { return } c.roles.Add(roleID, &RoleCacheEntry{Role: role, CacheTime: time.Now()}) } func (c *ACLCaches) RemoveIdentity(id string) { if c != nil && c.identities != nil { c.identities.Remove(id) } } func (c *ACLCaches) RemovePolicy(policyID string) { if c != nil && c.policies != nil { c.policies.Remove(policyID) } } func (c *ACLCaches) RemoveRole(roleID string) { if c != nil && c.roles != nil { c.roles.Remove(roleID) } } func (c *ACLCaches) Purge() { if c != nil { if c.identities != nil { c.identities.Purge() } if c.policies != nil { c.policies.Purge() } if c.parsedPolicies != nil { c.parsedPolicies.Purge() } if c.authorizers != nil { c.authorizers.Purge() } if c.roles != nil { c.roles.Purge() } } } func cacheIDSecretToken(token string) string { return "token-secret:" + token }