- Added a new handler for completing authentication flows, including session validation and cookie management. - Implemented flow rate limiting to restrict the number of flow start requests per client IP. - Enhanced flow session management with Redis support for persistent session storage. - Updated existing handlers to integrate the new flow completion logic and error handling for various session states. - Introduced unit tests for the new flow completion and rate limiting functionalities to ensure reliability.
75 lines
2.1 KiB
Go
75 lines
2.1 KiB
Go
package authapi
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
func TestValidateFlowSlugAllowed(t *testing.T) {
|
|
t.Parallel()
|
|
for _, slug := range []string{FlowEnrollment, FlowRecovery, FlowAuthentication} {
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodPost, "/flows/"+slug+"/start", nil)
|
|
got, ok := validateFlowSlug(rec, req, slug)
|
|
if !ok || got != slug {
|
|
t.Fatalf("slug %q: ok=%v got=%q code=%d", slug, ok, got, rec.Code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateFlowSlugRejected(t *testing.T) {
|
|
t.Parallel()
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodPost, "/flows/default-invalidation-flow/start", nil)
|
|
_, ok := validateFlowSlug(rec, req, "default-invalidation-flow")
|
|
if ok {
|
|
t.Fatal("expected slug to be rejected")
|
|
}
|
|
if rec.Code != http.StatusNotFound {
|
|
t.Fatalf("status = %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestRespondFlowMissingCookie(t *testing.T) {
|
|
t.Parallel()
|
|
h := NewHandler("http://127.0.0.1:1", "http://localhost:3004", nil)
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodPost, "/flows/ulti-enrollment/respond", bytes.NewReader([]byte(`{"payload":{"component":"x"}}`)))
|
|
rctx := chi.NewRouteContext()
|
|
rctx.URLParams.Add("slug", FlowEnrollment)
|
|
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
|
|
h.RespondFlow(rec, req)
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestBuildLoginRedirectURL(t *testing.T) {
|
|
t.Parallel()
|
|
got := buildLoginRedirectURL("http://localhost:3004", "/mail/inbox")
|
|
want := "http://localhost:3004/api/auth/login?returnTo=%2Fmail%2Finbox"
|
|
if got != want {
|
|
t.Fatalf("got %q want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFlowRateLimiterMemory(t *testing.T) {
|
|
t.Parallel()
|
|
lim := NewFlowRateLimiter(nil)
|
|
req := httptest.NewRequest(http.MethodPost, "/start", nil)
|
|
req.RemoteAddr = "203.0.113.1:1234"
|
|
for i := 0; i < flowRateLimitMax; i++ {
|
|
if !lim.Allow(req) {
|
|
t.Fatalf("attempt %d should be allowed", i+1)
|
|
}
|
|
}
|
|
if lim.Allow(req) {
|
|
t.Fatal("expected rate limit block")
|
|
}
|
|
}
|