553 lines
16 KiB
TypeScript
553 lines
16 KiB
TypeScript
import { Response } from 'express';
|
|
import { User } from '../models/user.model';
|
|
import bcrypt from 'bcryptjs';
|
|
import { AuthenticatedRequest } from '../types/auth';
|
|
import elkService from '../services/elkService';
|
|
|
|
// Get all users in tenant
|
|
export const getTenantUsers = async (req: AuthenticatedRequest, res: Response) => {
|
|
try {
|
|
console.log('Fetching users - Auth:', {
|
|
user: req.user,
|
|
headers: req.headers,
|
|
query: req.query
|
|
});
|
|
|
|
// The tenant middleware ensures req.user and req.tenantId exist
|
|
// and adds _tenantId to query params, so we don't need to check here
|
|
const tenantId = req.tenantId;
|
|
|
|
// Log the user fetch operation
|
|
elkService.logBusinessEvent('user_list_requested', {
|
|
tenantId,
|
|
requestedBy: req.user!.id,
|
|
filters: req.query
|
|
}, req.user!.id, tenantId);
|
|
|
|
// Build query
|
|
const query: any = { tenant: tenantId };
|
|
|
|
if (req.query.role) {
|
|
query.role = req.query.role;
|
|
}
|
|
|
|
if (req.query.status === 'active') {
|
|
query.isActive = true;
|
|
} else if (req.query.status === 'inactive') {
|
|
query.isActive = false;
|
|
}
|
|
|
|
if (req.query.search) {
|
|
query.$or = [
|
|
{ name: { $regex: req.query.search, $options: 'i' } },
|
|
{ email: { $regex: req.query.search, $options: 'i' } }
|
|
];
|
|
}
|
|
|
|
console.log('Fetching users with query:', query);
|
|
|
|
const users = await User.find(query)
|
|
.select('-password')
|
|
.sort({ createdAt: -1 });
|
|
|
|
console.log(`Found ${users.length} users`);
|
|
|
|
const formattedUsers = users.map(user => ({
|
|
id: user._id,
|
|
name: user.name,
|
|
email: user.email,
|
|
role: user.role,
|
|
isActive: user.isActive
|
|
}));
|
|
|
|
// Log successful user fetch
|
|
elkService.logBusinessEvent('user_list_fetched', {
|
|
tenantId,
|
|
requestedBy: req.user!.id,
|
|
userCount: formattedUsers.length,
|
|
filters: req.query
|
|
}, req.user!.id, tenantId);
|
|
|
|
// Return in the format expected by the frontend
|
|
return res.json({
|
|
success: true,
|
|
users: formattedUsers,
|
|
total: formattedUsers.length,
|
|
pages: 1 // Since we're not implementing pagination yet
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error fetching users:', error);
|
|
|
|
// Log the error
|
|
elkService.logSystemError(
|
|
error instanceof Error ? error : new Error('Unknown error'),
|
|
'user_list_fetch',
|
|
{ tenantId: req.tenantId, requestedBy: req.user?.id }
|
|
);
|
|
|
|
return res.status(500).json({
|
|
success: false,
|
|
message: 'Failed to fetch users',
|
|
error: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
}
|
|
};
|
|
|
|
// Add new user to tenant
|
|
export const addTenantUser = async (req: AuthenticatedRequest, res: Response) => {
|
|
try {
|
|
const { name, email, password, role } = req.body;
|
|
// The tenant middleware ensures req.tenantId exists
|
|
const tenantId = req.tenantId;
|
|
|
|
// Log user creation attempt
|
|
elkService.logBusinessEvent('user_creation_attempted', {
|
|
tenantId,
|
|
createdBy: req.user!.id,
|
|
targetUserEmail: email,
|
|
targetUserRole: role
|
|
}, req.user!.id, tenantId);
|
|
|
|
// Check if user with email already exists
|
|
const existingUser = await User.findOne({ email });
|
|
if (existingUser) {
|
|
elkService.logBusinessEvent('user_creation_failed', {
|
|
tenantId,
|
|
createdBy: req.user!.id,
|
|
targetUserEmail: email,
|
|
reason: 'email_already_exists'
|
|
}, req.user!.id, tenantId);
|
|
|
|
return res.status(400).json({ message: 'User with this email already exists' });
|
|
}
|
|
|
|
// Hash password
|
|
const salt = await bcrypt.genSalt(10);
|
|
const hashedPassword = await bcrypt.hash(password, salt);
|
|
|
|
// Create new user
|
|
const user = new User({
|
|
name,
|
|
email,
|
|
password: hashedPassword,
|
|
role,
|
|
tenant: tenantId,
|
|
isActive: true,
|
|
oauthProvider: 'local',
|
|
oauthId: email // Use email as unique identifier for local users
|
|
});
|
|
|
|
await user.save();
|
|
|
|
// Log successful user creation
|
|
elkService.logBusinessEvent('user_created', {
|
|
tenantId,
|
|
createdBy: req.user!.id,
|
|
newUserId: user._id.toString(),
|
|
newUserEmail: user.email,
|
|
newUserRole: user.role
|
|
}, req.user!.id, tenantId);
|
|
|
|
res.status(201).json({
|
|
id: user._id,
|
|
name: user.name,
|
|
email: user.email,
|
|
role: user.role,
|
|
isActive: user.isActive
|
|
});
|
|
} catch (error) {
|
|
console.error('Error creating user:', error);
|
|
|
|
// Log the error
|
|
elkService.logSystemError(
|
|
error instanceof Error ? error : new Error('Unknown error'),
|
|
'user_creation',
|
|
{ tenantId: req.tenantId, createdBy: req.user?.id, targetUserEmail: req.body?.email }
|
|
);
|
|
|
|
res.status(500).json({ message: 'Failed to create user' });
|
|
}
|
|
};
|
|
|
|
// Update user
|
|
export const updateTenantUser = async (req: AuthenticatedRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { name, email, role } = req.body;
|
|
// The tenant middleware ensures req.tenantId exists
|
|
const tenantId = req.tenantId;
|
|
|
|
// Log user update attempt
|
|
elkService.logBusinessEvent('user_update_attempted', {
|
|
tenantId,
|
|
updatedBy: req.user!.id,
|
|
targetUserId: id,
|
|
updateData: { name, email, role }
|
|
}, req.user!.id, tenantId);
|
|
|
|
// Find user and ensure they belong to the tenant
|
|
const user = await User.findOne({ _id: id, tenant: tenantId });
|
|
if (!user) {
|
|
elkService.logBusinessEvent('user_update_failed', {
|
|
tenantId,
|
|
updatedBy: req.user!.id,
|
|
targetUserId: id,
|
|
reason: 'user_not_found'
|
|
}, req.user!.id, tenantId);
|
|
|
|
return res.status(404).json({ message: 'User not found' });
|
|
}
|
|
|
|
// Check if email is being changed and if it's already in use
|
|
if (email !== user.email) {
|
|
const existingUser = await User.findOne({ email, _id: { $ne: id } });
|
|
if (existingUser) {
|
|
elkService.logBusinessEvent('user_update_failed', {
|
|
tenantId,
|
|
updatedBy: req.user!.id,
|
|
targetUserId: id,
|
|
reason: 'email_already_in_use',
|
|
conflictingEmail: email
|
|
}, req.user!.id, tenantId);
|
|
|
|
return res.status(400).json({ message: 'Email already in use' });
|
|
}
|
|
}
|
|
|
|
// Store original values for logging
|
|
const originalData = {
|
|
name: user.name,
|
|
email: user.email,
|
|
role: user.role
|
|
};
|
|
|
|
// Update user
|
|
user.name = name;
|
|
user.email = email;
|
|
user.role = role;
|
|
|
|
await user.save();
|
|
|
|
// Log successful user update
|
|
elkService.logBusinessEvent('user_updated', {
|
|
tenantId,
|
|
updatedBy: req.user!.id,
|
|
targetUserId: id,
|
|
originalData,
|
|
newData: { name, email, role }
|
|
}, req.user!.id, tenantId);
|
|
|
|
res.json({
|
|
id: user._id,
|
|
name: user.name,
|
|
email: user.email,
|
|
role: user.role,
|
|
isActive: user.isActive
|
|
});
|
|
} catch (error) {
|
|
console.error('Error updating user:', error);
|
|
|
|
// Log the error
|
|
elkService.logSystemError(
|
|
error instanceof Error ? error : new Error('Unknown error'),
|
|
'user_update',
|
|
{ tenantId: req.tenantId, updatedBy: req.user?.id, targetUserId: req.params?.id }
|
|
);
|
|
|
|
res.status(500).json({ message: 'Failed to update user' });
|
|
}
|
|
};
|
|
|
|
// Toggle user active status
|
|
export const toggleUserStatus = async (req: AuthenticatedRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
// The tenant middleware ensures req.tenantId exists
|
|
const tenantId = req.tenantId;
|
|
|
|
// Log user status toggle attempt
|
|
elkService.logBusinessEvent('user_status_toggle_attempted', {
|
|
tenantId,
|
|
toggledBy: req.user!.id,
|
|
targetUserId: id
|
|
}, req.user!.id, tenantId);
|
|
|
|
// Find user and ensure they belong to the tenant
|
|
const user = await User.findOne({ _id: id, tenant: tenantId });
|
|
if (!user) {
|
|
elkService.logBusinessEvent('user_status_toggle_failed', {
|
|
tenantId,
|
|
toggledBy: req.user!.id,
|
|
targetUserId: id,
|
|
reason: 'user_not_found'
|
|
}, req.user!.id, tenantId);
|
|
|
|
return res.status(404).json({ message: 'User not found' });
|
|
}
|
|
|
|
// Store original status for logging
|
|
const originalStatus = user.isActive;
|
|
|
|
// Toggle status
|
|
user.isActive = !user.isActive;
|
|
await user.save();
|
|
|
|
// Log successful status toggle
|
|
elkService.logBusinessEvent('user_status_toggled', {
|
|
tenantId,
|
|
toggledBy: req.user!.id,
|
|
targetUserId: id,
|
|
originalStatus,
|
|
newStatus: user.isActive
|
|
}, req.user!.id, tenantId);
|
|
|
|
res.json({
|
|
id: user._id,
|
|
isActive: user.isActive
|
|
});
|
|
} catch (error) {
|
|
console.error('Error toggling user status:', error);
|
|
|
|
// Log the error
|
|
elkService.logSystemError(
|
|
error instanceof Error ? error : new Error('Unknown error'),
|
|
'user_status_toggle',
|
|
{ tenantId: req.tenantId, toggledBy: req.user?.id, targetUserId: req.params?.id }
|
|
);
|
|
|
|
res.status(500).json({ message: 'Failed to toggle user status' });
|
|
}
|
|
};
|
|
|
|
// Get all users for superadmin (across all tenants)
|
|
export const getAllUsers = async (req: AuthenticatedRequest, res: Response) => {
|
|
try {
|
|
// Log superadmin user list access
|
|
elkService.logBusinessEvent('superadmin_users_list_accessed', {
|
|
accessedBy: req.user!.id
|
|
}, req.user!.id, 'system');
|
|
|
|
// Get all users with tenant information
|
|
const users = await User.find({})
|
|
.populate('tenant', 'name')
|
|
.select('-password')
|
|
.sort({ createdAt: -1 });
|
|
|
|
res.json(users);
|
|
} catch (error) {
|
|
console.error('Error fetching all users:', error);
|
|
|
|
elkService.logSystemError(
|
|
error instanceof Error ? error : new Error('Unknown error'),
|
|
'superadmin_users_list',
|
|
{ accessedBy: req.user?.id }
|
|
);
|
|
|
|
res.status(500).json({ message: 'Failed to fetch users' });
|
|
}
|
|
};
|
|
|
|
// Reset user password for superadmin
|
|
export const resetUserPassword = async (req: AuthenticatedRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { newPassword } = req.body;
|
|
|
|
if (!newPassword || newPassword.length < 6) {
|
|
return res.status(400).json({ message: 'Password must be at least 6 characters long' });
|
|
}
|
|
|
|
// Log password reset attempt
|
|
elkService.logBusinessEvent('superadmin_password_reset_attempted', {
|
|
resetBy: req.user!.id,
|
|
targetUserId: id
|
|
}, req.user!.id, 'system');
|
|
|
|
// Find user
|
|
const user = await User.findById(id);
|
|
if (!user) {
|
|
elkService.logBusinessEvent('superadmin_password_reset_failed', {
|
|
resetBy: req.user!.id,
|
|
targetUserId: id,
|
|
reason: 'user_not_found'
|
|
}, req.user!.id, 'system');
|
|
|
|
return res.status(404).json({ message: 'User not found' });
|
|
}
|
|
|
|
// Hash new password
|
|
const salt = await bcrypt.genSalt(10);
|
|
const hashedPassword = await bcrypt.hash(newPassword, salt);
|
|
|
|
// Update password
|
|
user.password = hashedPassword;
|
|
await user.save();
|
|
|
|
// Log successful password reset
|
|
elkService.logBusinessEvent('superadmin_password_reset_successful', {
|
|
resetBy: req.user!.id,
|
|
targetUserId: id,
|
|
targetUserEmail: user.email,
|
|
targetUserTenant: user.tenant
|
|
}, req.user!.id, 'system');
|
|
|
|
res.json({ message: 'Password reset successfully' });
|
|
} catch (error) {
|
|
console.error('Error resetting user password:', error);
|
|
|
|
elkService.logSystemError(
|
|
error instanceof Error ? error : new Error('Unknown error'),
|
|
'superadmin_password_reset',
|
|
{ resetBy: req.user?.id, targetUserId: req.params?.id }
|
|
);
|
|
|
|
res.status(500).json({ message: 'Failed to reset password' });
|
|
}
|
|
};
|
|
|
|
// Update user for superadmin (across all tenants)
|
|
export const updateUserBySuperAdmin = async (req: AuthenticatedRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { name, email, role } = req.body;
|
|
|
|
// Log user update attempt
|
|
elkService.logBusinessEvent('superadmin_user_update_attempted', {
|
|
updatedBy: req.user!.id,
|
|
targetUserId: id,
|
|
updateData: { name, email, role }
|
|
}, req.user!.id, 'system');
|
|
|
|
// Find user
|
|
const user = await User.findById(id);
|
|
if (!user) {
|
|
elkService.logBusinessEvent('superadmin_user_update_failed', {
|
|
updatedBy: req.user!.id,
|
|
targetUserId: id,
|
|
reason: 'user_not_found'
|
|
}, req.user!.id, 'system');
|
|
|
|
return res.status(404).json({ message: 'User not found' });
|
|
}
|
|
|
|
// Check if email is being changed and if it's already in use
|
|
if (email !== user.email) {
|
|
const existingUser = await User.findOne({ email, _id: { $ne: id } });
|
|
if (existingUser) {
|
|
elkService.logBusinessEvent('superadmin_user_update_failed', {
|
|
updatedBy: req.user!.id,
|
|
targetUserId: id,
|
|
reason: 'email_already_in_use',
|
|
conflictingEmail: email
|
|
}, req.user!.id, 'system');
|
|
|
|
return res.status(400).json({ message: 'Email already in use' });
|
|
}
|
|
}
|
|
|
|
// Store original values for logging
|
|
const originalData = {
|
|
name: user.name,
|
|
email: user.email,
|
|
role: user.role
|
|
};
|
|
|
|
// Update user
|
|
user.name = name;
|
|
user.email = email;
|
|
user.role = role;
|
|
|
|
await user.save();
|
|
|
|
// Log successful user update
|
|
elkService.logBusinessEvent('superadmin_user_updated', {
|
|
updatedBy: req.user!.id,
|
|
targetUserId: id,
|
|
targetUserTenant: user.tenant,
|
|
originalData,
|
|
newData: { name, email, role }
|
|
}, req.user!.id, 'system');
|
|
|
|
res.json({
|
|
id: user._id,
|
|
name: user.name,
|
|
email: user.email,
|
|
role: user.role,
|
|
isActive: user.isActive
|
|
});
|
|
} catch (error) {
|
|
console.error('Error updating user:', error);
|
|
|
|
elkService.logSystemError(
|
|
error instanceof Error ? error : new Error('Unknown error'),
|
|
'superadmin_user_update',
|
|
{ updatedBy: req.user?.id, targetUserId: req.params?.id }
|
|
);
|
|
|
|
res.status(500).json({ message: 'Failed to update user' });
|
|
}
|
|
};
|
|
|
|
// Superadmin function to activate users across all tenants
|
|
export const superAdminToggleUserStatus = async (req: AuthenticatedRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
// Only superusers can use this endpoint
|
|
if (req.user!.role !== 'superuser') {
|
|
return res.status(403).json({ message: 'Access denied. Superuser role required.' });
|
|
}
|
|
|
|
// Log superadmin user status toggle attempt
|
|
elkService.logBusinessEvent('superadmin_user_status_toggle_attempted', {
|
|
toggledBy: req.user!.id,
|
|
targetUserId: id
|
|
}, req.user!.id, 'system');
|
|
|
|
// Find user across all tenants (no tenant restriction)
|
|
const user = await User.findById(id);
|
|
if (!user) {
|
|
elkService.logBusinessEvent('superadmin_user_status_toggle_failed', {
|
|
toggledBy: req.user!.id,
|
|
targetUserId: id,
|
|
reason: 'user_not_found'
|
|
}, req.user!.id, 'system');
|
|
|
|
return res.status(404).json({ message: 'User not found' });
|
|
}
|
|
|
|
// Store original status for logging
|
|
const originalStatus = user.isActive;
|
|
|
|
// Toggle status
|
|
user.isActive = !user.isActive;
|
|
await user.save();
|
|
|
|
// Log successful status toggle
|
|
elkService.logBusinessEvent('superadmin_user_status_toggled', {
|
|
toggledBy: req.user!.id,
|
|
targetUserId: id,
|
|
targetUserTenant: user.tenant,
|
|
originalStatus,
|
|
newStatus: user.isActive
|
|
}, req.user!.id, 'system');
|
|
|
|
res.json({
|
|
id: user._id,
|
|
isActive: user.isActive,
|
|
message: user.isActive ? 'User activated successfully' : 'User deactivated successfully'
|
|
});
|
|
} catch (error) {
|
|
console.error('Error in superadmin toggle user status:', error);
|
|
|
|
// Log the error
|
|
elkService.logSystemError(
|
|
error instanceof Error ? error : new Error('Unknown error'),
|
|
'superadmin_user_status_toggle',
|
|
{ toggledBy: req.user?.id, targetUserId: req.params?.id }
|
|
);
|
|
|
|
res.status(500).json({ message: 'Failed to toggle user status' });
|
|
}
|
|
}; |