mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-14 04:07:46 +00:00
Fieldalignment
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user