diff options
author | jakob.stendahl <jakob.stendahl@infomedia.dk> | 2022-12-17 21:31:41 +0100 |
---|---|---|
committer | Jakob Stendahl <jakob.stendahl@outlook.com> | 2022-12-17 21:31:41 +0100 |
commit | 1e588718a855ae2871a8841f6c6e621f49795454 (patch) | |
tree | 6599b3959554b307a571a73373114cb2d34a98ef /src/SocketIO/index.js | |
parent | 6c37c28d7044a813fcde9ef80bf8852529b8305f (diff) | |
download | Luxcena-Neo-1e588718a855ae2871a8841f6c6e621f49795454.tar.gz Luxcena-Neo-1e588718a855ae2871a8841f6c6e621f49795454.zip |
Start moving to esm, work on updater
Diffstat (limited to 'src/SocketIO/index.js')
-rw-r--r-- | src/SocketIO/index.js | 409 |
1 files changed, 0 insertions, 409 deletions
diff --git a/src/SocketIO/index.js b/src/SocketIO/index.js deleted file mode 100644 index 675efc5..0000000 --- a/src/SocketIO/index.js +++ /dev/null @@ -1,409 +0,0 @@ -/** - * This module contains code for handling socketIO clients. - * - * There are two classes, one is a SocketIO controller module. - * The other one is a authorizedclient. - * - * @author jakobst1n. - * @since 19.12.2019 - */ - -let logger = require(__appdir + "/src/Logger"); -var exec = require('child_process').exec; -var CryptoJS = require("crypto-js"); -let fs = require("fs"); -const { performance } = require("perf_hooks"); - -let neoModules; - -const sanitizePath = (path) => path.match(/(user|remote|builtin\/[a-zA-Z0-9-_\/]{1,200})(\.[a-zA-Z0-9]{1,10})?/)[0]; - -/** - * Create the open socketio namespace and setup all listeners. - * - * @param {io} socketio - */ -function createOpenSocketNamespace(io) { - const openNamespace = io.of("/open") - - openNamespace.on("connection", (socket) => { - logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) connected.`); - - socket.on("name:get", () => { - socket.emit("name", neoModules.userData.config.instanceName); - }); - socket.on("mode:set", (modeId) => { - neoModules.neoRuntimeManager.mode.set(modeId); - }); - socket.on("mode:get", () => { - socket.emit("mode", neoModules.neoRuntimeManager.mode.current()); - }); - socket.on("modelist:get", () => { - socket.emit("modelist", neoModules.neoRuntimeManager.modes()) - }); - socket.on("brightness:set", (brightness) => { - neoModules.neoRuntimeManager.mode.globvars.set("brightness", brightness); - }); - socket.on("brightness:get", () => { - socket.emit("brightness", neoModules.neoRuntimeManager.mode.globvars.get().brightness); - }); - socket.on("power:set", (power) => { - neoModules.neoRuntimeManager.mode.globvars.set("power_on", power); - }); - socket.on("power:get", () => { - socket.emit("power", neoModules.neoRuntimeManager.mode.globvars.get().power_on); - }); - socket.on("var:set", (name, value) => { - neoModules.neoRuntimeManager.mode.variables.set(name, value.toString()); - }); - socket.on("vars:get", () => { - socket.emit("vars", neoModules.neoRuntimeManager.mode.variables.get()); - }); - socket.on("modeinfo:get", () => { - socket.emit("modeinfo", { - mode: neoModules.neoRuntimeManager.mode.current(), - brightness: neoModules.neoRuntimeManager.mode.globvars.get().brightness, - power: neoModules.neoRuntimeManager.mode.globvars.get().power_on, - vars: neoModules.neoRuntimeManager.mode.variables.get() - }); - }); - socket.on("authenticate:user", (username, password, callback) => { - let user = neoModules.userData.user.get(username); - if (user == null) { - callback({success: false, reason: "Invalid username/password"}) - logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) tried to log in as '${username}', wrong username and/or password.`); - return; - } - - let providedHash = hashPassword(password, user.salt); - if (providedHash.hash == user.password) { - let token = createToken(socket); - while (session_tokens.hasOwnProperty(token)) { - token = createToken(socket); - } - - session_tokens[token] = { - expire: (~~Date.now())+(2678400), - host: socket.handshake.headers.host, - user: {username: user.username} - }; - - callback({success: true, user: {username: username}, token: token}) - logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) authenticated as user '${username}'`); - return; - } - - callback({success: false, reason: "Invalid username/password"}) - logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) tried to log in as '${username}', wrong username and/or password.`); - }); - - socket.on("disconnect", () => { - logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) disconnected.`); - }); - - if (neoModules.selfUpdater.updater.updating) { - socket.emit("updater", "start"); - } - }); - - neoModules.neoRuntimeManager.event.on("change", (name, value) => { - if (name == "modelist") { - openNamespace.emit("modelist", neoModules.neoRuntimeManager.modes()); - } else if (["mode", "power_on", "brightness"].includes(name)) { - if (name == "power_on") { name = "power"; } - openNamespace.emit(name, value); - } else { - openNamespace.emit("var", name, value); - } - }); - neoModules.selfUpdater.updater.event.on("start", () => { - openNamespace.emit("updater", "start"); - }); - neoModules.selfUpdater.updater.event.on("end", () => { - openNamespace.emit("updater", "end"); - }); -} - -/** - * @type {object} This is the collection of valid session tokens. - */ -let session_tokens = {}; - -/** - * Middleware that will stop the request if the client does not have a valid - * session token. - * - * @param {object} socket - The socket instance of the connected client - * @param {function} next - The callback to continue the middleware chain - */ -function authorize_middleware(socket, next) { - const token = socket.handshake.auth.token; - - if (session_tokens.hasOwnProperty(token) && - // session_tokens[token].host === socket.handshake.headers.host && - session_tokens[token].expire > (~~(Date.now()))) { - socket.data.user = session_tokens[token].user; - next(); - } else { - const err = new Error("not authorized"); - err.data = { content: "invalid session token" }; - next(err); - } -} - -/** - * Create the open socketio namespace and setup all listeners. - * A valid session token is required to connect to this namespace. - * - * @param {io} socetio - */ -function createAuthorizedNamespace(io) { - const authorizedNamespace = io.of("/authed"); - authorizedNamespace.use(authorize_middleware); - authorizedNamespace.on("connection", (socket) => { - logger.access(`SOCKET:authed Client (${socket.id}@${socket.handshake.headers.host}) connected.`); - let debuggerOpen = false; - - socket.emit("user", socket.data.user); - - /* InstanceName */ - socket.on("name:set", (name, fn) => { - neoModules.userData.config.instanceName = name; - fn({success: true}); - io.emit("name", neoModules.userData.config.instanceName); - }); - - /* UserData */ - socket.on("mode:create", (name, template, fn) => { - fn(neoModules.userData.mode.create(name, template)); - }); - socket.on("mode:delete", (modeid, fn) => { - fn(neoModules.userData.mode.delete(modeid)); - }); - - /* LED Config */ - socket.on("led_config:get", () => { - socket.emit("led_config", neoModules.userData.strip.get()); - }); - socket.on("led_config:set", (config) => { - neoModules.userData.strip.set(config); - }); - - /* SelfUpdater */ - socket.on("version:current_number", () => { - socket.emit("version:current_number", neoModules.selfUpdater.localVersionNumber); - }); - socket.on("version:branch", (fn) => { - socket.emit("version:branch", neoModules.selfUpdater.branch); - }); - socket.on("version:newest_number", (fn) => { - socket.emit("version:newest_number", neoModules.selfUpdater.remoteVersionNumber); - }); - socket.on("version:check_for_update", (fn) => { - neoModules.selfUpdater.checkVersion().then(() => { - socket.emit("version:newest_number", neoModules.selfUpdater.remoteVersionNumber); - fn({success: true}); - }); - }); - socket.on("system:update_version", () => { - neoModules.selfUpdater.updater.forceUpdate(); - }); - - /* SSLCert */ - socket.on("sslcert:info", (fn) => { - socket.emit("sslcert:info", {...neoModules.SSLCert.getConfig(), "isValid": neoModules.SSLCert.checkValidity()}); - }); - socket.on("sslcert:generate_new", (fn) => { - neoModules.SSLCert.generateCert(); - fn({success: true}); - }); - - /* System actions */ - socket.on("restart:system", () => { - exec('shutdown -r now', function(error, stdout, stderr){ callback(stdout); }); - }); - socket.on("restart:service", () => { - let p = exec('systemctl restart luxcena-neo'); - p.unref(); - }); - - /* Users */ - socket.on("users:get", () => { - socket.emit("users", neoModules.userData.users()) - }); - socket.on("user:delete", (username, fn) => { - if (username == socket.data.user.username) { fn({success: false, reason: "cannot delete logged in account"}); return; } - fn(neoModules.userData.user.delete(username)); - socket.emit("users", neoModules.userData.users()) - }); - socket.on("user:changeusername", (oldusername, newusername, fn) => { - if (oldusername == socket.data.user.username) { fn({success: false, reason: "cannot change username of logged in account"}); return; } - let user = neoModules.userData.user.get(oldUserName); - if (user == null) { fn({success: false, reason: "unknown username", detail: oldusername}); return; } - user.username = newusername; - let res = neoModules.userData.user.save(user); - if (!res.success) { fn(res); return; } - res = neoModules.userData.user.delete(oldusername) - if (!res.success) { fn(res); return; } - fn({success: true}); - socket.emit("users", neoModules.userData.users()) - }); - socket.on("user:newpassword", (username, newPassword, fn) => { - let user = neoModules.userData.user.get(username); - if (user == null) { fn({success: false, reason: "unknown username", detail: username}); return; } - let newHash = hashPassword(newPassword); - fn(neoModules.userData.user.save(username, newHash.salt, newHash.hash)); - socket.emit("users", neoModules.userData.users()) - }); - socket.on("user:create", (username, newPassword, fn) => { - let user = neoModules.userData.user.get(username); - if (user != null) { fn({success: false, reason: "user already exists", detail: username}); return; } - if (username.length < 1) { fn({success: false, reason: "no username provided"}); return; } - let newHash = hashPassword(newPassword); - fn(neoModules.userData.user.save(username, newHash.salt, newHash.hash)); - socket.emit("users", neoModules.userData.users()) - }); - - /* Editor/debugger */ - let onProcStart = () => socket.emit("editor:proc:start"); - let onProcStop = (code) => socket.emit("editor:proc:exit", code); - let onProcStdout = (stdout) => socket.volatile.emit("editor:proc:stdout", stdout); - let onProcStderr = (stderr) => socket.volatile.emit("editor:proc:stderr", stderr); - let onDebuggerState = (state) => socket.volatile.emit("editor:debugger:state", state); - let closeDebugger = () => { - debuggerOpen = false; - neoModules.neoRuntimeManager.event.removeListener("proc:start", onProcStart); - neoModules.neoRuntimeManager.event.removeListener("proc:stop", onProcStop); - neoModules.neoRuntimeManager.event.removeListener("proc:stdout", onProcStdout); - neoModules.neoRuntimeManager.event.removeListener("proc:stderr", onProcStderr); - neoModules.neoRuntimeManager.event.removeListener("debugger:state", onDebuggerState); - return neoModules.neoRuntimeManager.stopDebugger(); - }; - socket.on("editor:open", (modeId, fn) => { - neoModules.neoRuntimeManager.event.on("proc:start", onProcStart); - neoModules.neoRuntimeManager.event.on("proc:exit", onProcStop); - neoModules.neoRuntimeManager.event.on("proc:stdout", onProcStdout); - neoModules.neoRuntimeManager.event.on("proc:stderr", onProcStderr); - neoModules.neoRuntimeManager.event.on("debugger:state", onDebuggerState); - let res = neoModules.neoRuntimeManager.startDebugger(modeId); - if (!res.success) { fn(res); return; } - logger.info(`Starting debugger for ${modeId}.`) - debuggerOpen = true; - fn({success: true}) - socket.emit("editor:code", modeId, res.code); - - if (neoModules.neoRuntimeManager.modeRunning()) { - socket.emit("editor:proc:start"); - } - }); - socket.on("editor:save", (modeId, code, fn) => { - if (!debuggerOpen) { fn({success: false, reason: "debugger not open"}); return; }; - fn(neoModules.neoRuntimeManager.saveModeCode(modeId, code)); - }); - socket.on("editor:startmode", (fn) => { - if (neoModules.neoRuntimeManager.modeRunning()) { - fn({success: true}); - socket.emit("editor:proc:start"); - } else { - fn(neoModules.neoRuntimeManager.startMode()); - } - }); - socket.on("editor:stopmode", (fn) => { - fn(neoModules.neoRuntimeManager.stopMode()); - }); - socket.on("editor:restartmode", (fn) => { - fn(neoModules.neoRuntimeManager.restartMode()); - }); - socket.on("editor:close", (fn) => { - fn(closeDebugger()); - logger.info("Stopped debugger"); - }); - - /* Matrix and strip buffer */ - socket.on("matrix:get", () => { - socket.emit("matrix", neoModules.neoRuntimeManager.matrix); - }); - - socket.on("disconnect", () => { - logger.access(`SOCKET:authed Client (${socket.id}@${socket.handshake.headers.host}) disconnected.`); - if (debuggerOpen) { - closeDebugger(); - logger.info("Stopped debugger because client disconnected") - } - }); - }); - - neoModules.neoRuntimeManager.event.on("matrix", (matrix) => { - authorizedNamespace.emit("matrix", matrix); - }); - let lastStripBufferEmit = performance.now(); - neoModules.neoRuntimeManager.event.on("strip_buffer", (strip_buffer) => { - if ((performance.now() - lastStripBufferEmit) > 50) { - authorizedNamespace.volatile.emit("strip_buffer", strip_buffer); - lastStripBufferEmit = performance.now(); - } // We just drop packets - }); - neoModules.selfUpdater.updater.event.on("step", (step) => { - authorizedNamespace.emit("updater:step", step); - }); - neoModules.selfUpdater.updater.event.on("command", (command) => { - authorizedNamespace.emit("updater:command", command); - }); - neoModules.selfUpdater.updater.event.on("error", (updateLog) => { - authorizedNamespace.emit("updater:error", updateLog); - }); -} - -/** - * Protect - */ -function limitEmits(fn) { - let lastEmit = performance.now(); - - return { - } -} - -/** - * Creates an access-token from the clients host-name and the current EPOCH. - * - * @param {client} - * - * @return {string} - The access-token. - */ - function createToken(client) { - let time = Date.now().toString(); - let host = client.handshake.headers.host; - return (CryptoJS.SHA256(`${host}${time}`).toString()); -} - -/** - * Create a new salt and hash from a password. - * - * @param {string} password - The password to hash. - * @param {string} salt - If set, this salt will be used, else a new salt is generated. - * - * @return {object} A object containing a password and a salt property. - */ -function hashPassword(password, salt = null) { - if (salt == null) { - salt = CryptoJS.lib.WordArray.random(128 / 2); - } else { - salt = CryptoJS.enc.Hex.parse(salt); - } - let hash = CryptoJS.PBKDF2(password, salt, { - keySize: 512 / 32, - iterations: 1000, - hasher: CryptoJS.algo.SHA512 - }); - return {hash: hash.toString(), salt: salt.toString()} -} - -module.exports = (_neoModules, io) => { - neoModules = _neoModules; - return { - openNamespace: createOpenSocketNamespace(io), - authorizedNamespace: createAuthorizedNamespace(io) - } -}; - |