From 16657a715fc56af9dd6bdec8496456b557d8a2a0 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Tue, 3 Jun 2025 23:22:48 -0700 Subject: [PATCH 01/17] add audit log schema --- api/main_endpoints/models/AuditLog.js | 5 +++-- api/main_endpoints/util/auditActions.js | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 api/main_endpoints/util/auditActions.js diff --git a/api/main_endpoints/models/AuditLog.js b/api/main_endpoints/models/AuditLog.js index 1618b5d8f..7a51d9358 100644 --- a/api/main_endpoints/models/AuditLog.js +++ b/api/main_endpoints/models/AuditLog.js @@ -1,6 +1,6 @@ const mongoose = require('mongoose'); const Schema = mongoose.Schema; -const AuditLogActions = require('../util/auditLogActions'); +const AuditActions = require('../util/auditActions'); const AuditLogSchema = new Schema( { @@ -11,11 +11,12 @@ const AuditLogSchema = new Schema( }, action: { type: String, - enum: Object.keys(AuditLogActions), + enum: Object.keys(AuditActions), required: true, }, documentId: { type: Schema.Types.ObjectId, + ref: 'User', default: null, }, details: { diff --git a/api/main_endpoints/util/auditActions.js b/api/main_endpoints/util/auditActions.js new file mode 100644 index 000000000..bf0519d44 --- /dev/null +++ b/api/main_endpoints/util/auditActions.js @@ -0,0 +1,12 @@ +const AuditActions = { + LOG_IN: 'LOG_IN', + UPDATE_USER: 'UPDATE_USER', + PRINT_PAGE: 'PRINT_PAGE', + SIGN_UP: 'SIGN_UP', + VERIFY_EMAIL: 'VERIFY_EMAIL', + EMAIL_SENT: 'EMAIL_SENT', + CHANGE_PW: 'CHANGE_PW', + RESET_PW: 'RESET_PW', +}; + +module.exports = AuditActions; From d2a952a5aef4a058b33debad8caa57622a05cff4 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Wed, 4 Jun 2025 15:04:38 -0700 Subject: [PATCH 02/17] add audit util + login audit log --- api/main_endpoints/routes/Auth.js | 20 ++++++++++++-------- api/util/logAudit.js | 16 ++++++++++++++++ test/api/User.js | 26 ++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 api/util/logAudit.js diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index 7d7798834..729d85a6d 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -30,8 +30,8 @@ const PASSWORD_RESET_EXPIRATION = require('../../util/constants').PASSWORD_RESET const { sendVerificationEmail, sendPasswordReset } = require('../util/emailHelpers'); const { userWithEmailExists, checkIfPageCountResets, findPasswordReset } = require('../util/userHelpers'); -const AuditLogActions = require('../util/auditLogActions.js'); -const AuditLog = require('../models/AuditLog.js'); +const logAudit = require('../../util/logAudit.js') +const AuditActions = require('../util/auditActions.js') // Register a member router.post('/register', async (req, res) => { @@ -176,16 +176,20 @@ router.post('/login', function(req, res) { }; user .save() - .then(() => { + .then(async () => { const token = jwt.sign( userToBeSigned, config.secretKey, jwtOptions ); - // Create audit log on successful sign-in - AuditLog.create({ + + // audit log successful login + await logAudit({ userId: user._id, - action: AuditLogActions.LOG_IN, - details: { email: user.email } - }).catch(logger.error); + action: AuditActions.LOG_IN, + details: { + ip: req.ip, + userAgent: req.headers['user-agent'] + } + }) res.json({ token: 'JWT ' + token }); }) diff --git a/api/util/logAudit.js b/api/util/logAudit.js new file mode 100644 index 000000000..604380f05 --- /dev/null +++ b/api/util/logAudit.js @@ -0,0 +1,16 @@ +const AuditLog = require('../main_endpoints/models/AuditLog'); + +async function logAudit({ userId, action, documentId = null, details = {} }) { + try { + await AuditLog.create({ + userId, + action, + documentId, + details, + }); + } catch (error) { + console.error(error); + } +} + +module.exports = logAudit; diff --git a/test/api/User.js b/test/api/User.js index 6994c47c5..c72743822 100644 --- a/test/api/User.js +++ b/test/api/User.js @@ -447,4 +447,30 @@ describe('User', () => { expect(result).to.have.status(UNAUTHORIZED); }); }); + + describe('/POST login', () => { + it('Should create an audit log entry on successful login', async() => { + const loginPayload = { + email: 'a@b.c', + password: 'Passw0rd' + } + + const res = await test.sendPostRequest('/api/Auth/login', loginPayload) + + expect(res).to.have.status(OK) + expect(res.body).to.have.property('token') + + const AuditActions = require('../../api/main_endpoints/util/auditActions.js') + const AuditLog = require('../../api/main_endpoints/models/AuditLog.js') + + const auditEntry = await AuditLog.findOne({ + action: AuditActions.LOG_IN, + 'details.email': loginPayload.email, + }).lean() + + expect(auditEntry).to.exist + expect(auditEntry).to.have.property('userId') + expect(auditEntry.details).to.have.property('email', loginPayload.email) + }) + }) }); From 3a55b4dce598b30361ae5de9e78969424031185f Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Wed, 4 Jun 2025 15:10:49 -0700 Subject: [PATCH 03/17] move audit-log imports to top --- test/api/User.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/api/User.js b/test/api/User.js index c72743822..68a431941 100644 --- a/test/api/User.js +++ b/test/api/User.js @@ -4,6 +4,9 @@ process.env.NODE_ENV = 'test'; const User = require('../../api/main_endpoints/models/User.js'); +const AuditActions = require('../../api/main_endpoints/util/auditActions.js') +const AuditLog = require('../../api/main_endpoints/models/AuditLog.js') + // Require the dev-dependencies const chai = require('chai'); const mongoose = require('mongoose'); @@ -460,9 +463,6 @@ describe('User', () => { expect(res).to.have.status(OK) expect(res.body).to.have.property('token') - const AuditActions = require('../../api/main_endpoints/util/auditActions.js') - const AuditLog = require('../../api/main_endpoints/models/AuditLog.js') - const auditEntry = await AuditLog.findOne({ action: AuditActions.LOG_IN, 'details.email': loginPayload.email, From 077876a3363f327a6aa9c239991279196efc5604 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Wed, 4 Jun 2025 19:41:24 -0700 Subject: [PATCH 04/17] Add test to ensure login succeeds even if audit logging fails + minor util and schema changes --- api/main_endpoints/models/AuditLog.js | 1 - api/main_endpoints/routes/Auth.js | 2 +- api/util/logAudit.js | 4 ++-- test/api/User.js | 25 +++++++++++++++++++++++-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/api/main_endpoints/models/AuditLog.js b/api/main_endpoints/models/AuditLog.js index 7a51d9358..27523126e 100644 --- a/api/main_endpoints/models/AuditLog.js +++ b/api/main_endpoints/models/AuditLog.js @@ -16,7 +16,6 @@ const AuditLogSchema = new Schema( }, documentId: { type: Schema.Types.ObjectId, - ref: 'User', default: null, }, details: { diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index 729d85a6d..6fa261d71 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -182,7 +182,7 @@ router.post('/login', function(req, res) { ); // audit log successful login - await logAudit({ + logAudit({ userId: user._id, action: AuditActions.LOG_IN, details: { diff --git a/api/util/logAudit.js b/api/util/logAudit.js index 604380f05..245824c8d 100644 --- a/api/util/logAudit.js +++ b/api/util/logAudit.js @@ -1,8 +1,8 @@ const AuditLog = require('../main_endpoints/models/AuditLog'); -async function logAudit({ userId, action, documentId = null, details = {} }) { +function logAudit({ userId, action, documentId = null, details = {} }) { try { - await AuditLog.create({ + AuditLog.create({ userId, action, documentId, diff --git a/test/api/User.js b/test/api/User.js index 68a431941..bc10d1a60 100644 --- a/test/api/User.js +++ b/test/api/User.js @@ -6,6 +6,7 @@ const User = require('../../api/main_endpoints/models/User.js'); const AuditActions = require('../../api/main_endpoints/util/auditActions.js') const AuditLog = require('../../api/main_endpoints/models/AuditLog.js') +const AuditUtil = require('../../api/util/logAudit.js') // Require the dev-dependencies const chai = require('chai'); @@ -452,12 +453,16 @@ describe('User', () => { }); describe('/POST login', () => { - it('Should create an audit log entry on successful login', async() => { - const loginPayload = { + const loginPayload = { email: 'a@b.c', password: 'Passw0rd' } + before(async () => { + await AuditLog.deleteMany({}) + }) + + it('Should create an audit log entry on successful login', async() => { const res = await test.sendPostRequest('/api/Auth/login', loginPayload) expect(res).to.have.status(OK) @@ -472,5 +477,21 @@ describe('User', () => { expect(auditEntry).to.have.property('userId') expect(auditEntry.details).to.have.property('email', loginPayload.email) }) + + it('Should return 200 even if audit logging fails', async() => { + const logAuditStub = sinon.stub(AuditUtil).throws(new Error('Audit logging failed')) + try { + const res = await test.sendPostRequest('/api/Auth/login', loginPayload) + + expect(res).to.have.status(OK) + expect(res.body).to.have.property('token') + + const auditEntry = await AuditLog.findOne().lean() + expect(auditEntry.to.not.exist) + + } finally { + logAuditStub.restore() + } + }) }) }); From 2a840aa269609d25448d77ede88cf603d3efe7c6 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Fri, 6 Jun 2025 14:26:51 -0700 Subject: [PATCH 05/17] change audit file names --- .../util/{auditActions.js => auditLogctions.js} | 4 ++-- api/util/{logAudit.js => auditLog.js} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename api/main_endpoints/util/{auditActions.js => auditLogctions.js} (78%) rename api/util/{logAudit.js => auditLog.js} (100%) diff --git a/api/main_endpoints/util/auditActions.js b/api/main_endpoints/util/auditLogctions.js similarity index 78% rename from api/main_endpoints/util/auditActions.js rename to api/main_endpoints/util/auditLogctions.js index bf0519d44..7fb7a8814 100644 --- a/api/main_endpoints/util/auditActions.js +++ b/api/main_endpoints/util/auditLogctions.js @@ -1,4 +1,4 @@ -const AuditActions = { +const AuditLogctions = { LOG_IN: 'LOG_IN', UPDATE_USER: 'UPDATE_USER', PRINT_PAGE: 'PRINT_PAGE', @@ -9,4 +9,4 @@ const AuditActions = { RESET_PW: 'RESET_PW', }; -module.exports = AuditActions; +module.exports = AuditLogctions; diff --git a/api/util/logAudit.js b/api/util/auditLog.js similarity index 100% rename from api/util/logAudit.js rename to api/util/auditLog.js From d5f979d8482f6f0a9ed46a9b74a12e8abc16fb26 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Fri, 6 Jun 2025 17:22:45 -0700 Subject: [PATCH 06/17] fix audit import names --- api/main_endpoints/models/AuditLog.js | 4 ++-- api/main_endpoints/routes/Auth.js | 6 +++--- test/api/User.js | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/main_endpoints/models/AuditLog.js b/api/main_endpoints/models/AuditLog.js index 27523126e..e82201448 100644 --- a/api/main_endpoints/models/AuditLog.js +++ b/api/main_endpoints/models/AuditLog.js @@ -1,6 +1,6 @@ const mongoose = require('mongoose'); const Schema = mongoose.Schema; -const AuditActions = require('../util/auditActions'); +const AuditLogctions = require('../util/auditLogActions'); const AuditLogSchema = new Schema( { @@ -11,7 +11,7 @@ const AuditLogSchema = new Schema( }, action: { type: String, - enum: Object.keys(AuditActions), + enum: Object.keys(AuditLogctions), required: true, }, documentId: { diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index 6fa261d71..e67a1fcbf 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -30,8 +30,8 @@ const PASSWORD_RESET_EXPIRATION = require('../../util/constants').PASSWORD_RESET const { sendVerificationEmail, sendPasswordReset } = require('../util/emailHelpers'); const { userWithEmailExists, checkIfPageCountResets, findPasswordReset } = require('../util/userHelpers'); -const logAudit = require('../../util/logAudit.js') -const AuditActions = require('../util/auditActions.js') +const logAudit = require('../../util/auditLog.js') +const AuditLogctions = require('../util/auditLogctions.js') // Register a member router.post('/register', async (req, res) => { @@ -184,7 +184,7 @@ router.post('/login', function(req, res) { // audit log successful login logAudit({ userId: user._id, - action: AuditActions.LOG_IN, + action: AuditLogctions.LOG_IN, details: { ip: req.ip, userAgent: req.headers['user-agent'] diff --git a/test/api/User.js b/test/api/User.js index bc10d1a60..fef0ccc7b 100644 --- a/test/api/User.js +++ b/test/api/User.js @@ -4,9 +4,9 @@ process.env.NODE_ENV = 'test'; const User = require('../../api/main_endpoints/models/User.js'); -const AuditActions = require('../../api/main_endpoints/util/auditActions.js') +const AuditLogctions = require('../../api/main_endpoints/util/auditLogctions.js') const AuditLog = require('../../api/main_endpoints/models/AuditLog.js') -const AuditUtil = require('../../api/util/logAudit.js') +const AuditUtil = require('../../api/util/auditLog.js') // Require the dev-dependencies const chai = require('chai'); @@ -469,7 +469,7 @@ describe('User', () => { expect(res.body).to.have.property('token') const auditEntry = await AuditLog.findOne({ - action: AuditActions.LOG_IN, + action: AuditLogctions.LOG_IN, 'details.email': loginPayload.email, }).lean() From 7570ec9e954b5dfa5b69756b118920dd420706d4 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Fri, 6 Jun 2025 17:26:23 -0700 Subject: [PATCH 07/17] fix audit import names (2) --- api/main_endpoints/models/AuditLog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/main_endpoints/models/AuditLog.js b/api/main_endpoints/models/AuditLog.js index e82201448..a77745576 100644 --- a/api/main_endpoints/models/AuditLog.js +++ b/api/main_endpoints/models/AuditLog.js @@ -1,6 +1,6 @@ const mongoose = require('mongoose'); const Schema = mongoose.Schema; -const AuditLogctions = require('../util/auditLogActions'); +const AuditLogctions = require('../util/auditLogctions'); const AuditLogSchema = new Schema( { From 26b58e0b968f587375b689bd4826f1652962353d Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Sat, 7 Jun 2025 22:21:46 -0700 Subject: [PATCH 08/17] remove unnecessary audit details --- api/main_endpoints/routes/Auth.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index e67a1fcbf..9d2e6830d 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -184,11 +184,7 @@ router.post('/login', function(req, res) { // audit log successful login logAudit({ userId: user._id, - action: AuditLogctions.LOG_IN, - details: { - ip: req.ip, - userAgent: req.headers['user-agent'] - } + action: AuditLogctions.LOG_IN }) res.json({ token: 'JWT ' + token }); From 70a83c35780736cff43d69b77f809813532657e3 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Mon, 16 Jun 2025 20:55:46 -0700 Subject: [PATCH 09/17] move login audit log tests to correct file and update helper functions --- api/main_endpoints/models/AuditLog.js | 4 +-- api/main_endpoints/routes/Auth.js | 14 ++++---- api/util/auditLog.js | 9 ++--- test/api/User.js | 49 ++------------------------- 4 files changed, 17 insertions(+), 59 deletions(-) diff --git a/api/main_endpoints/models/AuditLog.js b/api/main_endpoints/models/AuditLog.js index a77745576..1618b5d8f 100644 --- a/api/main_endpoints/models/AuditLog.js +++ b/api/main_endpoints/models/AuditLog.js @@ -1,6 +1,6 @@ const mongoose = require('mongoose'); const Schema = mongoose.Schema; -const AuditLogctions = require('../util/auditLogctions'); +const AuditLogActions = require('../util/auditLogActions'); const AuditLogSchema = new Schema( { @@ -11,7 +11,7 @@ const AuditLogSchema = new Schema( }, action: { type: String, - enum: Object.keys(AuditLogctions), + enum: Object.keys(AuditLogActions), required: true, }, documentId: { diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index 9d2e6830d..b1aafd952 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -30,8 +30,8 @@ const PASSWORD_RESET_EXPIRATION = require('../../util/constants').PASSWORD_RESET const { sendVerificationEmail, sendPasswordReset } = require('../util/emailHelpers'); const { userWithEmailExists, checkIfPageCountResets, findPasswordReset } = require('../util/userHelpers'); -const logAudit = require('../../util/auditLog.js') -const AuditLogctions = require('../util/auditLogctions.js') +const AuditLogActions = require('../util/auditLogActions.js'); +const AuditLog = require('../models/AuditLog.js'); // Register a member router.post('/register', async (req, res) => { @@ -180,12 +180,12 @@ router.post('/login', function(req, res) { const token = jwt.sign( userToBeSigned, config.secretKey, jwtOptions ); - - // audit log successful login - logAudit({ + // Create audit log on successful sign-in + AuditLog.create({ userId: user._id, - action: AuditLogctions.LOG_IN - }) + action: AuditLogActions.LOG_IN, + details: { email: user.email } + }).catch(logger.error); res.json({ token: 'JWT ' + token }); }) diff --git a/api/util/auditLog.js b/api/util/auditLog.js index 245824c8d..42029059f 100644 --- a/api/util/auditLog.js +++ b/api/util/auditLog.js @@ -1,16 +1,17 @@ const AuditLog = require('../main_endpoints/models/AuditLog'); +const logger = require('../util/logger'); -function logAudit({ userId, action, documentId = null, details = {} }) { +async function logAudit({ userId, action, documentId = null, details = {} }) { try { - AuditLog.create({ + await AuditLog.create({ userId, action, documentId, details, }); } catch (error) { - console.error(error); + logger.error('Audit log failed to create:', error); } } -module.exports = logAudit; +module.exports = {logAudit}; diff --git a/test/api/User.js b/test/api/User.js index fef0ccc7b..ebea8b453 100644 --- a/test/api/User.js +++ b/test/api/User.js @@ -4,9 +4,9 @@ process.env.NODE_ENV = 'test'; const User = require('../../api/main_endpoints/models/User.js'); -const AuditLogctions = require('../../api/main_endpoints/util/auditLogctions.js') -const AuditLog = require('../../api/main_endpoints/models/AuditLog.js') -const AuditUtil = require('../../api/util/auditLog.js') +const AuditLogctions = require('../../api/main_endpoints/util/auditLogctions.js'); +const AuditLog = require('../../api/main_endpoints/models/AuditLog.js'); +const AuditUtil = require('../../api/util/auditLog.js'); // Require the dev-dependencies const chai = require('chai'); @@ -451,47 +451,4 @@ describe('User', () => { expect(result).to.have.status(UNAUTHORIZED); }); }); - - describe('/POST login', () => { - const loginPayload = { - email: 'a@b.c', - password: 'Passw0rd' - } - - before(async () => { - await AuditLog.deleteMany({}) - }) - - it('Should create an audit log entry on successful login', async() => { - const res = await test.sendPostRequest('/api/Auth/login', loginPayload) - - expect(res).to.have.status(OK) - expect(res.body).to.have.property('token') - - const auditEntry = await AuditLog.findOne({ - action: AuditLogctions.LOG_IN, - 'details.email': loginPayload.email, - }).lean() - - expect(auditEntry).to.exist - expect(auditEntry).to.have.property('userId') - expect(auditEntry.details).to.have.property('email', loginPayload.email) - }) - - it('Should return 200 even if audit logging fails', async() => { - const logAuditStub = sinon.stub(AuditUtil).throws(new Error('Audit logging failed')) - try { - const res = await test.sendPostRequest('/api/Auth/login', loginPayload) - - expect(res).to.have.status(OK) - expect(res.body).to.have.property('token') - - const auditEntry = await AuditLog.findOne().lean() - expect(auditEntry.to.not.exist) - - } finally { - logAuditStub.restore() - } - }) - }) }); From 8e73193744bedc8460792f2a211c5a78566000e3 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Mon, 16 Jun 2025 20:59:56 -0700 Subject: [PATCH 10/17] remove async from audit log at login endpoint --- api/main_endpoints/routes/Auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index b1aafd952..7d7798834 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -176,7 +176,7 @@ router.post('/login', function(req, res) { }; user .save() - .then(async () => { + .then(() => { const token = jwt.sign( userToBeSigned, config.secretKey, jwtOptions ); From 4472600a50c6e21d234751941dec675dd033acdb Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Wed, 18 Jun 2025 17:52:40 -0700 Subject: [PATCH 11/17] remove audit util function + fix file names --- api/main_endpoints/util/auditLogctions.js | 12 ------------ api/util/auditLog.js | 17 ----------------- test/api/User.js | 4 ---- 3 files changed, 33 deletions(-) delete mode 100644 api/main_endpoints/util/auditLogctions.js delete mode 100644 api/util/auditLog.js diff --git a/api/main_endpoints/util/auditLogctions.js b/api/main_endpoints/util/auditLogctions.js deleted file mode 100644 index 7fb7a8814..000000000 --- a/api/main_endpoints/util/auditLogctions.js +++ /dev/null @@ -1,12 +0,0 @@ -const AuditLogctions = { - LOG_IN: 'LOG_IN', - UPDATE_USER: 'UPDATE_USER', - PRINT_PAGE: 'PRINT_PAGE', - SIGN_UP: 'SIGN_UP', - VERIFY_EMAIL: 'VERIFY_EMAIL', - EMAIL_SENT: 'EMAIL_SENT', - CHANGE_PW: 'CHANGE_PW', - RESET_PW: 'RESET_PW', -}; - -module.exports = AuditLogctions; diff --git a/api/util/auditLog.js b/api/util/auditLog.js deleted file mode 100644 index 42029059f..000000000 --- a/api/util/auditLog.js +++ /dev/null @@ -1,17 +0,0 @@ -const AuditLog = require('../main_endpoints/models/AuditLog'); -const logger = require('../util/logger'); - -async function logAudit({ userId, action, documentId = null, details = {} }) { - try { - await AuditLog.create({ - userId, - action, - documentId, - details, - }); - } catch (error) { - logger.error('Audit log failed to create:', error); - } -} - -module.exports = {logAudit}; diff --git a/test/api/User.js b/test/api/User.js index ebea8b453..6994c47c5 100644 --- a/test/api/User.js +++ b/test/api/User.js @@ -4,10 +4,6 @@ process.env.NODE_ENV = 'test'; const User = require('../../api/main_endpoints/models/User.js'); -const AuditLogctions = require('../../api/main_endpoints/util/auditLogctions.js'); -const AuditLog = require('../../api/main_endpoints/models/AuditLog.js'); -const AuditUtil = require('../../api/util/auditLog.js'); - // Require the dev-dependencies const chai = require('chai'); const mongoose = require('mongoose'); From 52eb9c818942778b98b5386b47cb885584d02248 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Sun, 8 Jun 2025 18:42:59 -0700 Subject: [PATCH 12/17] Add audit logging on user registration + corresponding tests --- api/main_endpoints/routes/Auth.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index 7d7798834..4da7cbc10 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -38,6 +38,13 @@ router.post('/register', async (req, res) => { const registrationStatus = await registerUser(req.body); if (registrationStatus.userSaved) { const name = req.body.firstName + ' ' + req.body.lastName; + + logAudit({ + userId: registrationStatus.userId, + action: AuditLogctions.SIGN_UP, + details: {email: req.body.email} + }) + sendVerificationEmail(name, req.body.email); return res.sendStatus(OK); } From 219683a5eedf5475ca14a5027f3c0422fcfc113b Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Mon, 9 Jun 2025 18:12:33 -0700 Subject: [PATCH 13/17] fix audit log to use correct user id --- api/main_endpoints/routes/Auth.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index 4da7cbc10..85e073e8a 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -38,12 +38,15 @@ router.post('/register', async (req, res) => { const registrationStatus = await registerUser(req.body); if (registrationStatus.userSaved) { const name = req.body.firstName + ' ' + req.body.lastName; + const user = await User.findOne({email: req.body.email}) - logAudit({ - userId: registrationStatus.userId, - action: AuditLogctions.SIGN_UP, - details: {email: req.body.email} - }) + if (user) { + logAudit({ + userId: user._id, + action: AuditLogctions.SIGN_UP, + details: {email: req.body.email} + }) + } sendVerificationEmail(name, req.body.email); return res.sendStatus(OK); From df6dfacff954464e0472f24e1d219ca505c1eee1 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Wed, 18 Jun 2025 18:19:45 -0700 Subject: [PATCH 14/17] Update test to ensure audit log entry on successful signup --- api/main_endpoints/routes/Auth.js | 8 ++++---- test/api/Auth.js | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index 85e073e8a..601f2ecb3 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -38,14 +38,14 @@ router.post('/register', async (req, res) => { const registrationStatus = await registerUser(req.body); if (registrationStatus.userSaved) { const name = req.body.firstName + ' ' + req.body.lastName; - const user = await User.findOne({email: req.body.email}) + const user = await User.findOne({email: req.body.email}); if (user) { - logAudit({ + AuditLog.create({ userId: user._id, - action: AuditLogctions.SIGN_UP, + action: AuditLogActions.SIGN_UP, details: {email: req.body.email} - }) + }); } sendVerificationEmail(name, req.body.email); diff --git a/test/api/Auth.js b/test/api/Auth.js index d93728091..2d878e602 100644 --- a/test/api/Auth.js +++ b/test/api/Auth.js @@ -196,6 +196,32 @@ describe('Auth', () => { await User.deleteOne({email: user.email}); } }); + + it('Should create an audit log entry on successful signup', async () => { + const registerPayload = { + email: 'newuser@example.com', + password: 'Passw0rd123!', + firstName: 'Testfirst', + lastName: 'Testlast' + }; + + // ensure Audit log and User DB starts fresh before this test + await AuditLog.deleteMany({}); + await User.deleteOne({email: registerPayload.email}); + + const res = await test.sendPostRequest('/api/Auth/register', registerPayload); + expect(res).to.have.status(OK); + + const auditEntry = await AuditLog.findOne({ + action: AuditLogActions.SIGN_UP, + 'details.email': registerPayload.email + }).lean(); + + expect(auditEntry).to.exist; + expect(auditEntry).to.have.property('userId'); + expect(auditEntry.details).to.have.property('email', registerPayload.email); + + }); }); describe('/POST login', () => { From 22d5236aab933d2c8b12cae86b7e64e3853110c3 Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Fri, 20 Jun 2025 00:32:45 -0700 Subject: [PATCH 15/17] add error logging on failed sign-up audit + minor test fixes --- api/main_endpoints/routes/Auth.js | 2 +- test/api/Auth.js | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/api/main_endpoints/routes/Auth.js b/api/main_endpoints/routes/Auth.js index 601f2ecb3..168646298 100644 --- a/api/main_endpoints/routes/Auth.js +++ b/api/main_endpoints/routes/Auth.js @@ -45,7 +45,7 @@ router.post('/register', async (req, res) => { userId: user._id, action: AuditLogActions.SIGN_UP, details: {email: req.body.email} - }); + }).catch(logger.error); } sendVerificationEmail(name, req.body.email); diff --git a/test/api/Auth.js b/test/api/Auth.js index 2d878e602..e59ff08c4 100644 --- a/test/api/Auth.js +++ b/test/api/Auth.js @@ -212,10 +212,7 @@ describe('Auth', () => { const res = await test.sendPostRequest('/api/Auth/register', registerPayload); expect(res).to.have.status(OK); - const auditEntry = await AuditLog.findOne({ - action: AuditLogActions.SIGN_UP, - 'details.email': registerPayload.email - }).lean(); + const auditEntry = await AuditLog.findOne().lean(); expect(auditEntry).to.exist; expect(auditEntry).to.have.property('userId'); From 87cb11334382c4b8b4f412d15556efbaefa88daf Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Mon, 23 Jun 2025 19:29:51 -0700 Subject: [PATCH 16/17] Rebase --- test/api/User.js | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/test/api/User.js b/test/api/User.js index 6994c47c5..58dc57ada 100644 --- a/test/api/User.js +++ b/test/api/User.js @@ -447,4 +447,93 @@ describe('User', () => { expect(result).to.have.status(UNAUTHORIZED); }); }); + + describe('/POST login', () => { + const loginPayload = { + email: 'a@b.c', + password: 'Passw0rd' + } + + before(async () => { + await AuditLog.deleteMany({}) + }) + + it('Should create an audit log entry on successful login', async() => { + const res = await test.sendPostRequest('/api/Auth/login', loginPayload) + + expect(res).to.have.status(OK) + expect(res.body).to.have.property('token') + + const auditEntry = await AuditLog.findOne({ + action: AuditLogctions.LOG_IN, + 'details.email': loginPayload.email, + }).lean() + + expect(auditEntry).to.exist + expect(auditEntry).to.have.property('userId') + expect(auditEntry.details).to.have.property('email', loginPayload.email) + }) + + it('Should return 200 even if audit logging fails', async() => { + const logAuditStub = sinon.stub(AuditUtil).throws(new Error('Audit logging failed')) + try { + const res = await test.sendPostRequest('/api/Auth/login', loginPayload) + + expect(res).to.have.status(OK) + expect(res.body).to.have.property('token') + + const auditEntry = await AuditLog.findOne().lean() + expect(auditEntry).to.not.exist + + } finally { + logAuditStub.restore() + } + }) + }) + + describe('/POST register', () => { + const registerPayload = { + email: 'newuser@example.com', + password: 'Passw0rd123!', + firstName: 'Testfirst', + lastName: 'Testlast' + } + + before(async() => { + await AuditLog.deleteMany({}) + await User.deleteOne({ email: registerPayload.email }) + }) + + it('Should create an audit log entry on successful signup', async() => { + const res = await test.sendPostRequest('/api/Auth/register', registerPayload) + expect(res).to.have.status(OK) + + const auditEntry = await AuditLog.findOne({ + action: AuditLogctions.SIGN_UP, + 'details.email': registerPayload.email + }).lean() + + expect(auditEntry).to.exist + expect(auditEntry).to.have.property('userId') + expect(auditEntry.details).to.have.property('email', registerPayload.email) + }) + + it('Should return 200 even if audit logging fails', async () => { + const logAuditStub = sinon.stub(AuditUtil, 'logAudit').throws(new Error('Audit logging failed')) + try { + const res = await test.sendPostRequest('/api/Auth/register', { + ...registerPayload, + email: 'anotherEmail@example.com' + }) + + expect(res).to.have.status(OK) + + const auditEntry = await AuditLog.findOne({'details.email': 'anotherEmail@example.com'}).lean() + expect(auditEntry).to.not.exist + + } finally { + logAuditStub.restore() + } + }) + }) }); From d695c91416fa94e77518178cc3dc91d1a7f09d3a Mon Sep 17 00:00:00 2001 From: Patrick Hoang Date: Mon, 23 Jun 2025 19:37:55 -0700 Subject: [PATCH 17/17] fix tests --- test/api/User.js | 89 ------------------------------------------------ 1 file changed, 89 deletions(-) diff --git a/test/api/User.js b/test/api/User.js index 58dc57ada..6994c47c5 100644 --- a/test/api/User.js +++ b/test/api/User.js @@ -447,93 +447,4 @@ describe('User', () => { expect(result).to.have.status(UNAUTHORIZED); }); }); - - describe('/POST login', () => { - const loginPayload = { - email: 'a@b.c', - password: 'Passw0rd' - } - - before(async () => { - await AuditLog.deleteMany({}) - }) - - it('Should create an audit log entry on successful login', async() => { - const res = await test.sendPostRequest('/api/Auth/login', loginPayload) - - expect(res).to.have.status(OK) - expect(res.body).to.have.property('token') - - const auditEntry = await AuditLog.findOne({ - action: AuditLogctions.LOG_IN, - 'details.email': loginPayload.email, - }).lean() - - expect(auditEntry).to.exist - expect(auditEntry).to.have.property('userId') - expect(auditEntry.details).to.have.property('email', loginPayload.email) - }) - - it('Should return 200 even if audit logging fails', async() => { - const logAuditStub = sinon.stub(AuditUtil).throws(new Error('Audit logging failed')) - try { - const res = await test.sendPostRequest('/api/Auth/login', loginPayload) - - expect(res).to.have.status(OK) - expect(res.body).to.have.property('token') - - const auditEntry = await AuditLog.findOne().lean() - expect(auditEntry).to.not.exist - - } finally { - logAuditStub.restore() - } - }) - }) - - describe('/POST register', () => { - const registerPayload = { - email: 'newuser@example.com', - password: 'Passw0rd123!', - firstName: 'Testfirst', - lastName: 'Testlast' - } - - before(async() => { - await AuditLog.deleteMany({}) - await User.deleteOne({ email: registerPayload.email }) - }) - - it('Should create an audit log entry on successful signup', async() => { - const res = await test.sendPostRequest('/api/Auth/register', registerPayload) - expect(res).to.have.status(OK) - - const auditEntry = await AuditLog.findOne({ - action: AuditLogctions.SIGN_UP, - 'details.email': registerPayload.email - }).lean() - - expect(auditEntry).to.exist - expect(auditEntry).to.have.property('userId') - expect(auditEntry.details).to.have.property('email', registerPayload.email) - }) - - it('Should return 200 even if audit logging fails', async () => { - const logAuditStub = sinon.stub(AuditUtil, 'logAudit').throws(new Error('Audit logging failed')) - try { - const res = await test.sendPostRequest('/api/Auth/register', { - ...registerPayload, - email: 'anotherEmail@example.com' - }) - - expect(res).to.have.status(OK) - - const auditEntry = await AuditLog.findOne({'details.email': 'anotherEmail@example.com'}).lean() - expect(auditEntry).to.not.exist - - } finally { - logAuditStub.restore() - } - }) - }) });