From 7c9edccaa58a1e10c349469bccc44f0834cf4e2f Mon Sep 17 00:00:00 2001 From: WlodekM Date: Wed, 3 Jul 2024 16:39:35 +0300 Subject: auth and :jsonGet --- README.md | 2 +- accounts.js | 31 +++++++++++++++++++++++++++++++ commands.js | 43 ++++++++++++++++++++++++++++++++++++------- db/users.json | 1 + index.js | 49 +++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 accounts.js create mode 100644 db/users.json diff --git a/README.md b/README.md index bc14503..e08f31f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ node index.js - ~~Config (JSON)~~ - ~~MOTD~~ - ~~Custom commands (plugins)~~ -- Auth (optional) +- ~~Auth (optional)~~ ## IN PROGRESS diff --git a/accounts.js b/accounts.js new file mode 100644 index 0000000..f45ab6e --- /dev/null +++ b/accounts.js @@ -0,0 +1,31 @@ +import { createHash } from "crypto" +import cuid from 'cuid'; +import fs from "fs" + +if(!fs.existsSync("db")) fs.mkdirSync("db"); +if(!fs.existsSync("db/users.json")) fs.writeFileSync("db/users.json", "{}"); + +const db = JSON.parse(fs.readFileSync("db/users.json").toString()) + +function syncDB() { + fs.writeFileSync("db/users.json", JSON.stringify(db)) +} + +export function checkAccount(username) { + return db[username] != undefined +} + +export function createAccount(username, password) { + let hashedPassword = createHash('sha256').update(password).digest('hex'); + db[username] = { + id: cuid(), + password: hashedPassword, + t: Number(new Date()) / 1000 + }; + syncDB(); +} + +export function checkPassword(username, password) { + let hashedPassword = createHash('sha256').update(password).digest('hex'); + return db[username]?.password === hashedPassword +} \ No newline at end of file diff --git a/commands.js b/commands.js index 5c7a440..3fbd63e 100644 --- a/commands.js +++ b/commands.js @@ -8,10 +8,10 @@ export const commands = { if(args.length < 1) return user.socket.send("Error: You need to specify a channel (example: /join #home)."); if(!args[0].startsWith("#")) return user.socket.send("Error: Channel not found, run /channels to see a list of channels."); if(!server.channels.includes(args[0].replace("#", ""))) return user.socket.send("Error: Channel not found, run /channels to see a list of channels."); - sendInChannel(`${user.username} left #${user.channel}.`, user.channel) + sendInChannel(`${user.name()} left #${user.channel}.`, user.channel) user.channel = args[0].replace("#", ""); - console.info(`${user.username} went to #${user.channel}`) - sendInChannel(`${user.username} joined #${user.channel}!`, user.channel) + console.info(`${user.name()} went to #${user.channel}`) + sendInChannel(`${user.name()} joined #${user.channel}!`, user.channel) } }, 'channels': { @@ -29,8 +29,8 @@ export const commands = { if(args[0].length < 3 ) return user.socket.send("Error: Nick too short."); if(args[0].length > 20) return user.socket.send("Error: Nick too long."); if(Object.values(server.users).find(usr => usr.username == args[0])) return user.socket.send("Error: Nick already used."); - sendInChannel(`${user.username} changed their nick to ${args[0]}!`, user.channel) - user.username = args[0]; + sendInChannel(`${user.name()} changed their nick to ${args[0]}!`, user.channel) + user.nickname = args[0]; } }, 'about': { @@ -45,7 +45,7 @@ export const commands = { aliases: [], command: function({user, server, args}) { if(args.length < 1) return user.socket.send('Please provide username'); - if(Object.values(server.users).find(usr => usr.username == args[0])) return user.socket.send('User not found'); + if(!Object.values(server.users).find(usr => usr.username == args[0])) return user.socket.send('User not found'); let userFound = Object.values(server.users).find(usr => usr.username == args[0]) userFound.id = Object.keys(server.users).find(usr => server.users[usr].username == args[0]) user.socket.send(`${userFound.username}\nClient: ${userFound.client ?? ""}\nID: ${userFound.id}`) @@ -55,7 +55,7 @@ export const commands = { name: 'users', aliases: [], command: function({user, server, args}) { - user.socket.send(`Users${args[0] != "global" ? ` in ${user.channel}` : ""}:\n${Object.values(server.users).filter(usr => (usr.channel == user.channel) || args[0] == "global").map(usr => ` * ${usr.username}`).join("\n")}`) + user.socket.send(`Users${args[0] != "global" ? ` in ${user.channel}` : ""}:\n${Object.values(server.users).filter(usr => (usr.channel == user.channel) || args[0] == "global").map(usr => ` * ${usr.name()}`).join("\n")}`) } }, 'help': { @@ -65,6 +65,35 @@ export const commands = { user.socket.send(`Commands available:\n${Object.values(commands).map(cmd => `* /${cmd.name} (Aliases: ${(cmd.aliases.join(", ")) || ""})`).join("\n")}`) } }, + 'login': { + name: 'login', + aliases: ['signin'], + command: function({server, args, user, sendInChannel}) { + if(args.length < 2) return user.socket.send(`Usage: /login `); + if(!server.accounts.checkAccount(args[0])) return user.socket.send(`Account "${args[0]}" not found!`); + if(!server.accounts.checkPassword(args[0], args[1])) return user.socket.send(`Password incorrect.`); + user.username = args[0]; + user.nickname = ""; + user.guest = false; + sendInChannel(`${user.name()} logged in as ${args[0]}!`, user.channel) + } + }, + 'register': { + name: 'register', + aliases: ['signup'], + command: function({server, args, user, sendInChannel}) { + if(args.length < 2) return user.socket.send(`Usage: /register `); + if(args[0].length < 3) return user.socket.send(`Username too short!`); + if(args[0].length > 20) return user.socket.send(`Username too long!`); + if(args[1].length < 6) return user.socket.send(`Password too short!`); + if(server.accounts.checkAccount(args[0])) return user.socket.send(`User with username "${args[0]}" already exists!`); + server.accounts.createAccount(args[0], args[1]); + user.username = args[0]; + user.nickname = ""; + user.guest = false; + sendInChannel(`${user.name()} logged in as ${args[0]}!`, user.channel) + } + }, } export function register(cmd, data) { diff --git a/db/users.json b/db/users.json new file mode 100644 index 0000000..6cf28da --- /dev/null +++ b/db/users.json @@ -0,0 +1 @@ +{"wlodekm":{"id":"cly5vozj9000188i8e7wyfrv4","password":"fa4920b0404de71b545ed34855c099d274869553118f5dab6b9d6232a813caf3","t":1720013610.213}} \ No newline at end of file diff --git a/index.js b/index.js index c17c134..73c92db 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ import { WebSocketServer } from "ws"; import { getRandomInt } from "./lib.js" import { commands } from "./commands.js"; +import * as accounts from "./accounts.js" import cuid from 'cuid'; import fs from 'fs'; @@ -8,6 +9,7 @@ const server = { config: JSON.parse(String(fs.readFileSync("config.json"))), channels: ["home", "off-topic"], users: {}, + accounts: accounts, } const ws = new WebSocketServer({ @@ -37,10 +39,13 @@ ws.on('connection', (socket, request) => { socket.close(1001, "Server full") } let userID = cuid() + console.info(`${userID} joined the server.`) socket.send(format(server.config.motd)) let anonID = getRandomInt(0, 99999) server.users[userID] = { username: `Anonymous${"0".repeat(5 - anonID.toString().length) + anonID.toString()}`, + nickname: `Anonymous${"0".repeat(5 - anonID.toString().length) + anonID.toString()}`, + guest: true, socket: socket, joinReq: request, t: { @@ -48,12 +53,14 @@ ws.on('connection', (socket, request) => { unix: Math.floor(new Date().getTime() / 1000), str: String(new Date()) }, - channel: 'home' + channel: 'home', + name: function(){return this.nickname != "" ? this.nickname : this.username} } - console.info(`${server.users[userID].username} joined the server!`) - sendInChannel(`${server.users[userID].username} joined #${server.users[userID].channel}!`, server.users[userID].channel) + const user = server.users[userID] + console.info(`${user.name()} joined the server!`) + sendInChannel(`${user.name()} joined #${server.users[userID].channel}!`, server.users[userID].channel) socket.on('close', function (code, reason) { - sendInChannel(`${server.users[userID].username} left.`, server.users[userID].channel) + sendInChannel(`${user.name()} left.`, server.users[userID].channel) delete server.users[userID] }) socket.on('message', function (rawData) { @@ -61,12 +68,12 @@ ws.on('connection', (socket, request) => { let args = String(rawData).replace("/", "").split(" "); let command = args.shift(); let commandObj = Object.values(commands).find(cmd => cmd.name == command || cmd.aliases.includes(command)) - console.log(`${server.users[userID].username} used /${command}`) + console.log(`${user.name()} used /${command}`) if (!commandObj) return socket.send(`Error: Command "${command}" not found!`); - let user = server.users[userID] try { commandObj.command({ user, command, args, sendInChannel, server, commands }) } catch (error) { + console.error(error) user.socket.send(`Unexpected error ocurred while running the command`) } return @@ -80,24 +87,46 @@ ws.on('connection', (socket, request) => { return } if (rawData.toString().startsWith(":jsonGet")) { - let params = String(rawData).replace(":jsonGet", "").split(" "); + let params = String(rawData).split(" "); + params.shift() switch (params[0]) { case 'channels': socket.send(JSON.stringify(server.channels)); break; case 'users': - socket.send(JSON.stringify(server.users)); + socket.send(JSON.stringify(Object.values(server.users).map(usr => {return { + username: usr.username, + nickname: usr.nickname, + t: usr.t, + channel: user.channel, + displayName: user.name() + }}))); + break; + case 'usersLocal': + socket.send( + JSON.stringify( + Object.values(server.users) + .filter(usr => (usr.channel == user.channel)) + .map(usr => {return { + username: usr.username, + nickname: usr.nickname, + t: usr.t, + channel: user.channel, + displayName: user.name() + }}) + )); break; default: + socket.send(`unknown "${params[0]}"`); break; } return } if (rawData.length < 1) return socket.send("Error: message too short!") if (rawData.length >= 2000) return socket.send("Error: message too long!") - sendInChannel(`<${server.users[userID].username}> ${rawData}`, server.users[userID].channel) - console.log(`(#${server.users[userID].channel}) <${server.users[userID].username}> ${rawData}`) + sendInChannel(`<${user.name()}${user.guest ? " (guest)" : ""}> ${rawData}`, server.users[userID].channel) + console.log(`(#${server.users[userID].channel}) <${user.name()}> ${rawData}`) }) }) -- cgit 1.4.1-2-gfad0