From 7704fa9ecb1e67e7c57ecd631c40cb5021f452a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Z=C3=BCrcher?= Date: Sat, 8 Nov 2025 11:27:45 +0100 Subject: [PATCH] new user expiration with user date format and never option --- handlers/login.go | 91 ++++++++++++++++++++++------------------------- models/user.go | 31 ++++++++-------- 2 files changed, 56 insertions(+), 66 deletions(-) diff --git a/handlers/login.go b/handlers/login.go index 34534c1..cd81738 100644 --- a/handlers/login.go +++ b/handlers/login.go @@ -52,39 +52,18 @@ func (aH *AccessHandler) Login(c *gin.Context) { } // Fetch user record from DB - var dbRecord []models.User - err := aH.dbHandler.GetByKey(&dbRecord, "user_name", user.Name, false) - if err != nil { - aH.logger.Error("Login", err) - c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err)) - return - } - - if len(dbRecord) == 0 { - aH.logger.Error("Login", "no user "+user.Name+" found") - c.JSON(http.StatusUnauthorized, models.NewJsonMessageResponse("invalid credentials")) - return - } - - if len(dbRecord) > 1 { - aH.logger.Error("Login", "more than one record found") - c.JSON(http.StatusInternalServerError, models.NewJsonMessageResponse("internal error")) - return - } - - if !dbRecord[0].ExpirationIsValid() { - aH.logger.Error("Login", fmt.Sprintf("user %s is expired", user.Name)) - c.JSON(http.StatusUnauthorized, models.NewJsonMessageResponse("user "+user.Name+" is expired")) + dbUser, hasError := aH.getUserFromDB(c, user.Name) + if hasError { return } // Check password - if !utils.CheckPassword(user.Password, dbRecord[0].Password) { + if !utils.CheckPassword(user.Password, dbUser.Password) { aH.logger.Error("Login", "invalid password") c.JSON(http.StatusUnauthorized, models.NewJsonMessageResponse("invalid credentials")) return } - user = dbRecord[0] + user = dbUser // ----------------------------- // 🔑 TOKEN CREATION @@ -96,22 +75,20 @@ func (aH *AccessHandler) Login(c *gin.Context) { // Create access token accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "id": user.Id, - "username": user.Name, - "role": user.Role, - "type": "access", - "exp": accessTokenExp.Unix(), - "userExpiration": user.Expiration, + "id": user.Id, + "username": user.Name, + "role": user.Role, + "type": "access", + "exp": accessTokenExp.Unix(), }) // Create refresh token refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "id": user.Id, - "username": user.Name, - "role": user.Role, - "type": "refresh", - "exp": refreshTokenExp.Unix(), - "userExpiration": user.GetExpiration(), + "id": user.Id, + "username": user.Name, + "role": user.Role, + "type": "refresh", + "exp": refreshTokenExp.Unix(), }) // Sign tokens @@ -189,10 +166,8 @@ func (aH *AccessHandler) Refresh(c *gin.Context) { id := int(claims["id"].(float64)) role := claims["role"].(string) - if !expirationDateValid(claims["userExpiration"].(string)) { - aH.Logout(c) - aH.logger.Error("Refresh", fmt.Sprintf("user %s is expired", username)) - c.JSON(http.StatusUnauthorized, models.NewJsonMessageResponse("user "+username+" is expired")) + _, hasError := aH.getUserFromDB(c, username) + if hasError { return } @@ -262,15 +237,33 @@ func (aH *AccessHandler) Logout(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "logged out"}) } -func expirationDateValid(expiration string) bool { - if expiration == "" { - return true // No expiration = always valid - } - - t, err := time.Parse(time.RFC3339, expiration) +func (aH *AccessHandler) getUserFromDB(c *gin.Context, userName string) (user models.User, hasError bool) { + hasError = true + // Fetch user record from DB + var dbRecord []models.User + err := aH.dbHandler.GetByKey(&dbRecord, "user_name", userName, false) if err != nil { - return false // Invalid date format + aH.logger.Error("Login", err) + c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err)) + return } - return time.Now().Before(t) + if len(dbRecord) == 0 { + aH.logger.Error("Login", "no user "+userName+" found") + c.JSON(http.StatusUnauthorized, models.NewJsonMessageResponse("invalid credentials")) + return + } + + if len(dbRecord) > 1 { + aH.logger.Error("Login", "more than one record found") + c.JSON(http.StatusInternalServerError, models.NewJsonMessageResponse("internal error")) + return + } + + if !dbRecord[0].ExpirationIsValid() { + aH.logger.Error("Login", fmt.Sprintf("user %s is expired", userName)) + c.JSON(http.StatusUnauthorized, models.NewJsonMessageResponse("user "+userName+" is expired")) + return + } + return dbRecord[0], false } diff --git a/models/user.go b/models/user.go index 8e59b7f..4253174 100644 --- a/models/user.go +++ b/models/user.go @@ -1,15 +1,17 @@ package models -import "time" +import ( + "time" +) type User struct { - Id uint `gorm:"primaryKey" json:"id"` - Name string `gorm:"column:user_name" json:"user"` - Email string `gorm:"column:email" json:"email"` - Role string `gorm:"column:role" json:"role,omitempty"` - Password string `gorm:"column:password" json:"password"` - Expiration *time.Time `gorm:"column:expiration" json:"expiration,omitempty"` - Settings Settings `gorm:"type:json" json:"settings"` + Id uint `gorm:"primaryKey" json:"id"` + Name string `gorm:"column:user_name" json:"user"` + Email string `gorm:"column:email" json:"email"` + Role string `gorm:"column:role" json:"role,omitempty"` + Password string `gorm:"column:password" json:"password"` + Expiration string `gorm:"column:expiration" json:"expiration,omitempty"` + Settings Settings `gorm:"type:json" json:"settings"` } func (u *User) IsValid() bool { @@ -17,15 +19,10 @@ func (u *User) IsValid() bool { } func (u *User) ExpirationIsValid() bool { - if u.Expiration == nil { + if u.Expiration == "" || u.Expiration == "never" { return true } - return time.Now().Before(*u.Expiration) -} - -func (u *User) GetExpiration() string { - if u.Expiration == nil { - return "" - } - return u.Expiration.Format(time.RFC3339) + loc := time.Now().Location() + parsedTime, _ := time.ParseInLocation("2006-01-02 15:04:05", u.Expiration, loc) + return parsedTime.After(time.Now()) }