package cabana import ( "context" "errors" "net/http" "net/http/httptest" "testing" "git.golem15.com/golem15/summercms/modules/backpack" "git.golem15.com/golem15/summercms/modules/bouncer" ) // TestDispatchMarkerSmoke covers ResolveBackend with the in-process dispatch // marker (phase 19.7 D-17): a backend marker is honoured without a bearer or // a published resolver, a frontend marker is refused, and a scoped marker is // still checked against the cover codes. func TestDispatchMarkerSmoke(t *testing.T) { app := backpack.New(nil) withMarker := func(p *bouncer.Principal) *http.Request { r := httptest.NewRequest(http.MethodGet, "/acme/posts", nil) marker := &bouncer.Dispatch{Guards: []string{"backend"}, Principal: p, Credential: "cred"} return r.WithContext(bouncer.WithDispatch(r.Context(), marker)) } // A backend marker: the principal is returned and stored on the context. r, principal, err := ResolveBackend(app, withMarker(&bouncer.Principal{ID: 42, Backend: true}), "acme.*") if err != nil || principal == nil || principal.ID != 42 { t.Fatalf("backend marker: %+v, %v", principal, err) } if got, ok := bouncer.User(r.Context()); !ok || got.ID != 42 { t.Fatalf("principal not stored on the returned request: %+v, %v", got, ok) } if cred, ok := bouncer.Credential(r.Context()); !ok || cred != "cred" { t.Fatalf("credential not stored on the returned request: %v, %v", cred, ok) } // A frontend marker (Backend false) is never a backend principal. if _, _, err := ResolveBackend(app, withMarker(&bouncer.Principal{ID: 7}), "acme.*"); !errors.Is(err, ErrUnauthenticated) { t.Fatalf("frontend marker: err = %v, want ErrUnauthenticated", err) } // A scoped marker whose scope does not cover acme.* is refused as a // scoped token would be; one that covers it passes. scoped := &bouncer.Principal{ID: 42, Backend: true, Scope: &bouncer.ScopeLimit{Scopes: [][]string{{"other.*"}}}} if _, _, err := ResolveBackend(app, withMarker(scoped), "acme.*"); !errors.Is(err, ErrTokenScope) { t.Fatalf("scoped marker: err = %v, want ErrTokenScope", err) } covering := &bouncer.Principal{ID: 42, Backend: true, Scope: &bouncer.ScopeLimit{Scopes: [][]string{{"acme.*"}}}} if _, _, err := ResolveBackend(app, withMarker(covering), "acme.*"); err != nil { t.Fatalf("covering scoped marker: %v", err) } // Without a marker the request still needs a bearer; a forged header is // not a marker. plain := httptest.NewRequest(http.MethodGet, "/acme/posts", nil) plain.Header.Set("X-Summer-Dispatch", "42") if _, _, err := ResolveBackend(app, plain, "acme.*"); !errors.Is(err, ErrUnauthenticated) { t.Fatalf("no marker: err = %v, want ErrUnauthenticated", err) } // The additive resolvers answer unauthenticated on an app without cabana. if _, _, _, err := ResolveBackendToken(context.Background(), app, "g15_0000000000000000000000000000000000000000"); !errors.Is(err, ErrUnauthenticated) { t.Fatalf("ResolveBackendToken without cabana: %v", err) } if limit, known, methodOK := ScopeLimitFor(app, []string{"*"}, http.MethodGet); limit != nil || known != 0 || methodOK != 0 { t.Fatalf("ScopeLimitFor without cabana = %v, %d, %d", limit, known, methodOK) } if _, err := BackendPrincipal(context.Background(), app, 1); !errors.Is(err, ErrUnauthenticated) { t.Fatalf("BackendPrincipal without cabana: %v", err) } }