io.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. 'use strict';
  2. // This file contains all the logic for Socket.IO
  3. const coreClass = require("../core");
  4. const socketio = require("socket.io");
  5. const async = require("async");
  6. module.exports = class extends coreClass {
  7. constructor(name, moduleManager) {
  8. super(name, moduleManager);
  9. this.dependsOn = ["app", "db", "cache"];
  10. }
  11. initialize() {
  12. return new Promise((resolve, reject) => {
  13. this.setStage(1);
  14. const logger = this.logger,
  15. app = this.moduleManager.modules["app"],
  16. cache = this.moduleManager.modules["cache"],
  17. utils = this.moduleManager.modules["utils"],
  18. db = this.moduleManager.modules["db"],
  19. punishments = this.moduleManager.modules["punishments"];
  20. const actions = require('../logic/actions');
  21. //TODO Check every 30s/60s, for all sockets, if they are still allowed to be in the rooms they are in, and on socket at all (permission changing/banning)
  22. this.io = socketio(app.server);
  23. this.io.use(async (socket, next) => {
  24. try { await this._validateHook(); } catch { return; }
  25. let cookies = socket.request.headers.cookie;
  26. let SID;
  27. socket.ip = socket.request.headers['x-forwarded-for'] || '0.0.0.0';
  28. async.waterfall([
  29. (next) => {
  30. utils.parseCookies(cookies).then((parsedCookies) => {
  31. SID = parsedCookies.SID;
  32. next(null);
  33. });
  34. },
  35. (next) => {
  36. if (!SID) return next('No SID.');
  37. next();
  38. },
  39. (next) => {
  40. cache.hget('sessions', SID, next);
  41. },
  42. (session, next) => {
  43. if (!session) return next('No session found.');
  44. session.refreshDate = Date.now();
  45. socket.session = session;
  46. cache.hset('sessions', SID, session, next);
  47. },
  48. (res, next) => {
  49. punishments.getPunishments((err, punishments) => {
  50. const isLoggedIn = !!(socket.session && socket.session.refreshDate);
  51. const userId = (isLoggedIn) ? socket.session.userId : null;
  52. let ban = 0;
  53. let banned = false;
  54. punishments.forEach(punishment => {
  55. if (punishment.expiresAt > ban) ban = punishment;
  56. if (punishment.type === 'banUserId' && isLoggedIn && punishment.value === userId) banned = true;
  57. if (punishment.type === 'banUserIp' && punishment.value === socket.ip) banned = true;
  58. });
  59. socket.banned = banned;
  60. socket.ban = ban;
  61. next();
  62. });
  63. }
  64. ], () => {
  65. if (!socket.session) {
  66. socket.session = { socketId: socket.id };
  67. } else socket.session.socketId = socket.id;
  68. next();
  69. });
  70. });
  71. this.io.on('connection', async socket => {
  72. try { await this._validateHook(); } catch { return; }
  73. let sessionInfo = '';
  74. if (socket.session.sessionId) sessionInfo = ` UserID: ${socket.session.userId}.`;
  75. if (socket.banned) {
  76. logger.info('IO_BANNED_CONNECTION', `A user tried to connect, but is currently banned. IP: ${socket.ip}.${sessionInfo}`);
  77. socket.emit('keep.event:banned', socket.ban);
  78. socket.disconnect(true);
  79. } else {
  80. logger.info('IO_CONNECTION', `User connected. IP: ${socket.ip}.${sessionInfo}`);
  81. // catch when the socket has been disconnected
  82. socket.on('disconnect', (reason) => {
  83. let sessionInfo = '';
  84. if (socket.session.sessionId) sessionInfo = ` UserID: ${socket.session.userId}.`;
  85. logger.info('IO_DISCONNECTION', `User disconnected. IP: ${socket.ip}.${sessionInfo}`);
  86. });
  87. // catch errors on the socket (internal to socket.io)
  88. socket.on('error', err => console.error(err));
  89. // have the socket listen for each action
  90. Object.keys(actions).forEach((namespace) => {
  91. Object.keys(actions[namespace]).forEach((action) => {
  92. // the full name of the action
  93. let name = `${namespace}.${action}`;
  94. // listen for this action to be called
  95. socket.on(name, async (...args) => {
  96. let cb = args[args.length - 1];
  97. if (typeof cb !== "function")
  98. cb = () => {
  99. this.logger.info("IO_MODULE", `There was no callback provided for ${name}.`);
  100. }
  101. else args.pop();
  102. try { await this._validateHook(); } catch { return cb({status: 'failure', message: 'Lockdown'}); }
  103. // load the session from the cache
  104. cache.hget('sessions', socket.session.sessionId, (err, session) => {
  105. if (err && err !== true) {
  106. if (typeof cb === 'function') return cb({
  107. status: 'error',
  108. message: 'An error occurred while obtaining your session'
  109. });
  110. }
  111. // make sure the sockets sessionId isn't set if there is no session
  112. if (socket.session.sessionId && session === null) delete socket.session.sessionId;
  113. // call the action, passing it the session, and the arguments socket.io passed us
  114. actions[namespace][action].apply(null, [socket.session].concat(args).concat([
  115. (result) => {
  116. // respond to the socket with our message
  117. if (typeof cb === 'function') return cb(result);
  118. }
  119. ]));
  120. });
  121. })
  122. })
  123. });
  124. if (socket.session.sessionId) {
  125. cache.hget('sessions', socket.session.sessionId, (err, session) => {
  126. if (err && err !== true) socket.emit('ready', false);
  127. else if (session && session.userId) {
  128. db.models.user.findOne({ _id: session.userId }, (err, user) => {
  129. if (err || !user) return socket.emit('ready', false);
  130. let role = '';
  131. let username = '';
  132. let userId = '';
  133. if (user) {
  134. role = user.role;
  135. username = user.username;
  136. userId = session.userId;
  137. }
  138. socket.emit('ready', true, role, username, userId);
  139. });
  140. } else socket.emit('ready', false);
  141. })
  142. } else socket.emit('ready', false);
  143. }
  144. });
  145. resolve();
  146. });
  147. }
  148. }