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
+3 -14
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 maintains the usage order; most recently used items are at the back.
order *list.List order *list.List
// elems maps keys to their corresponding list elements for O(1) access.
elems map[string]*list.Element 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
// stopCleanup channel to terminate the auto cleanup goroutine.
stopCleanup chan struct{} stopCleanup chan struct{}
maxSize int
autoCleanupInterval time.Duration
mutex sync.RWMutex
} }
// DefaultMaxSize is the default maximum number of items in the cache. // DefaultMaxSize is the default maximum number of items in the cache.
+13 -28
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
mutex sync.RWMutex logger *Logger
maxFailures int
// Metrics timeout time.Duration
resetTimeout time.Duration
state CircuitBreakerState
failures int64
totalRequests int64 totalRequests int64
totalFailures int64 totalFailures int64
totalSuccesses int64 totalSuccesses int64
mutex sync.RWMutex
// 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)
// Health checks for dependencies
healthChecks map[string]func() bool healthChecks map[string]func() bool
// Configuration
config GracefulDegradationConfig
// State tracking
degradedServices map[string]time.Time degradedServices map[string]time.Time
mutex sync.RWMutex
logger *Logger logger *Logger
config GracefulDegradationConfig
mutex sync.RWMutex
} }
// 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
+1 -10
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 is the OAuth 2.0 access token for API access
AccessToken string `json:"access_token"` 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"`
// ExpiresIn is the lifetime in seconds of the access token
ExpiresIn int `json:"expires_in"`
// TokenType is the type of token, typically "Bearer"
TokenType string `json:"token_type"` TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
} }
// 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
maxTokenLength int
maxURLLength int
maxHeaderLength int
maxClaimLength int
maxEmailLength int
maxUsernameLength int
// Compiled regex patterns
emailRegex *regexp.Regexp
urlRegex *regexp.Regexp
tokenRegex *regexp.Regexp
usernameRegex *regexp.Regexp usernameRegex *regexp.Regexp
tokenRegex *regexp.Regexp
// Security patterns to detect
sqlInjectionPatterns []string
xssPatterns []string
pathTraversalPatterns []string
logger *Logger logger *Logger
urlRegex *regexp.Regexp
emailRegex *regexp.Regexp
sqlInjectionPatterns []string
pathTraversalPatterns []string
xssPatterns []string
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",
+4 -5
View File
@@ -33,13 +33,12 @@ type JWKSet struct {
} }
type JWKCache struct { type JWKCache struct {
jwks *JWKSet
expiresAt time.Time expiresAt time.Time
mutex sync.RWMutex jwks *JWKSet
// CacheLifetime is configurable to determine how long the JWKS is cached. internalCache *Cache
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.
+38 -40
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
issuerURL string
revocationURL string
jwkCache JWKCacheInterface
metadataCache *MetadataCache
tokenBlacklist *Cache // Replaced TokenBlacklist with generic Cache
jwksURL string
clientID string
clientSecret string
authURL string
tokenURL string tokenURL string
scopes []string authURL string
limiter *rate.Limiter
forceHTTPS bool
enablePKCE bool
scheme string
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 endSessionURL string
postLogoutRedirectURI string postLogoutRedirectURI string
sessionManager *SessionManager scheme string
tokenExchanger TokenExchanger // Added field for mocking jwksURL string
refreshGracePeriod time.Duration // Configurable grace period for proactive refresh issuerURL string
headerTemplates map[string]*template.Template // Parsed templates for custom headers revocationURL string
tokenCleanupStopChan chan struct{} // Channel to stop token cleanup goroutine scopes []string
metadataRefreshStopChan chan struct{} // Channel to stop metadata refresh goroutine goroutineWG sync.WaitGroup
goroutineWG sync.WaitGroup // WaitGroup to track background goroutines refreshGracePeriod time.Duration
ctx context.Context
cancelFunc context.CancelFunc
shutdownOnce sync.Once shutdownOnce sync.Once
suppressDiagnosticLogs bool // Flag to suppress diagnostic logs during tests forceHTTPS bool
enablePKCE bool
suppressDiagnosticLogs bool
} }
// 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.
+25 -57
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
// Timing metrics
avgVerificationTime time.Duration avgVerificationTime time.Duration
avgValidationTime time.Duration avgValidationTime time.Duration
avgRefreshTime time.Duration avgRefreshTime time.Duration
cacheHits int64
// Resource metrics
memoryUsage int64
goroutineCount int64 goroutineCount int64
memoryPressure int64 // Memory pressure level (0-100) memoryPressure int64
gcPauseTime int64 // Last GC pause time in nanoseconds gcPauseTime int64
heapSize int64 // Current heap size tokenRefreshes int64
heapInUse int64 // Heap memory in use heapInUse int64
// Error metrics (kept for backward compatibility)
verificationErrors int64 verificationErrors int64
validationErrors int64 validationErrors int64
refreshErrors int64 refreshErrors int64
// Rate limiting metrics
rateLimitedRequests int64 rateLimitedRequests int64
// Session metrics
activeSessions int64 activeSessions int64
sessionCreations int64 sessionCreations int64
sessionDeletions int64 sessionDeletions int64
cacheMisses int64
// Timing tracking tokenValidations int64
tokenVerifications int64
cacheSize int64
cacheEvictions int64
timingMutex sync.RWMutex 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
maxMemoryBytes int64
// Cache limits
maxCacheSize int64
// Session limits
maxSessions int64
// Cache size tracking
cacheSizes map[string]int64 cacheSizes map[string]int64
cacheMutex sync.RWMutex
// Monitoring state
alertThresholds map[string]float64 alertThresholds map[string]float64
alerts []ResourceAlert
alertsMutex sync.RWMutex
// Performance metrics reference
perfMetrics *PerformanceMetrics perfMetrics *PerformanceMetrics
logger *Logger logger *Logger
alerts []ResourceAlert
maxMemoryBytes int64
maxCacheSize int64
maxSessions int64
cacheMutex sync.RWMutex
alertsMutex sync.RWMutex
} }
// 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",
+19 -42
View File
@@ -13,66 +13,50 @@ 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
// IP-based tracking
ipFailures map[string]*IPFailureTracker
ipMutex sync.RWMutex 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
shortWindow time.Duration // 1 minute
mediumWindow time.Duration // 5 minutes
longWindow time.Duration // 15 minutes
// Pattern thresholds
rapidFailureThreshold int // failures in short window
distributedAttackThreshold int // failures across IPs in medium window
persistentAttackThreshold int // failures in long window
// Pattern tracking
recentEvents []SecurityEvent recentEvents []SecurityEvent
shortWindow time.Duration
mediumWindow time.Duration
longWindow time.Duration
rapidFailureThreshold int
distributedAttackThreshold int
persistentAttackThreshold int
eventsMutex sync.RWMutex eventsMutex sync.RWMutex
} }
@@ -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"`
// Monitoring settings
EnableDetailedLogging bool `json:"enable_detailed_logging"`
LogSuspiciousOnly bool `json:"log_suspicious_only"`
// Cleanup settings
CleanupIntervalMinutes int `json:"cleanup_interval_minutes"` CleanupIntervalMinutes int `json:"cleanup_interval_minutes"`
RetentionHours int `json:"retention_hours"` RetentionHours int `json:"retention_hours"`
EnablePatternDetection bool `json:"enable_pattern_detection"`
EnableDetailedLogging bool `json:"enable_detailed_logging"`
LogSuspiciousOnly bool `json:"log_suspicious_only"`
} }
// 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
}{ }{
{ {
+19 -88
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)
// Example: https://accounts.google.com
ProviderURL string `json:"providerURL"`
// RevocationURL is the endpoint for revoking tokens (optional)
// If not provided, it will be discovered from provider metadata
RevocationURL string `json:"revocationURL"`
// EnablePKCE enables Proof Key for Code Exchange (PKCE) for the authorization code flow (optional)
// This enhances security but might not be supported by all OIDC providers
// Default: false
EnablePKCE bool `json:"enablePKCE"`
// CallbackURL is the path where the OIDC provider will redirect after authentication (required)
// Example: /oauth2/callback
CallbackURL string `json:"callbackURL"`
// LogoutURL is the path for handling logout requests (optional)
// If not provided, it will be set to CallbackURL + "/logout"
LogoutURL string `json:"logoutURL"`
// 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 HTTPClient *http.Client
ProviderURL string `json:"providerURL"`
// RefreshGracePeriodSeconds defines how many seconds before a token expires RevocationURL string `json:"revocationURL"`
// the plugin should attempt to refresh it proactively (optional) CallbackURL string `json:"callbackURL"`
// Default: 60 LogoutURL string `json:"logoutURL"`
RefreshGracePeriodSeconds int `json:"refreshGracePeriodSeconds"` ClientID string `json:"clientID"`
// Headers defines custom HTTP headers to set with templated values (optional) ClientSecret string `json:"clientSecret"`
// Values can reference tokens and claims using Go templates with the following variables: PostLogoutRedirectURI string `json:"postLogoutRedirectURI"`
// - {{.AccessToken}} - The access token (ID token) LogLevel string `json:"logLevel"`
// - {{.IdToken}} - Same as AccessToken (for consistency) SessionEncryptionKey string `json:"sessionEncryptionKey"`
// - {{.RefreshToken}} - The refresh token OIDCEndSessionURL string `json:"oidcEndSessionURL"`
// - {{.Claims.email}} - Access token claims (use proper case for claim names) AllowedRolesAndGroups []string `json:"allowedRolesAndGroups"`
// Examples: ExcludedURLs []string `json:"excludedURLs"`
// AllowedUserDomains []string `json:"allowedUserDomains"`
// [{Name: "X-Forwarded-Email", Value: "{{.Claims.email}}"}] AllowedUsers []string `json:"allowedUsers"`
// [{Name: "Authorization", Value: "Bearer {{.AccessToken}}"}] Scopes []string `json:"scopes"`
Headers []TemplatedHeader `json:"headers"` Headers []TemplatedHeader `json:"headers"`
RateLimit int `json:"rateLimit"`
RefreshGracePeriodSeconds int `json:"refreshGracePeriodSeconds"`
ForceHTTPS bool `json:"forceHTTPS"`
EnablePKCE bool `json:"enablePKCE"`
} }
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",