Splitting code into multiple files with reasonable testing coverage.

```
ok      github.com/lukaszraczylo/traefikoidc    117.017s        coverage: 72.6% of statements
ok      github.com/lukaszraczylo/traefikoidc/auth       0.505s  coverage: 87.1% of statements
ok      github.com/lukaszraczylo/traefikoidc/circuit_breaker    0.283s  coverage: 99.0% of statements
        github.com/lukaszraczylo/traefikoidc/config             coverage: 0.0% of statements
ok      github.com/lukaszraczylo/traefikoidc/handlers   0.349s  coverage: 98.2% of statements
ok      github.com/lukaszraczylo/traefikoidc/internal/providers (cached)        coverage: 94.3% of statements
ok      github.com/lukaszraczylo/traefikoidc/middleware 0.808s  coverage: 78.0% of statements
ok      github.com/lukaszraczylo/traefikoidc/recovery   0.653s  coverage: 100.0% of statements
ok      github.com/lukaszraczylo/traefikoidc/session/chunking   (cached)        coverage: 87.8% of statements
ok      github.com/lukaszraczylo/traefikoidc/session/core       (cached)        coverage: 85.6% of statements
ok      github.com/lukaszraczylo/traefikoidc/session/crypto     (cached)        coverage: 81.8% of statements
ok      github.com/lukaszraczylo/traefikoidc/session/storage    (cached)        coverage: 93.5% of statements
ok      github.com/lukaszraczylo/traefikoidc/session/validators (cached)        coverage: 98.8% of statements
````
This commit is contained in:
2025-09-07 12:57:18 +01:00
parent c9a23dc6c0
commit 4371f9fffd
26 changed files with 15091 additions and 243 deletions
+1 -243
View File
@@ -31,251 +31,9 @@ func createDefaultHTTPClient() *http.Client {
}
const (
ConstSessionTimeout = 86400
defaultBlacklistDuration = 24 * time.Hour
defaultMaxBlacklistSize = 10000
ConstSessionTimeout = 86400
)
var (
globalCacheManager *CacheManager
cacheManagerOnce sync.Once
cacheManagerMutex sync.RWMutex
)
// CacheManager manages all caching components for the OIDC middleware.
// It provides thread-safe access to token blacklist, token cache, metadata cache,
// and JWK cache. This centralizes cache management to ensure efficient memory
// usage and consistent cache behavior across the application.
// CacheInterface defines the common cache operations
type CacheInterface interface {
Set(key string, value interface{}, ttl time.Duration)
Get(key string) (interface{}, bool)
Delete(key string)
SetMaxSize(size int)
Cleanup()
Close()
}
type CacheManager struct {
tokenBlacklist CacheInterface
tokenCache *TokenCache
metadataCache *MetadataCache
jwkCache JWKCacheInterface
mu sync.RWMutex
}
// GetGlobalCacheManager returns a singleton instance of CacheManager.
// It initializes all cache components on first call and reuses the same instance
// for subsequent calls. This ensures consistent cache state and efficient memory
// usage across the entire application lifecycle.
// Parameters:
// - wg: WaitGroup for coordinating cache cleanup during shutdown.
//
// Returns:
// - The global CacheManager instance.
func GetGlobalCacheManager(wg *sync.WaitGroup) *CacheManager {
cacheManagerOnce.Do(func() {
globalCacheManager = &CacheManager{
tokenBlacklist: func() CacheInterface {
config := DefaultUnifiedCacheConfig()
config.MaxSize = defaultMaxBlacklistSize
c := NewUnifiedCache(config)
return NewCacheAdapter(c)
}(),
tokenCache: NewTokenCache(),
metadataCache: NewMetadataCache(wg),
jwkCache: &JWKCache{},
}
})
return globalCacheManager
}
// GetSharedTokenBlacklist returns the shared token blacklist cache.
// This cache stores revoked or expired tokens to prevent replay attacks.
// Access is protected by read lock to ensure thread safety.
// Returns:
// - The shared token blacklist CacheInterface instance.
func (cm *CacheManager) GetSharedTokenBlacklist() CacheInterface {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.tokenBlacklist
}
// GetSharedTokenCache returns the shared token cache for verified tokens.
// This cache stores claims from successfully verified tokens to avoid
// repeated verification of the same tokens.
// Access is protected by read lock to ensure thread safety.
// Returns:
// - The shared TokenCache instance.
func (cm *CacheManager) GetSharedTokenCache() *TokenCache {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.tokenCache
}
// GetSharedMetadataCache returns the shared metadata cache.
// This cache stores OIDC provider metadata (endpoints, keys) to avoid
// repeated requests to the provider's .well-known configuration endpoint.
// Access is protected by read lock to ensure thread safety.
// Returns:
// - The shared MetadataCache instance.
func (cm *CacheManager) GetSharedMetadataCache() *MetadataCache {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.metadataCache
}
// GetSharedJWKCache returns the shared JWK (JSON Web Key) cache.
// This cache stores public keys from the provider's JWKS endpoint
// for token signature verification.
// Access is protected by read lock to ensure thread safety.
// Returns:
// - The shared JWKCacheInterface instance.
func (cm *CacheManager) GetSharedJWKCache() JWKCacheInterface {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.jwkCache
}
// Close gracefully shuts down all cache components and releases resources.
// It closes all individual caches and cleans up their associated goroutines.
// This method should be called when the middleware is shutting down.
// Safe to call multiple times.
// Returns:
// - An error if any cache fails to close properly, nil otherwise.
func (cm *CacheManager) Close() error {
cm.mu.Lock()
defer cm.mu.Unlock()
if cm.tokenBlacklist != nil {
cm.tokenBlacklist.Close()
cm.tokenBlacklist = nil
}
if cm.tokenCache != nil {
cm.tokenCache.Close()
cm.tokenCache = nil
}
if cm.metadataCache != nil {
cm.metadataCache.Close()
cm.metadataCache = nil
}
if cm.jwkCache != nil {
cm.jwkCache.Close()
cm.jwkCache = nil
}
return nil
}
// CleanupGlobalCacheManager cleans up the global cache manager instance.
// It closes all cache components but does NOT reset the singleton for re-initialization
// to avoid race conditions with sync.Once. It's safe to call multiple times.
// Returns:
// - An error if cache cleanup fails, nil otherwise.
func CleanupGlobalCacheManager() error {
cacheManagerMutex.Lock()
defer cacheManagerMutex.Unlock()
if globalCacheManager != nil {
err := globalCacheManager.Close()
// Don't reset globalCacheManager to nil or cacheManagerOnce to avoid race conditions
// The cache manager remains closed and the Close() method handles multiple calls
return err
}
return nil
}
// TokenVerifier interface defines token verification capabilities.
// Implementations should validate token format, signature, and claims.
type TokenVerifier interface {
VerifyToken(token string) error
}
// JWTVerifier interface defines JWT-specific verification capabilities.
// Implementations should validate JWT structure, signature using JWKs, and standard claims.
type JWTVerifier interface {
VerifyJWTSignatureAndClaims(jwt *JWT, token string) error
}
// TokenExchanger interface defines OAuth 2.0 and OpenID Connect token exchange capabilities.
// Implementations should handle authorization code exchange, refresh tokens, and revocation
// according to the OAuth 2.0 and OpenID Connect specifications.
type TokenExchanger interface {
ExchangeCodeForToken(ctx context.Context, grantType string, codeOrToken string, redirectURL string, codeVerifier string) (*TokenResponse, error)
GetNewTokenWithRefreshToken(refreshToken string) (*TokenResponse, error)
RevokeTokenWithProvider(token, tokenType string) error
}
// TraefikOidc is the main middleware struct that implements OIDC authentication for Traefik.
// It integrates with various OIDC providers, manages sessions, caches tokens, and handles
// the complete authentication flow. It's designed to work seamlessly with Traefik's
// plugin system and provides flexible configuration options.
type TraefikOidc struct {
jwkCache JWKCacheInterface
jwtVerifier JWTVerifier
ctx context.Context
tokenVerifier TokenVerifier
next http.Handler
tokenExchanger TokenExchanger
initComplete chan struct{}
limiter *rate.Limiter
tokenBlacklist CacheInterface
headerTemplates map[string]*template.Template
sessionManager *SessionManager
tokenCleanupStopChan chan struct{}
excludedURLs map[string]struct{}
extractClaimsFunc func(tokenString string) (map[string]interface{}, error)
initiateAuthenticationFunc func(rw http.ResponseWriter, req *http.Request, session *SessionData, redirectURL string)
metadataCache *MetadataCache
allowedRolesAndGroups map[string]struct{}
allowedUsers map[string]struct{}
allowedUserDomains map[string]struct{}
tokenCache *TokenCache
httpClient *http.Client
tokenHTTPClient *http.Client
logger *Logger
metadataRefreshStopChan chan struct{}
cancelFunc context.CancelFunc
errorRecoveryManager *ErrorRecoveryManager
goroutineWG *sync.WaitGroup
clientSecret string
clientID string
name string
redirURLPath string
logoutURLPath string
tokenURL string
authURL string
endSessionURL string
postLogoutRedirectURI string
scheme string
jwksURL string
issuerURL string
revocationURL string
providerURL string
scopes []string
refreshGracePeriod time.Duration
shutdownOnce sync.Once
firstRequestMutex sync.Mutex
forceHTTPS bool
enablePKCE bool
overrideScopes bool
suppressDiagnosticLogs bool
firstRequestReceived bool
metadataRefreshStarted bool
}
// ProviderMetadata represents OIDC provider configuration data.
// This data is typically retrieved from the provider's .well-known/openid-configuration endpoint
// and contains essential URLs for authentication, token exchange, and key retrieval.
type ProviderMetadata struct {
Issuer string `json:"issuer"`
AuthURL string `json:"authorization_endpoint"`
TokenURL string `json:"token_endpoint"`
JWKSURL string `json:"jwks_uri"`
RevokeURL string `json:"revocation_endpoint"`
EndSessionURL string `json:"end_session_endpoint"`
}
// isTestMode detects if the code is running in a test environment.
// It checks various indicators including environment variables, command-line arguments,
// and runtime compiler information to determine test context.