Files
cgm-remote-monitor/lib/api3/storageSocket.js
T
2dfc471b8a Forwarded fix (#7593)
* 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>
2023-01-01 13:17:26 +02:00

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;