package traefikoidc import ( "testing" "text/template" ) func TestTemplatedHeaderValidation(t *testing.T) { tests := []struct { name string header TemplatedHeader expectedError string }{ { name: "Empty Name", header: TemplatedHeader{Name: "", Value: "{{.Claims.email}}"}, expectedError: "header name cannot be empty", }, { name: "Empty Value", header: TemplatedHeader{Name: "X-Email", Value: ""}, expectedError: "header value template cannot be empty", }, { name: "Not a Template", header: TemplatedHeader{Name: "X-Email", Value: "static-value"}, expectedError: "header value 'static-value' does not appear to be a valid template (missing {{ }})", }, { name: "Lowercase claims", header: TemplatedHeader{Name: "X-Email", Value: "{{.claims.email}}"}, expectedError: "header template '{{.claims.email}}' appears to use lowercase 'claims' - use '{{.Claims...' instead (case sensitive)", }, { name: "Lowercase accessToken", header: TemplatedHeader{Name: "X-Token", Value: "Bearer {{.accessToken}}"}, expectedError: "header template 'Bearer {{.accessToken}}' appears to use lowercase 'accessToken' - use '{{.AccessToken...' instead (case sensitive)", }, { name: "Lowercase idToken", header: TemplatedHeader{Name: "X-Token", Value: "Bearer {{.idToken}}"}, expectedError: "header template 'Bearer {{.idToken}}' appears to use lowercase 'idToken' - use '{{.IdToken...' instead (case sensitive)", }, { name: "Lowercase refreshToken", header: TemplatedHeader{Name: "X-Refresh", Value: "Bearer {{.refreshToken}}"}, expectedError: "header template 'Bearer {{.refreshToken}}' appears to use lowercase 'refreshToken' - use '{{.RefreshToken...' instead (case sensitive)", }, { name: "Valid Template", header: TemplatedHeader{Name: "X-Email", Value: "{{.Claims.email}}"}, expectedError: "", }, { name: "Valid Bearer Token Template", header: TemplatedHeader{Name: "Authorization", Value: "Bearer {{.AccessToken}}"}, expectedError: "", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { config := &Config{ ProviderURL: "https://provider.com", CallbackURL: "/callback", ClientID: "client-id", ClientSecret: "client-secret", SessionEncryptionKey: "this-is-a-long-enough-encryption-key", RateLimit: 10, // Adding minimum required rate limit Headers: []TemplatedHeader{tc.header}, } err := config.Validate() if tc.expectedError == "" { if err != nil { t.Errorf("Expected no error, got: %v", err) } } else { if err == nil { t.Errorf("Expected error: %s, got nil", tc.expectedError) } else if err.Error() != tc.expectedError { t.Errorf("Expected error: %s, got: %s", tc.expectedError, err.Error()) } } }) } } func TestTemplateParsingInNew(t *testing.T) { // Test successful parsing of templates during middleware creation tests := []struct { name string headers []TemplatedHeader expectedTemplates int expectError bool }{ { name: "Single Valid Template", headers: []TemplatedHeader{ {Name: "X-Email", Value: "{{.Claims.email}}"}, }, expectedTemplates: 1, expectError: false, }, { name: "Multiple Valid Templates", headers: []TemplatedHeader{ {Name: "X-Email", Value: "{{.Claims.email}}"}, {Name: "X-User-ID", Value: "{{.Claims.sub}}"}, {Name: "Authorization", Value: "Bearer {{.AccessToken}}"}, }, expectedTemplates: 3, expectError: false, }, { name: "Invalid Template", headers: []TemplatedHeader{ {Name: "X-Email", Value: "{{.Claims.email"}, // Missing closing braces }, expectedTemplates: 0, expectError: true, }, { name: "Mix of Valid and Invalid Templates", headers: []TemplatedHeader{ {Name: "X-Email", Value: "{{.Claims.email}}"}, {Name: "X-Invalid", Value: "{{if .Claims.admin}}Admin{{end"}, // Invalid template }, expectedTemplates: 1, // Only the valid template should be parsed expectError: true, // We expect an error for the invalid template, but we'll handle it }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { // For testing template parsing, we'll directly try to parse the templates instead of using New() // This avoids the provider discovery that would fail in tests headerTemplates := make(map[string]*template.Template) // Special handling for the mixed valid/invalid templates case if tc.name == "Mix of Valid and Invalid Templates" { // Process templates one at a time so we can still have valid templates for _, header := range tc.headers { tmpl, err := template.New(header.Name).Parse(header.Value) if err != nil { // We expect an error for the invalid template if !tc.expectError { t.Errorf("Unexpected error parsing template %s: %v", header.Name, err) } // Skip this template but continue processing others continue } headerTemplates[header.Name] = tmpl } } else { // Normal handling for other test cases var parseErr error for _, header := range tc.headers { tmpl, err := template.New(header.Name).Parse(header.Value) if err != nil { parseErr = err break } headerTemplates[header.Name] = tmpl } if tc.expectError { if parseErr == nil { t.Error("Expected error parsing templates but got nil") } return } if parseErr != nil { t.Fatalf("Unexpected error: %v", parseErr) } } // Check the number of parsed templates if len(headerTemplates) != tc.expectedTemplates { t.Errorf("Expected %d parsed templates, got %d", tc.expectedTemplates, len(headerTemplates)) } // Check each template was parsed for _, header := range tc.headers { // Skip the known invalid templates if header.Value == "{{.Claims.email" || header.Value == "{{if .Claims.admin}}Admin{{end" { continue } if _, ok := headerTemplates[header.Name]; !ok { t.Errorf("Template for header %s was not parsed", header.Name) } } }) } }