package handlers import ( "net/http" "time" "git.workaround.org/chaas/imc/backend/internal/auth" "git.workaround.org/chaas/imc/backend/internal/db" "github.com/gin-gonic/gin" ) const MaxLoginAttempts = 5 type AuthHandler struct { db *db.DB jwtManager *auth.JWTManager trustedProxies []string } func NewAuthHandler(database *db.DB, jwtManager *auth.JWTManager, trustedProxies []string) *AuthHandler { return &AuthHandler{ db: database, jwtManager: jwtManager, trustedProxies: trustedProxies, } } type LoginRequest struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` } type ChangePasswordRequest struct { OldPassword string `json:"oldPassword" binding:"required"` NewPassword string `json:"newPassword" binding:"required,min=8"` } type UserResponse struct { ID uint `json:"id"` Username string `json:"username"` Role string `json:"role"` } func (h *AuthHandler) Login(c *gin.Context) { var req LoginRequest if err := c.ShouldBindJSON(&req); err != nil { Error(c, http.StatusBadRequest, "invalid request body") return } user, err := h.db.GetImcUserByUsername(c.Request.Context(), req.Username) if err != nil || !auth.CheckPassword(req.Password, user.PasswordHash) { Error(c, http.StatusUnauthorized, "invalid credentials") return } token, err := h.jwtManager.GenerateToken(uint(user.ID), user.Username, string(user.Role.ImcUsersRole), 24*time.Hour) if err != nil { Error(c, http.StatusInternalServerError, "failed to generate token") return } Success(c, map[string]interface{}{ "token": token, "user": UserResponse{ ID: uint(user.ID), Username: user.Username, Role: string(user.Role.ImcUsersRole), }, }) } func (h *AuthHandler) Me(c *gin.Context) { authCtx := GetAuthContext(c) if authCtx == nil { Error(c, http.StatusUnauthorized, "not authenticated") return } user, err := h.db.GetImcUserByID(c.Request.Context(), uint32(authCtx.UserID)) if err != nil || user == nil { Error(c, http.StatusNotFound, "user not found") return } Success(c, UserResponse{ ID: uint(user.ID), Username: user.Username, Role: string(user.Role.ImcUsersRole), }) } func (h *AuthHandler) ChangePassword(c *gin.Context) { authCtx := GetAuthContext(c) if authCtx == nil { Error(c, http.StatusUnauthorized, "authentication required") return } var req ChangePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { Error(c, http.StatusBadRequest, "invalid request body") return } user, err := h.db.GetImcUserByID(c.Request.Context(), uint32(authCtx.UserID)) if err != nil || user == nil { Error(c, http.StatusNotFound, "user not found") return } if !auth.CheckPassword(req.OldPassword, user.PasswordHash) { Error(c, http.StatusUnauthorized, "current password is incorrect") return } newHash, err := auth.HashPassword(req.NewPassword) if err != nil { Error(c, http.StatusInternalServerError, "failed to hash password") return } err = h.db.UpdateImcUserPassword(c.Request.Context(), user.ID, newHash) if err != nil { Error(c, http.StatusInternalServerError, "failed to update password") return } Success(c, map[string]string{"message": "password updated successfully"}) } func (h *AuthHandler) Logout(c *gin.Context) { Success(c, map[string]string{"message": "logged out"}) } func (h *AuthHandler) getClientIP(c *gin.Context) string { return c.ClientIP() }