package cache
import (
"fmt"
"time"
"github.com/golang/glog"
"github.com/hashicorp/golang-lru"
kerrs "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/auth/user"
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
"github.com/openshift/origin/pkg/auth/authenticator"
)
type CacheAuthenticator struct {
authenticator authenticator.Token
cache *lru.Cache
ttl time.Duration
now func() time.Time
}
type cacheRecord struct {
created time.Time
user user.Info
ok bool
err error
}
// NewAuthenticator returns an authenticator that caches the results of the given authenticator
func NewAuthenticator(a authenticator.Token, ttl time.Duration, maxCount int) (authenticator.Token, error) {
cache, err := lru.New(maxCount)
if err != nil {
return nil, err
}
return &CacheAuthenticator{
authenticator: a,
cache: cache,
ttl: ttl,
now: time.Now,
}, nil
}
func (c *CacheAuthenticator) AuthenticateToken(token string) (user.Info, bool, error) {
if value, hit := c.cache.Get(token); hit {
switch record := value.(type) {
case *cacheRecord:
if record.created.Add(c.ttl).After(c.now()) {
glog.V(5).Infof("cache record found: %#v", record)
return record.user, record.ok, record.err
} else {
glog.V(5).Infof("cache record expired: %#v", record)
c.cache.Remove(token)
}
default:
utilruntime.HandleError(fmt.Errorf("invalid cache record type: %#v", record))
}
}
u, ok, err := c.authenticator.AuthenticateToken(token)
// Don't cache results if there was an error unrelated to authentication
// TODO: figure out a better way to determine this
if err == nil || kerrs.IsUnauthorized(err) {
c.cache.Add(token, &cacheRecord{created: c.now(), user: u, ok: ok, err: err})
}
return u, ok, err
}