File size: 3,931 Bytes
8c7b7ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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
};