package controllers import ( "crypto/rand" "encoding/base64" "net/http" "strings" "github.com/gin-gonic/gin" "gitlab.com/pactual1/backend/database/user" usr "gitlab.com/pactual1/backend/database/user" "gitlab.com/pactual1/backend/models" "gitlab.com/pactual1/backend/services/messaging" "gitlab.com/pactual1/backend/shared" "golang.org/x/crypto/bcrypt" ) func ResetPassword(c *gin.Context) { var req models.ResetPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } dbUser, err := user.GetUserByEmail(req.Email) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } resetToken, err := GenerateResetToken() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } err = user.SaveResetTokenToDB(dbUser.ID, resetToken) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } subject := "Password Reset Request" body := "Here is your password reset link: https://pactualdev.com/setNewPassword?token=" + resetToken email := models.EmailNotification{Body: body, Subject: subject, Email: dbUser.Email} go func(email models.EmailNotification) { emailChannel := messaging.GetEmailChannel() emailChannel <- email }(email) c.JSON(http.StatusOK, gin.H{"message": "Reset email sent"}) } func GenerateResetToken() (string, error) { // Generate 32 random bytes (256 bits) randomBytes := make([]byte, 32) _, err := rand.Read(randomBytes) if err != nil { return "", err // return an error if there was one } // Encode the random bytes into a URL-safe base64 string resetToken := base64.URLEncoding.EncodeToString(randomBytes) return resetToken, nil } func UpdatePassword(c *gin.Context) { var req models.UpdatePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Find the PasswordTokens entry var passwordToken models.PasswordTokens if err := shared.GetDb().Where("token = ?", req.Token).First(&passwordToken).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Invalid token"}) return } // Find the associated User var user models.User if err := shared.GetDb().Where("id = ?", passwordToken.UserID).First(&user).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } // Hash the password before saving it hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Update the user's password and set them as active user.Password = string(hashedPassword) user.IsActive = true if err := shared.GetDb().Save(&user).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Delete the PasswordTokens entry if err := shared.GetDb().Delete(&passwordToken).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Password updated successfully"}) } func Login(c *gin.Context) { var req models.User if err := c.BindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"}) return } user, err := usr.GetUserByEmail(req.Email) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"}) return } if usr.CheckPassword(user.Password, req.Password) { if user.IsActive && user.LoginAttempts < 10 { // Proceed with creating JWT token and resetting login attempts token, err := usr.CreateSessionToken(user.ID, user.CompanyID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create JWT token"}) return } usr.ResetLoginAttempts(*user) c.JSON(http.StatusOK, gin.H{"token": token}) } else { c.JSON(http.StatusForbidden, gin.H{"error": "Account locked or too many attempts"}) } } else { // Wrong password, increment login attempts usr.IncrementLoginAttempts(*user) c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"}) } } func Logout(c *gin.Context) { // Extract the token from the request, typically from the Authorization header tokenString := c.GetHeader("Authorization") // If using a Bearer token, strip the 'Bearer ' prefix if len(tokenString) > 7 && strings.ToUpper(tokenString[0:7]) == "BEARER " { tokenString = tokenString[7:] } // Invalidate the session token err := usr.InvalidateSessionToken(tokenString) if err != nil { // Handle error, could be not found or database error c.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to logout"}) return } // Respond with success c.JSON(http.StatusOK, gin.H{"message": "Successfully logged out"}) }