Spaces:
Runtime error
Runtime error
| const bcrypt = require('bcryptjs'); | |
| const dayjs = require('dayjs'); | |
| const jwt = require('jsonwebtoken'); | |
| const { body } = require('express-validator'); | |
| const { User } = require('../models'); | |
| const env = require('../config/env'); | |
| const validate = require('../utils/validation'); | |
| const PASSWORD_ROTATION_ROLES = new Set(['Admin', 'HR', 'Safety_Officer']); | |
| const PASSWORD_ROTATION_DAYS = 90; | |
| const loginValidators = [ | |
| body('email').isEmail().withMessage('Valid email is required'), | |
| body('password').isLength({ min: 6 }).withMessage('Password is required'), | |
| validate | |
| ]; | |
| const changePasswordValidators = [ | |
| body('current_password').isLength({ min: 6 }).withMessage('current_password is required'), | |
| body('new_password').isLength({ min: 8 }).withMessage('new_password must be at least 8 characters'), | |
| validate | |
| ]; | |
| function getPasswordDueDate(role, passwordChangedAt) { | |
| if (!PASSWORD_ROTATION_ROLES.has(role)) return null; | |
| return dayjs(passwordChangedAt).add(PASSWORD_ROTATION_DAYS, 'day').format('DD/MM/YYYY'); | |
| } | |
| function isPasswordRotationExpired(role, passwordChangedAt) { | |
| if (!PASSWORD_ROTATION_ROLES.has(role)) return false; | |
| if (!passwordChangedAt) return true; | |
| return dayjs().startOf('day').diff(dayjs(passwordChangedAt).startOf('day'), 'day') >= PASSWORD_ROTATION_DAYS; | |
| } | |
| async function login(req, res, next) { | |
| try { | |
| const { email, password } = req.body; | |
| const user = await User.findOne({ email: String(email).toLowerCase() }).lean(); | |
| if (!user || !user.is_active) { | |
| return res.status(401).json({ message: 'Invalid credentials' }); | |
| } | |
| const isMatch = await bcrypt.compare(password, user.password_hash); | |
| if (!isMatch) { | |
| return res.status(401).json({ message: 'Invalid credentials' }); | |
| } | |
| const mustResetPassword = Boolean(user.must_reset_password); | |
| const passwordRotationExpired = isPasswordRotationExpired(user.role, user.password_changed_at); | |
| const mustChangePassword = mustResetPassword || passwordRotationExpired; | |
| const token = jwt.sign( | |
| { | |
| id: String(user._id), | |
| full_name: user.full_name, | |
| email: user.email, | |
| role: user.role, | |
| vendor_id: user.vendor_id ? String(user.vendor_id) : null | |
| }, | |
| env.jwtSecret, | |
| { expiresIn: '12h' } | |
| ); | |
| return res.json({ | |
| token, | |
| user: { | |
| id: user.id, | |
| full_name: user.full_name, | |
| email: user.email, | |
| role: user.role, | |
| vendor_id: user.vendor_id ? String(user.vendor_id) : null, | |
| must_reset_password: mustResetPassword, | |
| must_change_password: mustChangePassword, | |
| password_due_date: getPasswordDueDate(user.role, user.password_changed_at) | |
| } | |
| }); | |
| } catch (error) { | |
| return next(error); | |
| } | |
| } | |
| async function changePassword(req, res, next) { | |
| try { | |
| const { current_password, new_password } = req.body; | |
| if (current_password === new_password) { | |
| return res.status(400).json({ message: 'New password must be different from current password' }); | |
| } | |
| const user = await User.findOne({ _id: req.user.id, is_active: true }); | |
| if (!user) { | |
| return res.status(404).json({ message: 'User not found' }); | |
| } | |
| const isMatch = await bcrypt.compare(current_password, user.password_hash); | |
| if (!isMatch) { | |
| return res.status(401).json({ message: 'Current password is incorrect' }); | |
| } | |
| user.password_hash = await bcrypt.hash(new_password, 10); | |
| user.must_reset_password = false; | |
| user.password_changed_at = new Date(); | |
| await user.save(); | |
| return res.json({ | |
| message: 'Password changed successfully', | |
| user: { | |
| must_reset_password: false, | |
| must_change_password: false, | |
| password_due_date: getPasswordDueDate(user.role, new Date()) | |
| } | |
| }); | |
| } catch (error) { | |
| return next(error); | |
| } | |
| } | |
| module.exports = { | |
| loginValidators, | |
| changePasswordValidators, | |
| login, | |
| changePassword | |
| }; | |