fix(cabana): enforce tokens_valid_after and is_activated on admin refresh
Fixes review finding CR-01 (quick 260927-q23): POST {prefix}/api/v1/auth/refresh
minted a new token without loading the admin, so a session kept alive by the
SPA's refresh-on-401 survived admin:reset-password, deactivation and deletion.
This broke Phase 9 truth T-09-04.
- bouncer: extract the JWT guard's subject lookup into subjectPrincipal and
issuedBeforeCutoff (same order and messages), add ErrSubjectRejected
- bouncer: add RefreshAudienceFor, which runs the guard's subject checks
after the token-only checks and before minting; Refresh and
RefreshAudience are unchanged (nil hook)
- cabana: share one lazyBackendUsers provider between the backend guard and
refresh; a cookie refresh refused for its subject expires summer_admin
- test: TestAdminRefreshRevocation (Postgres, real admin:reset-password)
This commit is contained in:
@@ -2,6 +2,7 @@ package cabana
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -22,6 +23,20 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// refreshSubjects is a map-backed bouncer.UserProvider for refresh tests
|
||||
// that run without a database; err, when set, is returned for every lookup.
|
||||
type refreshSubjects struct {
|
||||
byID map[uint]*bouncer.Principal
|
||||
err error
|
||||
}
|
||||
|
||||
func (p refreshSubjects) FindByID(_ context.Context, id uint) (*bouncer.Principal, error) {
|
||||
if p.err != nil {
|
||||
return nil, p.err
|
||||
}
|
||||
return p.byID[id], nil
|
||||
}
|
||||
|
||||
// emptyOptionsRow is a filter source whose scope has no choices yet;
|
||||
// literalOptionsRow's choice label is a literal rather than a phrase key.
|
||||
type emptyOptionsRow struct {
|
||||
@@ -281,6 +296,7 @@ func TestPhase10Coverage(t *testing.T) {
|
||||
refreshTTL: 2 * time.Hour,
|
||||
issuer: "https://app.test" + DefaultAdminPrefix,
|
||||
bl: bouncer.NewMemoryBlacklist(),
|
||||
users: refreshSubjects{byID: map[uint]*bouncer.Principal{5: {ID: 5, Backend: true}}},
|
||||
}
|
||||
sign := func(iat, exp time.Time, jti string) string {
|
||||
t.Helper()
|
||||
|
||||
Reference in New Issue
Block a user