ResetPassword resets a user's password.
(w http.ResponseWriter, r *http.Request)
| 465 | |
| 466 | // ResetPassword resets a user's password. |
| 467 | func (h *UsersHandler) ResetPassword(w http.ResponseWriter, r *http.Request) { |
| 468 | userID := chi.URLParam(r, "userId") |
| 469 | var req struct { |
| 470 | NewPassword string `json:"newPassword"` |
| 471 | } |
| 472 | if err := decodeJSON(r, &req); err != nil { |
| 473 | Error(w, http.StatusBadRequest, "Invalid request body") |
| 474 | return |
| 475 | } |
| 476 | if req.NewPassword == "" { |
| 477 | Error(w, http.StatusBadRequest, "New password is required") |
| 478 | return |
| 479 | } |
| 480 | |
| 481 | existing, err := h.users.GetByID(r.Context(), userID) |
| 482 | if err != nil || existing == nil { |
| 483 | Error(w, http.StatusNotFound, "User not found") |
| 484 | return |
| 485 | } |
| 486 | if !existing.IsActive { |
| 487 | Error(w, http.StatusBadRequest, "Cannot reset password for inactive user") |
| 488 | return |
| 489 | } |
| 490 | |
| 491 | // Prevent resetting the password of users with a higher-privilege role. |
| 492 | callerRole, _ := r.Context().Value(middleware.UserRoleKey).(string) |
| 493 | if roleRank(existing.Role) > roleRank(callerRole) { |
| 494 | Error(w, http.StatusForbidden, "Cannot reset password for a user with a higher-privilege role") |
| 495 | return |
| 496 | } |
| 497 | if existing.Role == "superadmin" && callerRole != "superadmin" { |
| 498 | perm, permErr := h.permissions.GetByRole(r.Context(), callerRole) |
| 499 | if permErr != nil || perm == nil || !perm.CanManageSuperusers { |
| 500 | Error(w, http.StatusForbidden, "You do not have permission to reset superadmin passwords") |
| 501 | return |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | if err := ValidatePasswordPolicy(h.resolved, req.NewPassword); err != nil { |
| 506 | Error(w, http.StatusBadRequest, err.Error()) |
| 507 | return |
| 508 | } |
| 509 | |
| 510 | hash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), 12) |
| 511 | if err != nil { |
| 512 | Error(w, http.StatusInternalServerError, "Failed to hash password") |
| 513 | return |
| 514 | } |
| 515 | |
| 516 | if err := h.users.UpdatePassword(r.Context(), userID, string(hash)); err != nil { |
| 517 | Error(w, http.StatusInternalServerError, "Failed to reset password") |
| 518 | return |
| 519 | } |
| 520 | |
| 521 | // Revoke all sessions and trusted devices for the target user after password |
| 522 | // reset. An admin reset is the canonical post-compromise action; leaving |
| 523 | // either surface alive would let an attacker retain access despite the reset. |
| 524 | if err := h.sessions.RevokeAllForUser(r.Context(), userID, ""); err != nil && h.log != nil { |
nothing calls this directly
no test coverage detected