From fe6875904f0c8f4ed8359efe961570307d9cbae6 Mon Sep 17 00:00:00 2001 From: RblSb Date: Mon, 2 Mar 2020 03:26:01 +0300 Subject: addAdmin server command --- .gitignore | 1 + .vscode/launch.json | 3 +- build/server.js | 346 +++++++++++++++++++++++++++++++++++++++++---- res/client.js | 286 ++++++++++++++++++++++++++++++++----- res/index.html | 3 + src/Types.hx | 15 +- src/client/Buttons.hx | 13 +- src/client/Main.hx | 46 +++++- src/server/ConsoleInput.hx | 59 ++++++++ src/server/Main.hx | 78 ++++++++-- user/README.md | 11 +- 11 files changed, 778 insertions(+), 83 deletions(-) create mode 100644 src/server/ConsoleInput.hx diff --git a/.gitignore b/.gitignore index 575465a..40a4754 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /res/temp /user/config.json /user/state.json +/user/users.json /user/res/ /user/logs/ /user/crashes/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 2153bb9..a511189 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,8 @@ "name": "Node: build and run", "program": "${workspaceFolder}/build/server.js", "sourceMaps": true, - "preLaunchTask": "Build all" + "preLaunchTask": "Build all", + "console": "integratedTerminal" } ] } diff --git a/build/server.js b/build/server.js index 0a90ca2..dac26c4 100644 --- a/build/server.js +++ b/build/server.js @@ -309,6 +309,20 @@ StringTools.startsWith = function(s,start) { StringTools.replace = function(s,sub,by) { return s.split(sub).join(by); }; +StringTools.hex = function(n,digits) { + var s = ""; + while(true) { + s = "0123456789ABCDEF".charAt(n & 15) + s; + n >>>= 4; + if(!(n > 0)) { + break; + } + } + if(digits != null) { + while(s.length < digits) s = "0" + s; + } + return s; +}; var _$VideoList_VideoList_$Impl_$ = {}; _$VideoList_VideoList_$Impl_$.__name__ = true; _$VideoList_VideoList_$Impl_$._new = function() { @@ -409,6 +423,190 @@ haxe_Timer.prototype = { ,run: function() { } }; +var haxe_crypto_Sha256 = function() { +}; +haxe_crypto_Sha256.__name__ = true; +haxe_crypto_Sha256.encode = function(s) { + var sh = new haxe_crypto_Sha256(); + return sh.hex(sh.doEncode(haxe_crypto_Sha256.str2blks(s),s.length * 8)); +}; +haxe_crypto_Sha256.str2blks = function(s) { + var s1 = haxe_io_Bytes.ofString(s); + var nblk = (s1.length + 8 >> 6) + 1; + var blks = []; + var _g = 0; + var _g1 = nblk * 16; + while(_g < _g1) blks[_g++] = 0; + var _g2 = 0; + var _g3 = s1.length; + while(_g2 < _g3) { + var i = _g2++; + blks[i >> 2] |= s1.b[i] << 24 - ((i & 3) << 3); + } + var i1 = s1.length; + blks[i1 >> 2] |= 128 << 24 - ((i1 & 3) << 3); + blks[nblk * 16 - 1] = s1.length * 8; + return blks; +}; +haxe_crypto_Sha256.prototype = { + doEncode: function(m,l) { + var K = [1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525,-778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998]; + var HASH = [1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225]; + var W = []; + W[64] = 0; + var a; + var b; + var c; + var d; + var e; + var f; + var g; + var h; + var T1; + var T2; + m[l >> 5] |= 128 << 24 - l % 32; + m[(l + 64 >> 9 << 4) + 15] = l; + var i = 0; + while(i < m.length) { + a = HASH[0]; + b = HASH[1]; + c = HASH[2]; + d = HASH[3]; + e = HASH[4]; + f = HASH[5]; + g = HASH[6]; + h = HASH[7]; + var _g = 0; + while(_g < 64) { + var j = _g++; + if(j < 16) { + W[j] = m[j + i]; + } else { + var x = W[j - 2]; + var x1 = (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ x >>> 10; + var y = W[j - 7]; + var lsw = (x1 & 65535) + (y & 65535); + var x2 = (x1 >> 16) + (y >> 16) + (lsw >> 16) << 16 | lsw & 65535; + var x3 = W[j - 15]; + var y1 = (x3 >>> 7 | x3 << 25) ^ (x3 >>> 18 | x3 << 14) ^ x3 >>> 3; + var lsw1 = (x2 & 65535) + (y1 & 65535); + var x4 = (x2 >> 16) + (y1 >> 16) + (lsw1 >> 16) << 16 | lsw1 & 65535; + var y2 = W[j - 16]; + var lsw2 = (x4 & 65535) + (y2 & 65535); + W[j] = (x4 >> 16) + (y2 >> 16) + (lsw2 >> 16) << 16 | lsw2 & 65535; + } + var y3 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7); + var lsw3 = (h & 65535) + (y3 & 65535); + var x5 = (h >> 16) + (y3 >> 16) + (lsw3 >> 16) << 16 | lsw3 & 65535; + var y4 = e & f ^ ~e & g; + var lsw4 = (x5 & 65535) + (y4 & 65535); + var x6 = (x5 >> 16) + (y4 >> 16) + (lsw4 >> 16) << 16 | lsw4 & 65535; + var y5 = K[j]; + var lsw5 = (x6 & 65535) + (y5 & 65535); + var x7 = (x6 >> 16) + (y5 >> 16) + (lsw5 >> 16) << 16 | lsw5 & 65535; + var y6 = W[j]; + var lsw6 = (x7 & 65535) + (y6 & 65535); + T1 = (x7 >> 16) + (y6 >> 16) + (lsw6 >> 16) << 16 | lsw6 & 65535; + var x8 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10); + var y7 = a & b ^ a & c ^ b & c; + var lsw7 = (x8 & 65535) + (y7 & 65535); + T2 = (x8 >> 16) + (y7 >> 16) + (lsw7 >> 16) << 16 | lsw7 & 65535; + h = g; + g = f; + f = e; + var lsw8 = (d & 65535) + (T1 & 65535); + e = (d >> 16) + (T1 >> 16) + (lsw8 >> 16) << 16 | lsw8 & 65535; + d = c; + c = b; + b = a; + var lsw9 = (T1 & 65535) + (T2 & 65535); + a = (T1 >> 16) + (T2 >> 16) + (lsw9 >> 16) << 16 | lsw9 & 65535; + } + var y8 = HASH[0]; + var lsw10 = (a & 65535) + (y8 & 65535); + HASH[0] = (a >> 16) + (y8 >> 16) + (lsw10 >> 16) << 16 | lsw10 & 65535; + var y9 = HASH[1]; + var lsw11 = (b & 65535) + (y9 & 65535); + HASH[1] = (b >> 16) + (y9 >> 16) + (lsw11 >> 16) << 16 | lsw11 & 65535; + var y10 = HASH[2]; + var lsw12 = (c & 65535) + (y10 & 65535); + HASH[2] = (c >> 16) + (y10 >> 16) + (lsw12 >> 16) << 16 | lsw12 & 65535; + var y11 = HASH[3]; + var lsw13 = (d & 65535) + (y11 & 65535); + HASH[3] = (d >> 16) + (y11 >> 16) + (lsw13 >> 16) << 16 | lsw13 & 65535; + var y12 = HASH[4]; + var lsw14 = (e & 65535) + (y12 & 65535); + HASH[4] = (e >> 16) + (y12 >> 16) + (lsw14 >> 16) << 16 | lsw14 & 65535; + var y13 = HASH[5]; + var lsw15 = (f & 65535) + (y13 & 65535); + HASH[5] = (f >> 16) + (y13 >> 16) + (lsw15 >> 16) << 16 | lsw15 & 65535; + var y14 = HASH[6]; + var lsw16 = (g & 65535) + (y14 & 65535); + HASH[6] = (g >> 16) + (y14 >> 16) + (lsw16 >> 16) << 16 | lsw16 & 65535; + var y15 = HASH[7]; + var lsw17 = (h & 65535) + (y15 & 65535); + HASH[7] = (h >> 16) + (y15 >> 16) + (lsw17 >> 16) << 16 | lsw17 & 65535; + i += 16; + } + return HASH; + } + ,hex: function(a) { + var str = ""; + var _g = 0; + while(_g < a.length) str += StringTools.hex(a[_g++],8); + return str.toLowerCase(); + } +}; +var haxe_io_Bytes = function(data) { + this.length = data.byteLength; + this.b = new Uint8Array(data); + this.b.bufferValue = data; + data.hxBytes = this; + data.bytes = this.b; +}; +haxe_io_Bytes.__name__ = true; +haxe_io_Bytes.ofString = function(s,encoding) { + if(encoding == haxe_io_Encoding.RawNative) { + var buf = new Uint8Array(s.length << 1); + var _g = 0; + var _g1 = s.length; + while(_g < _g1) { + var i = _g++; + var c = s.charCodeAt(i); + buf[i << 1] = c & 255; + buf[i << 1 | 1] = c >> 8; + } + return new haxe_io_Bytes(buf.buffer); + } + var a = []; + var i1 = 0; + while(i1 < s.length) { + var c1 = s.charCodeAt(i1++); + if(55296 <= c1 && c1 <= 56319) { + c1 = c1 - 55232 << 10 | s.charCodeAt(i1++) & 1023; + } + if(c1 <= 127) { + a.push(c1); + } else if(c1 <= 2047) { + a.push(192 | c1 >> 6); + a.push(128 | c1 & 63); + } else if(c1 <= 65535) { + a.push(224 | c1 >> 12); + a.push(128 | c1 >> 6 & 63); + a.push(128 | c1 & 63); + } else { + a.push(240 | c1 >> 18); + a.push(128 | c1 >> 12 & 63); + a.push(128 | c1 >> 6 & 63); + a.push(128 | c1 & 63); + } + } + return new haxe_io_Bytes(new Uint8Array(a).buffer); +}; +var haxe_io_Encoding = $hxEnums["haxe.io.Encoding"] = { __ename__ : true, __constructs__ : ["UTF8","RawNative"] + ,UTF8: {_hx_index:0,__enum__:"haxe.io.Encoding",toString:$estr} + ,RawNative: {_hx_index:1,__enum__:"haxe.io.Encoding",toString:$estr} +}; var haxe_io_Path = function(path) { switch(path) { case ".":case "..": @@ -565,7 +763,52 @@ var js_node_Fs = require("fs"); var js_node_Http = require("http"); var js_node_Os = require("os"); var js_node_Path = require("path"); +var js_node_Readline = require("readline"); var js_npm_ws_Server = require("ws").Server; +var server_ConsoleInput = function(main) { + this.main = main; +}; +server_ConsoleInput.__name__ = true; +server_ConsoleInput.prototype = { + initConsoleInput: function() { + var _gthis = this; + var rl = js_node_Readline.createInterface(process.stdin,process.stdout); + haxe_Log.trace = function(msg,pos) { + js_node_Readline.clearLine(process.stdout,0); + js_node_Readline.cursorTo(process.stdout,0,null); + console.log(msg); + rl.prompt(true); + return; + }; + rl.prompt(); + rl.on("line",function(line) { + _gthis.parseLine(line); + rl.prompt(); + return; + }); + } + ,parseLine: function(line) { + if(StringTools.startsWith(line,"/addAdmin")) { + var args = line.split(" "); + if(args.length != 3) { + haxe_Log.trace("Wrong count of arguments",{ fileName : "src/server/ConsoleInput.hx", lineNumber : 36, className : "server.ConsoleInput", methodName : "parseLine"}); + return; + } + var name = args[1]; + var password = args[2]; + if(this.main.badNickName(name)) { + haxe_Log.trace(StringTools.replace(Lang.get("usernameError"),"$MAX","" + this.main.config.maxLoginLength),{ fileName : "src/server/ConsoleInput.hx", lineNumber : 44, className : "server.ConsoleInput", methodName : "parseLine"}); + return; + } + this.main.addAdmin(name,password); + } else if(line == "/exit") { + this.main.exit(); + return; + } else { + haxe_Log.trace("Unknown command \"" + line + "\". List:\n/addAdmin name password | Adds channel admin\n/exit | Exit process",{ fileName : "src/server/ConsoleInput.hx", lineNumber : 53, className : "server.ConsoleInput", methodName : "parseLine"}); + } + } +}; var server_HttpServer = function() { }; server_HttpServer.__name__ = true; server_HttpServer.init = function(dir,customDir) { @@ -701,34 +944,34 @@ var server_Main = function(port,wsPort) { port = envPort; } this.statePath = "" + this.rootDir + "/user/state.json"; - var exit = function() { - _gthis.saveState(); - process.exit(); - }; - process.on("SIGINT",exit); - process.on("SIGUSR1",exit); - process.on("SIGUSR2",exit); - process.on("SIGTERM",exit); + process.on("SIGINT",$bind(this,this.exit)); + process.on("SIGUSR1",$bind(this,this.exit)); + process.on("SIGUSR2",$bind(this,this.exit)); + process.on("SIGTERM",$bind(this,this.exit)); process.on("uncaughtException",function(err) { _gthis.logError("uncaughtException",{ message : err.message, stack : err.stack}); - exit(); + _gthis.exit(); return; }); process.on("unhandledRejection",function(reason,promise) { _gthis.logError("unhandledRejection",reason); - exit(); + _gthis.exit(); return; }); + this.consoleInput = new server_ConsoleInput(this); + this.consoleInput.initConsoleInput(); this.initIntergationHandlers(); this.loadState(); - this.config = this.getUserConfig(); + this.config = this.loadUserConfig(); + this.userList = this.loadUsers(); + this.config.salt = this.generateConfigSalt(); this.localIp = server_Utils.getLocalIp(); this.globalIp = this.localIp; this.port = port; server_Utils.getGlobalIp(function(ip) { _gthis.globalIp = ip; - haxe_Log.trace("Local: http://" + _gthis.localIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 71, className : "server.Main", methodName : "new"}); - haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 72, className : "server.Main", methodName : "new"}); + haxe_Log.trace("Local: http://" + _gthis.localIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 74, className : "server.Main", methodName : "new"}); + haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 75, className : "server.Main", methodName : "new"}); return; }); var dir = "" + this.rootDir + "/res"; @@ -747,7 +990,18 @@ server_Main.main = function() { new server_Main(); }; server_Main.prototype = { - getUserConfig: function() { + exit: function() { + this.saveState(); + process.exit(); + } + ,generateConfigSalt: function() { + if(this.userList.salt == null) { + var tmp = "" + Math.random(); + this.userList.salt = haxe_crypto_Sha256.encode(tmp); + } + return this.userList.salt; + } + ,loadUserConfig: function() { var config = JSON.parse(js_node_Fs.readFileSync("" + this.rootDir + "/default-config.json",{ encoding : "utf8"})); var customPath = "" + this.rootDir + "/user/config.json"; if(!sys_FileSystem.exists(customPath)) { @@ -760,14 +1014,28 @@ server_Main.prototype = { var field = _g1[_g]; ++_g; if(Reflect.field(config,field) == null) { - haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 93, className : "server.Main", methodName : "getUserConfig"}); + haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 107, className : "server.Main", methodName : "loadUserConfig"}); } config[field] = Reflect.field(customConfig,field); } return config; } + ,loadUsers: function() { + var customPath = "" + this.rootDir + "/user/users.json"; + if(!sys_FileSystem.exists(customPath)) { + return { admins : []}; + } + return JSON.parse(js_node_Fs.readFileSync(customPath,{ encoding : "utf8"})); + } + ,writeUsers: function(users) { + var folder = "" + this.rootDir + "/user"; + if(!sys_FileSystem.exists(folder)) { + sys_FileSystem.createDirectory(folder); + } + js_node_Fs.writeFileSync("" + folder + "/users.json",JSON.stringify(users,null,"\t")); + } ,saveState: function() { - haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 100, className : "server.Main", methodName : "saveState"}); + haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 131, className : "server.Main", methodName : "saveState"}); var json = JSON.stringify({ videoList : this.videoList, itemPos : this.itemPos, messages : this.messages, timer : { time : this.videoTimer.getTime(), paused : this.videoTimer.isPaused()}},null,"\t"); js_node_Fs.writeFileSync(this.statePath,json); } @@ -775,7 +1043,7 @@ server_Main.prototype = { if(!sys_FileSystem.exists(this.statePath)) { return; } - haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 116, className : "server.Main", methodName : "loadState"}); + haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 147, className : "server.Main", methodName : "loadState"}); var data = JSON.parse(js_node_Fs.readFileSync(this.statePath,{ encoding : "utf8"})); this.videoList.length = 0; this.messages.length = 0; @@ -791,7 +1059,7 @@ server_Main.prototype = { this.videoTimer.pause(); } ,logError: function(type,data) { - haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 129, className : "server.Main", methodName : "logError", customParams : [data]}); + haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 160, className : "server.Main", methodName : "logError", customParams : [data]}); var crashesFolder = "" + this.rootDir + "/user/crashes"; var name = new Date().toISOString() + "-" + type; if(!sys_FileSystem.exists(crashesFolder)) { @@ -807,23 +1075,31 @@ server_Main.prototype = { return; } var url = "http://" + process.env["APP_URL"]; - haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 143, className : "server.Main", methodName : "initIntergationHandlers"}); + haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 174, className : "server.Main", methodName : "initIntergationHandlers"}); js_node_Http.get(url,function(r) { return; }); }; } } + ,addAdmin: function(name,password) { + password += this.config.salt; + var hash = haxe_crypto_Sha256.encode(password); + if(this.userList.admins == null) { + this.userList.admins = []; + } + this.userList.admins.push({ name : name, hash : hash}); + this.writeUsers(this.userList); + haxe_Log.trace("Admin " + name + " added.",{ fileName : "src/server/Main.hx", lineNumber : 189, className : "server.Main", methodName : "addAdmin"}); + } ,onConnect: function(ws,req) { var _gthis = this; var ip = req.connection.remoteAddress; var id = this.freeIds.length > 0 ? this.freeIds.shift() : this.clients.length; var name = "Guest " + (id + 1); - haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 153, className : "server.Main", methodName : "onConnect"}); + haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 196, className : "server.Main", methodName : "onConnect"}); var client = new Client(ws,req,id,name,0); - if(req.connection.localAddress == ip) { - client.group |= 4; - } + client.setGroupFlag(ClientGroup.Admin,req.connection.localAddress == ip); this.clients.push(client); if(this.clients.length == 1 && this.videoList.length > 0) { if(this.videoTimer.isPaused()) { @@ -845,7 +1121,7 @@ server_Main.prototype = { return; }); ws.on("close",function(err) { - haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 182, className : "server.Main", methodName : "onConnect"}); + haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 225, className : "server.Main", methodName : "onConnect"}); server_Utils.sortedPush(_gthis.freeIds,client.id); HxOverrides.remove(_gthis.clients,client); _gthis.sendClientList(); @@ -914,6 +1190,26 @@ server_Main.prototype = { this.send(client,{ type : "LoginError"}); return; } + var hash = data.login.passHash; + if(hash == null) { + if(Lambda.exists(this.userList.admins,function(a) { + return a.name == name; + })) { + this.send(client,{ type : "PasswordRequest"}); + return; + } + } else if(Lambda.exists(this.userList.admins,function(a1) { + if(a1.name == name) { + return a1.hash == hash; + } else { + return false; + } + })) { + client.setGroupFlag(ClientGroup.Admin,true); + } else { + this.send(client,{ type : "LoginError"}); + return; + } client.name = name; client.setGroupFlag(ClientGroup.User,true); this.send(client,{ type : data.type, login : { isUnknownClient : true, clientName : client.name, clients : this.clientList()}}); @@ -945,6 +1241,8 @@ server_Main.prototype = { } this.broadcast(data); break; + case "PasswordRequest": + break; case "Pause": if(this.videoList.length == 0) { return; diff --git a/res/client.js b/res/client.js index 7b90a05..14feb83 100644 --- a/res/client.js +++ b/res/client.js @@ -118,25 +118,6 @@ Lambda.exists = function(it,f) { } return false; }; -var haxe_ds_StringMap = function() { - this.h = { }; -}; -haxe_ds_StringMap.__name__ = true; -haxe_ds_StringMap.prototype = { - setReserved: function(key,value) { - if(this.rh == null) { - this.rh = { }; - } - this.rh["$" + key] = value; - } - ,getReserved: function(key) { - if(this.rh == null) { - return null; - } else { - return this.rh["$" + key]; - } - } -}; var Lang = function() { }; Lang.__name__ = true; Lang.request = function(path,callback) { @@ -326,6 +307,20 @@ StringTools.startsWith = function(s,start) { StringTools.replace = function(s,sub,by) { return s.split(sub).join(by); }; +StringTools.hex = function(n,digits) { + var s = ""; + while(true) { + s = "0123456789ABCDEF".charAt(n & 15) + s; + n >>>= 4; + if(!(n > 0)) { + break; + } + } + if(digits != null) { + while(s.length < digits) s = "0" + s; + } + return s; +}; var _$VideoList_VideoList_$Impl_$ = {}; _$VideoList_VideoList_$Impl_$.__name__ = true; _$VideoList_VideoList_$Impl_$._new = function() { @@ -588,17 +583,21 @@ client_Buttons.hideMenus = function() { client_Buttons.initChatInput = function(main) { var guestName = window.document.querySelector("#guestname"); guestName.onkeydown = function(e) { - if(guestName.value.length == 0) { - return; - } if(e.keyCode == 13) { - main.send({ type : "Login", login : { clientName : guestName.value}}); + main.guestLogin(guestName.value); + } + return; + }; + var guestPass = window.document.querySelector("#guestpass"); + guestPass.onkeydown = function(e1) { + if(e1.keyCode == 13) { + main.userLogin(guestName.value,guestPass.value); } return; }; var chatLine = window.document.querySelector("#chatline"); - chatLine.onkeydown = function(e1) { - switch(e1.keyCode) { + chatLine.onkeydown = function(e2) { + switch(e2.keyCode) { case 13: if(chatLine.value.length == 0) { return; @@ -821,7 +820,7 @@ client_Main.prototype = { var data = JSON.parse(e.data); var t = data.type; var t1 = t.charAt(0).toLowerCase() + HxOverrides.substr(t,1,null); - haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 211, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]}); + haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 212, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]}); switch(data.type) { case "AddVideo": this.player.addVideoItem(data.addVideo.item,data.addVideo.atEnd); @@ -868,6 +867,7 @@ client_Main.prototype = { case "LoginError": var text = StringTools.replace(Lang.get("usernameError"),"$MAX","" + this.config.maxLoginLength); this.serverMessage(4,text); + this.showGuestLoginPanel(); break; case "Logout": this.updateClients(data.logout.clients); @@ -877,6 +877,9 @@ client_Main.prototype = { case "Message": this.addMessage(data.message.clientName,data.message.text); break; + case "PasswordRequest": + this.showGuestPasswordPanel(); + break; case "Pause": if((this.personal.group & 1 << ClientGroup.Leader._hx_index) != 0) { return; @@ -958,8 +961,11 @@ client_Main.prototype = { this.onLogin(connected.clients,connected.clientName); } var guestName = window.document.querySelector("#guestname"); - if(guestName.value.length > 0) { - this.send({ type : "Login", login : { clientName : guestName.value}}); + var guestPass = window.document.querySelector("#guestpass"); + if(this.config.salt != null && guestPass.value.length > 0) { + this.userLogin(guestName.value,guestPass.value); + } else { + this.guestLogin(guestName.value); } this.clearChat(); this.serverMessage(1); @@ -972,6 +978,24 @@ client_Main.prototype = { } this.player.setItems(connected.videoList,connected.itemPos); } + ,guestLogin: function(name) { + if(name.length == 0) { + return; + } + this.send({ type : "Login", login : { clientName : name}}); + } + ,userLogin: function(name,password) { + if(this.config.salt == null) { + return; + } + if(password.length == 0) { + return; + } + if(name.length == 0) { + return; + } + this.send({ type : "Login", login : { clientName : name, passHash : haxe_crypto_Sha256.encode(password + this.config.salt)}}); + } ,setConfig: function(config) { this.config = config; this.pageTitle = config.channelName; @@ -994,9 +1018,8 @@ client_Main.prototype = { } var smilesWrap = window.document.querySelector("#smileswrap"); smilesWrap.onclick = function(e) { - var el = e.target; var form = window.document.querySelector("#chatline"); - form.value += " " + el.title; + form.value += " " + e.target.title; form.focus(); return; }; @@ -1024,12 +1047,14 @@ client_Main.prototype = { } ,showGuestLoginPanel: function() { window.document.querySelector("#guestlogin").style.display = "block"; + window.document.querySelector("#guestpassword").style.display = "none"; window.document.querySelector("#chatline").style.display = "none"; window.document.querySelector("#exitBtn").textContent = Lang.get("login"); window.dispatchEvent(new Event("resize")); } ,hideGuestLoginPanel: function() { window.document.querySelector("#guestlogin").style.display = "none"; + window.document.querySelector("#guestpassword").style.display = "none"; window.document.querySelector("#chatline").style.display = "block"; window.document.querySelector("#exitBtn").textContent = Lang.get("exit"); if((this.personal.group & 4) != 0) { @@ -1037,6 +1062,11 @@ client_Main.prototype = { } window.dispatchEvent(new Event("resize")); } + ,showGuestPasswordPanel: function() { + window.document.querySelector("#guestlogin").style.display = "none"; + window.document.querySelector("#chatline").style.display = "none"; + window.document.querySelector("#guestpassword").style.display = "block"; + } ,updateClients: function(newClients) { this.clients.length = 0; var _g = 0; @@ -1719,7 +1749,8 @@ client_players_Youtube.prototype = { if(_gthis.playerEl.contains(video)) { _gthis.playerEl.removeChild(video); } - callback({ duration : _gthis.youtube.getDuration()}); + var tmp = _gthis.youtube.getDuration(); + callback({ duration : tmp}); return; }, onError : function(e1) { haxe_Log.trace("Error " + e1.data,{ fileName : "src/client/players/Youtube.hx", lineNumber : 116, className : "client.players.Youtube", methodName : "getRemoteDataFallback"}); @@ -1844,6 +1875,159 @@ haxe_Timer.prototype = { ,run: function() { } }; +var haxe_crypto_Sha256 = function() { +}; +haxe_crypto_Sha256.__name__ = true; +haxe_crypto_Sha256.encode = function(s) { + var sh = new haxe_crypto_Sha256(); + return sh.hex(sh.doEncode(haxe_crypto_Sha256.str2blks(s),s.length * 8)); +}; +haxe_crypto_Sha256.str2blks = function(s) { + var s1 = haxe_io_Bytes.ofString(s); + var nblk = (s1.length + 8 >> 6) + 1; + var blks = []; + var _g = 0; + var _g1 = nblk * 16; + while(_g < _g1) blks[_g++] = 0; + var _g2 = 0; + var _g3 = s1.length; + while(_g2 < _g3) { + var i = _g2++; + blks[i >> 2] |= s1.b[i] << 24 - ((i & 3) << 3); + } + var i1 = s1.length; + blks[i1 >> 2] |= 128 << 24 - ((i1 & 3) << 3); + blks[nblk * 16 - 1] = s1.length * 8; + return blks; +}; +haxe_crypto_Sha256.prototype = { + doEncode: function(m,l) { + var K = [1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525,-778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998]; + var HASH = [1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225]; + var W = []; + W[64] = 0; + var a; + var b; + var c; + var d; + var e; + var f; + var g; + var h; + var T1; + var T2; + m[l >> 5] |= 128 << 24 - l % 32; + m[(l + 64 >> 9 << 4) + 15] = l; + var i = 0; + while(i < m.length) { + a = HASH[0]; + b = HASH[1]; + c = HASH[2]; + d = HASH[3]; + e = HASH[4]; + f = HASH[5]; + g = HASH[6]; + h = HASH[7]; + var _g = 0; + while(_g < 64) { + var j = _g++; + if(j < 16) { + W[j] = m[j + i]; + } else { + var x = W[j - 2]; + var x1 = (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ x >>> 10; + var y = W[j - 7]; + var lsw = (x1 & 65535) + (y & 65535); + var x2 = (x1 >> 16) + (y >> 16) + (lsw >> 16) << 16 | lsw & 65535; + var x3 = W[j - 15]; + var y1 = (x3 >>> 7 | x3 << 25) ^ (x3 >>> 18 | x3 << 14) ^ x3 >>> 3; + var lsw1 = (x2 & 65535) + (y1 & 65535); + var x4 = (x2 >> 16) + (y1 >> 16) + (lsw1 >> 16) << 16 | lsw1 & 65535; + var y2 = W[j - 16]; + var lsw2 = (x4 & 65535) + (y2 & 65535); + W[j] = (x4 >> 16) + (y2 >> 16) + (lsw2 >> 16) << 16 | lsw2 & 65535; + } + var y3 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7); + var lsw3 = (h & 65535) + (y3 & 65535); + var x5 = (h >> 16) + (y3 >> 16) + (lsw3 >> 16) << 16 | lsw3 & 65535; + var y4 = e & f ^ ~e & g; + var lsw4 = (x5 & 65535) + (y4 & 65535); + var x6 = (x5 >> 16) + (y4 >> 16) + (lsw4 >> 16) << 16 | lsw4 & 65535; + var y5 = K[j]; + var lsw5 = (x6 & 65535) + (y5 & 65535); + var x7 = (x6 >> 16) + (y5 >> 16) + (lsw5 >> 16) << 16 | lsw5 & 65535; + var y6 = W[j]; + var lsw6 = (x7 & 65535) + (y6 & 65535); + T1 = (x7 >> 16) + (y6 >> 16) + (lsw6 >> 16) << 16 | lsw6 & 65535; + var x8 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10); + var y7 = a & b ^ a & c ^ b & c; + var lsw7 = (x8 & 65535) + (y7 & 65535); + T2 = (x8 >> 16) + (y7 >> 16) + (lsw7 >> 16) << 16 | lsw7 & 65535; + h = g; + g = f; + f = e; + var lsw8 = (d & 65535) + (T1 & 65535); + e = (d >> 16) + (T1 >> 16) + (lsw8 >> 16) << 16 | lsw8 & 65535; + d = c; + c = b; + b = a; + var lsw9 = (T1 & 65535) + (T2 & 65535); + a = (T1 >> 16) + (T2 >> 16) + (lsw9 >> 16) << 16 | lsw9 & 65535; + } + var y8 = HASH[0]; + var lsw10 = (a & 65535) + (y8 & 65535); + HASH[0] = (a >> 16) + (y8 >> 16) + (lsw10 >> 16) << 16 | lsw10 & 65535; + var y9 = HASH[1]; + var lsw11 = (b & 65535) + (y9 & 65535); + HASH[1] = (b >> 16) + (y9 >> 16) + (lsw11 >> 16) << 16 | lsw11 & 65535; + var y10 = HASH[2]; + var lsw12 = (c & 65535) + (y10 & 65535); + HASH[2] = (c >> 16) + (y10 >> 16) + (lsw12 >> 16) << 16 | lsw12 & 65535; + var y11 = HASH[3]; + var lsw13 = (d & 65535) + (y11 & 65535); + HASH[3] = (d >> 16) + (y11 >> 16) + (lsw13 >> 16) << 16 | lsw13 & 65535; + var y12 = HASH[4]; + var lsw14 = (e & 65535) + (y12 & 65535); + HASH[4] = (e >> 16) + (y12 >> 16) + (lsw14 >> 16) << 16 | lsw14 & 65535; + var y13 = HASH[5]; + var lsw15 = (f & 65535) + (y13 & 65535); + HASH[5] = (f >> 16) + (y13 >> 16) + (lsw15 >> 16) << 16 | lsw15 & 65535; + var y14 = HASH[6]; + var lsw16 = (g & 65535) + (y14 & 65535); + HASH[6] = (g >> 16) + (y14 >> 16) + (lsw16 >> 16) << 16 | lsw16 & 65535; + var y15 = HASH[7]; + var lsw17 = (h & 65535) + (y15 & 65535); + HASH[7] = (h >> 16) + (y15 >> 16) + (lsw17 >> 16) << 16 | lsw17 & 65535; + i += 16; + } + return HASH; + } + ,hex: function(a) { + var str = ""; + var _g = 0; + while(_g < a.length) str += StringTools.hex(a[_g++],8); + return str.toLowerCase(); + } +}; +var haxe_ds_StringMap = function() { + this.h = { }; +}; +haxe_ds_StringMap.__name__ = true; +haxe_ds_StringMap.prototype = { + setReserved: function(key,value) { + if(this.rh == null) { + this.rh = { }; + } + this.rh["$" + key] = value; + } + ,getReserved: function(key) { + if(this.rh == null) { + return null; + } else { + return this.rh["$" + key]; + } + } +}; var haxe_http_HttpBase = function(url) { this.url = url; this.headers = []; @@ -2009,6 +2193,44 @@ var haxe_io_Bytes = function(data) { data.bytes = this.b; }; haxe_io_Bytes.__name__ = true; +haxe_io_Bytes.ofString = function(s,encoding) { + if(encoding == haxe_io_Encoding.RawNative) { + var buf = new Uint8Array(s.length << 1); + var _g = 0; + var _g1 = s.length; + while(_g < _g1) { + var i = _g++; + var c = s.charCodeAt(i); + buf[i << 1] = c & 255; + buf[i << 1 | 1] = c >> 8; + } + return new haxe_io_Bytes(buf.buffer); + } + var a = []; + var i1 = 0; + while(i1 < s.length) { + var c1 = s.charCodeAt(i1++); + if(55296 <= c1 && c1 <= 56319) { + c1 = c1 - 55232 << 10 | s.charCodeAt(i1++) & 1023; + } + if(c1 <= 127) { + a.push(c1); + } else if(c1 <= 2047) { + a.push(192 | c1 >> 6); + a.push(128 | c1 & 63); + } else if(c1 <= 65535) { + a.push(224 | c1 >> 12); + a.push(128 | c1 >> 6 & 63); + a.push(128 | c1 & 63); + } else { + a.push(240 | c1 >> 18); + a.push(128 | c1 >> 12 & 63); + a.push(128 | c1 >> 6 & 63); + a.push(128 | c1 & 63); + } + } + return new haxe_io_Bytes(new Uint8Array(a).buffer); +}; haxe_io_Bytes.ofData = function(b) { var hb = b.hxBytes; if(hb != null) { @@ -2242,10 +2464,10 @@ js_youtube_Youtube.init = function(onAPIReady) { function $getIterator(o) { if( o instanceof Array ) return HxOverrides.iter(o); else return o.iterator(); } function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = m.bind(o); o.hx__closures__[m.__id__] = f; } return f; } $global.$haxeUID |= 0; -var __map_reserved = {}; if( String.fromCodePoint == null ) String.fromCodePoint = function(c) { return c < 0x10000 ? String.fromCharCode(c) : String.fromCharCode((c>>10)+0xD7C0)+String.fromCharCode((c&0x3FF)+0xDC00); } String.__name__ = true; Array.__name__ = true; +var __map_reserved = {}; Object.defineProperty(js__$Boot_HaxeError.prototype,"message",{ get : function() { return String(this.val); }}); diff --git a/res/index.html b/res/index.html index bbc2751..049600f 100644 --- a/res/index.html +++ b/res/index.html @@ -71,6 +71,9 @@ +
diff --git a/src/Types.hx b/src/Types.hx index 8eda528..49e93e4 100644 --- a/src/Types.hx +++ b/src/Types.hx @@ -15,9 +15,20 @@ typedef Config = { videoLimit:Int, leaderRequest:String, emotes:Array, - filters:Array + filters:Array, + ?salt:String }; +typedef UserList = { + admins:Array, + ?salt:String +} + +typedef UserField = { + name:String, + hash:String +} + typedef Emote = { name:String, image:String @@ -58,6 +69,7 @@ typedef WsEvent = { }, ?login:{ clientName:String, + ?passHash:String, ?clients:Array, ?isUnknownClient:Bool, }, @@ -119,6 +131,7 @@ typedef WsEvent = { enum abstract WsEventType(String) { var Connected; var Login; + var PasswordRequest; var LoginError; var Logout; var Message; diff --git a/src/client/Buttons.hx b/src/client/Buttons.hx index 51ba75d..e567331 100644 --- a/src/client/Buttons.hx +++ b/src/client/Buttons.hx @@ -209,13 +209,12 @@ class Buttons { static function initChatInput(main:Main):Void { final guestName:InputElement = cast ge("#guestname"); guestName.onkeydown = e -> { - if (guestName.value.length == 0) return; - if (e.keyCode == 13) main.send({ - type: Login, - login: { - clientName: guestName.value - } - }); + if (e.keyCode == 13) main.guestLogin(guestName.value); + } + + final guestPass:InputElement = cast ge("#guestpass"); + guestPass.onkeydown = e -> { + if (e.keyCode == 13) main.userLogin(guestName.value, guestPass.value); } final chatLine:InputElement = cast ge("#chatline"); diff --git a/src/client/Main.hx b/src/client/Main.hx index c74a600..9d383bc 100644 --- a/src/client/Main.hx +++ b/src/client/Main.hx @@ -1,5 +1,6 @@ package client; +import haxe.crypto.Sha256; import haxe.Timer; import js.html.MouseEvent; import js.html.KeyboardEvent; @@ -217,10 +218,14 @@ class Main { case Login: onLogin(data.login.clients, data.login.clientName); + case PasswordRequest: + showGuestPasswordPanel(); + case LoginError: final text = Lang.get("usernameError") .replace("$MAX", '${config.maxLoginLength}'); serverMessage(4, text); + showGuestLoginPanel(); case Logout: updateClients(data.logout.clients); @@ -324,12 +329,12 @@ class Main { onLogin(connected.clients, connected.clientName); } final guestName:InputElement = cast ge("#guestname"); - if (guestName.value.length > 0) send({ - type: Login, - login: { - clientName: guestName.value - } - }); + final guestPass:InputElement = cast ge("#guestpass"); + if (config.salt != null && guestPass.value.length > 0) { + userLogin(guestName.value, guestPass.value); + } else { + guestLogin(guestName.value); + } clearChat(); serverMessage(1); for (message in connected.history) { @@ -338,6 +343,27 @@ class Main { player.setItems(connected.videoList, connected.itemPos); } + public function guestLogin(name:String):Void { + if (name.length == 0) return; + send({ + type: Login, login: { + clientName: name + } + }); + } + + public function userLogin(name:String, password:String):Void { + if (config.salt == null) return; + if (password.length == 0) return; + if (name.length == 0) return; + send({ + type: Login, login: { + clientName: name, + passHash: Sha256.encode(password + config.salt) + } + }); + } + function setConfig(config:Config):Void { this.config = config; pageTitle = config.channelName; @@ -386,6 +412,7 @@ class Main { function showGuestLoginPanel():Void { ge("#guestlogin").style.display = "block"; + ge("#guestpassword").style.display = "none"; ge("#chatline").style.display = "none"; ge("#exitBtn").textContent = Lang.get("login"); Browser.window.dispatchEvent(new Event("resize")); @@ -393,12 +420,19 @@ class Main { function hideGuestLoginPanel():Void { ge("#guestlogin").style.display = "none"; + ge("#guestpassword").style.display = "none"; ge("#chatline").style.display = "block"; ge("#exitBtn").textContent = Lang.get("exit"); if (isAdmin()) ge("#clearchatbtn").style.display = "inline-block"; Browser.window.dispatchEvent(new Event("resize")); } + function showGuestPasswordPanel():Void { + ge("#guestlogin").style.display = "none"; + ge("#chatline").style.display = "none"; + ge("#guestpassword").style.display = "block"; + } + function updateClients(newClients:Array):Void { clients.resize(0); for (client in newClients) { diff --git a/src/server/ConsoleInput.hx b/src/server/ConsoleInput.hx new file mode 100644 index 0000000..8b2463f --- /dev/null +++ b/src/server/ConsoleInput.hx @@ -0,0 +1,59 @@ +package server; + +import js.html.Console; +import js.node.Readline; +import js.Node.process; +using StringTools; + +class ConsoleInput { + + final main:Main; + + public function new(main:Main) { + this.main = main; + } + + public function initConsoleInput():Void { + final rl = Readline.createInterface(process.stdin, process.stdout); + haxe.Log.trace = (msg, ?pos) -> { + Readline.clearLine(process.stdout, 0); + Readline.cursorTo(process.stdout, 0, null); + Console.log(msg); + rl.prompt(true); + }; + rl.prompt(); + rl.on("line", line -> { + parseLine(line); + rl.prompt(); + }); + // rl.on("close", exit); + } + + function parseLine(line:String):Void { + if (line.startsWith("/addAdmin")) { + final args = line.split(" "); + if (args.length != 3) { + trace("Wrong count of arguments"); + return; + } + final name = args[1]; + final password = args[2]; + if (main.badNickName(name)) { + final error = Lang.get("usernameError") + .replace("$MAX", '${main.config.maxLoginLength}'); + trace(error); + return; + } + main.addAdmin(name, password); + + } else if (line == "/exit") { + main.exit(); + return; + } else { + trace('Unknown command "$line". List: +/addAdmin name password | Adds channel admin +/exit | Exit process'); + } + } + +} diff --git a/src/server/Main.hx b/src/server/Main.hx index aaf9277..424f22f 100644 --- a/src/server/Main.hx +++ b/src/server/Main.hx @@ -1,5 +1,6 @@ package server; +import haxe.crypto.Sha256; import js.lib.Date; import sys.FileSystem; import sys.io.File; @@ -25,9 +26,11 @@ class Main { final localIp:String; var globalIp:String; final port:Int; - final config:Config; + public final config:Config; + final userList:UserList; final clients:Array = []; final freeIds:Array = []; + final consoleInput:ConsoleInput; final videoList = new VideoList(); final videoTimer = new VideoTimer(); final messages:Array = []; @@ -39,10 +42,6 @@ class Main { final envPort = (process.env : Dynamic).PORT; if (envPort != null) port = envPort; statePath = '$rootDir/user/state.json'; - function exit() { - saveState(); - process.exit(); - } // process.on("exit", exit); process.on("SIGINT", exit); // ctrl+c process.on("SIGUSR1", exit); // kill pid @@ -59,9 +58,13 @@ class Main { logError("unhandledRejection", reason); exit(); }); + consoleInput = new ConsoleInput(this); + consoleInput.initConsoleInput(); initIntergationHandlers(); loadState(); - config = getUserConfig(); + config = loadUserConfig(); + userList = loadUsers(); + config.salt = generateConfigSalt(); localIp = Utils.getLocalIp(); globalIp = localIp; this.port = port; @@ -84,7 +87,18 @@ class Main { wss.on("connection", onConnect); } - function getUserConfig():Config { + public function exit():Void { + saveState(); + process.exit(); + } + + function generateConfigSalt():String { + if (userList.salt == null) + userList.salt = Sha256.encode('${Math.random()}'); + return userList.salt; + } + + function loadUserConfig():Config { final config:Config = Json.parse(File.getContent('$rootDir/default-config.json')); final customPath = '$rootDir/user/config.json'; if (!FileSystem.exists(customPath)) return config; @@ -96,6 +110,23 @@ class Main { return config; } + function loadUsers():UserList { + final customPath = '$rootDir/user/users.json'; + if (!FileSystem.exists(customPath)) return { + admins: [] + }; + return Json.parse(File.getContent(customPath)); + } + + function writeUsers(users:UserList):Void { + final folder = '$rootDir/user'; + if (!FileSystem.exists(folder)) { + FileSystem.createDirectory(folder); + } + final data = Json.stringify(users, "\t"); + File.saveContent('$folder/users.json', data); + } + function saveState():Void { trace("Saving state..."); final data:ServerState = { @@ -146,6 +177,18 @@ class Main { } } + public function addAdmin(name:String, password:String):Void { + password += config.salt; + final hash = Sha256.encode(password); + if (userList.admins == null) userList.admins = []; + userList.admins.push({ + name: name, + hash: hash + }); + writeUsers(userList); + trace('Admin $name added.'); + } + function onConnect(ws:WebSocket, req:IncomingMessage):Void { final ip = req.connection.remoteAddress; final id = freeIds.length > 0 ? freeIds.shift() : clients.length; @@ -153,7 +196,7 @@ class Main { trace('$name connected ($ip)'); final isAdmin = req.connection.localAddress == ip; final client = new Client(ws, req, id, name, 0); - if (isAdmin) client.group.set(Admin); + client.isAdmin = isAdmin; clients.push(client); if (clients.length == 1 && videoList.length > 0) if (videoTimer.isPaused()) videoTimer.play(); @@ -205,6 +248,22 @@ class Main { send(client, {type: LoginError}); return; } + final hash = data.login.passHash; + if (hash == null) { + if (userList.admins.exists(a -> a.name == name)) { + send(client, {type: PasswordRequest}); + return; + } + } else { + if (userList.admins.exists( + a -> a.name == name && a.hash == hash + )) client.isAdmin = true; + else { + // TODO server msg type + send(client, {type: LoginError}); + return; + } + } client.name = name; client.isUser = true; send(client, { @@ -217,6 +276,7 @@ class Main { }); sendClientList(); + case PasswordRequest: case LoginError: case Logout: final oldName = client.name; @@ -425,7 +485,7 @@ class Main { final htmlChars = ~/[&^<>'"]/; - function badNickName(name:String):Bool { + public function badNickName(name:String):Bool { if (name.length == 0) return true; if (htmlChars.match(name)) return true; return false; diff --git a/user/README.md b/user/README.md index f59e576..e244d09 100644 --- a/user/README.md +++ b/user/README.md @@ -12,7 +12,12 @@ File example: You can patch any file you want in project `res/` by creating `user/res/sameName` files. For example `user/res/index.html` or `user/res/css/custom.css`. You can also add any new files. + +## Server commands +Server has input commands, for example, to set admin users. Simple enter anything to terminal after run to get command list. + ## Other files here -- `state.json` - saved state of latest server session (messages, videos, video time). -- `logs/` - latest 10 logs. You can change count in config. -- `crashes/` - folder with latest error logs, when the server had to restart itself. +- `state.json` - Saved state of latest server session (messages, videos, video time). +- `users.json` - Admin names with password hashes and random channel-specific salt. Do not share this file! +- `crashes/` - Latest error logs, when the server had to restart itself. +- `logs/` - (TODO) Latest activity logs. -- cgit v1.2.3