mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-06-05 22:44:17 +00:00
168 lines
5.1 KiB
Go
168 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
// stubMiddleware lets us test endpoint wiring without spinning up a full
|
|
// traefikoidc instance. Each test injects the behavior it wants.
|
|
type stubMiddleware struct {
|
|
calls []stubCall
|
|
fn func(rw http.ResponseWriter, req *http.Request)
|
|
}
|
|
|
|
type stubCall struct {
|
|
path string
|
|
header http.Header
|
|
}
|
|
|
|
func (s *stubMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|
s.calls = append(s.calls, stubCall{path: req.URL.Path, header: req.Header.Clone()})
|
|
if s.fn != nil {
|
|
s.fn(rw, req)
|
|
}
|
|
}
|
|
|
|
func TestAuth_RewritesToSentinel_AndConverts302To401(t *testing.T) {
|
|
stub := &stubMiddleware{
|
|
fn: func(rw http.ResponseWriter, req *http.Request) {
|
|
rw.Header().Set("Location", "https://idp.example/authorize?state=abc")
|
|
rw.Header().Add("Set-Cookie", "_oidc_state=abc; Path=/")
|
|
rw.WriteHeader(http.StatusFound)
|
|
},
|
|
}
|
|
h := newAuthHandler(stub)
|
|
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/oauth2/auth", nil)
|
|
req.Header.Set("X-Forwarded-Uri", "/protected/page")
|
|
h.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Fatalf("status: want 401, got %d", rec.Code)
|
|
}
|
|
if len(stub.calls) != 1 || stub.calls[0].path != sentinelPath {
|
|
t.Fatalf("middleware path: want %q, got %v", sentinelPath, stub.calls)
|
|
}
|
|
if rec.Header().Get("X-Auth-Redirect") == "" {
|
|
t.Error("X-Auth-Redirect should carry Location")
|
|
}
|
|
if got := stub.calls[0].header.Get("X-Forwarded-Uri"); got != "/protected/page" {
|
|
t.Errorf("X-Forwarded-Uri must pass through to middleware: want /protected/page, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestAuth_AuthenticatedReturnsHeadersAnd200(t *testing.T) {
|
|
stub := &stubMiddleware{
|
|
fn: func(rw http.ResponseWriter, req *http.Request) {
|
|
// Middleware would stamp X-Forwarded-User on req then call next.
|
|
req.Header.Set("X-Forwarded-User", "alice")
|
|
newSuccessHandler().ServeHTTP(rw, req)
|
|
},
|
|
}
|
|
h := newAuthHandler(stub)
|
|
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/oauth2/auth", nil)
|
|
h.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status: want 200, got %d", rec.Code)
|
|
}
|
|
if got := rec.Header().Get("X-Forwarded-User"); got != "alice" {
|
|
t.Errorf("X-Forwarded-User mirrored: want alice, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestStart_DelegatesWithSentinel_NoInterception(t *testing.T) {
|
|
stub := &stubMiddleware{
|
|
fn: func(rw http.ResponseWriter, req *http.Request) {
|
|
rw.Header().Set("Location", "https://idp.example/authorize")
|
|
rw.WriteHeader(http.StatusFound)
|
|
},
|
|
}
|
|
h := newStartHandler(stub)
|
|
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/oauth2/start?rd=/back", nil)
|
|
h.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusFound {
|
|
t.Fatalf("start: 302 must flow through, got %d", rec.Code)
|
|
}
|
|
if stub.calls[0].path != sentinelPath {
|
|
t.Fatalf("start path rewrite: want %q, got %q", sentinelPath, stub.calls[0].path)
|
|
}
|
|
}
|
|
|
|
func TestStart_ForwardsRdAsXForwardedURI(t *testing.T) {
|
|
stub := &stubMiddleware{
|
|
fn: func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusFound) },
|
|
}
|
|
h := newStartHandler(stub)
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/oauth2/start?rd=/back/here", nil)
|
|
h.ServeHTTP(rec, req)
|
|
if got := stub.calls[0].header.Get("X-Forwarded-Uri"); got != "/back/here" {
|
|
t.Fatalf("?rd should become X-Forwarded-Uri: want /back/here, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestStart_RdQueryWinsOverUpstreamHeader(t *testing.T) {
|
|
stub := &stubMiddleware{
|
|
fn: func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusFound) },
|
|
}
|
|
h := newStartHandler(stub)
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/oauth2/start?rd=/explicit", nil)
|
|
req.Header.Set("X-Forwarded-Uri", "/ambient")
|
|
h.ServeHTTP(rec, req)
|
|
if got := stub.calls[0].header.Get("X-Forwarded-Uri"); got != "/explicit" {
|
|
t.Fatalf("?rd= must win over upstream X-Forwarded-Uri: want /explicit, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestCallback_RewritesToConfiguredCallbackURL(t *testing.T) {
|
|
var seenPath, seenQuery string
|
|
stub := &stubMiddleware{
|
|
fn: func(rw http.ResponseWriter, req *http.Request) {
|
|
seenPath = req.URL.Path
|
|
seenQuery = req.URL.RawQuery
|
|
rw.WriteHeader(http.StatusOK)
|
|
},
|
|
}
|
|
h := newCallbackHandler(stub, "/oauth2/callback")
|
|
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/oauth2/callback?code=abc&state=xyz", nil)
|
|
h.ServeHTTP(rec, req)
|
|
|
|
if seenPath != "/oauth2/callback" {
|
|
t.Fatalf("callback path: want /oauth2/callback, got %q", seenPath)
|
|
}
|
|
if seenQuery != "code=abc&state=xyz" {
|
|
t.Fatalf("callback query must survive rewrite: want code=abc&state=xyz, got %q", seenQuery)
|
|
}
|
|
}
|
|
|
|
func TestLogout_RewritesToConfiguredLogoutURL(t *testing.T) {
|
|
var seenPath string
|
|
stub := &stubMiddleware{
|
|
fn: func(rw http.ResponseWriter, req *http.Request) {
|
|
seenPath = req.URL.Path
|
|
rw.WriteHeader(http.StatusOK)
|
|
},
|
|
}
|
|
h := newLogoutHandler(stub, "/oauth2/logout")
|
|
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodPost, "/oauth2/logout", nil)
|
|
h.ServeHTTP(rec, req)
|
|
|
|
if seenPath != "/oauth2/logout" {
|
|
t.Fatalf("logout path: want /oauth2/logout, got %q", seenPath)
|
|
}
|
|
}
|