mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
* Use 'forwarded' always Getting the remote IP from the socket doesn't work behind a reverse proxy. 'Forwarded' is there to fix there, but wasn't used here. * Consistently use getRemoteIP() throughout Stay in line with other modules that do this * Use getRemoteIP consistently One more missing case; in this file it isn't helpful, but it is consistent with other files. Co-authored-by: Sulka Haro <sulka@sulka.net>
152 lines
4.5 KiB
JavaScript
152 lines
4.5 KiB
JavaScript
'use strict';
|
|
|
|
const apiConst = require('./const');
|
|
const forwarded = require('forwarded-for');
|
|
|
|
function getRemoteIP (req) {
|
|
const address = forwarded(req, req.headers);
|
|
return address.ip;
|
|
}
|
|
|
|
/**
|
|
* Socket.IO broadcaster of any storage change
|
|
*/
|
|
function StorageSocket (app, env, ctx) {
|
|
|
|
const self = this;
|
|
|
|
const LOG_GREEN = '\x1B[32m'
|
|
, LOG_MAGENTA = '\x1B[35m'
|
|
, LOG_RESET = '\x1B[0m'
|
|
, LOG = LOG_GREEN + 'STORAGE SOCKET: ' + LOG_RESET
|
|
, LOG_ERROR = LOG_MAGENTA + 'STORAGE SOCKET: ' + LOG_RESET
|
|
, NAMESPACE = '/storage'
|
|
;
|
|
|
|
|
|
/**
|
|
* Initialize socket namespace and bind the events
|
|
* @param {Object} io Socket.IO object to multiplex namespaces
|
|
*/
|
|
self.init = function init (io) {
|
|
self.io = io;
|
|
|
|
self.namespace = io.of(NAMESPACE);
|
|
self.namespace.on('connection', function onConnected (socket) {
|
|
|
|
const remoteIP = getRemoteIP(socket.request);
|
|
console.log(LOG + 'Connection from client ID: ', socket.client.id, ' IP: ', remoteIP);
|
|
|
|
socket.on('disconnect', function onDisconnect () {
|
|
console.log(LOG + 'Disconnected client ID: ', socket.client.id);
|
|
});
|
|
|
|
socket.on('subscribe', function onSubscribe (message, returnCallback) {
|
|
self.subscribe(socket, message, returnCallback);
|
|
});
|
|
});
|
|
|
|
ctx.bus.on('storage-socket-create', self.emitCreate);
|
|
ctx.bus.on('storage-socket-update', self.emitUpdate);
|
|
ctx.bus.on('storage-socket-delete', self.emitDelete);
|
|
};
|
|
|
|
|
|
/**
|
|
* Authorize Socket.IO client and subscribe him to authorized rooms
|
|
* @param {Object} socket
|
|
* @param {Object} message input message from the client
|
|
* @param {Function} returnCallback function for returning a value back to the client
|
|
*/
|
|
self.subscribe = function subscribe (socket, message, returnCallback) {
|
|
const shouldCallBack = typeof(returnCallback) === 'function';
|
|
|
|
if (message && message.accessToken) {
|
|
return ctx.authorization.resolveAccessToken(message.accessToken, function resolveFinish (err, auth) {
|
|
if (err) {
|
|
console.log(`${LOG_ERROR} Authorization failed for accessToken:`, message.accessToken);
|
|
|
|
if (shouldCallBack) {
|
|
returnCallback({ success: false, message: apiConst.MSG.SOCKET_MISSING_OR_BAD_ACCESS_TOKEN });
|
|
}
|
|
return err;
|
|
}
|
|
else {
|
|
return self.subscribeAuthorized(socket, message, auth, returnCallback);
|
|
}
|
|
});
|
|
}
|
|
|
|
console.log(`${LOG_ERROR} Authorization failed for message:`, message);
|
|
if (shouldCallBack) {
|
|
returnCallback({ success: false, message: apiConst.MSG.SOCKET_MISSING_OR_BAD_ACCESS_TOKEN});
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Subscribe already authorized Socket.IO client to his rooms
|
|
* @param {Object} socket
|
|
* @param {Object} message input message from the client
|
|
* @param {Object} auth authorization of the client
|
|
* @param {Function} returnCallback function for returning a value back to the client
|
|
*/
|
|
self.subscribeAuthorized = function subscribeAuthorized (socket, message, auth, returnCallback) {
|
|
const shouldCallBack = typeof(returnCallback) === 'function';
|
|
const enabledCols = app.get('enabledCollections');
|
|
const cols = Array.isArray(message.collections) ? message.collections : enabledCols;
|
|
const subscribed = [];
|
|
|
|
for (const col of cols) {
|
|
if (enabledCols.includes(col)) {
|
|
const permission = (col === 'settings') ? `api:${col}:admin` : `api:${col}:read`;
|
|
|
|
if (ctx.authorization.checkMultiple(permission, auth.shiros)) {
|
|
socket.join(col);
|
|
subscribed.push(col);
|
|
}
|
|
}
|
|
}
|
|
|
|
const doc = subscribed.length > 0
|
|
? { success: true, collections: subscribed }
|
|
: { success: false, message: apiConst.MSG.SOCKET_UNAUTHORIZED_TO_ANY };
|
|
if (shouldCallBack) {
|
|
returnCallback(doc);
|
|
}
|
|
return doc;
|
|
};
|
|
|
|
|
|
/**
|
|
* Emit create event to the subscribers (of the collection's room)
|
|
* @param {Object} event
|
|
*/
|
|
self.emitCreate = function emitCreate (event) {
|
|
self.namespace.to(event.colName)
|
|
.emit('create', event);
|
|
};
|
|
|
|
|
|
/**
|
|
* Emit update event to the subscribers (of the collection's room)
|
|
* @param {Object} event
|
|
*/
|
|
self.emitUpdate = function emitUpdate (event) {
|
|
self.namespace.to(event.colName)
|
|
.emit('update', event);
|
|
};
|
|
|
|
|
|
/**
|
|
* Emit delete event to the subscribers (of the collection's room)
|
|
* @param {Object} event
|
|
*/
|
|
self.emitDelete = function emitDelete (event) {
|
|
self.namespace.to(event.colName)
|
|
.emit('delete', event);
|
|
}
|
|
}
|
|
|
|
module.exports = StorageSocket;
|