Add Certificates page and Traefik API frontend client
This commit is contained in:
parent
9e4c612dcb
commit
7fc90524b5
40 changed files with 6585 additions and 359 deletions
|
|
@ -37,6 +37,13 @@ func NewAuthHandler(
|
|||
}
|
||||
|
||||
func (h *AuthHandler) Login(c *gin.Context) {
|
||||
// Rate limiting per IP
|
||||
clientIP := c.ClientIP()
|
||||
if !auth.DefaultLoginLimiter.Allow(clientIP) {
|
||||
c.JSON(http.StatusTooManyRequests, gin.H{"error": "too many login attempts, try again later"})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
|
|
@ -45,14 +52,17 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
|||
|
||||
user, err := h.userRepo.GetByUsername(req.Username)
|
||||
if err != nil || user == nil {
|
||||
auth.DefaultLoginLimiter.RecordFailure(clientIP)
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid username or password"})
|
||||
return
|
||||
}
|
||||
|
||||
if !auth.CheckPassword(req.Password, user.PasswordHash) {
|
||||
auth.DefaultLoginLimiter.RecordFailure(clientIP)
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid username or password"})
|
||||
return
|
||||
}
|
||||
auth.DefaultLoginLimiter.RecordSuccess(clientIP)
|
||||
|
||||
sessionData, err := auth.NewSessionData(user.ID)
|
||||
if err != nil {
|
||||
|
|
@ -60,17 +70,20 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
sessionID, err := auth.GenerateSessionID()
|
||||
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],
|
||||
ID: sessionID,
|
||||
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
|
||||
|
|
@ -109,13 +122,32 @@ func (h *AuthHandler) Me(c *gin.Context) {
|
|||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
// Include current CSRF token for bootstrap after page reload
|
||||
var csrfToken string
|
||||
if sVal, exists := c.Get("session"); exists {
|
||||
if s, ok := sVal.(*models.Session); ok && s != nil {
|
||||
csrfToken = s.CSRFToken
|
||||
}
|
||||
}
|
||||
resp := gin.H{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"role": user.Role,
|
||||
})
|
||||
}
|
||||
if csrfToken != "" {
|
||||
resp["csrf_token"] = csrfToken
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) GetCSRF(c *gin.Context) {
|
||||
val, _ := c.Get("session"); sess, _ := val.(*models.Session)
|
||||
if sess == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authenticated"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"csrf_token": sess.CSRFToken})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) setSessionCookie(c *gin.Context, sessionID string, expiresAt time.Time) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue