114 lines
3.1 KiB
Go
114 lines
3.1 KiB
Go
|
|
package controllers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"crypto/rand"
|
||
|
|
"encoding/base64"
|
||
|
|
"net/http"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
"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"})
|
||
|
|
}
|