Add new password change endpoint

This commit is contained in:
KernelDeimos
2024-05-11 02:04:57 -04:00
committed by KernelDeimos
parent a89c9d59cf
commit 9076fddc0d
2 changed files with 81 additions and 3 deletions

View File

@@ -1,7 +1,85 @@
// TODO: DRY: This is the same function used by UIWindowChangePassword!
const { invalidate_cached_user } = require("../../helpers");
const { DB_WRITE } = require("../../services/database/consts");
// duplicate definition is in src/helpers.js (puter GUI)
const check_password_strength = (password) => {
// Define criteria for password strength
const criteria = {
minLength: 8,
hasUpperCase: /[A-Z]/.test(password),
hasLowerCase: /[a-z]/.test(password),
hasNumber: /\d/.test(password),
hasSpecialChar: /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)
};
let overallPass = true;
// Initialize report object
let criteria_report = {
minLength: {
message: `Password must be at least ${criteria.minLength} characters long`,
pass: password.length >= criteria.minLength,
},
hasUpperCase: {
message: 'Password must contain at least one uppercase letter',
pass: criteria.hasUpperCase,
},
hasLowerCase: {
message: 'Password must contain at least one lowercase letter',
pass: criteria.hasLowerCase,
},
hasNumber: {
message: 'Password must contain at least one number',
pass: criteria.hasNumber,
},
hasSpecialChar: {
message: 'Password must contain at least one special character',
pass: criteria.hasSpecialChar,
},
};
// Check overall pass status and add messages
for (let criterion in criteria) {
if (!criteria_report[criterion].pass) {
overallPass = false;
break;
}
}
return {
overallPass: overallPass,
report: criteria_report,
};
}
module.exports = {
route: '/change-password',
methods: ['POST'],
handler: async (req, res, next) => {
res.send('this is a test response');
// Validate new password
const { new_pass } = req.body;
const { overallPass: strong } = check_password_strength(new_pass);
if ( ! strong ) {
req.status(400).send('Password does not meet requirements.');
}
// Update user
// TODO: DI for endpoint definitions like this one
const bcrypt = require('bcrypt');
const db = req.services.get('database').get(DB_WRITE, 'auth');
await db.write(
'UPDATE user SET password=?, `pass_recovery_token` = NULL, `change_email_confirm_token` = NULL WHERE `id` = ?',
[await bcrypt.hash(req.body.new_pass, 8), req.user.id]
);
invalidate_cached_user(req.user);
// Notify user about password change
// TODO: audit log for user in security tab
const svc_email = req.services.get('email');
svc_email.send_email({ email: req.user.email }, 'password_change_notification');
return res.send('Password successfully updated.')
}
};

View File

@@ -114,7 +114,7 @@ async function UIWindowChangePassword(options){
$(el_window).find('.form-error-msg').hide();
$.ajax({
url: window.api_origin + "/passwd",
url: window.api_origin + "/user-protected/change-password",
type: 'POST',
async: true,
headers: {
@@ -122,7 +122,7 @@ async function UIWindowChangePassword(options){
},
contentType: "application/json",
data: JSON.stringify({
old_pass: current_password,
password: current_password,
new_pass: new_password,
}),
success: function (data){