2019-05-29 05:21:06 -04:00
|
|
|
let UserSessionsManager
|
|
|
|
const Settings = require('settings-sharelatex')
|
|
|
|
const logger = require('logger-sharelatex')
|
|
|
|
const Async = require('async')
|
|
|
|
const _ = require('underscore')
|
2019-12-10 03:10:05 -05:00
|
|
|
const { promisify } = require('util')
|
2019-05-29 05:21:06 -04:00
|
|
|
const UserSessionsRedis = require('./UserSessionsRedis')
|
|
|
|
const rclient = UserSessionsRedis.client()
|
|
|
|
|
2019-12-10 03:10:05 -05:00
|
|
|
UserSessionsManager = {
|
2019-05-29 05:21:06 -04:00
|
|
|
// mimic the key used by the express sessions
|
|
|
|
_sessionKey(sessionId) {
|
|
|
|
return `sess:${sessionId}`
|
|
|
|
},
|
|
|
|
|
|
|
|
trackSession(user, sessionId, callback) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!user) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return callback(null)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!sessionId) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return callback(null)
|
|
|
|
}
|
|
|
|
const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
|
|
|
const value = UserSessionsManager._sessionKey(sessionId)
|
2019-09-24 04:43:43 -04:00
|
|
|
rclient
|
2019-05-29 05:21:06 -04:00
|
|
|
.multi()
|
|
|
|
.sadd(sessionSetKey, value)
|
2019-09-12 04:04:06 -04:00
|
|
|
.pexpire(sessionSetKey, `${Settings.cookieSessionLength}`) // in milliseconds
|
2019-05-29 05:21:06 -04:00
|
|
|
.exec(function(err, response) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (err) {
|
2019-07-01 09:48:09 -04:00
|
|
|
logger.warn(
|
2019-05-29 05:21:06 -04:00
|
|
|
{ err, user_id: user._id, sessionSetKey },
|
|
|
|
'error while adding session key to UserSessions set'
|
|
|
|
)
|
|
|
|
return callback(err)
|
|
|
|
}
|
|
|
|
UserSessionsManager._checkSessions(user, function() {})
|
2019-09-24 04:43:43 -04:00
|
|
|
callback()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
untrackSession(user, sessionId, callback) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!callback) {
|
|
|
|
callback = function() {}
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!user) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return callback(null)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!sessionId) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return callback(null)
|
|
|
|
}
|
|
|
|
const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
|
|
|
const value = UserSessionsManager._sessionKey(sessionId)
|
2019-09-24 04:43:43 -04:00
|
|
|
rclient
|
2019-05-29 05:21:06 -04:00
|
|
|
.multi()
|
|
|
|
.srem(sessionSetKey, value)
|
2019-09-12 04:04:06 -04:00
|
|
|
.pexpire(sessionSetKey, `${Settings.cookieSessionLength}`) // in milliseconds
|
2019-05-29 05:21:06 -04:00
|
|
|
.exec(function(err, response) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (err) {
|
2019-07-01 09:48:09 -04:00
|
|
|
logger.warn(
|
2019-05-29 05:21:06 -04:00
|
|
|
{ err, user_id: user._id, sessionSetKey },
|
|
|
|
'error while removing session key from UserSessions set'
|
|
|
|
)
|
|
|
|
return callback(err)
|
|
|
|
}
|
|
|
|
UserSessionsManager._checkSessions(user, function() {})
|
2019-09-24 04:43:43 -04:00
|
|
|
callback()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
getAllUserSessions(user, exclude, callback) {
|
|
|
|
exclude = _.map(exclude, UserSessionsManager._sessionKey)
|
|
|
|
const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
2019-09-24 04:43:43 -04:00
|
|
|
rclient.smembers(sessionSetKey, function(err, sessionKeys) {
|
|
|
|
if (err) {
|
2019-07-01 09:48:09 -04:00
|
|
|
logger.warn(
|
2019-05-29 05:21:06 -04:00
|
|
|
{ user_id: user._id },
|
|
|
|
'error getting all session keys for user from redis'
|
|
|
|
)
|
|
|
|
return callback(err)
|
|
|
|
}
|
|
|
|
sessionKeys = _.filter(sessionKeys, k => !_.contains(exclude, k))
|
|
|
|
if (sessionKeys.length === 0) {
|
|
|
|
logger.log({ user_id: user._id }, 'no other sessions found, returning')
|
|
|
|
return callback(null, [])
|
|
|
|
}
|
|
|
|
|
2019-09-24 04:43:43 -04:00
|
|
|
Async.mapSeries(sessionKeys, (k, cb) => rclient.get(k, cb), function(
|
|
|
|
err,
|
|
|
|
sessions
|
|
|
|
) {
|
|
|
|
if (err) {
|
|
|
|
logger.warn(
|
|
|
|
{ user_id: user._id },
|
|
|
|
'error getting all sessions for user from redis'
|
|
|
|
)
|
|
|
|
return callback(err)
|
|
|
|
}
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2019-09-24 04:43:43 -04:00
|
|
|
const result = []
|
|
|
|
for (let session of Array.from(sessions)) {
|
|
|
|
if (!session) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
session = JSON.parse(session)
|
|
|
|
let sessionUser = session.passport && session.passport.user
|
|
|
|
if (!sessionUser) {
|
|
|
|
sessionUser = session.user
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
|
2019-09-24 04:43:43 -04:00
|
|
|
result.push({
|
|
|
|
ip_address: sessionUser.ip_address,
|
|
|
|
session_created: sessionUser.session_created
|
|
|
|
})
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
|
|
|
|
callback(null, result)
|
|
|
|
})
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
revokeAllUserSessions(user, retain, callback) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!retain) {
|
2019-05-29 05:21:06 -04:00
|
|
|
retain = []
|
|
|
|
}
|
|
|
|
retain = retain.map(i => UserSessionsManager._sessionKey(i))
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!user) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return callback(null)
|
|
|
|
}
|
|
|
|
const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
2019-09-24 04:43:43 -04:00
|
|
|
rclient.smembers(sessionSetKey, function(err, sessionKeys) {
|
|
|
|
if (err) {
|
2019-07-01 09:48:09 -04:00
|
|
|
logger.warn(
|
2019-05-29 05:21:06 -04:00
|
|
|
{ err, user_id: user._id, sessionSetKey },
|
|
|
|
'error getting contents of UserSessions set'
|
|
|
|
)
|
|
|
|
return callback(err)
|
|
|
|
}
|
|
|
|
const keysToDelete = _.filter(
|
|
|
|
sessionKeys,
|
|
|
|
k => !Array.from(retain).includes(k)
|
|
|
|
)
|
|
|
|
if (keysToDelete.length === 0) {
|
|
|
|
logger.log(
|
|
|
|
{ user_id: user._id },
|
|
|
|
'no sessions in UserSessions set to delete, returning'
|
|
|
|
)
|
|
|
|
return callback(null)
|
|
|
|
}
|
|
|
|
logger.log(
|
|
|
|
{ user_id: user._id, count: keysToDelete.length },
|
|
|
|
'deleting sessions for user'
|
|
|
|
)
|
|
|
|
|
|
|
|
const deletions = keysToDelete.map(k => cb => rclient.del(k, cb))
|
|
|
|
|
2019-09-24 04:43:43 -04:00
|
|
|
Async.series(deletions, function(err, _result) {
|
|
|
|
if (err) {
|
2019-07-01 09:48:09 -04:00
|
|
|
logger.warn(
|
2019-05-29 05:21:06 -04:00
|
|
|
{ err, user_id: user._id, sessionSetKey },
|
|
|
|
'errror revoking all sessions for user'
|
|
|
|
)
|
|
|
|
return callback(err)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
rclient.srem(sessionSetKey, keysToDelete, function(err) {
|
|
|
|
if (err) {
|
2019-07-01 09:48:09 -04:00
|
|
|
logger.warn(
|
2019-05-29 05:21:06 -04:00
|
|
|
{ err, user_id: user._id, sessionSetKey },
|
|
|
|
'error removing session set for user'
|
|
|
|
)
|
|
|
|
return callback(err)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
callback(null)
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
touch(user, callback) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!user) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return callback(null)
|
|
|
|
}
|
|
|
|
const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
2019-09-24 04:43:43 -04:00
|
|
|
rclient.pexpire(
|
2019-05-29 05:21:06 -04:00
|
|
|
sessionSetKey,
|
2019-09-12 04:04:06 -04:00
|
|
|
`${Settings.cookieSessionLength}`, // in milliseconds
|
2019-05-29 05:21:06 -04:00
|
|
|
function(err, response) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (err) {
|
2019-07-01 09:48:09 -04:00
|
|
|
logger.warn(
|
2019-05-29 05:21:06 -04:00
|
|
|
{ err, user_id: user._id },
|
|
|
|
'error while updating ttl on UserSessions set'
|
|
|
|
)
|
|
|
|
return callback(err)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
callback(null)
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
_checkSessions(user, callback) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!user) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return callback(null)
|
|
|
|
}
|
|
|
|
const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
2019-09-24 04:43:43 -04:00
|
|
|
rclient.smembers(sessionSetKey, function(err, sessionKeys) {
|
|
|
|
if (err) {
|
2019-07-01 09:48:09 -04:00
|
|
|
logger.warn(
|
2019-05-29 05:21:06 -04:00
|
|
|
{ err, user_id: user._id, sessionSetKey },
|
|
|
|
'error getting contents of UserSessions set'
|
|
|
|
)
|
|
|
|
return callback(err)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
Async.series(
|
2019-05-29 05:21:06 -04:00
|
|
|
sessionKeys.map(key => next =>
|
|
|
|
rclient.get(key, function(err, val) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (err) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return next(err)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!val) {
|
|
|
|
rclient.srem(sessionSetKey, key, function(err, result) {
|
|
|
|
return next(err)
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
} else {
|
2019-09-24 04:43:43 -04:00
|
|
|
next()
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
})
|
|
|
|
),
|
|
|
|
function(err, results) {
|
2019-09-24 04:43:43 -04:00
|
|
|
callback(err)
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-12-10 03:10:05 -05:00
|
|
|
|
|
|
|
UserSessionsManager.promises = {
|
|
|
|
revokeAllUserSessions: promisify(UserSessionsManager.revokeAllUserSessions)
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = UserSessionsManager
|