Boot runs before summer serve publishes the database, so the plugin's GORM hooks, the Postgres jwt_blacklist store and its sweep now go through lagoon.OnDatabase, and the jwt guard resolves the published store on every request. A logged-out token is refused on jwt.auth, /fetch and /refresh. The jwt guard reads the cookie names from classes.SessionCookieNames().
87 lines
2.7 KiB
Go
87 lines
2.7 KiB
Go
package user
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/bouncer"
|
|
)
|
|
|
|
// TestAppBlacklist: the jwt guard's store resolves the app's published
|
|
// bouncer.BlacklistStore on every call. With none published (or only an
|
|
// appBlacklist, which would recurse) no token is blacklisted and Add and
|
|
// Sweep do nothing; otherwise calls are delegated and a store error comes
|
|
// back unchanged, so the guard still fails closed.
|
|
func TestAppBlacklist(t *testing.T) {
|
|
ctx := t.Context()
|
|
now := time.Now()
|
|
newApp := func() *appBlacklist {
|
|
return &appBlacklist{app: sessionUserApp(t, "SUMMER_GOLEM15__USER__JWT__SECRET="+sessionSecret)}
|
|
}
|
|
assertNoStore := func(t *testing.T, bl *appBlacklist) {
|
|
t.Helper()
|
|
if got, err := bl.IsBlacklisted(ctx, "jti-none"); got || err != nil {
|
|
t.Fatalf("IsBlacklisted with no store = %v, %v", got, err)
|
|
}
|
|
if err := bl.Add(ctx, "jti-none", now.Add(time.Hour), now); err != nil {
|
|
t.Fatalf("Add with no store = %v", err)
|
|
}
|
|
if err := bl.Sweep(ctx, now); err != nil {
|
|
t.Fatalf("Sweep with no store = %v", err)
|
|
}
|
|
}
|
|
|
|
t.Run("no store", func(t *testing.T) {
|
|
assertNoStore(t, newApp())
|
|
})
|
|
|
|
t.Run("wrapper published as the store", func(t *testing.T) {
|
|
bl := newApp()
|
|
if err := bl.app.Publish[bouncer.BlacklistStore](&appBlacklist{app: bl.app}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assertNoStore(t, bl)
|
|
})
|
|
|
|
t.Run("delegates", func(t *testing.T) {
|
|
bl := newApp()
|
|
mem := bouncer.NewMemoryBlacklist()
|
|
if err := bl.app.Publish[bouncer.BlacklistStore](mem); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Blacklisted from now on, kept for an hour.
|
|
if err := bl.Add(ctx, "jti-ended", now.Add(time.Hour), now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, err := mem.IsBlacklisted(ctx, "jti-ended"); !got || err != nil {
|
|
t.Fatalf("memory store after Add through the wrapper = %v, %v", got, err)
|
|
}
|
|
if got, err := bl.IsBlacklisted(ctx, "jti-ended"); !got || err != nil {
|
|
t.Fatalf("IsBlacklisted(jti-ended) = %v, %v", got, err)
|
|
}
|
|
if got, err := bl.IsBlacklisted(ctx, "jti-other"); got || err != nil {
|
|
t.Fatalf("IsBlacklisted(jti-other) = %v, %v", got, err)
|
|
}
|
|
if err := bl.Sweep(ctx, now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
|
|
t.Run("store error passes through", func(t *testing.T) {
|
|
bl := newApp()
|
|
if err := bl.app.Publish[bouncer.BlacklistStore](failingBlacklist{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := bl.IsBlacklisted(ctx, "jti"); !errors.Is(err, errBlacklistDown) {
|
|
t.Fatalf("IsBlacklisted error = %v", err)
|
|
}
|
|
if err := bl.Add(ctx, "jti", now.Add(time.Hour), now); !errors.Is(err, errBlacklistDown) {
|
|
t.Fatalf("Add error = %v", err)
|
|
}
|
|
if err := bl.Sweep(ctx, now); !errors.Is(err, errBlacklistDown) {
|
|
t.Fatalf("Sweep error = %v", err)
|
|
}
|
|
})
|
|
}
|