From 86eef472a981ee34a713c2fe266ffb1ea36ab897 Mon Sep 17 00:00:00 2001 From: glenneth Date: Mon, 3 Nov 2025 20:15:45 +0300 Subject: [PATCH] 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 --- auth-routes.lisp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/auth-routes.lisp b/auth-routes.lisp index 9a9942a..de553a7 100644 --- a/auth-routes.lisp +++ b/auth-routes.lisp @@ -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))))