Files
traefikoidc/cache_compat.go
lukaszraczylo 1b49e133da Complete rebuild of the plugin
* Fix bug affecting Azure OIDC authentication ( and most likely others )

* Fixes issue #51

* Ensure that appended roles are unique. Update the documentation.

* Improvements targetting possible memory usage spikes.

* Additional fixes and cleanup

* Refactoring code to fix the issues identified by the users.

* Modernize run

* Fieldalignment

* Multiple changes to improve performance and reduce complexity.
- Optimise the errors and recovery.
- Deduplicate code in metadata cache.
- Remove unused performance monitoring code.
- Simplify session management and settings handling.

* Fix claims issue.

* Add ability to overwrite the default scopes in the settings file

* Well.. that escalated quickly.

Completely forgot that Traefik uses outdated Yaegi and requires compatibility with 1.20 ( pre-generic Go code ).

* Bugfix #51: Ensures that user provided scopes overrides work.

* fixup! Bugfix #51: Ensures that user provided scopes overrides work.

* fixup! fixup! Bugfix #51: Ensures that user provided scopes overrides work.

* Abstract the provider logic into a separate package.

* Additional micro fixes and cleanups.

* Simplify all the things.

* fixup! Simplify all the things.

* fixup! fixup! Simplify all the things.

* fixup! fixup! fixup! Simplify all the things.

* fixup! fixup! fixup! fixup! Simplify all the things.

* ...

* Cleanup tests.

* fixup! Cleanup tests.

* fixup! fixup! fixup! Cleanup tests.

* fixup! fixup! fixup! fixup! Cleanup tests.

* fixup! fixup! fixup! fixup! fixup! Cleanup tests.

* Issue #53: Fix CSRF token handling in reverse proxy

1.  HTTPS Detection Fixed (session.go:723)
- Now uses X-Forwarded-Proto header instead of r.URL.Scheme
- Properly detects HTTPS in reverse proxy environments
2.  SameSite Cookie Attribute Fixed
- Removed automatic SameSiteStrictMode for HTTPS (would break OAuth)
- Keeps SameSiteLaxMode to allow OAuth callbacks from external domains
- Only uses Strict for AJAX requests which don't involve OAuth redirects
3.  Cookie Domain Handling Fixed
- Now respects X-Forwarded-Host header for cookie domain
- Ensures cookies are set for the public domain, not internal proxy domain
4.  EnhanceSessionSecurity Properly Integrated
- Function is now actually called during session save
- Applies security enhancements without breaking OAuth flow

Why Issue #53 Failed Before:

1. Cookies were not marked Secure in HTTPS environments (browser wouldn't send them back)
2. If they had been Secure with SameSite=Strict, Azure callbacks would still fail
3. Cookie domain might have been wrong (internal vs public domain)

Why It Works Now:

1. Cookies are properly marked Secure for HTTPS
2. Uses SameSite=Lax to allow OAuth provider callbacks
3. Cookie domain uses public domain from X-Forwarded-Host
4. CSRF token persists through the entire OAuth flow

* Next set of enhancements together with memory usage improvements.

* Memory leak fixes and optimisations.

* CSRF and Cookie Domain fixes

* fixup! CSRF and Cookie Domain fixes

* Metadata cache leak fix + profiling

* fixup! Metadata cache leak fix + profiling

* Memory leaks hunting, part 1337.

* Further pursue of perfection.

* fixup! Further pursue of perfection.

* fixup! fixup! Further pursue of perfection.

* fixup! fixup! fixup! Further pursue of perfection.

* fixup! fixup! fixup! fixup! Further pursue of perfection.

* fixup! fixup! fixup! fixup! fixup! Further pursue of perfection.

* fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection.

* fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection.

* fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection.

* fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection.

* Clear race conditions

* fixup! Clear race conditions

* Weekend fun with memory leaks

* 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
````

* fixup! Splitting code into multiple files with reasonable testing coverage.

* fixup! fixup! Splitting code into multiple files with reasonable testing coverage.

* Weekend fun with further optimisations.

* fixup! Weekend fun with further optimisations.

* fixup! fixup! Weekend fun with further optimisations.

* fixup! fixup! fixup! Weekend fun with further optimisations.

* fixup! fixup! fixup! fixup! Weekend fun with further optimisations.

* fixup! fixup! fixup! fixup! fixup! Weekend fun with further optimisations.

* Pre-release cleanup.

* Enhance test coverage.

* fixup! Enhance test coverage.

* fixup! fixup! Enhance test coverage.

* fixup! fixup! fixup! Enhance test coverage.
2025-09-18 11:01:30 +01:00

254 lines
6.3 KiB
Go

package traefikoidc
import (
"container/list"
"time"
)
// Cache compatibility layer - maps old cache types to UniversalCache
// NewCache creates a general purpose cache
func NewCache() CacheInterface {
config := UniversalCacheConfig{
Type: CacheTypeGeneral,
MaxSize: 1000,
Logger: GetSingletonNoOpLogger(),
}
return &CacheInterfaceWrapper{
cache: NewUniversalCache(config),
}
}
// NewBoundedCache creates a bounded cache with specified max size
func NewBoundedCache(maxSize int) CacheInterface {
config := UniversalCacheConfig{
Type: CacheTypeGeneral,
MaxSize: maxSize,
Logger: GetSingletonNoOpLogger(),
}
return &CacheInterfaceWrapper{
cache: NewUniversalCache(config),
}
}
// BoundedCache is an alias for compatibility
type BoundedCache = CacheInterfaceWrapper
// BoundedCacheAdapter is an alias for compatibility
type BoundedCacheAdapter = CacheInterfaceWrapper
// UnifiedCache wraps UniversalCache for backward compatibility
type UnifiedCache struct {
*UniversalCache
strategy CacheStrategy // For backward compatibility with tests
}
// SetMaxSize sets the maximum cache size
func (c *UnifiedCache) SetMaxSize(size int) {
c.UniversalCache.SetMaxSize(size)
}
// UnifiedCacheConfig is an alias for backward compatibility
type UnifiedCacheConfig = UniversalCacheConfig
// DefaultUnifiedCacheConfig returns default config for backward compatibility
func DefaultUnifiedCacheConfig() UniversalCacheConfig {
return UniversalCacheConfig{
Type: CacheTypeGeneral,
MaxSize: 500,
MaxMemoryBytes: 64 * 1024 * 1024,
CleanupInterval: 2 * time.Minute,
Logger: GetSingletonNoOpLogger(),
}
}
// NewUnifiedCache creates a universal cache for backward compatibility
func NewUnifiedCache(config UniversalCacheConfig) *UnifiedCache {
// Avoid circular reference by calling the real constructor
cache := createUniversalCache(config)
return &UnifiedCache{
UniversalCache: cache,
strategy: config.Strategy,
}
}
// CacheAdapter wraps UniversalCache for backward compatibility
type CacheAdapter = CacheInterfaceWrapper
// NewCacheAdapter creates a cache adapter
func NewCacheAdapter(cache interface{}) *CacheInterfaceWrapper {
switch c := cache.(type) {
case *UniversalCache:
return &CacheInterfaceWrapper{cache: c}
case *UnifiedCache:
return &CacheInterfaceWrapper{cache: c.UniversalCache}
default:
// Try to convert to UniversalCache
if uc, ok := cache.(*UniversalCache); ok {
return &CacheInterfaceWrapper{cache: uc}
}
return nil
}
}
// OptimizedCache is an alias for backward compatibility
type OptimizedCache = CacheInterfaceWrapper
// NewOptimizedCache creates an optimized cache
func NewOptimizedCache() *CacheInterfaceWrapper {
config := UniversalCacheConfig{
Type: CacheTypeGeneral,
MaxSize: 500,
MaxMemoryBytes: 64 * 1024 * 1024,
EnableMetrics: true,
Logger: GetSingletonNoOpLogger(),
}
return &CacheInterfaceWrapper{
cache: NewUniversalCache(config),
}
}
// LRUStrategy for backward compatibility
type LRUStrategy struct {
order *list.List
elements map[string]*list.Element
maxSize int
}
func NewLRUStrategy(maxSize int) CacheStrategy {
return &LRUStrategy{
order: list.New(),
elements: make(map[string]*list.Element),
maxSize: maxSize,
}
}
func (s *LRUStrategy) Name() string {
return "LRU"
}
func (s *LRUStrategy) ShouldEvict(item interface{}, now time.Time) bool {
return false
}
func (s *LRUStrategy) OnAccess(key string, item interface{}) {}
func (s *LRUStrategy) OnRemove(key string) {}
func (s *LRUStrategy) EstimateSize(item interface{}) int64 {
return 64
}
func (s *LRUStrategy) GetEvictionCandidate() (key string, found bool) {
return "", false
}
// CacheStrategy interface for backward compatibility
type CacheStrategy interface {
Name() string
ShouldEvict(item interface{}, now time.Time) bool
OnAccess(key string, item interface{})
OnRemove(key string)
EstimateSize(item interface{}) int64
GetEvictionCandidate() (key string, found bool)
}
// CacheEntry for backward compatibility
type CacheEntry struct {
Key string
Value interface{}
ExpiresAt time.Time
}
// Cache is an alias for backward compatibility
type Cache = CacheInterfaceWrapper
// OptimizedCacheConfig for backward compatibility
type OptimizedCacheConfig = UniversalCacheConfig
// NewOptimizedCacheWithConfig creates cache with config
func NewOptimizedCacheWithConfig(config OptimizedCacheConfig) *CacheInterfaceWrapper {
return &CacheInterfaceWrapper{
cache: NewUniversalCache(config),
}
}
// ListNode for backward compatibility
type ListNode struct {
Key string
Value interface{}
Next *ListNode
Prev *ListNode
}
// NewFixedMetadataCache creates a metadata cache with fixed configuration
func NewFixedMetadataCache(args ...interface{}) *MetadataCache {
// Accept variable arguments for backward compatibility
// Expected args: maxSize, maxMemoryMB, logger
logger := GetSingletonNoOpLogger()
maxSize := 100 // default
maxMemoryMB := int64(0) // default no limit
if len(args) > 0 {
if size, ok := args[0].(int); ok {
maxSize = size
}
}
if len(args) > 1 {
if memMB, ok := args[1].(int); ok {
maxMemoryMB = int64(memMB) * 1024 * 1024 // Convert MB to bytes
}
}
if len(args) > 2 {
if l, ok := args[2].(*Logger); ok {
logger = l
}
}
// Create a custom cache with the specified max size
config := UniversalCacheConfig{
Type: CacheTypeMetadata,
MaxSize: maxSize,
MaxMemoryBytes: maxMemoryMB,
DefaultTTL: 1 * time.Hour,
MetadataConfig: &MetadataCacheConfig{
GracePeriod: 5 * time.Minute,
ExtendedGracePeriod: 15 * time.Minute,
MaxGracePeriod: 30 * time.Minute,
SecurityCriticalMaxGracePeriod: 15 * time.Minute,
},
Logger: logger,
}
cache := NewUniversalCache(config)
return &MetadataCache{
cache: cache,
logger: logger,
wg: nil,
}
}
// DoublyLinkedList for backward compatibility
type DoublyLinkedList struct {
*list.List
}
// NewDoublyLinkedList creates a new doubly linked list
func NewDoublyLinkedList() *DoublyLinkedList {
return &DoublyLinkedList{
List: list.New(),
}
}
// PopFront removes and returns the front element
func (l *DoublyLinkedList) PopFront() interface{} {
if l.Len() == 0 {
return nil
}
elem := l.Front()
if elem != nil {
return l.Remove(elem)
}
return nil
}