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 }