/** * Management User Model * Completely separate from tenant users for security isolation */ const { DataTypes } = require('sequelize'); module.exports = (sequelize) => { const ManagementUser = sequelize.define('ManagementUser', { id: { type: DataTypes.UUID, defaultValue: sequelize.Sequelize.UUIDV4, primaryKey: true }, username: { type: DataTypes.STRING, allowNull: false, unique: true, validate: { len: [3, 50] }, comment: 'Unique management username' }, email: { type: DataTypes.STRING, allowNull: false, unique: true, validate: { isEmail: true }, comment: 'Management user email' }, password_hash: { type: DataTypes.STRING, allowNull: false, comment: 'Bcrypt hashed password' }, first_name: { type: DataTypes.STRING, allowNull: true }, last_name: { type: DataTypes.STRING, allowNull: true }, role: { type: DataTypes.ENUM('super_admin', 'platform_admin', 'support_admin'), allowNull: false, defaultValue: 'support_admin', comment: 'Management role - separate from tenant roles' }, is_active: { type: DataTypes.BOOLEAN, defaultValue: true, comment: 'Whether the management user is active' }, last_login: { type: DataTypes.DATE, allowNull: true, comment: 'Last successful login timestamp' }, login_attempts: { type: DataTypes.INTEGER, defaultValue: 0, comment: 'Failed login attempt counter' }, locked_until: { type: DataTypes.DATE, allowNull: true, comment: 'Account lock expiration time' }, two_factor_enabled: { type: DataTypes.BOOLEAN, defaultValue: false, comment: 'Whether 2FA is enabled' }, two_factor_secret: { type: DataTypes.STRING, allowNull: true, comment: 'TOTP secret for 2FA' }, api_access: { type: DataTypes.BOOLEAN, defaultValue: true, comment: 'Whether user can access management API' }, permissions: { type: DataTypes.JSONB, defaultValue: [], comment: 'Additional granular permissions' }, created_by: { type: DataTypes.STRING, allowNull: true, comment: 'Username of who created this management user' }, notes: { type: DataTypes.TEXT, allowNull: true, comment: 'Admin notes about this user' } }, { tableName: 'management_users', indexes: [ { unique: true, fields: ['username'] }, { unique: true, fields: ['email'] }, { fields: ['role'] }, { fields: ['is_active'] }, { fields: ['last_login'] } ], hooks: { beforeCreate: (user) => { // Ensure management users have strong password requirements // This would be handled in the route, but we can add validation here }, beforeUpdate: (user) => { if (user.changed('password_hash')) { // Log password changes for security audit console.log(`Management password change for user: ${user.username}`); } } } }); // Static methods for management user operations ManagementUser.createInitialAdmin = async function(adminData) { const bcrypt = require('bcryptjs'); const hashedPassword = await bcrypt.hash(adminData.password, 12); // Higher cost for management users return await this.create({ username: adminData.username, email: adminData.email, password_hash: hashedPassword, first_name: adminData.first_name, last_name: adminData.last_name, role: 'super_admin', created_by: 'system' }); }; ManagementUser.findByCredentials = async function(username, password) { const bcrypt = require('bcryptjs'); const user = await this.findOne({ where: { username: username, is_active: true } }); if (!user) { return null; } // Check if account is locked if (user.locked_until && user.locked_until > new Date()) { throw new Error('Account is temporarily locked'); } const isValidPassword = await bcrypt.compare(password, user.password_hash); if (!isValidPassword) { // Increment failed attempts await user.increment('login_attempts'); // Lock account after 5 failed attempts for 30 minutes if (user.login_attempts >= 4) { await user.update({ locked_until: new Date(Date.now() + 30 * 60 * 1000) // 30 minutes }); throw new Error('Account locked due to multiple failed attempts'); } return null; } // Reset login attempts on successful login await user.update({ login_attempts: 0, locked_until: null, last_login: new Date() }); return user; }; return ManagementUser; };