feat: Add password change and reset API endpoints

NEW API ENDPOINTS:

1. /api/asteroid/user/change-password (authenticated users)
   - Users can change their own password
   - Requires current password verification
   - Returns 401 if current password is incorrect
   - Returns 200 on success

2. /api/asteroid/admin/reset-password (admin only)
   - Admins can reset any user's password
   - No current password required
   - Returns 404 if user not found
   - Returns 200 on success

USAGE EXAMPLES:

User changes own password:
  curl -X POST http://localhost:8080/api/asteroid/user/change-password \
    -d 'current-password=asteroid123&new-password=newsecurepass' \
    -b cookies.txt

Admin resets user password:
  curl -X POST http://localhost:8080/api/asteroid/admin/reset-password \
    -d 'username=admin&new-password=newsecurepass' \
    -b cookies.txt

This addresses the security concern about the default admin password.
Admins can now reset it via API without needing REPL access.

Ref: TODO.org Problem 4 - Default admin password
This commit is contained in:
glenneth 2025-11-03 20:15:45 +03:00
parent dbe9a06247
commit 86eef472a9
1 changed files with 56 additions and 0 deletions

View File

@ -105,3 +105,59 @@
(api-output `(("status" . "error")
("message" . ,(format nil "Error creating user: ~a" e)))
:status 500))))
;; API: Change own password (authenticated users)
(define-api asteroid/user/change-password (current-password new-password) ()
"API endpoint for users to change their own password"
(require-authentication)
(handler-case
(if (and current-password new-password)
(let* ((current-user (auth:current-user))
(username (gethash "username" current-user))
(stored-hash (gethash "password-hash" current-user)))
;; Verify current password
(if (verify-password current-password
(if (listp stored-hash) (first stored-hash) stored-hash))
;; Current password is correct, update to new password
(if (reset-user-password username new-password)
(api-output `(("status" . "success")
("message" . "Password changed successfully")))
(api-output `(("status" . "error")
("message" . "Failed to update password"))
:status 500))
;; Current password is incorrect
(api-output `(("status" . "error")
("message" . "Current password is incorrect"))
:status 401)))
(api-output `(("status" . "error")
("message" . "Missing required fields"))
:status 400))
(error (e)
(api-output `(("status" . "error")
("message" . ,(format nil "Error changing password: ~a" e)))
:status 500))))
;; API: Reset user password (admin only)
(define-api asteroid/admin/reset-password (username new-password) ()
"API endpoint for admins to reset any user's password"
(require-role :admin)
(handler-case
(if (and username new-password)
(let ((user (find-user-by-username username)))
(if user
(if (reset-user-password username new-password)
(api-output `(("status" . "success")
("message" . ,(format nil "Password reset for user: ~a" username))))
(api-output `(("status" . "error")
("message" . "Failed to reset password"))
:status 500))
(api-output `(("status" . "error")
("message" . ,(format nil "User not found: ~a" username)))
:status 404)))
(api-output `(("status" . "error")
("message" . "Missing required fields"))
:status 400))
(error (e)
(api-output `(("status" . "error")
("message" . ,(format nil "Error resetting password: ~a" e)))
:status 500))))