diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index 198dd362..5d86812f 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -714,6 +714,29 @@ func (h *AuthHandler) ListUsers(c *echo.Context) error { return c.JSON(http.StatusOK, users) } +// GetPasswordRules handles GET /api/auth/password-rules (public — the +// register page is pre-auth). Exposes the ACTIVE complexity configuration +// so the UI requirement checkers mirror the real rules instead of +// hardcoding defaults. The server remains the enforcer. +func (h *AuthHandler) GetPasswordRules(c *echo.Context) error { + minLength, upper, lower, number, special := 8, true, true, true, true + if h.settings != nil { + r := h.settings.PasswordRules() + minLength, upper, lower, number, special = + r.MinLength, r.Upper, r.Lower, r.Number, r.Special + } + if minLength < 1 { + minLength = 8 + } + return c.JSON(http.StatusOK, map[string]interface{}{ + "min_length": minLength, + "require_upper": upper, + "require_lower": lower, + "require_number": number, + "require_special": special, + }) +} + // AdminCreateUser handles POST /api/auth/users (AdminMiddleware-gated). // The admin's counterpart to public registration: creating an account for // someone else must work even when self-service registration is disabled diff --git a/internal/router/auth.go b/internal/router/auth.go index 47608b13..0d1e4a3d 100644 --- a/internal/router/auth.go +++ b/internal/router/auth.go @@ -12,6 +12,7 @@ func registerAuthRoutes(cfg *Config, rateLimitMiddleware echo.MiddlewareFunc) { // Auth routes (no auth required, but rate limited) e.POST("/api/auth/register", rateLimitMiddleware(cfg.AuthHandler.Register)) e.POST("/api/auth/login", rateLimitMiddleware(cfg.AuthHandler.Login)) + e.GET("/api/auth/password-rules", cfg.AuthHandler.GetPasswordRules) // JWT middleware for protected routes jwtMiddleware := createJWTMiddleware(cfg) diff --git a/templates/admin_users.templ b/templates/admin_users.templ index 1719411b..46ffc63c 100644 --- a/templates/admin_users.templ +++ b/templates/admin_users.templ @@ -49,26 +49,8 @@ templ adminCreateUserForm() {

Password Requirements

- + +
diff --git a/templates/admin_users_templ.go b/templates/admin_users_templ.go index 8f136f73..9a59bf64 100644 --- a/templates/admin_users_templ.go +++ b/templates/admin_users_templ.go @@ -41,7 +41,7 @@ func adminCreateUserForm() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

Create User

Password Requirements

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

Create User

Password Requirements

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -106,7 +106,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue("user-" + user.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 120, Col: 33} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 102, Col: 33} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3) if templ_7745c5c3_Err != nil { @@ -127,7 +127,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 129, Col: 96} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 111, Col: 96} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { @@ -150,7 +150,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 134, Col: 87} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 116, Col: 87} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { @@ -163,7 +163,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(FormatInTimezone(user.CreatedAt, currentUser.Timezone)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 139, Col: 73} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 121, Col: 73} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { @@ -186,7 +186,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue("/api/auth/profile/" + user.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 156, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 138, Col: 50} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7) if templ_7745c5c3_Err != nil { @@ -199,7 +199,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var8 string templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue("#role-result-" + user.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 157, Col: 48} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 139, Col: 48} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8) if templ_7745c5c3_Err != nil { @@ -237,7 +237,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var9 string templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue("role-result-" + user.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 168, Col: 42} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 150, Col: 42} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9) if templ_7745c5c3_Err != nil { @@ -250,7 +250,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var10 string templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue("/admin/users/" + user.ID + "/profile-modal") if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 172, Col: 63} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 154, Col: 63} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10) if templ_7745c5c3_Err != nil { @@ -289,7 +289,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var11 string templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue("/api/auth/profile/" + user.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 191, Col: 53} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 173, Col: 53} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11) if templ_7745c5c3_Err != nil { @@ -302,7 +302,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component var templ_7745c5c3_Var12 string templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue("#user-" + user.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 192, Col: 41} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_users.templ`, Line: 174, Col: 41} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12) if templ_7745c5c3_Err != nil { diff --git a/templates/profile_form.templ b/templates/profile_form.templ index 2d3ecfa7..fd0a4081 100644 --- a/templates/profile_form.templ +++ b/templates/profile_form.templ @@ -112,7 +112,7 @@ templ ProfileForm(user User, actionURL string, requireCurrentPassword bool, show } -
+
@Icon("shield", "h-5 w-5")

Change Password

@@ -132,22 +132,24 @@ templ ProfileForm(user User, actionURL string, requireCurrentPassword bool, show
+
+
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -455,7 +455,7 @@ func ProfileForm(user User, actionURL string, requireCurrentPassword bool, showR return templ_7745c5c3_Err } if requireCurrentPassword { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "