mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-12 04:04:26 +00:00
75 lines
2.6 KiB
Go
75 lines
2.6 KiB
Go
package traefikoidc
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync/atomic"
|
|
"testing"
|
|
)
|
|
|
|
// TestSession_ReturnToPool_ExactlyOnce is a regression for ReturnToPool's
|
|
// inverted, non-atomic inUse guard. The old code returned the session only
|
|
// when it was NOT in use — so immediately after GetSession (inUse=true)
|
|
// ReturnToPool did nothing (leaked the object and left activeSessions
|
|
// inflated). The fix mirrors returnToPoolSafely: an atomic
|
|
// compare-and-swap returns the session exactly once. Observable contract:
|
|
// returning one in-use session decrements activeSessions by exactly 1.
|
|
func TestSession_ReturnToPool_ExactlyOnce(t *testing.T) {
|
|
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
oidc := makeBearerOIDC(t, next)
|
|
sm := oidc.sessionManager
|
|
|
|
sd, err := sm.GetSession(httptest.NewRequest("GET", "/", nil))
|
|
if err != nil {
|
|
t.Fatalf("GetSession: %v", err)
|
|
}
|
|
before := atomic.LoadInt64(&sm.activeSessions)
|
|
|
|
sd.ReturnToPool()
|
|
|
|
got := before - atomic.LoadInt64(&sm.activeSessions)
|
|
if got != 1 {
|
|
t.Fatalf("ReturnToPool returned the session %d times, want exactly 1 (leak or double-return)", got)
|
|
}
|
|
}
|
|
|
|
// TestSession_ReauthAfterClear_BalancesPool guards the IdP-initiated-logout
|
|
// re-auth fix: processAuthorizedRequest(RS) called session.Clear (which
|
|
// returns the object to the shared pool) and then reused that same object
|
|
// to write the re-auth challenge — a use-after-return raced with a
|
|
// concurrent GetSession. The fix re-acquires an owned session from the
|
|
// pool instead. This deterministic test locks in that the fixed
|
|
// reacquire-then-return pattern leaves activeSessions exactly balanced
|
|
// (no leak, no double decrement).
|
|
func TestSession_ReauthAfterClear_BalancesPool(t *testing.T) {
|
|
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
oidc := makeBearerOIDC(t, next)
|
|
sm := oidc.sessionManager
|
|
rw := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
|
|
start := atomic.LoadInt64(&sm.activeSessions)
|
|
sd, err := sm.GetSession(req)
|
|
if err != nil {
|
|
t.Fatalf("GetSession: %v", err)
|
|
}
|
|
// Handler branch: Clear returns sd to the pool, then we re-acquire an
|
|
// owned session for the re-auth challenge and return it.
|
|
if err := sd.Clear(req, rw); err != nil {
|
|
t.Fatalf("Clear: %v", err)
|
|
}
|
|
ns, gerr := sm.GetSession(req)
|
|
if gerr != nil {
|
|
t.Fatalf("re-acquire GetSession: %v", gerr)
|
|
}
|
|
ns.returnToPoolSafely()
|
|
|
|
if end := atomic.LoadInt64(&sm.activeSessions); end != start {
|
|
t.Fatalf("activeSessions changed by %d across clear+reacquire (start=%d end=%d)", end-start, start, end)
|
|
}
|
|
}
|