mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-06-05 22:44:17 +00:00
e64fc7f730
* Add redis support for distributed caching * Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * ... and another all nighter. * fixup! ... and another all nighter. * fixup! fixup! ... and another all nighter. * fixup! fixup! fixup! ... and another all nighter. * Resolve issue #85 by adding ability to set custom claims in JWT tokens * Remove redundant validation in auth middleware ( issue #89 ) * Add ability to set cookie prefix for session cookies ( #87 ) * fixup! Add ability to set cookie prefix for session cookies ( #87 ) * Add ability to set cookie max age - issue #91 * Potential fix for code scanning alert no. 10: Size computation for allocation may overflow Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fixup! Merge main into 0.8.0-redis: resolve conflicts --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
364 lines
9.5 KiB
Go
364 lines
9.5 KiB
Go
//go:build !yaegi
|
|
|
|
package config
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/lukaszraczylo/traefikoidc/internal/features"
|
|
)
|
|
|
|
// NewLegacyAdapter Tests
|
|
func TestNewLegacyAdapter(t *testing.T) {
|
|
unified := NewUnifiedConfig()
|
|
unified.Provider.IssuerURL = "https://provider.example.com"
|
|
unified.Provider.ClientID = "test-client"
|
|
unified.Provider.ClientSecret = "test-secret"
|
|
|
|
adapter := NewLegacyAdapter(unified)
|
|
|
|
if adapter == nil {
|
|
t.Fatal("Expected NewLegacyAdapter to return non-nil")
|
|
}
|
|
|
|
if adapter.unified != unified {
|
|
t.Error("Expected adapter to reference the unified config")
|
|
}
|
|
|
|
if adapter.adapter == nil {
|
|
t.Error("Expected internal adapter to be initialized")
|
|
}
|
|
}
|
|
|
|
// ToOldConfig Tests
|
|
func TestLegacyAdapter_ToOldConfig(t *testing.T) {
|
|
unified := NewUnifiedConfig()
|
|
unified.Provider.IssuerURL = "https://issuer.example.com"
|
|
unified.Provider.ClientID = "client-123"
|
|
unified.Provider.ClientSecret = "secret-456"
|
|
unified.Provider.RedirectURL = "https://app.example.com/callback"
|
|
unified.Provider.LogoutURL = "/logout"
|
|
unified.Provider.PostLogoutRedirectURI = "https://app.example.com"
|
|
unified.Provider.Scopes = []string{"openid", "profile"}
|
|
unified.Provider.OverrideScopes = true
|
|
unified.Session.EncryptionKey = "test-encryption-key-32-chars!!"
|
|
unified.Session.Domain = "example.com"
|
|
unified.Security.ForceHTTPS = true
|
|
unified.Security.EnablePKCE = true
|
|
unified.Security.AllowedUsers = []string{"user@example.com"}
|
|
unified.Security.AllowedUserDomains = []string{"example.com"}
|
|
unified.Security.AllowedRolesAndGroups = []string{"admin"}
|
|
unified.Security.ExcludedURLs = []string{"/health"}
|
|
unified.RateLimit.RequestsPerSecond = 100
|
|
unified.Logging.Level = "debug"
|
|
unified.Middleware.CustomHeaders = map[string]string{
|
|
"X-Header-1": "value1",
|
|
"X-Header-2": "value2",
|
|
}
|
|
|
|
adapter := NewLegacyAdapter(unified)
|
|
oldConfig := adapter.ToOldConfig()
|
|
|
|
if oldConfig == nil {
|
|
t.Fatal("Expected ToOldConfig to return non-nil")
|
|
}
|
|
|
|
// ToOldConfig behavior depends on feature flag
|
|
if !features.IsUnifiedConfigEnabled() {
|
|
// When feature is disabled, returns default config
|
|
if oldConfig.ProviderURL == "" {
|
|
t.Log("Feature flag disabled - ToOldConfig returns default config")
|
|
}
|
|
return
|
|
}
|
|
|
|
// When feature is enabled, verify all fields were correctly mapped
|
|
if oldConfig.ProviderURL != unified.Provider.IssuerURL {
|
|
t.Errorf("Expected ProviderURL '%s', got '%s'", unified.Provider.IssuerURL, oldConfig.ProviderURL)
|
|
}
|
|
|
|
if oldConfig.ClientID != unified.Provider.ClientID {
|
|
t.Errorf("Expected ClientID '%s', got '%s'", unified.Provider.ClientID, oldConfig.ClientID)
|
|
}
|
|
|
|
if oldConfig.ClientSecret != unified.Provider.ClientSecret {
|
|
t.Errorf("Expected ClientSecret '%s', got '%s'", unified.Provider.ClientSecret, oldConfig.ClientSecret)
|
|
}
|
|
|
|
if oldConfig.CallbackURL != unified.Provider.RedirectURL {
|
|
t.Error("Expected CallbackURL to match RedirectURL")
|
|
}
|
|
|
|
if oldConfig.LogoutURL != unified.Provider.LogoutURL {
|
|
t.Error("Expected LogoutURL to match")
|
|
}
|
|
|
|
if oldConfig.ForceHTTPS != unified.Security.ForceHTTPS {
|
|
t.Error("Expected ForceHTTPS to match")
|
|
}
|
|
|
|
if oldConfig.EnablePKCE != unified.Security.EnablePKCE {
|
|
t.Error("Expected EnablePKCE to match")
|
|
}
|
|
|
|
if oldConfig.RateLimit != unified.RateLimit.RequestsPerSecond {
|
|
t.Errorf("Expected RateLimit %d, got %d", unified.RateLimit.RequestsPerSecond, oldConfig.RateLimit)
|
|
}
|
|
|
|
if len(oldConfig.Headers) != 2 {
|
|
t.Errorf("Expected 2 headers, got %d", len(oldConfig.Headers))
|
|
}
|
|
}
|
|
|
|
// convertHeaders Tests
|
|
func TestLegacyAdapter_convertHeaders(t *testing.T) {
|
|
unified := NewUnifiedConfig()
|
|
unified.Middleware.CustomHeaders = map[string]string{
|
|
"X-Custom-Header-1": "value1",
|
|
"X-Custom-Header-2": "value2",
|
|
"X-Custom-Header-3": "value3",
|
|
}
|
|
|
|
adapter := NewLegacyAdapter(unified)
|
|
headers := adapter.convertHeaders()
|
|
|
|
if len(headers) != 3 {
|
|
t.Errorf("Expected 3 headers, got %d", len(headers))
|
|
}
|
|
|
|
// Check that headers were converted
|
|
headerMap := make(map[string]string)
|
|
for _, h := range headers {
|
|
headerMap[h.Name] = h.Value
|
|
}
|
|
|
|
if headerMap["X-Custom-Header-1"] != "value1" {
|
|
t.Error("Expected X-Custom-Header-1 to have value 'value1'")
|
|
}
|
|
|
|
if headerMap["X-Custom-Header-2"] != "value2" {
|
|
t.Error("Expected X-Custom-Header-2 to have value 'value2'")
|
|
}
|
|
}
|
|
|
|
func TestLegacyAdapter_convertHeaders_Empty(t *testing.T) {
|
|
unified := NewUnifiedConfig()
|
|
// No custom headers
|
|
|
|
adapter := NewLegacyAdapter(unified)
|
|
headers := adapter.convertHeaders()
|
|
|
|
if len(headers) != 0 {
|
|
t.Errorf("Expected 0 headers, got %d", len(headers))
|
|
}
|
|
}
|
|
|
|
// GetConfigInterface Tests
|
|
func TestGetConfigInterface(t *testing.T) {
|
|
cfg := GetConfigInterface()
|
|
|
|
if cfg == nil {
|
|
t.Fatal("Expected GetConfigInterface to return non-nil")
|
|
}
|
|
|
|
// Should return either UnifiedConfig or Config depending on feature flag
|
|
_, isUnified := cfg.(*UnifiedConfig)
|
|
_, isOld := cfg.(*Config)
|
|
|
|
if !isUnified && !isOld {
|
|
t.Error("Expected either *UnifiedConfig or *Config")
|
|
}
|
|
|
|
// Verify consistency with feature flag
|
|
if features.IsUnifiedConfigEnabled() {
|
|
if !isUnified {
|
|
t.Error("Expected *UnifiedConfig when unified config is enabled")
|
|
}
|
|
} else {
|
|
if !isOld {
|
|
t.Error("Expected *Config when unified config is disabled")
|
|
}
|
|
}
|
|
}
|
|
|
|
// ValidateConfig Tests
|
|
func TestValidateConfig_UnifiedConfig(t *testing.T) {
|
|
unified := NewUnifiedConfig()
|
|
unified.Provider.IssuerURL = "https://provider.example.com"
|
|
unified.Provider.ClientID = "client-id"
|
|
unified.Provider.ClientSecret = "client-secret"
|
|
unified.Session.EncryptionKey = "encryption-key-32-characters!!"
|
|
|
|
err := ValidateConfig(unified)
|
|
// Should succeed regardless of feature flag since we're passing the right type
|
|
if err != nil {
|
|
t.Errorf("Expected valid unified config to pass validation, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateConfig_OldConfig(t *testing.T) {
|
|
old := CreateConfig()
|
|
old.ProviderURL = "https://provider.example.com"
|
|
old.ClientID = "client-id"
|
|
old.ClientSecret = "client-secret"
|
|
old.SessionEncryptionKey = "encryption-key-32-characters!!"
|
|
|
|
err := ValidateConfig(old)
|
|
if err != nil {
|
|
t.Errorf("Expected valid old config to pass validation, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateConfig_InvalidType(t *testing.T) {
|
|
// Pass something that's not a config
|
|
err := ValidateConfig("not a config")
|
|
if err != nil {
|
|
t.Errorf("Expected nil for unknown type, got: %v", err)
|
|
}
|
|
}
|
|
|
|
// Config.Validate Tests
|
|
func TestConfig_Validate_Valid(t *testing.T) {
|
|
cfg := CreateConfig()
|
|
cfg.ProviderURL = "https://provider.example.com"
|
|
cfg.ClientID = "client-id"
|
|
cfg.ClientSecret = "client-secret"
|
|
cfg.SessionEncryptionKey = "encryption-key-32-characters!!"
|
|
|
|
err := cfg.Validate()
|
|
if err != nil {
|
|
t.Errorf("Expected valid config to pass, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestConfig_Validate_MissingProviderURL(t *testing.T) {
|
|
cfg := CreateConfig()
|
|
cfg.ClientID = "client-id"
|
|
cfg.ClientSecret = "client-secret"
|
|
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Error("Expected error for missing ProviderURL")
|
|
}
|
|
|
|
// Check if it's a ValidationErrors type
|
|
if verrs, ok := err.(ValidationErrors); ok {
|
|
found := false
|
|
for _, verr := range verrs {
|
|
if verr.Field == "ProviderURL" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Expected ProviderURL validation error")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfig_Validate_MissingClientID(t *testing.T) {
|
|
cfg := CreateConfig()
|
|
cfg.ProviderURL = "https://provider.example.com"
|
|
cfg.ClientSecret = "client-secret"
|
|
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Error("Expected error for missing ClientID")
|
|
}
|
|
|
|
if verrs, ok := err.(ValidationErrors); ok {
|
|
found := false
|
|
for _, verr := range verrs {
|
|
if verr.Field == "ClientID" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Expected ClientID validation error")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfig_Validate_MissingClientSecret_NoPKCE(t *testing.T) {
|
|
cfg := CreateConfig()
|
|
cfg.ProviderURL = "https://provider.example.com"
|
|
cfg.ClientID = "client-id"
|
|
cfg.EnablePKCE = false
|
|
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Error("Expected error for missing ClientSecret without PKCE")
|
|
}
|
|
|
|
if verrs, ok := err.(ValidationErrors); ok {
|
|
found := false
|
|
for _, verr := range verrs {
|
|
if verr.Field == "ClientSecret" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Expected ClientSecret validation error")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfig_Validate_MissingClientSecret_WithPKCE(t *testing.T) {
|
|
cfg := CreateConfig()
|
|
cfg.ProviderURL = "https://provider.example.com"
|
|
cfg.ClientID = "client-id"
|
|
cfg.EnablePKCE = true // PKCE enabled, so ClientSecret not required
|
|
|
|
err := cfg.Validate()
|
|
if err != nil {
|
|
t.Errorf("Expected no error with PKCE enabled and no ClientSecret, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestConfig_Validate_ShortEncryptionKey(t *testing.T) {
|
|
cfg := CreateConfig()
|
|
cfg.ProviderURL = "https://provider.example.com"
|
|
cfg.ClientID = "client-id"
|
|
cfg.ClientSecret = "client-secret"
|
|
cfg.SessionEncryptionKey = "short" // Too short
|
|
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Error("Expected error for short encryption key")
|
|
}
|
|
|
|
if verrs, ok := err.(ValidationErrors); ok {
|
|
found := false
|
|
for _, verr := range verrs {
|
|
if verr.Field == "SessionEncryptionKey" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Expected SessionEncryptionKey validation error")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfig_Validate_MultipleErrors(t *testing.T) {
|
|
cfg := CreateConfig()
|
|
// Missing ProviderURL, ClientID, and ClientSecret
|
|
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Fatal("Expected validation errors")
|
|
}
|
|
|
|
verrs, ok := err.(ValidationErrors)
|
|
if !ok {
|
|
t.Fatal("Expected ValidationErrors type")
|
|
}
|
|
|
|
if len(verrs) < 2 {
|
|
t.Errorf("Expected at least 2 validation errors, got %d", len(verrs))
|
|
}
|
|
}
|