* fix(security): harden auth and credential handling (HH-444) * fix(security): address HH-444 review blockers * fix(security): close remaining HH-444 review blockers --------- Co-authored-by: Rogee <rogee@ipao.vip>
67 lines
2.7 KiB
Go
67 lines
2.7 KiB
Go
package ws
|
|
|
|
import (
|
|
"context"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/gochat/gochat/internal/auth"
|
|
"github.com/gochat/gochat/internal/config"
|
|
"github.com/gochat/gochat/internal/model"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func TestWSAuthenticatorRequiresAndConsumesOneTimeTicket(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
jwtService := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", AccessExpiryMinutes: 15})
|
|
tickets := auth.NewWSTicketStore(nil, time.Minute)
|
|
authenticator := NewWSAuthenticator(jwtService, nil, tickets)
|
|
want := auth.WSTicketClaims{UserID: 7, AccountID: 3, Role: "agent", Provider: "email", ClientID: "browser-session"}
|
|
ticket, err := tickets.Issue(context.Background(), want)
|
|
require.NoError(t, err)
|
|
|
|
ctx := wsTestContext("/cable?ticket=" + ticket)
|
|
claims, err := authenticator.Authenticate(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, want.UserID, claims.UserID)
|
|
require.Equal(t, want.ClientID, claims.ClientID)
|
|
_, err = authenticator.Authenticate(wsTestContext("/cable?ticket=" + ticket))
|
|
require.ErrorIs(t, err, auth.ErrInvalidWSTicket)
|
|
|
|
pair, err := jwtService.GenerateTokenPair(&model.User{Base: model.Base{ID: 7}, Provider: "email"}, 3, "agent")
|
|
require.NoError(t, err)
|
|
_, err = authenticator.Authenticate(wsTestContext("/cable?token=" + pair.AccessToken))
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestWSAuthenticatorRejectsTicketAfterSessionRevocation(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=private"), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&model.User{}, &model.UserSession{}))
|
|
user := &model.User{Name: "Agent", Email: "agent@example.com", Provider: "email", Active: true}
|
|
require.NoError(t, db.Create(user).Error)
|
|
require.NoError(t, db.Create(&model.UserSession{UserID: user.ID, ClientID: "browser-session"}).Error)
|
|
|
|
tickets := auth.NewWSTicketStore(nil, time.Minute)
|
|
ticket, err := tickets.Issue(context.Background(), auth.WSTicketClaims{
|
|
UserID: user.ID, AccountID: 3, Role: "agent", Provider: "email", ClientID: "browser-session",
|
|
})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.Where("user_id = ? AND client_id = ?", user.ID, "browser-session").Delete(&model.UserSession{}).Error)
|
|
|
|
authenticator := NewWSAuthenticator(auth.NewJWTService(&config.JWTConfig{Secret: "test-secret"}), nil, db, tickets)
|
|
_, err = authenticator.Authenticate(wsTestContext("/cable?ticket=" + ticket))
|
|
require.ErrorIs(t, err, auth.ErrInvalidWSTicket)
|
|
}
|
|
|
|
func wsTestContext(target string) *gin.Context {
|
|
ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
|
|
ctx.Request = httptest.NewRequest("GET", target, nil)
|
|
return ctx
|
|
}
|