Add password complexity requirements

Require at least one uppercase letter, one lowercase letter, and one
digit in addition to the existing 8-character minimum.

Fixes #31
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthew Knight 2026-02-17 16:14:01 -08:00
parent fdcccce476
commit cba9b5c408
No known key found for this signature in database
1 changed files with 27 additions and 4 deletions

View File

@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"unicode"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/mattnite/forgejo-tickets/internal/auth" "github.com/mattnite/forgejo-tickets/internal/auth"
@ -11,6 +12,28 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
// validatePassword checks password complexity requirements.
func validatePassword(password string) string {
if len(password) < 8 {
return "Password must be at least 8 characters"
}
var hasUpper, hasLower, hasDigit bool
for _, r := range password {
switch {
case unicode.IsUpper(r):
hasUpper = true
case unicode.IsLower(r):
hasLower = true
case unicode.IsDigit(r):
hasDigit = true
}
}
if !hasUpper || !hasLower || !hasDigit {
return "Password must contain at least one uppercase letter, one lowercase letter, and one digit"
}
return ""
}
type AuthHandler struct { type AuthHandler struct {
deps Dependencies deps Dependencies
} }
@ -85,8 +108,8 @@ func (h *AuthHandler) Register(c *gin.Context) {
return return
} }
if len(password) < 8 { if errMsg := validatePassword(password); errMsg != "" {
data["Error"] = "Password must be at least 8 characters" data["Error"] = errMsg
h.deps.Renderer.Render(c.Writer, c.Request, "register", data) h.deps.Renderer.Render(c.Writer, c.Request, "register", data)
return return
} }
@ -189,10 +212,10 @@ func (h *AuthHandler) ResetPassword(c *gin.Context) {
return return
} }
if len(password) < 8 { if errMsg := validatePassword(password); errMsg != "" {
h.deps.Renderer.Render(c.Writer, c.Request, "reset-password", map[string]interface{}{ h.deps.Renderer.Render(c.Writer, c.Request, "reset-password", map[string]interface{}{
"Token": token, "Token": token,
"Error": "Password must be at least 8 characters", "Error": errMsg,
}) })
return return
} }