aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Types.hx15
-rw-r--r--src/client/Buttons.hx13
-rw-r--r--src/client/Main.hx46
-rw-r--r--src/server/ConsoleInput.hx59
-rw-r--r--src/server/Main.hx78
5 files changed, 188 insertions, 23 deletions
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<Emote>,
- filters:Array<Filter>
+ filters:Array<Filter>,
+ ?salt:String
};
+typedef UserList = {
+ admins:Array<UserField>,
+ ?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<ClientData>,
?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<ClientData>):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<Client> = [];
final freeIds:Array<Int> = [];
+ final consoleInput:ConsoleInput;
final videoList = new VideoList();
final videoTimer = new VideoTimer();
final messages:Array<Message> = [];
@@ -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;
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage