package middleware import ( "context" "fmt" "strings" "time" "github.com/perfect-panel/server/pkg/constant" "github.com/perfect-panel/server/pkg/logger" "github.com/gin-gonic/gin" "github.com/perfect-panel/server/internal/config" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/pkg/jwt" "github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/tool" "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" ) func AuthMiddleware(svc *svc.ServiceContext) func(c *gin.Context) { return func(c *gin.Context) { if !authenticateRequest(c, svc, c.GetHeader("Authorization"), true) { return } c.Next() } } func OptionalAuthMiddleware(svc *svc.ServiceContext) func(c *gin.Context) { return func(c *gin.Context) { token := c.GetHeader("Authorization") if token == "" { c.Next() return } if !authenticateRequest(c, svc, token, false) { return } c.Next() } } func authenticateRequest(c *gin.Context, svc *svc.ServiceContext, token string, requireToken bool) bool { ctx := c.Request.Context() if token == "" { if requireToken { logger.WithContext(c.Request.Context()).Debug("[AuthMiddleware] Token Empty") result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.ErrorTokenEmpty), "Token Empty")) c.Abort() } return !requireToken } claims, err := jwt.ParseJwtToken(token, svc.Config.JwtAuth.AccessSecret) if err != nil { logger.WithContext(c.Request.Context()).Debug("[AuthMiddleware] ParseJwtToken", logger.Field("error", err.Error()), logger.Field("token", token)) result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.ErrorTokenExpire), "Token Invalid")) c.Abort() return false } loginType := parseLoginType(claims) if claims["identifier"] != nil { ctx = context.WithValue(ctx, constant.CtxKeyIdentifier, claims["identifier"].(string)) } userId := int64(claims["UserId"].(float64)) sessionId := claims["SessionId"].(string) sessionIdCacheKey := fmt.Sprintf("%v:%v", config.SessionIdKey, sessionId) value, err := svc.Redis.Get(c, sessionIdCacheKey).Result() if err != nil { logger.WithContext(c.Request.Context()).Debug("[AuthMiddleware] Redis Get", logger.Field("error", err.Error()), logger.Field("sessionId", sessionId)) result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")) c.Abort() return false } if value != fmt.Sprintf("%v", userId) { logger.WithContext(c.Request.Context()).Debug("[AuthMiddleware] Invalid Access", logger.Field("userId", userId), logger.Field("sessionId", sessionId)) result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")) c.Abort() return false } svc.Redis.Expire(c, sessionIdCacheKey, time.Duration(svc.Config.JwtAuth.AccessExpire)*time.Second) userInfo, err := svc.UserModel.FindOne(c, userId) if err != nil { logger.WithContext(c.Request.Context()).Debug("[AuthMiddleware] UserModel FindOne", logger.Field("error", err.Error()), logger.Field("userId", userId)) result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Database Query Error")) c.Abort() return false } paths := strings.Split(c.Request.URL.Path, "/") if tool.StringSliceContains(paths, "admin") && !*userInfo.IsAdmin { logger.WithContext(c.Request.Context()).Debug("[AuthMiddleware] Not Admin User", logger.Field("userId", userId), logger.Field("sessionId", sessionId)) result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")) c.Abort() return false } ctx = context.WithValue(ctx, constant.CtxLoginType, loginType) ctx = context.WithValue(ctx, constant.CtxKeyUser, userInfo) ctx = context.WithValue(ctx, constant.CtxKeySessionID, sessionId) c.Request = c.Request.WithContext(ctx) return true } func parseLoginType(claims map[string]interface{}) string { if raw, exists := claims["CtxLoginType"]; exists { if loginType, ok := raw.(string); ok && loginType != "" { return loginType } } if raw, exists := claims["LoginType"]; exists { if loginType, ok := raw.(string); ok && loginType != "" { return loginType } } return "" }