mirror of https://github.com/portainer/portainer
80 lines
2.4 KiB
Go
80 lines
2.4 KiB
Go
package apikey
|
|
|
|
import (
|
|
portainer "github.com/portainer/portainer/api"
|
|
|
|
lru "github.com/hashicorp/golang-lru"
|
|
)
|
|
|
|
const DefaultAPIKeyCacheSize = 1024
|
|
|
|
// entry is a tuple containing the user and API key associated to an API key digest
|
|
type entry[T any] struct {
|
|
user T
|
|
apiKey portainer.APIKey
|
|
}
|
|
|
|
type UserCompareFn[T any] func(T, portainer.UserID) bool
|
|
|
|
// ApiKeyCache is a concurrency-safe, in-memory cache which primarily exists for to reduce database roundtrips.
|
|
// We store the api-key digest (keys) and the associated user and key-data (values) in the cache.
|
|
// This is required because HTTP requests will contain only the api-key digest in the x-api-key request header;
|
|
// digest value must be mapped to a portainer user (and respective key data) for validation.
|
|
// This cache is used to avoid multiple database queries to retrieve these user/key associated to the digest.
|
|
type ApiKeyCache[T any] struct {
|
|
// cache type [string]entry cache (key: string(digest), value: user/key entry)
|
|
// note: []byte keys are not supported by golang-lru Cache
|
|
cache *lru.Cache
|
|
userCmpFn UserCompareFn[T]
|
|
}
|
|
|
|
// NewAPIKeyCache creates a new cache for API keys
|
|
func NewAPIKeyCache[T any](cacheSize int, userCompareFn UserCompareFn[T]) *ApiKeyCache[T] {
|
|
cache, _ := lru.New(cacheSize)
|
|
|
|
return &ApiKeyCache[T]{cache: cache, userCmpFn: userCompareFn}
|
|
}
|
|
|
|
// Get returns the user/key associated to an api-key's digest
|
|
// This is required because HTTP requests will contain the digest of the API key in header,
|
|
// the digest value must be mapped to a portainer user.
|
|
func (c *ApiKeyCache[T]) Get(digest string) (T, portainer.APIKey, bool) {
|
|
val, ok := c.cache.Get(digest)
|
|
if !ok {
|
|
var t T
|
|
|
|
return t, portainer.APIKey{}, false
|
|
}
|
|
|
|
tuple := val.(entry[T])
|
|
|
|
return tuple.user, tuple.apiKey, true
|
|
}
|
|
|
|
// Set persists a user/key entry to the cache
|
|
func (c *ApiKeyCache[T]) Set(digest string, user T, apiKey portainer.APIKey) {
|
|
c.cache.Add(digest, entry[T]{
|
|
user: user,
|
|
apiKey: apiKey,
|
|
})
|
|
}
|
|
|
|
// Delete evicts a digest's user/key entry key from the cache
|
|
func (c *ApiKeyCache[T]) Delete(digest string) {
|
|
c.cache.Remove(digest)
|
|
}
|
|
|
|
// InvalidateUserKeyCache loops through all the api-keys associated to a user and removes them from the cache
|
|
func (c *ApiKeyCache[T]) InvalidateUserKeyCache(userId portainer.UserID) bool {
|
|
present := false
|
|
|
|
for _, k := range c.cache.Keys() {
|
|
user, _, _ := c.Get(k.(string))
|
|
if c.userCmpFn(user, userId) {
|
|
present = c.cache.Remove(k)
|
|
}
|
|
}
|
|
|
|
return present
|
|
}
|