- 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.
86 lines
2.4 KiB
Go
86 lines
2.4 KiB
Go
package authentik
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestFlowExecutorGetChallenge(t *testing.T) {
|
|
t.Parallel()
|
|
fe, err := NewFlowExecutor("http://localhost:9000", "test-flow")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if fe.slug != "test-flow" {
|
|
t.Fatalf("slug = %q", fe.slug)
|
|
}
|
|
}
|
|
|
|
func TestFlowDone(t *testing.T) {
|
|
t.Parallel()
|
|
done, denied := FlowDone(FlowChallenge{"component": "xak-flow-redirect"})
|
|
if !done || denied {
|
|
t.Fatalf("redirect: done=%v denied=%v", done, denied)
|
|
}
|
|
done, denied = FlowDone(FlowChallenge{"component": "ak-stage-access-denied"})
|
|
if !done || !denied {
|
|
t.Fatalf("denied: done=%v denied=%v", done, denied)
|
|
}
|
|
done, denied = FlowDone(FlowChallenge{"component": "ak-stage-prompt"})
|
|
if done || denied {
|
|
t.Fatalf("prompt: done=%v denied=%v", done, denied)
|
|
}
|
|
}
|
|
|
|
func TestFlowSessionStoreLifecycle(t *testing.T) {
|
|
t.Parallel()
|
|
store := NewFlowSessionStore("http://127.0.0.1:1", nil)
|
|
_, err := store.Respond(context.Background(), "missing", "ulti-enrollment", "", map[string]any{"component": "x"})
|
|
if err != ErrFlowSessionNotFound {
|
|
t.Fatalf("expected ErrFlowSessionNotFound, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCookieRoundTrip(t *testing.T) {
|
|
t.Parallel()
|
|
fe, err := NewFlowExecutor("http://localhost/auth", "test-flow")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fe.ImportCookies([]SerializedCookie{
|
|
{Name: "authentik_csrf", Value: "abc123", Path: "/auth/", SameSiteName: "Lax", HTTPOnly: true},
|
|
{Name: "authentik_session", Value: "sess", Path: "/auth/", SameSiteName: "Lax", HTTPOnly: true},
|
|
})
|
|
exported := fe.ExportCookies()
|
|
if len(exported) != 2 {
|
|
t.Fatalf("exported %d cookies", len(exported))
|
|
}
|
|
fe2, err := RestoreFlowExecutor("http://localhost/auth", "test-flow", exported)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if fe2.csrfToken() != "abc123" {
|
|
t.Fatalf("csrf = %q", fe2.csrfToken())
|
|
}
|
|
browser := BrowserAuthentikCookies(exported)
|
|
if len(browser) != 2 || browser[0].Path != "/auth/" {
|
|
t.Fatalf("browser cookies: %+v", browser)
|
|
}
|
|
}
|
|
|
|
func TestFlowSessionCompleteRequiresDone(t *testing.T) {
|
|
t.Parallel()
|
|
store := NewFlowSessionStore("http://127.0.0.1:1", nil)
|
|
store.mu.Lock()
|
|
store.items["sess1"] = &flowSessionEntry{
|
|
slug: "default-authentication-flow",
|
|
createdAt: time.Now(),
|
|
completed: false,
|
|
}
|
|
store.mu.Unlock()
|
|
_, err := store.CompleteSession(context.Background(), "sess1", "default-authentication-flow")
|
|
if err != ErrFlowSessionNotCompleted {
|
|
t.Fatalf("expected ErrFlowSessionNotCompleted, got %v", err)
|
|
}
|
|
} |