mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-06-05 22:44:17 +00:00
2d1b04c637
* Multiple fixes - refresh coordinator dedup + memory pressure wire - middleware sse consolidation + timer leak + claim cache - universal cache sync backfill + isDebug gate - lazy background task race - memory monitor stw cached + refresh() api * fix(auth): suppress OIDC redirects on non-navigation requests - [x] Add isNonNavigationRequest using Sec-Fetch-Mode and Accept headers - [x] Add comprehensive TestIsNonNavigationRequest - [x] Update ServeHTTP to 401 non-navigation and AJAX requests Fixes #129 * feat(config): add custom CA and insecure skip verify for OIDC TLS - [x] Add CACertPath, CACertPEM, InsecureSkipVerify to Config - [x] Implement loadCACertPool for CA bundle loading - [x] Update HTTPClientConfig with RootCAs and InsecureSkipVerify - [x] Apply CA pool and skip verify to pooled HTTP clients - [x] Enhance configKey to distinguish TLS configs - [x] Add comprehensive ca_cert_test.go Fixes #125 * feat(oidc): add custom CA certificate support for private OIDC providers - [x] Add caCertPath, caCertPEM, insecureSkipVerify config options - [x] Update traefik.yml with new OIDC client config fields - [x] Add configuration schema descriptions for new options - [x] Update README table and add Custom CA Certificates section * Fix the documentation. * test(redis): add oversized argument rejection test - [x] Add TestRedisConn_RejectOversizedArgumentBytes - [x] Import strings package * Dependencies cleanup
138 lines
3.6 KiB
Go
138 lines
3.6 KiB
Go
package traefikoidc
|
|
|
|
import (
|
|
"encoding/pem"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// testCertPEM returns a valid PEM-encoded certificate harvested from an
|
|
// httptest.NewTLSServer. Using httptest keeps the test free of any
|
|
// handwritten static cert that could expire.
|
|
func testCertPEM(t *testing.T) string {
|
|
t.Helper()
|
|
srv := httptest.NewTLSServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
cert := srv.Certificate()
|
|
if cert == nil {
|
|
t.Fatal("httptest.NewTLSServer did not expose a certificate")
|
|
}
|
|
return string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
|
|
}
|
|
|
|
func TestLoadCACertPool_Empty(t *testing.T) {
|
|
cfg := &Config{}
|
|
pool, err := cfg.loadCACertPool()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if pool != nil {
|
|
t.Errorf("expected nil pool when no CA source configured, got %v", pool)
|
|
}
|
|
}
|
|
|
|
func TestLoadCACertPool_InlinePEM(t *testing.T) {
|
|
cfg := &Config{CACertPEM: testCertPEM(t)}
|
|
pool, err := cfg.loadCACertPool()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if pool == nil {
|
|
t.Fatal("expected non-nil pool for valid CACertPEM")
|
|
}
|
|
}
|
|
|
|
func TestLoadCACertPool_InlinePEM_Garbage(t *testing.T) {
|
|
cfg := &Config{CACertPEM: "not a pem"}
|
|
pool, err := cfg.loadCACertPool()
|
|
if err == nil {
|
|
t.Fatal("expected error for garbage CACertPEM, got nil")
|
|
}
|
|
if pool != nil {
|
|
t.Errorf("expected nil pool on error, got %v", pool)
|
|
}
|
|
if !strings.Contains(err.Error(), "caCertPEM") {
|
|
t.Errorf("error should name the failing field, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadCACertPool_FilePath(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "ca.pem")
|
|
if err := os.WriteFile(path, []byte(testCertPEM(t)), 0o600); err != nil {
|
|
t.Fatalf("writing temp PEM: %v", err)
|
|
}
|
|
|
|
cfg := &Config{CACertPath: path}
|
|
pool, err := cfg.loadCACertPool()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if pool == nil {
|
|
t.Fatal("expected non-nil pool for valid CACertPath")
|
|
}
|
|
}
|
|
|
|
func TestLoadCACertPool_FilePath_Missing(t *testing.T) {
|
|
cfg := &Config{CACertPath: "/does/not/exist/ca.pem"}
|
|
pool, err := cfg.loadCACertPool()
|
|
if err == nil {
|
|
t.Fatal("expected error for missing CACertPath, got nil")
|
|
}
|
|
if pool != nil {
|
|
t.Errorf("expected nil pool on error, got %v", pool)
|
|
}
|
|
}
|
|
|
|
func TestLoadCACertPool_Combined(t *testing.T) {
|
|
// Both inline and file sources populated — certificates from both should
|
|
// be accepted into the same pool.
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "ca.pem")
|
|
if err := os.WriteFile(path, []byte(testCertPEM(t)), 0o600); err != nil {
|
|
t.Fatalf("writing temp PEM: %v", err)
|
|
}
|
|
|
|
cfg := &Config{CACertPath: path, CACertPEM: testCertPEM(t)}
|
|
pool, err := cfg.loadCACertPool()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if pool == nil {
|
|
t.Fatal("expected non-nil pool when both sources set")
|
|
}
|
|
}
|
|
|
|
func TestSharedTransportPool_ConfigKeyDistinguishesCAAndSkipVerify(t *testing.T) {
|
|
p := GetGlobalTransportPool()
|
|
cfgSystem := DefaultHTTPClientConfig()
|
|
|
|
cfgSkip := DefaultHTTPClientConfig()
|
|
cfgSkip.InsecureSkipVerify = true
|
|
|
|
cfgCustomCA := DefaultHTTPClientConfig()
|
|
pool, err := (&Config{CACertPEM: testCertPEM(t)}).loadCACertPool()
|
|
if err != nil {
|
|
t.Fatalf("loadCACertPool: %v", err)
|
|
}
|
|
cfgCustomCA.RootCAs = pool
|
|
|
|
keys := map[string]string{
|
|
"system": p.configKey(cfgSystem),
|
|
"skip": p.configKey(cfgSkip),
|
|
"customCA": p.configKey(cfgCustomCA),
|
|
}
|
|
seen := make(map[string]string, len(keys))
|
|
for name, key := range keys {
|
|
if dup, ok := seen[key]; ok {
|
|
t.Errorf("configKey collision: %s and %s share key %q", name, dup, key)
|
|
}
|
|
seen[key] = name
|
|
}
|
|
}
|