mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
Adds audit log entry for user Logout event
GitOrigin-RevId: 5a305166ba0e017ae7cb3d426cdae541e8db62c3
This commit is contained in:
@@ -102,12 +102,29 @@ async function addEntry(userId, operation, initiatorId, ipAddress, info = {}) {
|
|||||||
await UserAuditLogEntry.create(entry)
|
await UserAuditLogEntry.create(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addEntryInBackground(
|
||||||
|
userId,
|
||||||
|
operation,
|
||||||
|
initiatorId,
|
||||||
|
ipAddress,
|
||||||
|
info = {}
|
||||||
|
) {
|
||||||
|
// Intentionally not awaited
|
||||||
|
addEntry(userId, operation, initiatorId, ipAddress, info).catch(err => {
|
||||||
|
logger.error(
|
||||||
|
{ err, userId, operation, initiatorId, ipAddress, info },
|
||||||
|
'error adding user audit log entry'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const UserAuditLogHandler = {
|
const UserAuditLogHandler = {
|
||||||
MANAGED_GROUP_USER_EVENTS,
|
MANAGED_GROUP_USER_EVENTS,
|
||||||
addEntry: callbackify(addEntry),
|
addEntry: callbackify(addEntry),
|
||||||
promises: {
|
promises: {
|
||||||
addEntry,
|
addEntry,
|
||||||
},
|
},
|
||||||
|
addEntryInBackground,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UserAuditLogHandler
|
export default UserAuditLogHandler
|
||||||
|
|||||||
@@ -473,6 +473,16 @@ async function doLogout(req) {
|
|||||||
logger.debug({ user }, 'logging out')
|
logger.debug({ user }, 'logging out')
|
||||||
const sessionId = req.sessionID
|
const sessionId = req.sessionID
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
UserAuditLogHandler.addEntryInBackground(
|
||||||
|
user._id,
|
||||||
|
'logout',
|
||||||
|
user._id,
|
||||||
|
req.ip,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof req.logout === 'function') {
|
if (typeof req.logout === 'function') {
|
||||||
// passport logout
|
// passport logout
|
||||||
const logout = promisify(req.logout.bind(req))
|
const logout = promisify(req.logout.bind(req))
|
||||||
|
|||||||
@@ -68,6 +68,70 @@ describe('Sessions', function () {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should update audit log on logout', function (done) {
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
next => {
|
||||||
|
redis.clearUserSessions(this.user1, next)
|
||||||
|
},
|
||||||
|
|
||||||
|
// login
|
||||||
|
next => {
|
||||||
|
this.user1.login(err => next(err))
|
||||||
|
},
|
||||||
|
|
||||||
|
// logout, should add logout audit log entry (happens in background)
|
||||||
|
next => {
|
||||||
|
this.user1.logout(err => next(err))
|
||||||
|
},
|
||||||
|
|
||||||
|
// poll for audit log entry since it's written in the background
|
||||||
|
next => {
|
||||||
|
let attempts = 0
|
||||||
|
const checkAuditLog = () => {
|
||||||
|
this.user1.getAuditLogWithoutNoise((error, auditLog) => {
|
||||||
|
if (error) return next(error)
|
||||||
|
|
||||||
|
const logoutEntries = auditLog.filter(
|
||||||
|
entry => entry.operation === 'logout'
|
||||||
|
)
|
||||||
|
|
||||||
|
// If we found the logout entry, we're done
|
||||||
|
if (logoutEntries.length > 0) {
|
||||||
|
expect(logoutEntries.length).to.be.greaterThan(0)
|
||||||
|
const lastLogout = logoutEntries[logoutEntries.length - 1]
|
||||||
|
expect(lastLogout.operation).to.equal('logout')
|
||||||
|
expect(lastLogout.ipAddress).to.exist
|
||||||
|
expect(lastLogout.initiatorId).to.exist
|
||||||
|
expect(lastLogout.timestamp).to.exist
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise retry up to 10 times
|
||||||
|
attempts++
|
||||||
|
if (attempts >= 10) {
|
||||||
|
return next(
|
||||||
|
new Error(
|
||||||
|
'Logout audit log entry not found after 10 attempts'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(checkAuditLog, 25)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
checkAuditLog()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
(err, result) => {
|
||||||
|
if (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('two sessions', function () {
|
describe('two sessions', function () {
|
||||||
@@ -465,11 +529,19 @@ describe('Sessions', function () {
|
|||||||
this.user1.getAuditLogWithoutNoise((error, auditLog) => {
|
this.user1.getAuditLogWithoutNoise((error, auditLog) => {
|
||||||
expect(error).not.to.exist
|
expect(error).not.to.exist
|
||||||
expect(auditLog).to.exist
|
expect(auditLog).to.exist
|
||||||
expect(auditLog[0].operation).to.equal('clear-sessions')
|
|
||||||
expect(auditLog[0].ipAddress).to.exist
|
// find the clear-sessions entry
|
||||||
expect(auditLog[0].initiatorId).to.exist
|
const clearSessionsEntries = auditLog.filter(
|
||||||
expect(auditLog[0].timestamp).to.exist
|
entry => entry.operation === 'clear-sessions'
|
||||||
expect(auditLog[0].info.sessions.length).to.equal(2)
|
)
|
||||||
|
expect(clearSessionsEntries.length).to.equal(1)
|
||||||
|
expect(clearSessionsEntries[0].operation).to.equal(
|
||||||
|
'clear-sessions'
|
||||||
|
)
|
||||||
|
expect(clearSessionsEntries[0].ipAddress).to.exist
|
||||||
|
expect(clearSessionsEntries[0].initiatorId).to.exist
|
||||||
|
expect(clearSessionsEntries[0].timestamp).to.exist
|
||||||
|
expect(clearSessionsEntries[0].info.sessions.length).to.equal(2)
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -58,6 +58,16 @@ class UserHelper {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get auditLog by operation
|
||||||
|
* @return {object[]}
|
||||||
|
*/
|
||||||
|
getAuditLogByOperation(operation) {
|
||||||
|
return (this.user.auditLog || []).filter(entry => {
|
||||||
|
return entry.operation === operation
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate default email from unique (per instantiation) user number
|
* Generate default email from unique (per instantiation) user number
|
||||||
* @returns {string} email
|
* @returns {string} email
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ describe('UserController', function () {
|
|||||||
promises: {
|
promises: {
|
||||||
addEntry: sinon.stub().resolves(),
|
addEntry: sinon.stub().resolves(),
|
||||||
},
|
},
|
||||||
|
addEntryInBackground: sinon.stub(),
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.RequestContentTypeDetection = {
|
ctx.RequestContentTypeDetection = {
|
||||||
|
|||||||
Reference in New Issue
Block a user