diff options
| author | James Elliott <james-d-elliott@users.noreply.github.com> | 2025-02-24 18:49:15 +1100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-24 18:49:15 +1100 |
| commit | 05fa254f27e6a1eee89abe8c5512d27de77cd7e7 (patch) | |
| tree | 8f56e80b855f611000fe142303026f0ed71ef7ed /internal/authentication/cached.go | |
| parent | 194dd221ab7a5f2c9667b967532152df4914226a (diff) | |
feat(handlers): basic authz caching (#8320)
This adds the ability to cache successful basic authz attempts. This is done via a memory store that uses the HMAC-SHA256 algorithm to perform irreversible comparison of input parameters and has a maximum lifetime.
Closes #5006
Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
Diffstat (limited to 'internal/authentication/cached.go')
| -rw-r--r-- | internal/authentication/cached.go | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/internal/authentication/cached.go b/internal/authentication/cached.go new file mode 100644 index 000000000..b84d988bf --- /dev/null +++ b/internal/authentication/cached.go @@ -0,0 +1,106 @@ +package authentication + +import ( + "crypto/hmac" + "crypto/rand" + "fmt" + "hash" + "sync" + "time" +) + +// NewCredentialCacheHMAC creates a new CredentialCacheHMAC with a given hash.Hash func and lifespan. +func NewCredentialCacheHMAC(h func() hash.Hash, lifespan time.Duration) *CredentialCacheHMAC { + secret := make([]byte, h().BlockSize()) + + _, _ = rand.Read(secret) + + return &CredentialCacheHMAC{ + mu: sync.Mutex{}, + hash: hmac.New(h, secret), + lifespan: lifespan, + + values: map[string]CachedCredential{}, + } +} + +// CredentialCacheHMAC implements in-memory credential caching using a HMAC function and effective lifespan. +type CredentialCacheHMAC struct { + mu sync.Mutex + hash hash.Hash + + lifespan time.Duration + + values map[string]CachedCredential +} + +// Valid checks the cache for results for a given username and password in the cache and returns two booleans. The valid +// return value is indicative if the credential cache had an exact match, and the ok return value returns true if a +// current cached value exists within the cache. +func (c *CredentialCacheHMAC) Valid(username, password string) (valid, ok bool) { + c.mu.Lock() + + defer c.mu.Unlock() + + var ( + entry CachedCredential + err error + ) + + if entry, ok = c.values[username]; ok { + if entry.expires.Before(time.Now()) { + delete(c.values, username) + + return false, false + } + } + + var value []byte + + if value, err = c.sum(username, password); err != nil { + return false, false + } + + valid = hmac.Equal(value, entry.value) + + c.hash.Reset() + + return valid, true +} + +func (c *CredentialCacheHMAC) sum(username, password string) (sum []byte, err error) { + defer c.hash.Reset() + + if _, err = c.hash.Write([]byte(password)); err != nil { + return nil, fmt.Errorf("error occurred calculating cache hmac: %w", err) + } + + if _, err = c.hash.Write([]byte(username)); err != nil { + return nil, fmt.Errorf("error occurred calculating cache hmac: %w", err) + } + + return c.hash.Sum(nil), nil +} + +// Put a new credential combination into the cache. +func (c *CredentialCacheHMAC) Put(username, password string) (err error) { + c.mu.Lock() + + defer c.mu.Unlock() + + var value []byte + + if value, err = c.sum(username, password); err != nil { + return err + } + + c.values[username] = CachedCredential{expires: time.Now().Add(c.lifespan), value: value} + + return nil +} + +// CachedCredential is a cached credential which has an expiration and checksum value. +type CachedCredential struct { + expires time.Time + value []byte +} |
