backup: pre-hardening baseline
This commit is contained in:
commit
9e4c612dcb
57 changed files with 10393 additions and 0 deletions
143
backend/internal/api/handlers/auth.go
Normal file
143
backend/internal/api/handlers/auth.go
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/traefik/traefik-gui/backend/internal/auth"
|
||||
"github.com/traefik/traefik-gui/backend/internal/database/repositories"
|
||||
"github.com/traefik/traefik-gui/backend/internal/models"
|
||||
)
|
||||
|
||||
const SessionCookieName = "traefik_gui_session"
|
||||
|
||||
type AuthHandler struct {
|
||||
userRepo *repositories.UserRepository
|
||||
sessionRepo *repositories.SessionRepository
|
||||
sessionSecret string
|
||||
cookieDomain string
|
||||
cookieSecure bool
|
||||
}
|
||||
|
||||
func NewAuthHandler(
|
||||
userRepo *repositories.UserRepository,
|
||||
sessionRepo *repositories.SessionRepository,
|
||||
sessionSecret string,
|
||||
cookieDomain string,
|
||||
cookieSecure bool,
|
||||
) *AuthHandler {
|
||||
return &AuthHandler{
|
||||
userRepo: userRepo,
|
||||
sessionRepo: sessionRepo,
|
||||
sessionSecret: sessionSecret,
|
||||
cookieDomain: cookieDomain,
|
||||
cookieSecure: cookieSecure,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Login(c *gin.Context) {
|
||||
var req models.LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userRepo.GetByUsername(req.Username)
|
||||
if err != nil || user == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid username or password"})
|
||||
return
|
||||
}
|
||||
|
||||
if !auth.CheckPassword(req.Password, user.PasswordHash) {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid username or password"})
|
||||
return
|
||||
}
|
||||
|
||||
sessionData, err := auth.NewSessionData(user.ID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create session"})
|
||||
return
|
||||
}
|
||||
|
||||
session := &models.Session{
|
||||
ID: sessionData.CreatedAt.Format("20060102150405") + "-" + sessionData.CSRFToken[:8],
|
||||
UserID: user.ID,
|
||||
CSRFToken: sessionData.CSRFToken,
|
||||
CreatedAt: sessionData.CreatedAt,
|
||||
ExpiresAt: sessionData.ExpiresAt,
|
||||
}
|
||||
|
||||
// Use a proper UUID for session ID
|
||||
session.ID, _ = auth.GenerateSessionID()
|
||||
|
||||
if err := h.sessionRepo.Create(session); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save session"})
|
||||
return
|
||||
}
|
||||
|
||||
h.setSessionCookie(c, session.ID, session.ExpiresAt)
|
||||
|
||||
if err := h.userRepo.UpdateLastLogin(user.ID); err != nil {
|
||||
// Log but don't fail
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"user": gin.H{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"role": user.Role,
|
||||
},
|
||||
"csrf_token": session.CSRFToken,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Logout(c *gin.Context) {
|
||||
sessionID, err := c.Cookie(SessionCookieName)
|
||||
if err == nil {
|
||||
h.sessionRepo.Delete(sessionID)
|
||||
}
|
||||
|
||||
h.clearSessionCookie(c)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "logged out"})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Me(c *gin.Context) {
|
||||
val, _ := c.Get("user"); user, _ := val.(*models.User)
|
||||
if user == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"role": user.Role,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) setSessionCookie(c *gin.Context, sessionID string, expiresAt time.Time) {
|
||||
c.SetCookie(
|
||||
SessionCookieName,
|
||||
sessionID,
|
||||
int(time.Until(expiresAt).Seconds()),
|
||||
"/",
|
||||
h.cookieDomain,
|
||||
h.cookieSecure,
|
||||
true, // HttpOnly
|
||||
)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) clearSessionCookie(c *gin.Context) {
|
||||
c.SetCookie(
|
||||
SessionCookieName,
|
||||
"",
|
||||
-1,
|
||||
"/",
|
||||
h.cookieDomain,
|
||||
h.cookieSecure,
|
||||
true,
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue