const express = require('express'); const bcrypt = require('bcryptjs'); const { audit, now, one, run } = require('../db'); const { getProfile, savePreferences } = require('../services/profile'); const { describe, getPolicy, minAgeRemainingMinutes, recordPassword, validatePassword } = require('../services/passwordPolicy'); const router = express.Router(); router.get('/profile', (req, res) => { res.json({ ...getProfile(req.user), passwordPolicy: { rules: describe() } }); }); router.put('/profile', (req, res) => { const preferences = savePreferences(req.user.id, req.body.preferences || {}); audit(req.user.id, 'update', 'profile_preferences', req.user.id, null, { preferences }); res.json({ user: getProfile(req.user).user, preferences }); }); // Self-service password change. Honors the configured policy (complexity, identifiers, history, // and minimum age), then clears any forced-change flag. router.put('/profile/password', (req, res, next) => { try { const { current_password: current, new_password: next } = req.body || {}; const user = one('SELECT * FROM users WHERE id = ?', [req.user.id]); if (!user || !bcrypt.compareSync(current || '', user.password_hash)) { return res.status(400).json({ error: 'Your current password is incorrect' }); } // The minimum-age rule never blocks a forced change (expired / admin-required). const remaining = minAgeRemainingMinutes(user, getPolicy()); if (remaining > 0 && !user.must_change_password) { return res.status(400).json({ error: `You can change your password again in ${remaining} minute(s)` }); } validatePassword(next, { id: user.id, name: user.name, email: user.email }); const hash = bcrypt.hashSync(next, 12); run('UPDATE users SET password_hash = ?, password_changed_at = ?, must_change_password = 0, updated_at = ? WHERE id = ?', [ hash, now(), now(), user.id ]); recordPassword(user.id, hash); audit(user.id, 'password_change', 'user', user.id, null, { self: true }); return res.json({ ok: true }); } catch (error) { return next(error); } }); module.exports = router;