Files
old-backend/controllers/users_controller.go
2023-11-21 08:00:08 +01:00

179 lines
5.0 KiB
Go

package controllers
import (
"crypto/rand"
"encoding/base64"
"log"
"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.LoginRequest
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
log.Printf("Companies length %v", len(user.Companies))
if len(user.Companies) == 0 {
c.JSON(http.StatusInternalServerError, gin.H{"error": "User is not connected to a company"})
return
}
token, err := usr.CreateSessionToken(user.ID, user.Companies[0].ID)
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"})
}