mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-15 04:18:03 +00:00
fix(cache): harden cache backends, resilience, recovery, and transports
- RESP -ERR no longer closes healthy conn / aborts batch; global cap replacement-aware - global health-check pass serialized (latent data race); MemoryMonitor via ResourceManager - token-exchange retry, open-circuit rejections counted as failures - TLS-posture matching transport reuse (no silent InsecureSkipVerify handoff)
This commit is contained in:
@@ -71,8 +71,8 @@ func (s *RedisStore) Save(ctx context.Context, providerURL string, creds *Client
|
||||
|
||||
key := s.makeKey(providerURL)
|
||||
|
||||
// Calculate TTL based on client_secret_expires_at if available
|
||||
ttl := 30 * 24 * time.Hour // Default: 30 days
|
||||
// Calculate TTL based on client_secret_expires_at if available.
|
||||
var ttl time.Duration
|
||||
if creds.ClientSecretExpiresAt > 0 {
|
||||
expiresAt := time.Unix(creds.ClientSecretExpiresAt, 0)
|
||||
ttl = time.Until(expiresAt)
|
||||
@@ -83,6 +83,13 @@ func (s *RedisStore) Save(ctx context.Context, providerURL string, creds *Client
|
||||
if ttl > time.Minute {
|
||||
ttl -= time.Minute
|
||||
}
|
||||
} else {
|
||||
// RFC 7591: client_secret_expires_at == 0 means the secret never
|
||||
// expires (areCredentialsValid treats it as always-valid). Persist it
|
||||
// effectively-permanently: a bounded TTL (previously 30 days) would
|
||||
// evict a valid non-expiring credential mid-use, forcing a needless
|
||||
// re-registration and orphaning the previous client at the IdP.
|
||||
ttl = 100 * 365 * 24 * time.Hour
|
||||
}
|
||||
|
||||
// Serialize credentials to JSON for storage
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package dcrstorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// recordingCache captures the TTL each Set received, so a test can assert the
|
||||
// exact TTL semantic without depending on wall-clock time.
|
||||
type recordingCache struct {
|
||||
mu sync.Mutex
|
||||
setTTLs map[string]time.Duration
|
||||
}
|
||||
|
||||
func newRecordingCache() *recordingCache {
|
||||
return &recordingCache{setTTLs: make(map[string]time.Duration)}
|
||||
}
|
||||
|
||||
func (c *recordingCache) Get(key string) (any, bool) { return nil, false }
|
||||
func (c *recordingCache) Delete(key string) {}
|
||||
func (c *recordingCache) Set(key string, value any, ttl time.Duration) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.setTTLs[key] = ttl
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestRedisStore_NoExpiry_EffectivePermanent guards the R116 fix: an IdP that
|
||||
// reports client_secret_expires_at == 0 (RFC 7591: never expires) must be
|
||||
// persisted effectively-permanently. Previously such credentials were given a
|
||||
// 30-day default TTL, so a valid non-expiring credential was evicted mid-use,
|
||||
// forcing a needless re-registration and orphaning the old client at the IdP.
|
||||
func TestRedisStore_NoExpiry_EffectivePermanent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cache := newRecordingCache()
|
||||
store := NewRedisStore(cache, "", nil)
|
||||
|
||||
creds := &ClientRegistrationResponse{
|
||||
ClientID: "noexp",
|
||||
ClientSecret: "secret",
|
||||
ClientSecretExpiresAt: 0, // never expires
|
||||
}
|
||||
|
||||
if err := store.Save(context.Background(), "https://noexp.example.com", creds); err != nil {
|
||||
t.Fatalf("failed to save: %v", err)
|
||||
}
|
||||
|
||||
key := store.makeKey("https://noexp.example.com")
|
||||
cache.mu.Lock()
|
||||
ttl, ok := cache.setTTLs[key]
|
||||
cache.mu.Unlock()
|
||||
if !ok {
|
||||
t.Fatal("expected credential to be written to cache")
|
||||
}
|
||||
|
||||
// Effective permanence: well beyond any realistic rotation horizon. The
|
||||
// old behavior was a 30-day default, so require >= 1 year.
|
||||
if ttl < 365*24*time.Hour {
|
||||
t.Fatalf("expected effectively-permanent TTL (>=1y) for exp==0, got %v", ttl)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user