mirror of
https://github.com/versity/versitygw.git
synced 2026-09-23 00:14:15 +00:00
Merge pull request #2325 from potatogim/feat/iam-cache-fresh
auth: add GetUserAccountFresh to bypass the IAM cache
This commit is contained in:
@@ -177,6 +177,21 @@ func (c *IAMCache) ResolveAccounts(accessKeyIDs []string) ([]string, error) {
|
||||
return resolveAccountsByLookup(accessKeyIDs, c.GetUserAccount)
|
||||
}
|
||||
|
||||
// GetUserAccountFresh bypasses the in-memory cache and fetches the
|
||||
// account directly from the underlying IAM service, then refreshes
|
||||
// the cached entry with the result. This gives callers that need
|
||||
// revocation to take effect immediately (rather than after the
|
||||
// cache TTL) a way to observe the backing store state.
|
||||
func (c *IAMCache) GetUserAccountFresh(access string) (Account, error) {
|
||||
a, err := c.service.GetUserAccount(access)
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
|
||||
c.iamcache.set(access, a)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// DeleteUserAccount deletes account from IAM service and cache
|
||||
func (c *IAMCache) DeleteUserAccount(access string) error {
|
||||
err := c.service.DeleteUserAccount(access)
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type freshTestService struct {
|
||||
IAMService
|
||||
accounts map[string]Account
|
||||
fetches int
|
||||
}
|
||||
|
||||
func (s *freshTestService) GetUserAccount(access string) (Account, error) {
|
||||
s.fetches++
|
||||
a, ok := s.accounts[access]
|
||||
if !ok {
|
||||
return Account{}, ErrNoSuchUser
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// GetUserAccountFresh must bypass a cached-but-stale entry and
|
||||
// reflect the backing service state, refreshing the cache.
|
||||
func TestIAMCacheGetUserAccountFresh(t *testing.T) {
|
||||
svc := &freshTestService{accounts: map[string]Account{
|
||||
"acct": {Access: "acct", Secret: "old", Role: RoleUser},
|
||||
}}
|
||||
c := NewCache(svc, time.Minute, time.Hour)
|
||||
defer c.Shutdown()
|
||||
|
||||
// Prime the cache with the old secret.
|
||||
if _, err := c.GetUserAccount("acct"); err != nil {
|
||||
t.Fatalf("prime: %v", err)
|
||||
}
|
||||
|
||||
// Change the backing store behind the cache.
|
||||
svc.accounts["acct"] = Account{Access: "acct", Secret: "new", Role: RoleAdmin}
|
||||
|
||||
// Cached read still serves the stale entry.
|
||||
got, err := c.GetUserAccount("acct")
|
||||
if err != nil {
|
||||
t.Fatalf("cached read: %v", err)
|
||||
}
|
||||
if got.Secret != "old" {
|
||||
t.Fatalf("cached read: expected stale secret, got %q", got.Secret)
|
||||
}
|
||||
|
||||
// Fresh read must observe the update.
|
||||
got, err = c.GetUserAccountFresh("acct")
|
||||
if err != nil {
|
||||
t.Fatalf("fresh read: %v", err)
|
||||
}
|
||||
if got.Secret != "new" || got.Role != RoleAdmin {
|
||||
t.Fatalf("fresh read: got %+v", got)
|
||||
}
|
||||
|
||||
// And the cache is refreshed as a side effect.
|
||||
got, err = c.GetUserAccount("acct")
|
||||
if err != nil {
|
||||
t.Fatalf("post-fresh cached read: %v", err)
|
||||
}
|
||||
if got.Secret != "new" {
|
||||
t.Fatalf("post-fresh cached read: cache not refreshed, got %q", got.Secret)
|
||||
}
|
||||
|
||||
if svc.fetches != 2 {
|
||||
t.Fatalf("expected 2 backing fetches (prime + fresh), got %d", svc.fetches)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user