| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- package session
- import (
- "encoding/gob"
- "net/http"
- "time"
- "github.com/mhsanaei/3x-ui/v3/database"
- "github.com/mhsanaei/3x-ui/v3/database/model"
- "github.com/mhsanaei/3x-ui/v3/logger"
- "github.com/gin-contrib/sessions"
- "github.com/gin-gonic/gin"
- )
- const (
- loginUserKey = "LOGIN_USER"
- loginEpochKey = "LOGIN_EPOCH"
- apiAuthUserKey = "api_auth_user"
- sessionCookieName = "3x-ui"
- )
- func init() {
- gob.Register(model.User{})
- }
- func SetLoginUser(c *gin.Context, user *model.User) error {
- if user == nil {
- return nil
- }
- s := sessions.Default(c)
- s.Set(loginUserKey, user.Id)
- s.Set(loginEpochKey, user.LoginEpoch)
- return s.Save()
- }
- func SetAPIAuthUser(c *gin.Context, user *model.User) {
- if user == nil {
- return
- }
- c.Set(apiAuthUserKey, user)
- }
- func GetLoginUser(c *gin.Context) *model.User {
- if v, ok := c.Get(apiAuthUserKey); ok {
- if u, ok2 := v.(*model.User); ok2 {
- return u
- }
- }
- s := sessions.Default(c)
- obj := s.Get(loginUserKey)
- if obj == nil {
- return nil
- }
- userID, ok := sessionUserID(obj)
- if !ok {
- s.Delete(loginUserKey)
- s.Delete(loginEpochKey)
- if err := s.Save(); err != nil {
- logger.Warning("session: failed to drop stale user payload:", err)
- }
- return nil
- }
- if legacyUserID, ok := legacySessionUserID(obj); ok {
- s.Set(loginUserKey, legacyUserID)
- if err := s.Save(); err != nil {
- logger.Warning("session: failed to migrate legacy user payload:", err)
- }
- }
- user, err := getUserByID(userID)
- if err != nil {
- logger.Warning("session: failed to load user:", err)
- s.Delete(loginUserKey)
- s.Delete(loginEpochKey)
- if saveErr := s.Save(); saveErr != nil {
- logger.Warning("session: failed to drop missing user:", saveErr)
- }
- return nil
- }
- if !sessionEpochMatches(s.Get(loginEpochKey), user.LoginEpoch) {
- s.Delete(loginUserKey)
- s.Delete(loginEpochKey)
- if saveErr := s.Save(); saveErr != nil {
- logger.Warning("session: failed to drop stale epoch:", saveErr)
- }
- return nil
- }
- return user
- }
- func sessionEpochMatches(cookieVal any, userEpoch int64) bool {
- var got int64
- switch v := cookieVal.(type) {
- case nil:
- case int64:
- got = v
- case int:
- got = int64(v)
- case int32:
- got = int64(v)
- case float64:
- got = int64(v)
- default:
- return false
- }
- return got == userEpoch
- }
- func IsLogin(c *gin.Context) bool {
- return GetLoginUser(c) != nil
- }
- func sessionUserID(obj any) (int, bool) {
- switch v := obj.(type) {
- case int:
- return v, v > 0
- case int64:
- return int(v), v > 0
- case int32:
- return int(v), v > 0
- case float64:
- id := int(v)
- return id, v == float64(id) && id > 0
- case model.User:
- return v.Id, v.Id > 0
- case *model.User:
- if v == nil {
- return 0, false
- }
- return v.Id, v.Id > 0
- default:
- return 0, false
- }
- }
- func legacySessionUserID(obj any) (int, bool) {
- switch v := obj.(type) {
- case model.User:
- return v.Id, v.Id > 0
- case *model.User:
- if v == nil {
- return 0, false
- }
- return v.Id, v.Id > 0
- default:
- return 0, false
- }
- }
- func getUserByID(id int) (*model.User, error) {
- db := database.GetDB()
- if db == nil {
- return nil, http.ErrServerClosed
- }
- user := &model.User{}
- if err := db.Model(model.User{}).Where("id = ?", id).First(user).Error; err != nil {
- return nil, err
- }
- return user, nil
- }
- func ClearSession(c *gin.Context) error {
- s := sessions.Default(c)
- s.Clear()
- cookiePath := c.GetString("base_path")
- if cookiePath == "" {
- cookiePath = "/"
- }
- secure := c.Request.TLS != nil
- s.Options(sessions.Options{
- Path: cookiePath,
- MaxAge: -1,
- HttpOnly: true,
- Secure: secure,
- SameSite: http.SameSiteLaxMode,
- })
- if err := s.Save(); err != nil {
- return err
- }
- if cookiePath != "/" {
- http.SetCookie(c.Writer, &http.Cookie{
- Name: sessionCookieName,
- Value: "",
- Path: "/",
- MaxAge: -1,
- Expires: time.Unix(0, 0),
- HttpOnly: true,
- Secure: secure,
- SameSite: http.SameSiteLaxMode,
- })
- }
- return nil
- }
|