mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-30 06:16:37 +08:00
ae3b792e16
- add util.Go with panic recovery for background goroutines - add util.EscapeLike and explicit ESCAPE clause for SQL LIKE queries - add DummyCheckPassword and subtle.ConstantTimeCompare against timing attacks - enforce session ID rotation upon login/oauth callback to prevent session fixation - add sliding window login failure rate limiting and oauth state rate limiting - fix redis client capture race in pubsub listeners and wait on stop channel - adjust global --primary to oklch(51.1% 0.262 276.966) for WCAG AA contrast - fix semantic heading levels and missing aria-labels across UI components - document security, concurrency, and a11y standards in AGENTS.md
102 lines
2.4 KiB
Go
102 lines
2.4 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package oauth
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
|
|
"github.com/Rain-kl/Wavelet/internal/infra/config"
|
|
"github.com/Rain-kl/Wavelet/internal/model"
|
|
"github.com/Rain-kl/Wavelet/internal/repository"
|
|
"github.com/gin-contrib/sessions"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
gsessions "github.com/gorilla/sessions"
|
|
)
|
|
|
|
// GetUserIDFromSession 从 Session 中提取用户 ID
|
|
func GetUserIDFromSession(s sessions.Session) uint64 {
|
|
userID, ok := s.Get(UserIDKey).(uint64)
|
|
if !ok {
|
|
return 0
|
|
}
|
|
return userID
|
|
}
|
|
|
|
// GetUserIDFromContext 从 Gin Context 的 Session 中提取用户 ID
|
|
func GetUserIDFromContext(c *gin.Context) uint64 {
|
|
session := sessions.Default(c)
|
|
return GetUserIDFromSession(session)
|
|
}
|
|
|
|
func ensureSessionToken(s sessions.Session) (string, bool) {
|
|
token, ok := s.Get(SessionTokenKey).(string)
|
|
if !ok || token == "" {
|
|
token = uuid.NewString()
|
|
s.Set(SessionTokenKey, token)
|
|
return token, true
|
|
}
|
|
return token, false
|
|
}
|
|
|
|
func hashSessionToken(token string) string {
|
|
h := sha256.New()
|
|
h.Write([]byte(token))
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|
|
|
|
func rotateSessionID(s sessions.Session) {
|
|
if inner, ok := s.(interface{ Session() *gsessions.Session }); ok {
|
|
if sess := inner.Session(); sess != nil {
|
|
sess.ID = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
// SetLoginSession writes the authenticated user into a freshly rotated session.
|
|
func SetLoginSession(ctx context.Context, c *gin.Context, user *model.User, extras ...map[string]any) error {
|
|
session := sessions.Default(c)
|
|
session.Clear()
|
|
rotateSessionID(session)
|
|
|
|
session.Set(UserIDKey, user.ID)
|
|
session.Set(UserNameKey, user.Username)
|
|
session.Set(PasswordHashKey, user.Password)
|
|
if len(extras) > 0 {
|
|
for key, value := range extras[0] {
|
|
session.Set(key, value)
|
|
}
|
|
}
|
|
|
|
// 根据系统配置动态设置 Session 过期时间
|
|
maxAge := config.Config.App.SessionAge
|
|
isSessionCookie := false
|
|
|
|
ttlHours, err := repository.GetIntByKey(ctx, model.ConfigKeyLoginSessionTTLHours)
|
|
if err == nil {
|
|
switch {
|
|
case ttlHours == -1:
|
|
// 永不过期,设置为 10 年
|
|
maxAge = 10 * 365 * 24 * 3600
|
|
case ttlHours > 0:
|
|
maxAge = ttlHours * 3600
|
|
case ttlHours == 0:
|
|
isSessionCookie = true
|
|
}
|
|
}
|
|
session.Options(GetSessionOptions(maxAge))
|
|
|
|
if err := session.Save(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if isSessionCookie {
|
|
StripCookieMaxAgeAndExpires(c.Writer.Header(), config.Config.App.SessionCookieName)
|
|
}
|
|
|
|
return nil
|
|
}
|