Skip to content

Commit 8881d8f

Browse files
FancMa01ydahal1
andauthored
Mfancher/archive users (#1025)
* Fixes and change model and migration for user to add column * set last login whenever logins or password resets occur * Remove Inactive accounts This sets up a job that runs every 12 hours to delete user accounts that are over 180 days since last login * clarify messaging * Fix Messaging and Remove Admin email since workflow is no longer required * rename function to clarify * Create user archive table Created user archive table with all of the same columns of User, without Hash, adding in removed By and removed At. This table will be used to hold deleted users * Update user_archive.js * remove user method created and introduced * remove unique email constraint on archive * Finish archive user * used delete user util function to clean up unverified user' --------- Co-authored-by: yadhap Dahal <yadhap.dahal@lexisnexisrisk.com>
1 parent bb2d216 commit 8881d8f

File tree

6 files changed

+90
-18
lines changed

6 files changed

+90
-18
lines changed

Tombolo/client-reactjs/src/components/admin/userManagement/ActionButton.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const UserManagementActionButton = ({
4444
</Menu.Item>
4545
<Menu.Item key="3" disabled={selectedRows.length < 2}>
4646
<Popconfirm
47-
title={`Are you sure you want to delete selected ${selectedRows.length} users?. `}
47+
title={`Are you sure you want to delete selected ${selectedRows.length} users? `}
4848
okButtonProps={{ type: 'primary', danger: true }}
4949
okText="Delete"
5050
onConfirm={deleteSelected}>

Tombolo/server/controllers/authController.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -575,8 +575,7 @@ const resetTempPassword = async (req, res) => {
575575
await generateAndSetCSRFToken(req, res, accessToken);
576576

577577
//set last login
578-
579-
await setLastLogin(newUser);
578+
await setLastLogin(user);
580579

581580
// User data obj to send to the client
582581
const userObj = {
@@ -1129,7 +1128,6 @@ const requestAccess = async (req, res) => {
11291128

11301129
// Resend verification code - user provides email
11311130
const resendVerificationCode = async (req, res) => {
1132-
t;
11331131
try {
11341132
const { email } = req.body;
11351133

Tombolo/server/controllers/userController.js

+27-8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
checkPasswordSecurityViolations,
1515
setPreviousPasswords,
1616
generatePassword,
17+
deleteUser: deleteUserUtil,
1718
} = require("../utils/authUtil");
1819

1920
// Constants
@@ -29,11 +30,11 @@ const deleteUser = async (req, res) => {
2930
try {
3031
const { id } = req.params;
3132

32-
const deletedCount = await User.destroy({ where: { id } });
33+
const deleted = await deleteUserUtil(id, "Admin Removal");
3334

3435
// If deleted count is 0, user not found
35-
if (deletedCount === 0) {
36-
throw { status: 404, message: "User not found" };
36+
if (!deleted) {
37+
throw { status: 404, message: "Error Removing User." };
3738
}
3839

3940
// User successfully deleted
@@ -225,14 +226,32 @@ const changePassword = async (req, res) => {
225226
const bulkDeleteUsers = async (req, res) => {
226227
try {
227228
const { ids } = req.body;
228-
const deletedCount = await User.destroy({ where: { id: ids } });
229229

230-
// If deleted count is 0, user not found
231-
if (deletedCount === 0) {
232-
throw { status: 404, message: "Users not found" };
230+
let deletedCount = 0;
231+
let idsCount = ids.length;
232+
// Loop through each user and delete
233+
for (let id of ids) {
234+
const deleted = await deleteUserUtil(id, "Admin Removal");
235+
if (deleted) {
236+
deletedCount++;
237+
}
233238
}
239+
240+
if (deletedCount !== idsCount) {
241+
res.status(207).json({
242+
success: false,
243+
message: "Some users could not be deleted",
244+
data: { deletedCount, idsCount },
245+
});
246+
}
247+
248+
res.status(200).json({
249+
success: true,
250+
message: "Users deleted successfully",
251+
data: { deletedCount },
252+
});
234253
} catch (err) {
235-
logger.error(`Update user applications: ${err.message}`);
254+
logger.error(`Bulk Delete Users: ${err.message}`);
236255
res
237256
.status(err.status || 500)
238257
.json({ success: false, message: err.message });

Tombolo/server/jobs/userManagement/accountDelete.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ const { v4: uuidv4 } = require("uuid");
55

66
//Local Imports
77
const models = require("../../models");
8-
const { trimURL, getSupportContactEmails } = require("../../utils/authUtil");
9-
const { trim } = require("lodash");
8+
const { trimURL, deleteUser } = require("../../utils/authUtil");
109

1110
// Constants
1211
const User = models.user;
@@ -201,15 +200,24 @@ const updateUserAndSendNotification = async (user, daysToExpiry, version) => {
201200
});
202201

203202
// delete user account
204-
// TODO -- Move user to archive table
205-
await user.destroy();
203+
const deleted = await deleteUser(userInternal.id, "Inactivity");
204+
if (!deleted) {
205+
parentPort &&
206+
parentPort.postMessage({
207+
level: "error",
208+
text:
209+
"Failed to delete userwith email " +
210+
userInternal.email +
211+
", due to inactivity.",
212+
});
213+
}
206214
}
207215
}
208216

209217
parentPort &&
210218
parentPort.postMessage({
211219
level: "info",
212-
text: "Account lock check job completed ...",
220+
text: "Account Delete check job completed ...",
213221
});
214222
} catch (error) {
215223
parentPort &&

Tombolo/server/jobs/userManagement/removeUnverifiedUsers.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// imports
22
const { parentPort } = require("worker_threads");
33
const { Op } = require("sequelize");
4+
const { deleteUser } = require("../../utils/authUtil");
45

56

67
//Local Imports
@@ -27,7 +28,7 @@ const { user} = models;
2728
parentPort && parentPort.postMessage({level: "info", text : `Number of unverified users to be removed: ${unverifiedUsers.length}`});
2829

2930
for (const user of unverifiedUsers) {
30-
await user.destroy();
31+
await deleteUser(user.id, "unverified user deleted by system");
3132
}
3233

3334
parentPort && parentPort.postMessage({level: "info", text : "Job to remove unverified user completed ..."});

Tombolo/server/utils/authUtil.js

+46
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const bcrypt = require("bcryptjs");
1313
const User = model.user;
1414
const UserRoles = model.UserRoles;
1515
const RoleTypes = model.RoleTypes;
16+
const userArchive = model.userArchive;
1617
const user_application = model.user_application;
1718
const Application = model.application;
1819
const InstanceSettings = model.instance_settings;
@@ -408,6 +409,50 @@ const setLastLogin = async (user) => {
408409
return;
409410
};
410411

412+
const deleteUser = async (id, reason) => {
413+
try {
414+
if (!reason || reason === "") {
415+
throw new Error("Reason for deletion is required");
416+
}
417+
418+
//get user
419+
const user = await User.findByPk(id);
420+
421+
if (!user) {
422+
throw new Error("User not found");
423+
}
424+
425+
const removedAt = Date.now();
426+
const removedBy = reason;
427+
428+
//remove hash from user
429+
user.dataValues.hash = null;
430+
431+
const archivedUser = await userArchive.create({
432+
...user.dataValues,
433+
removedAt,
434+
removedBy,
435+
});
436+
437+
if (!archivedUser) {
438+
throw new Error("Failed to archive user");
439+
}
440+
441+
//hard delete without paranoid
442+
await User.destroy({
443+
where: {
444+
id: id,
445+
},
446+
force: true,
447+
});
448+
449+
return true;
450+
} catch (e) {
451+
logger.error("Error while deleting user:" + e);
452+
return false;
453+
}
454+
};
455+
411456
//Exports
412457
module.exports = {
413458
generateAccessToken,
@@ -427,4 +472,5 @@ module.exports = {
427472
generatePassword,
428473
setLastLogin,
429474
setLastLoginAndReturn,
475+
deleteUser,
430476
};

0 commit comments

Comments
 (0)