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.
// It implements an LRU (Least Recently Used) eviction policy using a doubly-linked list for efficiency.
type Cache struct {
// items stores the cached data with string keys.
items map[string]CacheItem
// order maintains the usage order; most recently used items are at the back.
order *list.List
// 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.
items map[string]CacheItem
order *list.List
elems map[string]*list.Element
stopCleanup chan struct{}
maxSize int
autoCleanupInterval time.Duration
// stopCleanup channel to terminate the auto cleanup goroutine.
stopCleanup chan struct{}
mutex sync.RWMutex
}
// 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
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
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
// Metrics
totalRequests int64
totalFailures int64
totalSuccesses int64
// Logger
logger *Logger
}
// CircuitBreakerConfig holds configuration for circuit breakers
@@ -193,12 +186,12 @@ func (cb *CircuitBreaker) GetMetrics() map[string]any {
// RetryConfig holds configuration for retry mechanisms
type RetryConfig struct {
RetryableErrors []string `json:"retryable_errors"`
MaxAttempts int `json:"max_attempts"`
InitialDelay time.Duration `json:"initial_delay"`
MaxDelay time.Duration `json:"max_delay"`
BackoffFactor float64 `json:"backoff_factor"`
EnableJitter bool `json:"enable_jitter"`
RetryableErrors []string `json:"retryable_errors"`
}
// DefaultRetryConfig returns default retry configuration
@@ -220,8 +213,8 @@ func DefaultRetryConfig() RetryConfig {
// RetryExecutor implements retry logic with exponential backoff
type RetryExecutor struct {
config RetryConfig
logger *Logger
config RetryConfig
}
// 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
type HTTPError struct {
StatusCode int
Message string
StatusCode int
}
// Error implements the error interface
@@ -355,20 +348,12 @@ func (e *HTTPError) Error() string {
// GracefulDegradation implements graceful degradation patterns
type GracefulDegradation struct {
// Fallback functions for different operations
fallbacks map[string]func() (any, error)
// Health checks for dependencies
healthChecks map[string]func() bool
// Configuration
config GracefulDegradationConfig
// State tracking
fallbacks map[string]func() (any, error)
healthChecks map[string]func() bool
degradedServices map[string]time.Time
logger *Logger
config GracefulDegradationConfig
mutex sync.RWMutex
logger *Logger
}
// GracefulDegradationConfig holds configuration for graceful degradation
@@ -537,8 +522,8 @@ type ErrorRecoveryManager struct {
circuitBreakers map[string]*CircuitBreaker
retryExecutor *RetryExecutor
gracefulDegradation *GracefulDegradation
mutex sync.RWMutex
logger *Logger
mutex sync.RWMutex
}
// 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
// code exchange or token refresh operations.
type TokenResponse struct {
// IDToken is the OIDC ID token containing user claims
IDToken string `json:"id_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
IDToken string `json:"id_token"`
AccessToken string `json:"access_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.
+15 -21
View File
@@ -11,35 +11,29 @@ import (
// InputValidator provides comprehensive input validation and sanitization
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
// Security patterns to detect
usernameRegex *regexp.Regexp
tokenRegex *regexp.Regexp
logger *Logger
urlRegex *regexp.Regexp
emailRegex *regexp.Regexp
sqlInjectionPatterns []string
xssPatterns []string
pathTraversalPatterns []string
logger *Logger
xssPatterns []string
maxUsernameLength int
maxURLLength int
maxTokenLength int
maxEmailLength int
maxClaimLength int
maxHeaderLength int
}
// ValidationResult represents the result of input validation
type ValidationResult struct {
IsValid bool `json:"is_valid"`
Errors []string `json:"errors,omitempty"`
Warnings []string `json:"warnings,omitempty"`
SanitizedValue string `json:"sanitized_value,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
+1 -1
View File
@@ -204,8 +204,8 @@ func TestSanitizeInput(t *testing.T) {
tests := []struct {
name string
input string
maxLen int
expected string
maxLen int
}{
{
name: "Normal text",
+5 -6
View File
@@ -33,13 +33,12 @@ type JWKSet struct {
}
type JWKCache struct {
jwks *JWKSet
expiresAt time.Time
mutex sync.RWMutex
// CacheLifetime is configurable to determine how long the JWKS is cached.
expiresAt time.Time
jwks *JWKSet
internalCache *Cache
CacheLifetime time.Duration
internalCache *Cache // To hold the closable Cache instance from cache.go
maxSize int // Maximum number of items in the cache
maxSize int
mutex sync.RWMutex
}
type JWKCacheInterface interface {
+1 -1
View File
@@ -89,8 +89,8 @@ var ClockSkewTolerance = ClockSkewToleranceFuture
type JWT struct {
Header map[string]any
Claims map[string]any
Signature []byte
Token string
Signature []byte
}
// 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
type TraefikOidc struct {
jwkCache JWKCacheInterface
jwtVerifier JWTVerifier
ctx context.Context
tokenVerifier TokenVerifier
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
redirURLPath string
logoutURLPath string
tokenURL string
authURL string
endSessionURL string
postLogoutRedirectURI string
scheme string
jwksURL 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
scopes []string
limiter *rate.Limiter
goroutineWG sync.WaitGroup
refreshGracePeriod time.Duration
shutdownOnce sync.Once
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
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
suppressDiagnosticLogs bool
}
// 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
// Fields must be exported (uppercase) to be accessible in templates
templateData := struct {
// These fields need to be exported (uppercase) for template access
Claims map[string]any
AccessToken string
IdToken string
RefreshToken string
Claims map[string]any
}{
AccessToken: session.GetAccessToken(), // Provide AccessToken for templates if needed
IdToken: session.GetIDToken(),
+33 -33
View File
@@ -30,8 +30,8 @@ type TestSuite struct {
ecPrivateKey *ecdsa.PrivateKey
tOidc *TraefikOidc
mockJWKCache *MockJWKCache
token string
sessionManager *SessionManager
token string
}
// Setup initializes the test suite
@@ -410,15 +410,15 @@ func TestServeHTTP(t *testing.T) {
}
tests := []struct {
name string
requestPath string
sessionValues map[any]any
expectedStatus int
expectedBody string
setupSession func(*SessionData)
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
requestHeaders map[string]string // Added for setting headers like Accept
assertSessionAfterRequest func(t *testing.T, rr *httptest.ResponseRecorder, req *http.Request, sessionManager *SessionManager)
requestHeaders map[string]string
name string
requestPath string
expectedBody string
expectedStatus int
}{
{
name: "Excluded URL",
@@ -873,10 +873,10 @@ func TestJWKToPEM(t *testing.T) {
ts.Setup()
tests := []struct {
name string
jwk *JWK
expectError bool
name string
errorContains string
expectError bool
}{
{
name: "Unsupported Key Type",
@@ -928,8 +928,8 @@ func TestParseJWT(t *testing.T) {
tests := []struct {
name string
token string
expectError bool
errorContains string
expectError bool
}{
{
name: "Invalid Format",
@@ -989,11 +989,11 @@ func TestHandleCallback(t *testing.T) {
redirectURL := "http://example.com/"
tests := []struct {
name string
queryParams string
exchangeCodeForToken func(code string, redirectURL string, codeVerifier string) (*TokenResponse, error)
extractClaimsFunc func(tokenString string) (map[string]any, error)
sessionSetupFunc func(*SessionData)
name string
queryParams string
expectedStatus int
}{
{
@@ -1256,12 +1256,12 @@ func TestIsAllowedDomain(t *testing.T) {
ts.Setup()
tests := []struct {
name string
email string
allowedDomains map[string]struct{}
allowedUsers map[string]struct{}
name string
email string
expectedLogOutput string
allowed bool
expectedLogOutput string // For testing log messages
}{
{
name: "Allowed domain",
@@ -1343,11 +1343,11 @@ func TestOIDCHandler(t *testing.T) {
ts.token = "valid.jwt.token"
tests := []struct {
name string
queryParams string
exchangeCodeForToken func(code string, redirectURL string, codeVerifier string) (*TokenResponse, error)
extractClaimsFunc func(tokenString string) (map[string]any, error)
sessionSetupFunc func(session *sessions.Session)
name string
queryParams string
expectedStatus int
blacklist bool
rateLimit bool
@@ -1504,12 +1504,12 @@ func TestHandleLogout(t *testing.T) {
defer mockRevocationServer.Close()
tests := []struct {
name string
setupSession func(*SessionData)
name string
endSessionURL string
expectedStatus int
expectedURL string
host string
expectedStatus int
}{
{
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
tests := []struct {
name string
allowedRolesAndGroups map[string]struct{}
claims map[string]any
setupSession func(*SessionData)
expectedStatus int
expectedHeaders map[string]string
name string
expectedStatus int
}{
{
name: "User with allowed role",
@@ -2313,10 +2313,10 @@ func TestExchangeTokensWithRedirects(t *testing.T) {
ts.Setup()
tests := []struct {
name string
setupServer func() *httptest.Server
expectError bool
name string
errorContains string
expectError bool
}{
{
name: "Successful token exchange with redirects",
@@ -2424,9 +2424,9 @@ func TestBuildAuthURL(t *testing.T) {
redirectURL string
state string
nonce string
enablePKCE bool
codeChallenge string
expectedPrefix string
enablePKCE bool
checkPKCE bool
}{
{
@@ -2574,10 +2574,10 @@ func TestExchangeCodeForToken(t *testing.T) {
ts.Setup()
tests := []struct {
name string
enablePKCE bool
codeVerifier string
setupMock func(t *testing.T) *httptest.Server
name string
codeVerifier string
enablePKCE bool
}{
{
name: "With PKCE Enabled and Code Verifier",
@@ -2883,10 +2883,10 @@ func TestJWTVerifyWithSkipReplayCheck(t *testing.T) {
tests := []struct {
name string
errorContains string
skipReplayCheck bool
firstCall bool
expectError bool
errorContains string
}{
{
name: "First verification with skipReplayCheck=false should succeed",
@@ -3339,10 +3339,10 @@ func TestJTIBlacklistBehavior(t *testing.T) {
// Test JTI blacklist behavior
tests := []struct {
name string
action func() error
expectError bool
name string
description string
expectError bool
}{
{
name: "Initial verification adds JTI to blacklist",
@@ -3495,9 +3495,9 @@ func TestEdgeCasesWithDifferentTokenTypes(t *testing.T) {
nbf := now.Unix()
tests := []struct {
claims map[string]any
name string
tokenType string
claims map[string]any
expectError bool
}{
{
@@ -3697,10 +3697,10 @@ func TestScopeMergingEdgeCases(t *testing.T) {
tests := []struct {
name string
description string
defaultScopes []string
userScopes []string
expectedScopes []string
description string
}{
{
name: "Case sensitivity preserved",
@@ -3974,8 +3974,8 @@ func TestBuildAuthURLWithMergedScopes(t *testing.T) {
tests := []struct {
name string
scopes []string
expectedScopes string
scopes []string
}{
{
name: "Default scopes only",
+3 -3
View File
@@ -8,11 +8,11 @@ import (
)
type MetadataCache struct {
metadata *ProviderMetadata
expiresAt time.Time
mutex sync.RWMutex
autoCleanupInterval time.Duration
metadata *ProviderMetadata
stopCleanup chan struct{}
autoCleanupInterval time.Duration
mutex sync.RWMutex
}
// NewMetadataCache creates a new MetadataCache instance.
+38 -70
View File
@@ -10,61 +10,41 @@ import (
// PerformanceMetrics tracks various performance-related metrics
type PerformanceMetrics struct {
// Cache metrics
cacheHits int64
cacheMisses int64
cacheEvictions int64
cacheSize int64
// Token operation metrics
tokenVerifications int64
tokenValidations int64
tokenRefreshes int64
// Success/failure tracking
startTime time.Time
logger *Logger
verificationTimes []time.Duration
refreshTimes []time.Duration
validationTimes []time.Duration
memoryUsage int64
heapSize int64
successfulVerifications int64
successfulValidations int64
successfulRefreshes int64
failedVerifications int64
failedValidations int64
failedRefreshes int64
// Timing metrics
avgVerificationTime time.Duration
avgValidationTime time.Duration
avgRefreshTime time.Duration
// Resource metrics
memoryUsage int64
goroutineCount int64
memoryPressure int64 // Memory pressure level (0-100)
gcPauseTime int64 // Last GC pause time in nanoseconds
heapSize int64 // Current heap size
heapInUse int64 // Heap memory in use
// Error metrics (kept for backward compatibility)
verificationErrors int64
validationErrors int64
refreshErrors int64
// Rate limiting metrics
rateLimitedRequests int64
// 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
avgVerificationTime time.Duration
avgValidationTime time.Duration
avgRefreshTime time.Duration
cacheHits int64
goroutineCount int64
memoryPressure int64
gcPauseTime int64
tokenRefreshes int64
heapInUse int64
verificationErrors int64
validationErrors int64
refreshErrors int64
rateLimitedRequests int64
activeSessions int64
sessionCreations int64
sessionDeletions int64
cacheMisses int64
tokenValidations int64
tokenVerifications int64
cacheSize int64
cacheEvictions int64
timingMutex sync.RWMutex
}
// 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
type ResourceMonitor struct {
// Memory limits
maxMemoryBytes int64
// Cache limits
maxCacheSize int64
// Session limits
maxSessions int64
// Cache size tracking
cacheSizes map[string]int64
cacheMutex sync.RWMutex
// Monitoring state
cacheSizes map[string]int64
alertThresholds map[string]float64
perfMetrics *PerformanceMetrics
logger *Logger
alerts []ResourceAlert
maxMemoryBytes int64
maxCacheSize int64
maxSessions int64
cacheMutex sync.RWMutex
alertsMutex sync.RWMutex
// Performance metrics reference
perfMetrics *PerformanceMetrics
logger *Logger
}
// ResourceAlert represents a resource usage alert
type ResourceAlert struct {
Timestamp time.Time `json:"timestamp"`
Type string `json:"type"`
Message string `json:"message"`
Severity string `json:"severity"`
Threshold float64 `json:"threshold"`
CurrentValue float64 `json:"current_value"`
Timestamp time.Time `json:"timestamp"`
Severity string `json:"severity"`
}
// NewResourceMonitor creates a new resource monitor
+1 -1
View File
@@ -390,8 +390,8 @@ func TestMissingClaims(t *testing.T) {
// Test cases for missing claims
testCases := []struct {
name string
omittedClaims []string
expectedError string
omittedClaims []string
}{
{
name: "Missing Issuer",
+27 -50
View File
@@ -13,67 +13,51 @@ import (
// SecurityEvent represents a security-related event that should be logged and monitored
type SecurityEvent struct {
Timestamp time.Time `json:"timestamp"`
Details map[string]any `json:"details,omitempty"`
Type string `json:"type"`
Severity string `json:"severity"`
Timestamp time.Time `json:"timestamp"`
ClientIP string `json:"client_ip"`
UserAgent string `json:"user_agent"`
RequestPath string `json:"request_path"`
Message string `json:"message"`
Details map[string]any `json:"details,omitempty"`
}
// SecurityMonitor tracks security events and suspicious activity patterns
type SecurityMonitor struct {
// Event counters
ipFailures map[string]*IPFailureTracker
patternDetector *SuspiciousPatternDetector
logger *Logger
eventHandlers []SecurityEventHandler
config SecurityMonitorConfig
authFailures int64
tokenValidationFails int64
rateLimitHits int64
suspiciousRequests int64
// IP-based tracking
ipFailures map[string]*IPFailureTracker
ipMutex sync.RWMutex
// Pattern detection
patternDetector *SuspiciousPatternDetector
// Event handlers
eventHandlers []SecurityEventHandler
// Configuration
config SecurityMonitorConfig
// Logger
logger *Logger
ipMutex sync.RWMutex
}
// IPFailureTracker tracks failures for a specific IP address
type IPFailureTracker struct {
FailureCount int64
LastFailure time.Time
FirstFailure time.Time
FailureTypes map[string]int64
IsBlocked bool
BlockedUntil time.Time
FailureTypes map[string]int64
FailureCount int64
mutex sync.RWMutex
IsBlocked bool
}
// SuspiciousPatternDetector identifies patterns that may indicate attacks
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
eventsMutex sync.RWMutex
recentEvents []SecurityEvent
shortWindow time.Duration
mediumWindow time.Duration
longWindow time.Duration
rapidFailureThreshold int
distributedAttackThreshold int
persistentAttackThreshold int
eventsMutex sync.RWMutex
}
// SecurityEventHandler defines the interface for handling security events
@@ -83,22 +67,15 @@ type SecurityEventHandler interface {
// SecurityMonitorConfig contains configuration for the security monitor
type SecurityMonitorConfig struct {
// Failure thresholds
MaxFailuresPerIP int `json:"max_failures_per_ip"`
FailureWindowMinutes int `json:"failure_window_minutes"`
BlockDurationMinutes int `json:"block_duration_minutes"`
// Pattern detection settings
EnablePatternDetection bool `json:"enable_pattern_detection"`
MaxFailuresPerIP int `json:"max_failures_per_ip"`
FailureWindowMinutes int `json:"failure_window_minutes"`
BlockDurationMinutes int `json:"block_duration_minutes"`
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"`
RetentionHours int `json:"retention_hours"`
CleanupIntervalMinutes int `json:"cleanup_interval_minutes"`
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
+3 -10
View File
@@ -213,17 +213,10 @@ func decompressTokenInternal(compressed string) string {
// It provides functionality for storing and retrieving authentication state, tokens,
// and other session-related data across multiple cookies.
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
store sessions.Store
logger *Logger
forceHTTPS bool
}
// 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 {
name string
corruptChunk func(chunks map[int]*sessions.Session)
name string
expectEmpty bool
}{
{
+21 -90
View File
@@ -26,96 +26,27 @@ type TemplatedHeader struct {
// It provides all necessary settings to configure OpenID Connect authentication
// with various providers like Auth0, Logto, or any standard OIDC provider.
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
// 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"`
HTTPClient *http.Client
ProviderURL string `json:"providerURL"`
RevocationURL string `json:"revocationURL"`
CallbackURL string `json:"callbackURL"`
LogoutURL string `json:"logoutURL"`
ClientID string `json:"clientID"`
ClientSecret string `json:"clientSecret"`
PostLogoutRedirectURI string `json:"postLogoutRedirectURI"`
LogLevel string `json:"logLevel"`
SessionEncryptionKey string `json:"sessionEncryptionKey"`
OIDCEndSessionURL string `json:"oidcEndSessionURL"`
AllowedRolesAndGroups []string `json:"allowedRolesAndGroups"`
ExcludedURLs []string `json:"excludedURLs"`
AllowedUserDomains []string `json:"allowedUserDomains"`
AllowedUsers []string `json:"allowedUsers"`
Scopes []string `json:"scopes"`
Headers []TemplatedHeader `json:"headers"`
RateLimit int `json:"rateLimit"`
RefreshGracePeriodSeconds int `json:"refreshGracePeriodSeconds"`
ForceHTTPS bool `json:"forceHTTPS"`
EnablePKCE bool `json:"enablePKCE"`
}
const (
+4 -4
View File
@@ -254,10 +254,10 @@ func TestLogger(t *testing.T) {
var debugBuf, infoBuf, errorBuf bytes.Buffer
tests := []struct {
name string
logLevel string
testFunc func(*Logger)
checkFunc func(t *testing.T, debugOut, infoOut, errorOut string)
name string
logLevel string
}{
{
name: "Debug Level",
@@ -405,9 +405,9 @@ func TestHandleError(t *testing.T) {
// Test helper types
type testResponseRecorder struct {
statusCode int
body string
headers map[string][]string
body string
statusCode int
}
func (r *testResponseRecorder) Header() http.Header {
+1 -1
View File
@@ -178,10 +178,10 @@ func TestTemplateExecution(t *testing.T) {
func TestTemplateExecutionContext(t *testing.T) {
// Define a test struct that matches the one used in processAuthorizedRequest
type templateData struct {
Claims map[string]any
AccessToken string
IdToken string
RefreshToken string
Claims map[string]any
}
// Test cases
+3 -3
View File
@@ -20,12 +20,12 @@ func TestTemplatedHeadersIntegration(t *testing.T) {
ts.Setup()
tests := []struct {
name string
headers []TemplatedHeader
sessionSetup func(*SessionData)
claims map[string]any
expectedHeaders map[string]string
interceptedHeaders map[string]string
name string
headers []TemplatedHeader
}{
{
name: "Basic Email Header",
@@ -423,9 +423,9 @@ func TestEdgeCaseTemplatedHeaders(t *testing.T) {
ts.Setup()
tests := []struct {
claims map[string]any
name string
headers []TemplatedHeader
claims map[string]any
shouldExecuteCheck bool
}{
{
+3 -3
View File
@@ -28,11 +28,11 @@ func TestTokenCorruptionScenario(t *testing.T) {
validJWT := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OX0.signature"
tests := []struct {
corruptionScenario func(*SessionData)
name string
tokenSize int
iterations int
expectConsistent bool
corruptionScenario func(*SessionData) // Function to simulate corruption
}{
{
name: "Small token - multiple retrievals",
@@ -207,8 +207,8 @@ func TestChunkReassemblyEdgeCases(t *testing.T) {
// Test various corruption scenarios
corruptionTests := []struct {
name string
corruption func(map[int]*sessions.Session)
name string
expectEmpty bool
}{
{
@@ -367,8 +367,8 @@ func TestRaceConditionProtection(t *testing.T) {
// TestMemoryExhaustionProtection tests protection against memory exhaustion attacks
func TestMemoryExhaustionProtection(t *testing.T) {
tests := []struct {
name string
setupCorruption func() string
name string
expectRejection bool
}{
{
+2 -2
View File
@@ -19,10 +19,10 @@ import (
func TestTokenTypeDistinction(t *testing.T) {
// Define test data where AccessToken and IdToken are deliberately different
type templateData struct {
Claims map[string]any
AccessToken string
IdToken string
RefreshToken string
Claims map[string]any
}
testData := templateData{
@@ -323,12 +323,12 @@ func TestTokenCorruptionIntegrationFlows(t *testing.T) {
}
tests := []struct {
corruptAction func(*SessionData)
name string
accessToken string
refreshToken string
idToken string
expectSuccess bool
corruptAction func(*SessionData) // Function to corrupt session after storage
}{
{
name: "Normal flow - small tokens",