Files
gochat/backend/tests/e2e/session_e2e_test.go
T
Rogee 851ca7e372 refactor: 移除 SAML/LDAP/MFA 登录方式,仅保留本地账号密码和 OIDC
后端移除:
- SAML: auth/saml.go, handler/saml_handler.go, account_saml_settings_handler.go,
  model/account_saml_settings.go, model/saml_idp_config.go, repo/*.go
- LDAP: auth/ldap.go, handler/ldap_handler.go, model/account_ldap_settings.go,
  repo/account_ldap_settings_repo.go
- MFA: auth/mfa.go, handler/mfa_handler.go
- auth_service: 移除 mfaService 依赖、MFARequired 字段、LoginWithMFA 方法
- auth_handler: 移除 LoginMFA handler、MFA 分支逻辑
- bootstrap: 移除 SAML/LDAP/MFA service 初始化和 handler 注册
- sso_middleware: 精简为仅支持 OIDC provider
- router: 移除 SAML/LDAP/MFA 路由注册
- config: 移除 SAMLConfig/LDAPConfig struct 和 defaults

前端移除:
- v3/login: 移除 MFA 验证流程和 SAML 登录入口
- v3/api/auth: 移除 MFA 响应处理
- v3/routes: 移除 SSO login 路由
- dashboard: 移除 MFA 设置页面、SAML 安全设置页面
- i18n: 移除 mfa.json
- featureFlags: 移除 SAML feature flag

.env.example / .env: 移除 SAML/LDAP 配置段
2026-07-29 19:03:04 +08:00

279 lines
7.7 KiB
Go

package e2e
import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/gochat/gochat/internal/auth"
"github.com/gochat/gochat/internal/config"
"github.com/gochat/gochat/internal/middleware"
)
// SessionE2ETestSuite tests session store and middleware integration.
type SessionE2ETestSuite struct {
suite.Suite
store *auth.SessionStore
sessionCfg config.SessionConfig
}
func TestSessionE2ETestSuite(t *testing.T) {
suite.Run(t, new(SessionE2ETestSuite))
}
func (s *SessionE2ETestSuite) SetupSuite() {
// E2E tests require PostgreSQL; skip in SQLite test mode.
s.T().Skip("E2E tests require PostgreSQL; skipping in SQLite test mode")
s.sessionCfg = config.SessionConfig{
Enabled: true,
ExpirySeconds: 3600,
TokenLength: 32,
HeaderName: "X-Session-ID",
CleanupInterval: 300,
}
s.store = auth.NewSessionStore(&s.sessionCfg)
}
// --- Session Store Tests ---
func (s *SessionE2ETestSuite) TestSessionCreate() {
session, err := s.store.Create(1, 100, "agent", "email")
assert.NoError(s.T(), err)
assert.NotEmpty(s.T(), session.ID)
assert.Equal(s.T(), uint(1), session.UserID)
assert.Equal(s.T(), uint(100), session.AccountID)
assert.Equal(s.T(), "agent", session.Role)
assert.Equal(s.T(), "email", session.Provider)
assert.False(s.T(), session.CreatedAt.IsZero())
assert.False(s.T(), session.ExpiresAt.IsZero())
}
func (s *SessionE2ETestSuite) TestSessionGet() {
session, err := s.store.Create(2, 200, "administrator", "google")
assert.NoError(s.T(), err)
retrieved, err := s.store.Get(session.ID)
assert.NoError(s.T(), err)
assert.Equal(s.T(), session.ID, retrieved.ID)
assert.Equal(s.T(), uint(2), retrieved.UserID)
}
func (s *SessionE2ETestSuite) TestSessionGetNotFound() {
_, err := s.store.Get("nonexistent-session-id")
assert.Error(s.T(), err)
}
func (s *SessionE2ETestSuite) TestSessionDelete() {
session, err := s.store.Create(3, 300, "agent", "oidc")
assert.NoError(s.T(), err)
err = s.store.Delete(session.ID)
assert.NoError(s.T(), err)
_, err = s.store.Get(session.ID)
assert.Error(s.T(), err)
}
func (s *SessionE2ETestSuite) TestSessionDeleteByUserID() {
s.store.Create(4, 400, "agent", "email")
s.store.Create(4, 500, "agent", "email") // same user, different account
s.store.Create(5, 600, "administrator", "email") // different user
count := s.store.DeleteByUserID(4)
assert.Equal(s.T(), 2, count)
// User 5's session should still exist
assert.Equal(s.T(), 1, s.store.Count())
}
func (s *SessionE2ETestSuite) TestSessionRefresh() {
session, err := s.store.Create(6, 700, "agent", "email")
assert.NoError(s.T(), err)
originalExpiry := session.ExpiresAt
refreshed, err := s.store.Refresh(session.ID)
assert.NoError(s.T(), err)
assert.True(s.T(), refreshed.ExpiresAt.After(originalExpiry))
}
func (s *SessionE2ETestSuite) TestSessionSetGetData() {
session, err := s.store.Create(7, 800, "agent", "email")
assert.NoError(s.T(), err)
err = s.store.SetData(session.ID, "theme", "dark")
assert.NoError(s.T(), err)
val, err := s.store.GetData(session.ID, "theme")
assert.NoError(s.T(), err)
assert.Equal(s.T(), "dark", val)
}
func (s *SessionE2ETestSuite) TestSessionCleanupExpired() {
// Create a session with short expiry
shortCfg := config.SessionConfig{
Enabled: true,
ExpirySeconds: 1, // 1 second
TokenLength: 32,
}
shortStore := auth.NewSessionStore(&shortCfg)
session, err := shortStore.Create(8, 900, "agent", "email")
assert.NoError(s.T(), err)
// Wait for expiry
time.Sleep(2 * time.Second)
count := shortStore.CleanupExpired()
assert.Equal(s.T(), 1, count)
_, err = shortStore.Get(session.ID)
assert.Error(s.T(), err)
}
func (s *SessionE2ETestSuite) TestSessionCount() {
initialCount := s.store.Count()
s.store.Create(9, 1000, "agent", "email")
assert.Equal(s.T(), initialCount + 1, s.store.Count())
}
// --- Session Middleware Tests ---
func (s *SessionE2ETestSuite) TestSessionMiddlewareWithHeader() {
session, err := s.store.Create(10, 1100, "agent", "email")
assert.NoError(s.T(), err)
mwCfg := middleware.DefaultSessionMiddlewareConfig()
router := gin.New()
router.Use(middleware.SessionMiddleware(s.store, mwCfg))
router.GET("/test", func(c *gin.Context) {
sessionID, _ := c.Get("session_id")
c.JSON(200, gin.H{"session_id": sessionID})
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/test", nil)
req.Header.Set("X-Session-ID", session.ID)
router.ServeHTTP(w, req)
assert.Equal(s.T(), 200, w.Code)
}
func (s *SessionE2ETestSuite) TestSessionMiddlewareWithCookie() {
session, err := s.store.Create(11, 1200, "agent", "email")
assert.NoError(s.T(), err)
mwCfg := middleware.DefaultSessionMiddlewareConfig()
router := gin.New()
router.Use(middleware.SessionMiddleware(s.store, mwCfg))
router.GET("/test", func(c *gin.Context) {
sessionID, exists := c.Get("session_id")
if exists {
c.JSON(200, gin.H{"session_id": sessionID})
} else {
c.JSON(200, gin.H{"session_id": nil})
}
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/test", nil)
req.AddCookie(&http.Cookie{Name: "_gochat_session", Value: session.ID})
router.ServeHTTP(w, req)
assert.Equal(s.T(), 200, w.Code)
}
func (s *SessionE2ETestSuite) TestSessionMiddlewareNoSession() {
mwCfg := middleware.DefaultSessionMiddlewareConfig()
router := gin.New()
router.Use(middleware.SessionMiddleware(s.store, mwCfg))
router.GET("/test", func(c *gin.Context) {
_, exists := c.Get("session_id")
c.JSON(200, gin.H{"has_session": exists})
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/test", nil)
router.ServeHTTP(w, req)
assert.Equal(s.T(), 200, w.Code)
}
func (s *SessionE2ETestSuite) TestSessionMiddlewareSkipPath() {
mwCfg := middleware.DefaultSessionMiddlewareConfig()
router := gin.New()
router.Use(middleware.SessionMiddleware(s.store, mwCfg))
router.GET("/api/v1/auth/login", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/v1/auth/login", nil)
router.ServeHTTP(w, req)
assert.Equal(s.T(), 200, w.Code)
}
func (s *SessionE2ETestSuite) TestSessionMiddlewareExpiredSession() {
shortCfg := config.SessionConfig{
Enabled: true,
ExpirySeconds: 1,
TokenLength: 32,
}
shortStore := auth.NewSessionStore(&shortCfg)
session, err := shortStore.Create(12, 1300, "agent", "email")
assert.NoError(s.T(), err)
// Wait for expiry
time.Sleep(2 * time.Second)
mwCfg := middleware.DefaultSessionMiddlewareConfig()
router := gin.New()
router.Use(middleware.SessionMiddleware(shortStore, mwCfg))
router.GET("/test", func(c *gin.Context) {
_, exists := c.Get("session_id")
c.JSON(200, gin.H{"has_session": exists})
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/test", nil)
req.Header.Set("X-Session-ID", session.ID)
router.ServeHTTP(w, req)
assert.Equal(s.T(), 200, w.Code) // doesn't abort, just no session
}
func (s *SessionE2ETestSuite) TestRequireSessionMiddleware() {
router := gin.New()
router.Use(middleware.RequireSession())
router.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
// Without session_id in context → 401
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/test", nil)
router.ServeHTTP(w, req)
assert.Equal(s.T(), 401, w.Code)
// With session_id in context → 200
router2 := gin.New()
router2.Use(func(c *gin.Context) {
c.Set("session_id", "test-session-123")
c.Next()
})
router2.Use(middleware.RequireSession())
router2.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
w2 := httptest.NewRecorder()
req2, _ := http.NewRequest("GET", "/test", nil)
router2.ServeHTTP(w2, req2)
assert.Equal(s.T(), 200, w2.Code)
}