2016-06-29 10:35:25 +00:00
|
|
|
Settings = require('settings-sharelatex')
|
|
|
|
redis = require('redis-sharelatex')
|
|
|
|
logger = require("logger-sharelatex")
|
2016-07-04 08:27:38 +00:00
|
|
|
Async = require('async')
|
2016-07-05 13:20:47 +00:00
|
|
|
_ = require('underscore')
|
2016-06-29 10:35:25 +00:00
|
|
|
|
2016-07-04 09:05:34 +00:00
|
|
|
rclient = redis.createClient(Settings.redis.web)
|
|
|
|
|
2016-06-29 10:35:25 +00:00
|
|
|
module.exports = UserSessionsManager =
|
|
|
|
|
|
|
|
_sessionSetKey: (user) ->
|
|
|
|
return "UserSessions:#{user._id}"
|
|
|
|
|
2016-06-29 14:12:12 +00:00
|
|
|
# mimic the key used by the express sessions
|
|
|
|
_sessionKey: (sessionId) ->
|
|
|
|
return "sess:#{sessionId}"
|
|
|
|
|
2016-07-01 14:33:59 +00:00
|
|
|
trackSession: (user, sessionId, callback=(err)-> ) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if !user?
|
2016-07-01 14:55:50 +00:00
|
|
|
logger.log {sessionId}, "no user to track, returning"
|
|
|
|
return callback(null)
|
2016-07-07 08:35:44 +00:00
|
|
|
if !sessionId?
|
2016-07-01 14:55:50 +00:00
|
|
|
logger.log {user_id: user._id}, "no sessionId to track, returning"
|
|
|
|
return callback(null)
|
2016-06-29 10:35:25 +00:00
|
|
|
logger.log {user_id: user._id, sessionId}, "onLogin handler"
|
|
|
|
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
2016-06-29 14:12:12 +00:00
|
|
|
value = UserSessionsManager._sessionKey sessionId
|
2016-07-01 10:24:46 +00:00
|
|
|
rclient.multi()
|
|
|
|
.sadd(sessionSetKey, value)
|
|
|
|
.expire(sessionSetKey, "#{Settings.cookieSessionLength}")
|
|
|
|
.exec (err, response) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if err?
|
2016-07-01 12:37:56 +00:00
|
|
|
logger.err {err, user_id: user._id, sessionSetKey}, "error while adding session key to UserSessions set"
|
2016-07-01 10:24:46 +00:00
|
|
|
return callback(err)
|
2016-07-04 09:37:01 +00:00
|
|
|
UserSessionsManager._checkSessions(user, () ->)
|
2016-07-01 10:24:46 +00:00
|
|
|
callback()
|
2016-06-29 10:35:25 +00:00
|
|
|
|
2016-07-01 14:33:59 +00:00
|
|
|
untrackSession: (user, sessionId, callback=(err)-> ) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if !user?
|
2016-07-01 14:55:50 +00:00
|
|
|
logger.log {sessionId}, "no user to untrack, returning"
|
|
|
|
return callback(null)
|
2016-07-07 08:35:44 +00:00
|
|
|
if !sessionId?
|
2016-07-01 14:55:50 +00:00
|
|
|
logger.log {user_id: user._id}, "no sessionId to untrack, returning"
|
|
|
|
return callback(null)
|
2016-06-29 10:35:25 +00:00
|
|
|
logger.log {user_id: user._id, sessionId}, "onLogout handler"
|
|
|
|
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
2016-06-29 14:12:12 +00:00
|
|
|
value = UserSessionsManager._sessionKey sessionId
|
2016-07-01 10:24:46 +00:00
|
|
|
rclient.multi()
|
|
|
|
.srem(sessionSetKey, value)
|
|
|
|
.expire(sessionSetKey, "#{Settings.cookieSessionLength}")
|
|
|
|
.exec (err, response) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if err?
|
2016-07-01 12:37:56 +00:00
|
|
|
logger.err {err, user_id: user._id, sessionSetKey}, "error while removing session key from UserSessions set"
|
2016-07-01 10:24:46 +00:00
|
|
|
return callback(err)
|
2016-07-04 09:37:01 +00:00
|
|
|
UserSessionsManager._checkSessions(user, () ->)
|
2016-07-01 10:24:46 +00:00
|
|
|
callback()
|
2016-07-01 08:51:22 +00:00
|
|
|
|
2016-07-05 13:20:47 +00:00
|
|
|
revokeAllUserSessions: (user, retain, callback=(err)->) ->
|
|
|
|
if !retain
|
|
|
|
retain = []
|
2016-07-05 13:54:26 +00:00
|
|
|
retain = retain.map((i) -> UserSessionsManager._sessionKey(i))
|
2016-07-01 14:55:50 +00:00
|
|
|
if !user
|
|
|
|
logger.log {}, "no user to revoke sessions for, returning"
|
|
|
|
return callback(null)
|
2016-07-01 08:51:22 +00:00
|
|
|
logger.log {user_id: user._id}, "revoking all existing sessions for user"
|
2016-07-01 12:38:13 +00:00
|
|
|
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
|
|
|
rclient.smembers sessionSetKey, (err, sessionKeys) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if err?
|
2016-07-01 12:38:13 +00:00
|
|
|
logger.err {err, user_id: user._id, sessionSetKey}, "error getting contents of UserSessions set"
|
|
|
|
return callback(err)
|
2016-07-05 13:54:26 +00:00
|
|
|
keysToDelete = _.filter(sessionKeys, (k) -> k not in retain)
|
2016-07-06 14:39:03 +00:00
|
|
|
if keysToDelete.length == 0
|
|
|
|
logger.log {user_id: user._id}, "no sessions in UserSessions set to delete, returning"
|
|
|
|
return callback(null)
|
2016-07-05 13:20:47 +00:00
|
|
|
logger.log {user_id: user._id, count: keysToDelete.length}, "deleting sessions for user"
|
2016-07-01 12:38:13 +00:00
|
|
|
rclient.multi()
|
2016-07-05 13:20:47 +00:00
|
|
|
.del(keysToDelete)
|
|
|
|
.srem(sessionSetKey, keysToDelete)
|
2016-07-01 12:38:13 +00:00
|
|
|
.exec (err, result) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if err?
|
2016-07-01 12:38:13 +00:00
|
|
|
logger.err {err, user_id: user._id, sessionSetKey}, "error revoking all sessions for user"
|
|
|
|
return callback(err)
|
|
|
|
callback(null)
|
2016-07-01 10:24:46 +00:00
|
|
|
|
|
|
|
touch: (user, callback=(err)->) ->
|
|
|
|
if !user
|
2016-07-01 14:55:50 +00:00
|
|
|
logger.log {}, "no user to touch sessions for, returning"
|
2016-07-01 10:24:46 +00:00
|
|
|
return callback(null)
|
|
|
|
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
|
|
|
rclient.expire sessionSetKey, "#{Settings.cookieSessionLength}", (err, response) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if err?
|
2016-07-01 10:24:46 +00:00
|
|
|
logger.err {err, user_id: user._id}, "error while updating ttl on UserSessions set"
|
|
|
|
return callback(err)
|
2016-07-04 13:04:10 +00:00
|
|
|
callback(null)
|
2016-07-04 08:27:38 +00:00
|
|
|
|
|
|
|
_checkSessions: (user, callback=(err)->) ->
|
|
|
|
if !user
|
|
|
|
logger.log {}, "no user, returning"
|
|
|
|
return callback(null)
|
|
|
|
logger.log {user_id: user._id}, "checking sessions for user"
|
|
|
|
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
|
|
|
rclient.smembers sessionSetKey, (err, sessionKeys) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if err?
|
2016-07-04 08:27:38 +00:00
|
|
|
logger.err {err, user_id: user._id, sessionSetKey}, "error getting contents of UserSessions set"
|
|
|
|
return callback(err)
|
|
|
|
logger.log {user_id: user._id, count: sessionKeys.length}, "checking sessions for user"
|
|
|
|
Async.series(
|
|
|
|
sessionKeys.map(
|
|
|
|
(key) ->
|
|
|
|
(next) ->
|
|
|
|
rclient.get key, (err, val) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if err?
|
2016-07-04 08:27:38 +00:00
|
|
|
return next(err)
|
2016-07-07 08:35:44 +00:00
|
|
|
if !val?
|
2016-07-04 08:27:38 +00:00
|
|
|
logger.log {user_id: user._id, key}, ">> removing key from UserSessions set"
|
|
|
|
rclient.srem sessionSetKey, key, (err, result) ->
|
2016-07-07 08:35:44 +00:00
|
|
|
if err?
|
2016-07-04 08:27:38 +00:00
|
|
|
return next(err)
|
|
|
|
return next(null)
|
|
|
|
else
|
|
|
|
next()
|
|
|
|
)
|
|
|
|
, (err, results) ->
|
|
|
|
logger.log {user_id: user._id}, "done checking sessions for user"
|
|
|
|
return callback(err)
|
|
|
|
)
|