Block guest-role tokens from online payment routes (RequireNonGuest middleware)
Guests (account_role='guest') have no login flow and never receive a JWT normally, so this is defense-in-depth: any token whose role claim is 'guest' (forged/minted guest tokens or future changes) is refused 403 before the money handlers run. The check reads role from context ONLY — real guests are seeded with account_type='email', so account_type is never the discriminator. A missing role passes through (RequireAuth guarantees presence; same trust model as isVerifiedRole). Wired onto all user-facing money routes: booking payment, apply-redemption, payment-lock POST/DELETE, tip, gift-card redeem and gift-card buy. Admin and till routes (RequireAdmin) are untouched — admin can never be guest. The payment-methods routes gain RequireVerified (verified_email, admin) alongside, so only verified accounts can manage saved cards. Tests: 6 middleware tests (reject guest, allow verified/unverified/admin/ affiliate/missing-role) + 4 integration tests (guest 403 on booking payment with zero side effects, tip, gift-card buy; verified user still pays 200).
This commit is contained in:
@@ -307,6 +307,81 @@ func TestRequireVerified_Unverified(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// RequireNonGuest (blocks account_role='guest' tokens from money routes)
|
||||
// =============================================================================
|
||||
|
||||
func TestRequireNonGuest_AllowsVerifiedEmail(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
ctx := context.WithValue(req.Context(), UserRoleKey, "verified_email")
|
||||
req = req.WithContext(ctx)
|
||||
w := httptest.NewRecorder()
|
||||
RequireNonGuest(testHandler()).ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected 200 for verified_email, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireNonGuest_AllowsUnverifiedEmail(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
ctx := context.WithValue(req.Context(), UserRoleKey, "unverified_email")
|
||||
req = req.WithContext(ctx)
|
||||
w := httptest.NewRecorder()
|
||||
RequireNonGuest(testHandler()).ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected 200 for unverified_email, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireNonGuest_AllowsAdmin(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
ctx := context.WithValue(req.Context(), UserRoleKey, "admin")
|
||||
req = req.WithContext(ctx)
|
||||
w := httptest.NewRecorder()
|
||||
RequireNonGuest(testHandler()).ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected 200 for admin, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireNonGuest_AllowsAffiliate(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
ctx := context.WithValue(req.Context(), UserRoleKey, "affiliate")
|
||||
req = req.WithContext(ctx)
|
||||
w := httptest.NewRecorder()
|
||||
RequireNonGuest(testHandler()).ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected 200 for affiliate, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireNonGuest_RejectsGuest(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
ctx := context.WithValue(req.Context(), UserRoleKey, "guest")
|
||||
req = req.WithContext(ctx)
|
||||
w := httptest.NewRecorder()
|
||||
RequireNonGuest(testHandler()).ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusForbidden {
|
||||
t.Errorf("expected 403 for guest, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireNonGuest_NoRoleInContext(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
// No UserRoleKey in context — treated as not-guest, passes through
|
||||
w := httptest.NewRecorder()
|
||||
RequireNonGuest(testHandler()).ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected 200 when no role in context, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// RequireAdmin (RequireRole("admin") + UserIDKey guard)
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user