Fieldalignment

This commit is contained in:
2025-06-20 08:07:03 +01:00
parent c233aa92ef
commit d8d1b74175
21 changed files with 226 additions and 401 deletions
+6 -17
View File
@@ -23,24 +23,13 @@ type lruEntry struct {
// Cache provides a thread-safe in-memory caching mechanism with expiration support. // Cache provides a thread-safe in-memory caching mechanism with expiration support.
// It implements an LRU (Least Recently Used) eviction policy using a doubly-linked list for efficiency. // It implements an LRU (Least Recently Used) eviction policy using a doubly-linked list for efficiency.
type Cache struct { type Cache struct {
// items stores the cached data with string keys. items map[string]CacheItem
items map[string]CacheItem order *list.List
elems map[string]*list.Element
// order maintains the usage order; most recently used items are at the back. stopCleanup chan struct{}
order *list.List maxSize int
// elems maps keys to their corresponding list elements for O(1) access.
elems map[string]*list.Element
// mutex protects concurrent access to the cache.
mutex sync.RWMutex
// maxSize is the maximum number of items allowed in the cache.
maxSize int
// autoCleanupInterval defines how often Cleanup is called automatically.
autoCleanupInterval time.Duration autoCleanupInterval time.Duration
// stopCleanup channel to terminate the auto cleanup goroutine. mutex sync.RWMutex
stopCleanup chan struct{}
} }
// DefaultMaxSize is the default maximum number of items in the cache. // DefaultMaxSize is the default maximum number of items in the cache.
+17 -32
View File
@@ -26,25 +26,18 @@ const (
// CircuitBreaker implements the circuit breaker pattern for external service calls // CircuitBreaker implements the circuit breaker pattern for external service calls
type CircuitBreaker struct { type CircuitBreaker struct {
// Configuration
maxFailures int // Maximum failures before opening
timeout time.Duration // How long to wait before trying again
resetTimeout time.Duration // How long to wait in half-open state
// State
state CircuitBreakerState
failures int64
lastFailureTime time.Time lastFailureTime time.Time
lastSuccessTime time.Time lastSuccessTime time.Time
logger *Logger
maxFailures int
timeout time.Duration
resetTimeout time.Duration
state CircuitBreakerState
failures int64
totalRequests int64
totalFailures int64
totalSuccesses int64
mutex sync.RWMutex mutex sync.RWMutex
// Metrics
totalRequests int64
totalFailures int64
totalSuccesses int64
// Logger
logger *Logger
} }
// CircuitBreakerConfig holds configuration for circuit breakers // CircuitBreakerConfig holds configuration for circuit breakers
@@ -193,12 +186,12 @@ func (cb *CircuitBreaker) GetMetrics() map[string]any {
// RetryConfig holds configuration for retry mechanisms // RetryConfig holds configuration for retry mechanisms
type RetryConfig struct { type RetryConfig struct {
RetryableErrors []string `json:"retryable_errors"`
MaxAttempts int `json:"max_attempts"` MaxAttempts int `json:"max_attempts"`
InitialDelay time.Duration `json:"initial_delay"` InitialDelay time.Duration `json:"initial_delay"`
MaxDelay time.Duration `json:"max_delay"` MaxDelay time.Duration `json:"max_delay"`
BackoffFactor float64 `json:"backoff_factor"` BackoffFactor float64 `json:"backoff_factor"`
EnableJitter bool `json:"enable_jitter"` EnableJitter bool `json:"enable_jitter"`
RetryableErrors []string `json:"retryable_errors"`
} }
// DefaultRetryConfig returns default retry configuration // DefaultRetryConfig returns default retry configuration
@@ -220,8 +213,8 @@ func DefaultRetryConfig() RetryConfig {
// RetryExecutor implements retry logic with exponential backoff // RetryExecutor implements retry logic with exponential backoff
type RetryExecutor struct { type RetryExecutor struct {
config RetryConfig
logger *Logger logger *Logger
config RetryConfig
} }
// NewRetryExecutor creates a new retry executor // NewRetryExecutor creates a new retry executor
@@ -344,8 +337,8 @@ func (re *RetryExecutor) calculateDelay(attempt int) time.Duration {
// HTTPError represents an HTTP error with status code // HTTPError represents an HTTP error with status code
type HTTPError struct { type HTTPError struct {
StatusCode int
Message string Message string
StatusCode int
} }
// Error implements the error interface // Error implements the error interface
@@ -355,20 +348,12 @@ func (e *HTTPError) Error() string {
// GracefulDegradation implements graceful degradation patterns // GracefulDegradation implements graceful degradation patterns
type GracefulDegradation struct { type GracefulDegradation struct {
// Fallback functions for different operations fallbacks map[string]func() (any, error)
fallbacks map[string]func() (any, error) healthChecks map[string]func() bool
// Health checks for dependencies
healthChecks map[string]func() bool
// Configuration
config GracefulDegradationConfig
// State tracking
degradedServices map[string]time.Time degradedServices map[string]time.Time
logger *Logger
config GracefulDegradationConfig
mutex sync.RWMutex mutex sync.RWMutex
logger *Logger
} }
// GracefulDegradationConfig holds configuration for graceful degradation // GracefulDegradationConfig holds configuration for graceful degradation
@@ -537,8 +522,8 @@ type ErrorRecoveryManager struct {
circuitBreakers map[string]*CircuitBreaker circuitBreakers map[string]*CircuitBreaker
retryExecutor *RetryExecutor retryExecutor *RetryExecutor
gracefulDegradation *GracefulDegradation gracefulDegradation *GracefulDegradation
mutex sync.RWMutex
logger *Logger logger *Logger
mutex sync.RWMutex
} }
// NewErrorRecoveryManager creates a new error recovery manager // NewErrorRecoveryManager creates a new error recovery manager
+4 -13
View File
@@ -72,20 +72,11 @@ func deriveCodeChallenge(codeVerifier string) string {
// It contains the various tokens and metadata returned after successful // It contains the various tokens and metadata returned after successful
// code exchange or token refresh operations. // code exchange or token refresh operations.
type TokenResponse struct { type TokenResponse struct {
// IDToken is the OIDC ID token containing user claims IDToken string `json:"id_token"`
IDToken string `json:"id_token"` AccessToken string `json:"access_token"`
// AccessToken is the OAuth 2.0 access token for API access
AccessToken string `json:"access_token"`
// RefreshToken is the OAuth 2.0 refresh token for obtaining new tokens
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
TokenType string `json:"token_type"`
// ExpiresIn is the lifetime in seconds of the access token ExpiresIn int `json:"expires_in"`
ExpiresIn int `json:"expires_in"`
// TokenType is the type of token, typically "Bearer"
TokenType string `json:"token_type"`
} }
// exchangeTokens performs the OAuth 2.0 token exchange with the OIDC provider's token endpoint. // exchangeTokens performs the OAuth 2.0 token exchange with the OIDC provider's token endpoint.
+15 -21
View File
@@ -11,35 +11,29 @@ import (
// InputValidator provides comprehensive input validation and sanitization // InputValidator provides comprehensive input validation and sanitization
type InputValidator struct { type InputValidator struct {
// Configuration usernameRegex *regexp.Regexp
maxTokenLength int tokenRegex *regexp.Regexp
maxURLLength int logger *Logger
maxHeaderLength int urlRegex *regexp.Regexp
maxClaimLength int emailRegex *regexp.Regexp
maxEmailLength int
maxUsernameLength int
// Compiled regex patterns
emailRegex *regexp.Regexp
urlRegex *regexp.Regexp
tokenRegex *regexp.Regexp
usernameRegex *regexp.Regexp
// Security patterns to detect
sqlInjectionPatterns []string sqlInjectionPatterns []string
xssPatterns []string
pathTraversalPatterns []string pathTraversalPatterns []string
xssPatterns []string
logger *Logger maxUsernameLength int
maxURLLength int
maxTokenLength int
maxEmailLength int
maxClaimLength int
maxHeaderLength int
} }
// ValidationResult represents the result of input validation // ValidationResult represents the result of input validation
type ValidationResult struct { type ValidationResult struct {
IsValid bool `json:"is_valid"`
Errors []string `json:"errors,omitempty"`
Warnings []string `json:"warnings,omitempty"`
SanitizedValue string `json:"sanitized_value,omitempty"` SanitizedValue string `json:"sanitized_value,omitempty"`
SecurityRisk string `json:"security_risk,omitempty"` SecurityRisk string `json:"security_risk,omitempty"`
Errors []string `json:"errors,omitempty"`
Warnings []string `json:"warnings,omitempty"`
IsValid bool `json:"is_valid"`
} }
// InputValidationConfig holds configuration for input validation // InputValidationConfig holds configuration for input validation
+1 -1
View File
@@ -204,8 +204,8 @@ func TestSanitizeInput(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
input string input string
maxLen int
expected string expected string
maxLen int
}{ }{
{ {
name: "Normal text", name: "Normal text",
+5 -6
View File
@@ -33,13 +33,12 @@ type JWKSet struct {
} }
type JWKCache struct { type JWKCache struct {
jwks *JWKSet expiresAt time.Time
expiresAt time.Time jwks *JWKSet
mutex sync.RWMutex internalCache *Cache
// CacheLifetime is configurable to determine how long the JWKS is cached.
CacheLifetime time.Duration CacheLifetime time.Duration
internalCache *Cache // To hold the closable Cache instance from cache.go maxSize int
maxSize int // Maximum number of items in the cache mutex sync.RWMutex
} }
type JWKCacheInterface interface { type JWKCacheInterface interface {
+1 -1
View File
@@ -89,8 +89,8 @@ var ClockSkewTolerance = ClockSkewToleranceFuture
type JWT struct { type JWT struct {
Header map[string]any Header map[string]any
Claims map[string]any Claims map[string]any
Signature []byte
Token string Token string
Signature []byte
} }
// parseJWT decodes a raw JWT string into its constituent parts: header, claims, and signature. // parseJWT decodes a raw JWT string into its constituent parts: header, claims, and signature.
+37 -39
View File
@@ -196,52 +196,51 @@ type TokenExchanger interface {
// TraefikOidc is the main struct for the OIDC middleware // TraefikOidc is the main struct for the OIDC middleware
type TraefikOidc struct { type TraefikOidc struct {
jwkCache JWKCacheInterface
jwtVerifier JWTVerifier
ctx context.Context
tokenVerifier TokenVerifier
next http.Handler next http.Handler
tokenExchanger TokenExchanger
initComplete chan struct{}
limiter *rate.Limiter
tokenBlacklist *Cache
headerTemplates map[string]*template.Template
sessionManager *SessionManager
tokenCleanupStopChan chan struct{}
excludedURLs map[string]struct{}
extractClaimsFunc func(tokenString string) (map[string]any, 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
clientSecret string
clientID string
name string name string
redirURLPath string redirURLPath string
logoutURLPath string logoutURLPath string
tokenURL string
authURL string
endSessionURL string
postLogoutRedirectURI string
scheme string
jwksURL string
issuerURL string issuerURL string
revocationURL string revocationURL string
jwkCache JWKCacheInterface
metadataCache *MetadataCache
tokenBlacklist *Cache // Replaced TokenBlacklist with generic Cache
jwksURL string
clientID string
clientSecret string
authURL string
tokenURL string
scopes []string scopes []string
limiter *rate.Limiter goroutineWG sync.WaitGroup
refreshGracePeriod time.Duration
shutdownOnce sync.Once
forceHTTPS bool forceHTTPS bool
enablePKCE bool enablePKCE bool
scheme string suppressDiagnosticLogs bool
tokenCache *TokenCache
httpClient *http.Client
tokenHTTPClient *http.Client // Reusable HTTP client for token operations
logger *Logger
tokenVerifier TokenVerifier
jwtVerifier JWTVerifier
excludedURLs map[string]struct{}
allowedUserDomains map[string]struct{}
allowedUsers map[string]struct{} // Map for case-insensitive lookup of allowed email addresses
allowedRolesAndGroups map[string]struct{}
initiateAuthenticationFunc func(rw http.ResponseWriter, req *http.Request, session *SessionData, redirectURL string)
// exchangeCodeForTokenFunc func(code string, redirectURL string, codeVerifier string) (*TokenResponse, error) // Replaced by interface
extractClaimsFunc func(tokenString string) (map[string]any, error)
initComplete chan struct{}
endSessionURL string
postLogoutRedirectURI string
sessionManager *SessionManager
tokenExchanger TokenExchanger // Added field for mocking
refreshGracePeriod time.Duration // Configurable grace period for proactive refresh
headerTemplates map[string]*template.Template // Parsed templates for custom headers
tokenCleanupStopChan chan struct{} // Channel to stop token cleanup goroutine
metadataRefreshStopChan chan struct{} // Channel to stop metadata refresh goroutine
goroutineWG sync.WaitGroup // WaitGroup to track background goroutines
ctx context.Context
cancelFunc context.CancelFunc
shutdownOnce sync.Once
suppressDiagnosticLogs bool // Flag to suppress diagnostic logs during tests
} }
// ProviderMetadata holds OIDC provider metadata // ProviderMetadata holds OIDC provider metadata
@@ -1195,11 +1194,10 @@ func (t *TraefikOidc) processAuthorizedRequest(rw http.ResponseWriter, req *http
// Create template data context with available tokens and claims // Create template data context with available tokens and claims
// Fields must be exported (uppercase) to be accessible in templates // Fields must be exported (uppercase) to be accessible in templates
templateData := struct { templateData := struct {
// These fields need to be exported (uppercase) for template access Claims map[string]any
AccessToken string AccessToken string
IdToken string IdToken string
RefreshToken string RefreshToken string
Claims map[string]any
}{ }{
AccessToken: session.GetAccessToken(), // Provide AccessToken for templates if needed AccessToken: session.GetAccessToken(), // Provide AccessToken for templates if needed
IdToken: session.GetIDToken(), IdToken: session.GetIDToken(),
+33 -33
View File
@@ -30,8 +30,8 @@ type TestSuite struct {
ecPrivateKey *ecdsa.PrivateKey ecPrivateKey *ecdsa.PrivateKey
tOidc *TraefikOidc tOidc *TraefikOidc
mockJWKCache *MockJWKCache mockJWKCache *MockJWKCache
token string
sessionManager *SessionManager sessionManager *SessionManager
token string
} }
// Setup initializes the test suite // Setup initializes the test suite
@@ -410,15 +410,15 @@ func TestServeHTTP(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string
requestPath string
sessionValues map[any]any sessionValues map[any]any
expectedStatus int
expectedBody string
setupSession func(*SessionData) setupSession func(*SessionData)
mockRefreshTokenFunc func(originalFunc func(refreshToken string) (*TokenResponse, error)) func(refreshToken string) (*TokenResponse, error) mockRefreshTokenFunc func(originalFunc func(refreshToken string) (*TokenResponse, error)) func(refreshToken string) (*TokenResponse, error)
assertSessionAfterRequest func(t *testing.T, rr *httptest.ResponseRecorder, req *http.Request, sessionManager *SessionManager) // Added for post-request checks assertSessionAfterRequest func(t *testing.T, rr *httptest.ResponseRecorder, req *http.Request, sessionManager *SessionManager)
requestHeaders map[string]string // Added for setting headers like Accept requestHeaders map[string]string
name string
requestPath string
expectedBody string
expectedStatus int
}{ }{
{ {
name: "Excluded URL", name: "Excluded URL",
@@ -873,10 +873,10 @@ func TestJWKToPEM(t *testing.T) {
ts.Setup() ts.Setup()
tests := []struct { tests := []struct {
name string
jwk *JWK jwk *JWK
expectError bool name string
errorContains string errorContains string
expectError bool
}{ }{
{ {
name: "Unsupported Key Type", name: "Unsupported Key Type",
@@ -928,8 +928,8 @@ func TestParseJWT(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
token string token string
expectError bool
errorContains string errorContains string
expectError bool
}{ }{
{ {
name: "Invalid Format", name: "Invalid Format",
@@ -989,11 +989,11 @@ func TestHandleCallback(t *testing.T) {
redirectURL := "http://example.com/" redirectURL := "http://example.com/"
tests := []struct { tests := []struct {
name string
queryParams string
exchangeCodeForToken func(code string, redirectURL string, codeVerifier string) (*TokenResponse, error) exchangeCodeForToken func(code string, redirectURL string, codeVerifier string) (*TokenResponse, error)
extractClaimsFunc func(tokenString string) (map[string]any, error) extractClaimsFunc func(tokenString string) (map[string]any, error)
sessionSetupFunc func(*SessionData) sessionSetupFunc func(*SessionData)
name string
queryParams string
expectedStatus int expectedStatus int
}{ }{
{ {
@@ -1256,12 +1256,12 @@ func TestIsAllowedDomain(t *testing.T) {
ts.Setup() ts.Setup()
tests := []struct { tests := []struct {
name string
email string
allowedDomains map[string]struct{} allowedDomains map[string]struct{}
allowedUsers map[string]struct{} allowedUsers map[string]struct{}
name string
email string
expectedLogOutput string
allowed bool allowed bool
expectedLogOutput string // For testing log messages
}{ }{
{ {
name: "Allowed domain", name: "Allowed domain",
@@ -1343,11 +1343,11 @@ func TestOIDCHandler(t *testing.T) {
ts.token = "valid.jwt.token" ts.token = "valid.jwt.token"
tests := []struct { tests := []struct {
name string
queryParams string
exchangeCodeForToken func(code string, redirectURL string, codeVerifier string) (*TokenResponse, error) exchangeCodeForToken func(code string, redirectURL string, codeVerifier string) (*TokenResponse, error)
extractClaimsFunc func(tokenString string) (map[string]any, error) extractClaimsFunc func(tokenString string) (map[string]any, error)
sessionSetupFunc func(session *sessions.Session) sessionSetupFunc func(session *sessions.Session)
name string
queryParams string
expectedStatus int expectedStatus int
blacklist bool blacklist bool
rateLimit bool rateLimit bool
@@ -1504,12 +1504,12 @@ func TestHandleLogout(t *testing.T) {
defer mockRevocationServer.Close() defer mockRevocationServer.Close()
tests := []struct { tests := []struct {
name string
setupSession func(*SessionData) setupSession func(*SessionData)
name string
endSessionURL string endSessionURL string
expectedStatus int
expectedURL string expectedURL string
host string host string
expectedStatus int
}{ }{
{ {
name: "Successful logout with end session endpoint", name: "Successful logout with end session endpoint",
@@ -2104,12 +2104,12 @@ func TestServeHTTPRolesAndGroups(t *testing.T) {
nbf := now.Add(-2 * time.Minute).Unix() // Account for clock skew nbf := now.Add(-2 * time.Minute).Unix() // Account for clock skew
tests := []struct { tests := []struct {
name string
allowedRolesAndGroups map[string]struct{} allowedRolesAndGroups map[string]struct{}
claims map[string]any claims map[string]any
setupSession func(*SessionData) setupSession func(*SessionData)
expectedStatus int
expectedHeaders map[string]string expectedHeaders map[string]string
name string
expectedStatus int
}{ }{
{ {
name: "User with allowed role", name: "User with allowed role",
@@ -2313,10 +2313,10 @@ func TestExchangeTokensWithRedirects(t *testing.T) {
ts.Setup() ts.Setup()
tests := []struct { tests := []struct {
name string
setupServer func() *httptest.Server setupServer func() *httptest.Server
expectError bool name string
errorContains string errorContains string
expectError bool
}{ }{
{ {
name: "Successful token exchange with redirects", name: "Successful token exchange with redirects",
@@ -2424,9 +2424,9 @@ func TestBuildAuthURL(t *testing.T) {
redirectURL string redirectURL string
state string state string
nonce string nonce string
enablePKCE bool
codeChallenge string codeChallenge string
expectedPrefix string expectedPrefix string
enablePKCE bool
checkPKCE bool checkPKCE bool
}{ }{
{ {
@@ -2574,10 +2574,10 @@ func TestExchangeCodeForToken(t *testing.T) {
ts.Setup() ts.Setup()
tests := []struct { tests := []struct {
name string
enablePKCE bool
codeVerifier string
setupMock func(t *testing.T) *httptest.Server setupMock func(t *testing.T) *httptest.Server
name string
codeVerifier string
enablePKCE bool
}{ }{
{ {
name: "With PKCE Enabled and Code Verifier", name: "With PKCE Enabled and Code Verifier",
@@ -2883,10 +2883,10 @@ func TestJWTVerifyWithSkipReplayCheck(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
errorContains string
skipReplayCheck bool skipReplayCheck bool
firstCall bool firstCall bool
expectError bool expectError bool
errorContains string
}{ }{
{ {
name: "First verification with skipReplayCheck=false should succeed", name: "First verification with skipReplayCheck=false should succeed",
@@ -3339,10 +3339,10 @@ func TestJTIBlacklistBehavior(t *testing.T) {
// Test JTI blacklist behavior // Test JTI blacklist behavior
tests := []struct { tests := []struct {
name string
action func() error action func() error
expectError bool name string
description string description string
expectError bool
}{ }{
{ {
name: "Initial verification adds JTI to blacklist", name: "Initial verification adds JTI to blacklist",
@@ -3495,9 +3495,9 @@ func TestEdgeCasesWithDifferentTokenTypes(t *testing.T) {
nbf := now.Unix() nbf := now.Unix()
tests := []struct { tests := []struct {
claims map[string]any
name string name string
tokenType string tokenType string
claims map[string]any
expectError bool expectError bool
}{ }{
{ {
@@ -3697,10 +3697,10 @@ func TestScopeMergingEdgeCases(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
description string
defaultScopes []string defaultScopes []string
userScopes []string userScopes []string
expectedScopes []string expectedScopes []string
description string
}{ }{
{ {
name: "Case sensitivity preserved", name: "Case sensitivity preserved",
@@ -3974,8 +3974,8 @@ func TestBuildAuthURLWithMergedScopes(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
scopes []string
expectedScopes string expectedScopes string
scopes []string
}{ }{
{ {
name: "Default scopes only", name: "Default scopes only",
+3 -3
View File
@@ -8,11 +8,11 @@ import (
) )
type MetadataCache struct { type MetadataCache struct {
metadata *ProviderMetadata
expiresAt time.Time expiresAt time.Time
mutex sync.RWMutex metadata *ProviderMetadata
autoCleanupInterval time.Duration
stopCleanup chan struct{} stopCleanup chan struct{}
autoCleanupInterval time.Duration
mutex sync.RWMutex
} }
// NewMetadataCache creates a new MetadataCache instance. // NewMetadataCache creates a new MetadataCache instance.
+38 -70
View File
@@ -10,61 +10,41 @@ import (
// PerformanceMetrics tracks various performance-related metrics // PerformanceMetrics tracks various performance-related metrics
type PerformanceMetrics struct { type PerformanceMetrics struct {
// Cache metrics startTime time.Time
cacheHits int64 logger *Logger
cacheMisses int64 verificationTimes []time.Duration
cacheEvictions int64 refreshTimes []time.Duration
cacheSize int64 validationTimes []time.Duration
memoryUsage int64
// Token operation metrics heapSize int64
tokenVerifications int64
tokenValidations int64
tokenRefreshes int64
// Success/failure tracking
successfulVerifications int64 successfulVerifications int64
successfulValidations int64 successfulValidations int64
successfulRefreshes int64 successfulRefreshes int64
failedVerifications int64 failedVerifications int64
failedValidations int64 failedValidations int64
failedRefreshes int64 failedRefreshes int64
avgVerificationTime time.Duration
// Timing metrics avgValidationTime time.Duration
avgVerificationTime time.Duration avgRefreshTime time.Duration
avgValidationTime time.Duration cacheHits int64
avgRefreshTime time.Duration goroutineCount int64
memoryPressure int64
// Resource metrics gcPauseTime int64
memoryUsage int64 tokenRefreshes int64
goroutineCount int64 heapInUse int64
memoryPressure int64 // Memory pressure level (0-100) verificationErrors int64
gcPauseTime int64 // Last GC pause time in nanoseconds validationErrors int64
heapSize int64 // Current heap size refreshErrors int64
heapInUse int64 // Heap memory in use rateLimitedRequests int64
activeSessions int64
// Error metrics (kept for backward compatibility) sessionCreations int64
verificationErrors int64 sessionDeletions int64
validationErrors int64 cacheMisses int64
refreshErrors int64 tokenValidations int64
tokenVerifications int64
// Rate limiting metrics cacheSize int64
rateLimitedRequests int64 cacheEvictions int64
timingMutex sync.RWMutex
// Session metrics
activeSessions int64
sessionCreations int64
sessionDeletions int64
// Timing tracking
timingMutex sync.RWMutex
verificationTimes []time.Duration
validationTimes []time.Duration
refreshTimes []time.Duration
// Start time for uptime calculation
startTime time.Time
logger *Logger
} }
// NewPerformanceMetrics creates a new performance metrics tracker // NewPerformanceMetrics creates a new performance metrics tracker
@@ -441,38 +421,26 @@ func (pm *PerformanceMetrics) calculateTimingStats(times []time.Duration) map[st
// ResourceMonitor tracks resource usage and limits // ResourceMonitor tracks resource usage and limits
type ResourceMonitor struct { type ResourceMonitor struct {
// Memory limits cacheSizes map[string]int64
maxMemoryBytes int64
// Cache limits
maxCacheSize int64
// Session limits
maxSessions int64
// Cache size tracking
cacheSizes map[string]int64
cacheMutex sync.RWMutex
// Monitoring state
alertThresholds map[string]float64 alertThresholds map[string]float64
perfMetrics *PerformanceMetrics
logger *Logger
alerts []ResourceAlert alerts []ResourceAlert
maxMemoryBytes int64
maxCacheSize int64
maxSessions int64
cacheMutex sync.RWMutex
alertsMutex sync.RWMutex alertsMutex sync.RWMutex
// Performance metrics reference
perfMetrics *PerformanceMetrics
logger *Logger
} }
// ResourceAlert represents a resource usage alert // ResourceAlert represents a resource usage alert
type ResourceAlert struct { type ResourceAlert struct {
Timestamp time.Time `json:"timestamp"`
Type string `json:"type"` Type string `json:"type"`
Message string `json:"message"` Message string `json:"message"`
Severity string `json:"severity"`
Threshold float64 `json:"threshold"` Threshold float64 `json:"threshold"`
CurrentValue float64 `json:"current_value"` CurrentValue float64 `json:"current_value"`
Timestamp time.Time `json:"timestamp"`
Severity string `json:"severity"`
} }
// NewResourceMonitor creates a new resource monitor // NewResourceMonitor creates a new resource monitor
+1 -1
View File
@@ -390,8 +390,8 @@ func TestMissingClaims(t *testing.T) {
// Test cases for missing claims // Test cases for missing claims
testCases := []struct { testCases := []struct {
name string name string
omittedClaims []string
expectedError string expectedError string
omittedClaims []string
}{ }{
{ {
name: "Missing Issuer", name: "Missing Issuer",
+27 -50
View File
@@ -13,67 +13,51 @@ import (
// SecurityEvent represents a security-related event that should be logged and monitored // SecurityEvent represents a security-related event that should be logged and monitored
type SecurityEvent struct { type SecurityEvent struct {
Timestamp time.Time `json:"timestamp"`
Details map[string]any `json:"details,omitempty"`
Type string `json:"type"` Type string `json:"type"`
Severity string `json:"severity"` Severity string `json:"severity"`
Timestamp time.Time `json:"timestamp"`
ClientIP string `json:"client_ip"` ClientIP string `json:"client_ip"`
UserAgent string `json:"user_agent"` UserAgent string `json:"user_agent"`
RequestPath string `json:"request_path"` RequestPath string `json:"request_path"`
Message string `json:"message"` Message string `json:"message"`
Details map[string]any `json:"details,omitempty"`
} }
// SecurityMonitor tracks security events and suspicious activity patterns // SecurityMonitor tracks security events and suspicious activity patterns
type SecurityMonitor struct { type SecurityMonitor struct {
// Event counters ipFailures map[string]*IPFailureTracker
patternDetector *SuspiciousPatternDetector
logger *Logger
eventHandlers []SecurityEventHandler
config SecurityMonitorConfig
authFailures int64 authFailures int64
tokenValidationFails int64 tokenValidationFails int64
rateLimitHits int64 rateLimitHits int64
suspiciousRequests int64 suspiciousRequests int64
ipMutex sync.RWMutex
// IP-based tracking
ipFailures map[string]*IPFailureTracker
ipMutex sync.RWMutex
// Pattern detection
patternDetector *SuspiciousPatternDetector
// Event handlers
eventHandlers []SecurityEventHandler
// Configuration
config SecurityMonitorConfig
// Logger
logger *Logger
} }
// IPFailureTracker tracks failures for a specific IP address // IPFailureTracker tracks failures for a specific IP address
type IPFailureTracker struct { type IPFailureTracker struct {
FailureCount int64
LastFailure time.Time LastFailure time.Time
FirstFailure time.Time FirstFailure time.Time
FailureTypes map[string]int64
IsBlocked bool
BlockedUntil time.Time BlockedUntil time.Time
FailureTypes map[string]int64
FailureCount int64
mutex sync.RWMutex mutex sync.RWMutex
IsBlocked bool
} }
// SuspiciousPatternDetector identifies patterns that may indicate attacks // SuspiciousPatternDetector identifies patterns that may indicate attacks
type SuspiciousPatternDetector struct { type SuspiciousPatternDetector struct {
// Time-based windows for pattern detection recentEvents []SecurityEvent
shortWindow time.Duration // 1 minute shortWindow time.Duration
mediumWindow time.Duration // 5 minutes mediumWindow time.Duration
longWindow time.Duration // 15 minutes longWindow time.Duration
rapidFailureThreshold int
// Pattern thresholds distributedAttackThreshold int
rapidFailureThreshold int // failures in short window persistentAttackThreshold int
distributedAttackThreshold int // failures across IPs in medium window eventsMutex sync.RWMutex
persistentAttackThreshold int // failures in long window
// Pattern tracking
recentEvents []SecurityEvent
eventsMutex sync.RWMutex
} }
// SecurityEventHandler defines the interface for handling security events // SecurityEventHandler defines the interface for handling security events
@@ -83,22 +67,15 @@ type SecurityEventHandler interface {
// SecurityMonitorConfig contains configuration for the security monitor // SecurityMonitorConfig contains configuration for the security monitor
type SecurityMonitorConfig struct { type SecurityMonitorConfig struct {
// Failure thresholds MaxFailuresPerIP int `json:"max_failures_per_ip"`
MaxFailuresPerIP int `json:"max_failures_per_ip"` FailureWindowMinutes int `json:"failure_window_minutes"`
FailureWindowMinutes int `json:"failure_window_minutes"` BlockDurationMinutes int `json:"block_duration_minutes"`
BlockDurationMinutes int `json:"block_duration_minutes"`
// Pattern detection settings
EnablePatternDetection bool `json:"enable_pattern_detection"`
RapidFailureThreshold int `json:"rapid_failure_threshold"` RapidFailureThreshold int `json:"rapid_failure_threshold"`
CleanupIntervalMinutes int `json:"cleanup_interval_minutes"`
// Monitoring settings RetentionHours int `json:"retention_hours"`
EnableDetailedLogging bool `json:"enable_detailed_logging"` EnablePatternDetection bool `json:"enable_pattern_detection"`
LogSuspiciousOnly bool `json:"log_suspicious_only"` EnableDetailedLogging bool `json:"enable_detailed_logging"`
LogSuspiciousOnly bool `json:"log_suspicious_only"`
// Cleanup settings
CleanupIntervalMinutes int `json:"cleanup_interval_minutes"`
RetentionHours int `json:"retention_hours"`
} }
// DefaultSecurityMonitorConfig returns a default configuration // DefaultSecurityMonitorConfig returns a default configuration
+3 -10
View File
@@ -213,17 +213,10 @@ func decompressTokenInternal(compressed string) string {
// It provides functionality for storing and retrieving authentication state, tokens, // It provides functionality for storing and retrieving authentication state, tokens,
// and other session-related data across multiple cookies. // and other session-related data across multiple cookies.
type SessionManager struct { type SessionManager struct {
// store is the underlying session store for cookie management.
store sessions.Store
// forceHTTPS enforces secure cookie attributes regardless of request scheme.
forceHTTPS bool
// logger provides structured logging capabilities.
logger *Logger
// sessionPool is a sync.Pool for reusing SessionData objects.
sessionPool sync.Pool sessionPool sync.Pool
store sessions.Store
logger *Logger
forceHTTPS bool
} }
// NewSessionManager creates a new session manager with the specified configuration. // NewSessionManager creates a new session manager with the specified configuration.
+1 -1
View File
@@ -441,8 +441,8 @@ func TestTokenChunkingCorruptionResistance(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string
corruptChunk func(chunks map[int]*sessions.Session) corruptChunk func(chunks map[int]*sessions.Session)
name string
expectEmpty bool expectEmpty bool
}{ }{
{ {
+21 -90
View File
@@ -26,96 +26,27 @@ type TemplatedHeader struct {
// It provides all necessary settings to configure OpenID Connect authentication // It provides all necessary settings to configure OpenID Connect authentication
// with various providers like Auth0, Logto, or any standard OIDC provider. // with various providers like Auth0, Logto, or any standard OIDC provider.
type Config struct { type Config struct {
// ProviderURL is the base URL of the OIDC provider (required) HTTPClient *http.Client
// Example: https://accounts.google.com ProviderURL string `json:"providerURL"`
ProviderURL string `json:"providerURL"` RevocationURL string `json:"revocationURL"`
CallbackURL string `json:"callbackURL"`
// RevocationURL is the endpoint for revoking tokens (optional) LogoutURL string `json:"logoutURL"`
// If not provided, it will be discovered from provider metadata ClientID string `json:"clientID"`
RevocationURL string `json:"revocationURL"` ClientSecret string `json:"clientSecret"`
PostLogoutRedirectURI string `json:"postLogoutRedirectURI"`
// EnablePKCE enables Proof Key for Code Exchange (PKCE) for the authorization code flow (optional) LogLevel string `json:"logLevel"`
// This enhances security but might not be supported by all OIDC providers SessionEncryptionKey string `json:"sessionEncryptionKey"`
// Default: false OIDCEndSessionURL string `json:"oidcEndSessionURL"`
EnablePKCE bool `json:"enablePKCE"` AllowedRolesAndGroups []string `json:"allowedRolesAndGroups"`
ExcludedURLs []string `json:"excludedURLs"`
// CallbackURL is the path where the OIDC provider will redirect after authentication (required) AllowedUserDomains []string `json:"allowedUserDomains"`
// Example: /oauth2/callback AllowedUsers []string `json:"allowedUsers"`
CallbackURL string `json:"callbackURL"` Scopes []string `json:"scopes"`
Headers []TemplatedHeader `json:"headers"`
// LogoutURL is the path for handling logout requests (optional) RateLimit int `json:"rateLimit"`
// If not provided, it will be set to CallbackURL + "/logout" RefreshGracePeriodSeconds int `json:"refreshGracePeriodSeconds"`
LogoutURL string `json:"logoutURL"` ForceHTTPS bool `json:"forceHTTPS"`
EnablePKCE bool `json:"enablePKCE"`
// ClientID is the OAuth 2.0 client identifier (required)
ClientID string `json:"clientID"`
// ClientSecret is the OAuth 2.0 client secret (required)
ClientSecret string `json:"clientSecret"`
// Scopes defines the OAuth 2.0 scopes to request (optional)
// Defaults to ["openid", "profile", "email"] if not provided
Scopes []string `json:"scopes"`
// LogLevel sets the logging verbosity (optional)
// Valid values: "debug", "info", "error"
// Default: "info"
LogLevel string `json:"logLevel"`
// SessionEncryptionKey is used to encrypt session data (required)
// Must be a secure random string
SessionEncryptionKey string `json:"sessionEncryptionKey"`
// ForceHTTPS forces the use of HTTPS for all URLs (optional)
// Default: false
ForceHTTPS bool `json:"forceHTTPS"`
// RateLimit sets the maximum number of requests per second (optional)
// Default: 100
RateLimit int `json:"rateLimit"`
// ExcludedURLs lists paths that bypass authentication (optional)
// Example: ["/health", "/metrics"]
ExcludedURLs []string `json:"excludedURLs"`
// AllowedUserDomains restricts access to specific email domains (optional)
// Example: ["company.com", "subsidiary.com"]
AllowedUserDomains []string `json:"allowedUserDomains"`
// AllowedUsers restricts access to specific email addresses (optional)
// Example: ["[email protected]", "[email protected]"]
AllowedUsers []string `json:"allowedUsers"`
// AllowedRolesAndGroups restricts access to users with specific roles or groups (optional)
// Example: ["admin", "developer"]
AllowedRolesAndGroups []string `json:"allowedRolesAndGroups"`
// OIDCEndSessionURL is the provider's end session endpoint (optional)
// If not provided, it will be discovered from provider metadata
OIDCEndSessionURL string `json:"oidcEndSessionURL"`
// PostLogoutRedirectURI is the URL to redirect to after logout (optional)
// Default: "/"
PostLogoutRedirectURI string `json:"postLogoutRedirectURI"`
// HTTPClient allows customizing the HTTP client used for OIDC operations (optional)
HTTPClient *http.Client
// RefreshGracePeriodSeconds defines how many seconds before a token expires
// the plugin should attempt to refresh it proactively (optional)
// Default: 60
RefreshGracePeriodSeconds int `json:"refreshGracePeriodSeconds"`
// Headers defines custom HTTP headers to set with templated values (optional)
// Values can reference tokens and claims using Go templates with the following variables:
// - {{.AccessToken}} - The access token (ID token)
// - {{.IdToken}} - Same as AccessToken (for consistency)
// - {{.RefreshToken}} - The refresh token
// - {{.Claims.email}} - Access token claims (use proper case for claim names)
// Examples:
//
// [{Name: "X-Forwarded-Email", Value: "{{.Claims.email}}"}]
// [{Name: "Authorization", Value: "Bearer {{.AccessToken}}"}]
Headers []TemplatedHeader `json:"headers"`
} }
const ( const (
+4 -4
View File
@@ -254,10 +254,10 @@ func TestLogger(t *testing.T) {
var debugBuf, infoBuf, errorBuf bytes.Buffer var debugBuf, infoBuf, errorBuf bytes.Buffer
tests := []struct { tests := []struct {
name string
logLevel string
testFunc func(*Logger) testFunc func(*Logger)
checkFunc func(t *testing.T, debugOut, infoOut, errorOut string) checkFunc func(t *testing.T, debugOut, infoOut, errorOut string)
name string
logLevel string
}{ }{
{ {
name: "Debug Level", name: "Debug Level",
@@ -405,9 +405,9 @@ func TestHandleError(t *testing.T) {
// Test helper types // Test helper types
type testResponseRecorder struct { type testResponseRecorder struct {
statusCode int
body string
headers map[string][]string headers map[string][]string
body string
statusCode int
} }
func (r *testResponseRecorder) Header() http.Header { func (r *testResponseRecorder) Header() http.Header {
+1 -1
View File
@@ -178,10 +178,10 @@ func TestTemplateExecution(t *testing.T) {
func TestTemplateExecutionContext(t *testing.T) { func TestTemplateExecutionContext(t *testing.T) {
// Define a test struct that matches the one used in processAuthorizedRequest // Define a test struct that matches the one used in processAuthorizedRequest
type templateData struct { type templateData struct {
Claims map[string]any
AccessToken string AccessToken string
IdToken string IdToken string
RefreshToken string RefreshToken string
Claims map[string]any
} }
// Test cases // Test cases
+3 -3
View File
@@ -20,12 +20,12 @@ func TestTemplatedHeadersIntegration(t *testing.T) {
ts.Setup() ts.Setup()
tests := []struct { tests := []struct {
name string
headers []TemplatedHeader
sessionSetup func(*SessionData) sessionSetup func(*SessionData)
claims map[string]any claims map[string]any
expectedHeaders map[string]string expectedHeaders map[string]string
interceptedHeaders map[string]string interceptedHeaders map[string]string
name string
headers []TemplatedHeader
}{ }{
{ {
name: "Basic Email Header", name: "Basic Email Header",
@@ -423,9 +423,9 @@ func TestEdgeCaseTemplatedHeaders(t *testing.T) {
ts.Setup() ts.Setup()
tests := []struct { tests := []struct {
claims map[string]any
name string name string
headers []TemplatedHeader headers []TemplatedHeader
claims map[string]any
shouldExecuteCheck bool shouldExecuteCheck bool
}{ }{
{ {
+3 -3
View File
@@ -28,11 +28,11 @@ func TestTokenCorruptionScenario(t *testing.T) {
validJWT := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OX0.signature" validJWT := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OX0.signature"
tests := []struct { tests := []struct {
corruptionScenario func(*SessionData)
name string name string
tokenSize int tokenSize int
iterations int iterations int
expectConsistent bool expectConsistent bool
corruptionScenario func(*SessionData) // Function to simulate corruption
}{ }{
{ {
name: "Small token - multiple retrievals", name: "Small token - multiple retrievals",
@@ -207,8 +207,8 @@ func TestChunkReassemblyEdgeCases(t *testing.T) {
// Test various corruption scenarios // Test various corruption scenarios
corruptionTests := []struct { corruptionTests := []struct {
name string
corruption func(map[int]*sessions.Session) corruption func(map[int]*sessions.Session)
name string
expectEmpty bool expectEmpty bool
}{ }{
{ {
@@ -367,8 +367,8 @@ func TestRaceConditionProtection(t *testing.T) {
// TestMemoryExhaustionProtection tests protection against memory exhaustion attacks // TestMemoryExhaustionProtection tests protection against memory exhaustion attacks
func TestMemoryExhaustionProtection(t *testing.T) { func TestMemoryExhaustionProtection(t *testing.T) {
tests := []struct { tests := []struct {
name string
setupCorruption func() string setupCorruption func() string
name string
expectRejection bool expectRejection bool
}{ }{
{ {
+2 -2
View File
@@ -19,10 +19,10 @@ import (
func TestTokenTypeDistinction(t *testing.T) { func TestTokenTypeDistinction(t *testing.T) {
// Define test data where AccessToken and IdToken are deliberately different // Define test data where AccessToken and IdToken are deliberately different
type templateData struct { type templateData struct {
Claims map[string]any
AccessToken string AccessToken string
IdToken string IdToken string
RefreshToken string RefreshToken string
Claims map[string]any
} }
testData := templateData{ testData := templateData{
@@ -323,12 +323,12 @@ func TestTokenCorruptionIntegrationFlows(t *testing.T) {
} }
tests := []struct { tests := []struct {
corruptAction func(*SessionData)
name string name string
accessToken string accessToken string
refreshToken string refreshToken string
idToken string idToken string
expectSuccess bool expectSuccess bool
corruptAction func(*SessionData) // Function to corrupt session after storage
}{ }{
{ {
name: "Normal flow - small tokens", name: "Normal flow - small tokens",