UserSessionsManager.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. max-len,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS101: Remove unnecessary use of Array.from
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * DS103: Rewrite code to no longer use __guard__
  13. * DS207: Consider shorter variations of null checks
  14. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  15. */
  16. let UserSessionsManager
  17. const Settings = require('settings-sharelatex')
  18. const logger = require('logger-sharelatex')
  19. const Async = require('async')
  20. const _ = require('underscore')
  21. const UserSessionsRedis = require('./UserSessionsRedis')
  22. const rclient = UserSessionsRedis.client()
  23. module.exports = UserSessionsManager = {
  24. // mimic the key used by the express sessions
  25. _sessionKey(sessionId) {
  26. return `sess:${sessionId}`
  27. },
  28. trackSession(user, sessionId, callback) {
  29. if (callback == null) {
  30. callback = function(err) {}
  31. }
  32. if (user == null) {
  33. logger.log({ sessionId }, 'no user to track, returning')
  34. return callback(null)
  35. }
  36. if (sessionId == null) {
  37. logger.log({ user_id: user._id }, 'no sessionId to track, returning')
  38. return callback(null)
  39. }
  40. logger.log({ user_id: user._id, sessionId }, 'onLogin handler')
  41. const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
  42. const value = UserSessionsManager._sessionKey(sessionId)
  43. return rclient
  44. .multi()
  45. .sadd(sessionSetKey, value)
  46. .expire(sessionSetKey, `${Settings.cookieSessionLength}`)
  47. .exec(function(err, response) {
  48. if (err != null) {
  49. logger.warn(
  50. { err, user_id: user._id, sessionSetKey },
  51. 'error while adding session key to UserSessions set'
  52. )
  53. return callback(err)
  54. }
  55. UserSessionsManager._checkSessions(user, function() {})
  56. return callback()
  57. })
  58. },
  59. untrackSession(user, sessionId, callback) {
  60. if (callback == null) {
  61. callback = function(err) {}
  62. }
  63. if (user == null) {
  64. logger.log({ sessionId }, 'no user to untrack, returning')
  65. return callback(null)
  66. }
  67. if (sessionId == null) {
  68. logger.log({ user_id: user._id }, 'no sessionId to untrack, returning')
  69. return callback(null)
  70. }
  71. logger.log({ user_id: user._id, sessionId }, 'onLogout handler')
  72. const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
  73. const value = UserSessionsManager._sessionKey(sessionId)
  74. return rclient
  75. .multi()
  76. .srem(sessionSetKey, value)
  77. .expire(sessionSetKey, `${Settings.cookieSessionLength}`)
  78. .exec(function(err, response) {
  79. if (err != null) {
  80. logger.warn(
  81. { err, user_id: user._id, sessionSetKey },
  82. 'error while removing session key from UserSessions set'
  83. )
  84. return callback(err)
  85. }
  86. UserSessionsManager._checkSessions(user, function() {})
  87. return callback()
  88. })
  89. },
  90. getAllUserSessions(user, exclude, callback) {
  91. if (callback == null) {
  92. callback = function(err, sessionKeys) {}
  93. }
  94. exclude = _.map(exclude, UserSessionsManager._sessionKey)
  95. const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
  96. return rclient.smembers(sessionSetKey, function(err, sessionKeys) {
  97. if (err != null) {
  98. logger.warn(
  99. { user_id: user._id },
  100. 'error getting all session keys for user from redis'
  101. )
  102. return callback(err)
  103. }
  104. sessionKeys = _.filter(sessionKeys, k => !_.contains(exclude, k))
  105. if (sessionKeys.length === 0) {
  106. logger.log({ user_id: user._id }, 'no other sessions found, returning')
  107. return callback(null, [])
  108. }
  109. return Async.mapSeries(
  110. sessionKeys,
  111. (k, cb) => rclient.get(k, cb),
  112. function(err, sessions) {
  113. if (err != null) {
  114. logger.warn(
  115. { user_id: user._id },
  116. 'error getting all sessions for user from redis'
  117. )
  118. return callback(err)
  119. }
  120. const result = []
  121. for (let session of Array.from(sessions)) {
  122. if (session === null) {
  123. continue
  124. }
  125. session = JSON.parse(session)
  126. const session_user =
  127. (session != null ? session.user : undefined) ||
  128. __guard__(
  129. session != null ? session.passport : undefined,
  130. x => x.user
  131. )
  132. result.push({
  133. ip_address: session_user.ip_address,
  134. session_created: session_user.session_created
  135. })
  136. }
  137. return callback(null, result)
  138. }
  139. )
  140. })
  141. },
  142. revokeAllUserSessions(user, retain, callback) {
  143. if (callback == null) {
  144. callback = function(err) {}
  145. }
  146. if (retain == null) {
  147. retain = []
  148. }
  149. retain = retain.map(i => UserSessionsManager._sessionKey(i))
  150. if (user == null) {
  151. logger.log({}, 'no user to revoke sessions for, returning')
  152. return callback(null)
  153. }
  154. logger.log({ user_id: user._id }, 'revoking all existing sessions for user')
  155. const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
  156. return rclient.smembers(sessionSetKey, function(err, sessionKeys) {
  157. if (err != null) {
  158. logger.warn(
  159. { err, user_id: user._id, sessionSetKey },
  160. 'error getting contents of UserSessions set'
  161. )
  162. return callback(err)
  163. }
  164. const keysToDelete = _.filter(
  165. sessionKeys,
  166. k => !Array.from(retain).includes(k)
  167. )
  168. if (keysToDelete.length === 0) {
  169. logger.log(
  170. { user_id: user._id },
  171. 'no sessions in UserSessions set to delete, returning'
  172. )
  173. return callback(null)
  174. }
  175. logger.log(
  176. { user_id: user._id, count: keysToDelete.length },
  177. 'deleting sessions for user'
  178. )
  179. const deletions = keysToDelete.map(k => cb => rclient.del(k, cb))
  180. return Async.series(deletions, function(err, _result) {
  181. if (err != null) {
  182. logger.warn(
  183. { err, user_id: user._id, sessionSetKey },
  184. 'errror revoking all sessions for user'
  185. )
  186. return callback(err)
  187. }
  188. return rclient.srem(sessionSetKey, keysToDelete, function(err) {
  189. if (err != null) {
  190. logger.warn(
  191. { err, user_id: user._id, sessionSetKey },
  192. 'error removing session set for user'
  193. )
  194. return callback(err)
  195. }
  196. return callback(null)
  197. })
  198. })
  199. })
  200. },
  201. touch(user, callback) {
  202. if (callback == null) {
  203. callback = function(err) {}
  204. }
  205. if (user == null) {
  206. logger.log({}, 'no user to touch sessions for, returning')
  207. return callback(null)
  208. }
  209. const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
  210. return rclient.expire(
  211. sessionSetKey,
  212. `${Settings.cookieSessionLength}`,
  213. function(err, response) {
  214. if (err != null) {
  215. logger.warn(
  216. { err, user_id: user._id },
  217. 'error while updating ttl on UserSessions set'
  218. )
  219. return callback(err)
  220. }
  221. return callback(null)
  222. }
  223. )
  224. },
  225. _checkSessions(user, callback) {
  226. if (callback == null) {
  227. callback = function(err) {}
  228. }
  229. if (user == null) {
  230. logger.log({}, 'no user, returning')
  231. return callback(null)
  232. }
  233. logger.log({ user_id: user._id }, 'checking sessions for user')
  234. const sessionSetKey = UserSessionsRedis.sessionSetKey(user)
  235. return rclient.smembers(sessionSetKey, function(err, sessionKeys) {
  236. if (err != null) {
  237. logger.warn(
  238. { err, user_id: user._id, sessionSetKey },
  239. 'error getting contents of UserSessions set'
  240. )
  241. return callback(err)
  242. }
  243. logger.log(
  244. { user_id: user._id, count: sessionKeys.length },
  245. 'checking sessions for user'
  246. )
  247. return Async.series(
  248. sessionKeys.map(key => next =>
  249. rclient.get(key, function(err, val) {
  250. if (err != null) {
  251. return next(err)
  252. }
  253. if (val == null) {
  254. logger.log(
  255. { user_id: user._id, key },
  256. '>> removing key from UserSessions set'
  257. )
  258. return rclient.srem(sessionSetKey, key, function(err, result) {
  259. if (err != null) {
  260. return next(err)
  261. }
  262. return next(null)
  263. })
  264. } else {
  265. return next()
  266. }
  267. })
  268. ),
  269. function(err, results) {
  270. logger.log({ user_id: user._id }, 'done checking sessions for user')
  271. return callback(err)
  272. }
  273. )
  274. })
  275. }
  276. }
  277. function __guard__(value, transform) {
  278. return typeof value !== 'undefined' && value !== null
  279. ? transform(value)
  280. : undefined
  281. }