Add ID generation methods, add tags to userset updates

This commit is contained in:
Hri7566 2024-08-03 06:50:56 -04:00
parent 05299383a4
commit ee2d6c4dde
13 changed files with 99 additions and 52 deletions

View File

@ -2,3 +2,4 @@ DATABASE_URL="file:./db.sqlite"
PORT=8443 PORT=8443
ADMIN_PASS= ADMIN_PASS=
SALT= SALT=
COLOR_SALT=

View File

@ -26,6 +26,7 @@ Brandon's server originally used MongoDB for storing user data, but there are to
- Usernames/colors - Usernames/colors
- Allowing color changing can be toggled in the config, similar to MPP.com - Allowing color changing can be toggled in the config, similar to MPP.com
- Default user parameters can be set - Default user parameters can be set
- Configurable ID and color generation methods
- Channels - Channels
- Channel list - Channel list
- Channel settings - Channel settings
@ -46,6 +47,8 @@ Brandon's server originally used MongoDB for storing user data, but there are to
- Server must be setup as a pm2/docker/systemd process for automatic restarting - Server must be setup as a pm2/docker/systemd process for automatic restarting
- Ability to change tags - Ability to change tags
- Similar to the MPP.net server, but uses a Brandon-style admin message - Similar to the MPP.net server, but uses a Brandon-style admin message
- Ability to rename channels
- Chat clearing similar to MPP.net
## TODO ## TODO
@ -67,6 +70,7 @@ Brandon's server originally used MongoDB for storing user data, but there are to
- Test every frontend - Test every frontend
- Test fishing bot - Test fishing bot
- Remote console - Remote console
- Modify frontend to use templating
## Backlog/Notes ## Backlog/Notes
@ -75,7 +79,6 @@ Brandon's server originally used MongoDB for storing user data, but there are to
- Split script.js into multiple files - Split script.js into multiple files
- Implement tags as a server option, toggles code on frontend - Implement tags as a server option, toggles code on frontend
- Same with color changing - Same with color changing
- Bun memory usage can skyrocket
- Reload config files on save - Reload config files on save
- Expose API? - Expose API?
@ -115,7 +118,7 @@ such as enabling the color changing option in the userset modal menu, or sending
$ cp .env.template .env $ cp .env.template .env
``` ```
Edit `.env` to your needs. Edit `.env` to your needs. Some variables are required for certain features to work.
- Edit the files in the `config` folder to match your needs - Edit the files in the `config` folder to match your needs

View File

@ -1,9 +1,8 @@
# Which channels to load on startup. # Channel config file
forceLoad: forceLoad:
- lobby - lobby
- test/awkward - test/awkward
# Default channel settings for lobby channels.
lobbySettings: lobbySettings:
lobby: true lobby: true
chat: true chat: true
@ -11,36 +10,20 @@ lobbySettings:
visible: true visible: true
color: "#73b3cc" color: "#73b3cc"
color2: "#273546" color2: "#273546"
# Default channel settings for normal user-created channels.
# Note that this allows for channel settings users can't set, like visible and lobby.
defaultSettings: defaultSettings:
chat: true chat: true
crownsolo: false crownsolo: false
color: "#3b5054" color: "#3b5054"
color2: "#001014" color2: "#001014"
visible: true visible: true
# Regex patterns for lobby channel names.
# Any channel name that matches any of these patterns will be considered a lobby channel on instantiation.
lobbyRegexes: lobbyRegexes:
- ^lobby[0-9][0-9]$ - ^lobby[0-9][0-9]$
- ^lobby[0-9]$ - ^lobby[0-9]$
- ^lobby$ - ^lobby$
- ^lobbyNaN$ - ^lobbyNaN$
- ^test/.+$ - ^test/.+$
# The channel to use for the lobby backdoor.
lobbyBackdoor: lolwutsecretlobbybackdoor lobbyBackdoor: lolwutsecretlobbybackdoor
# The channel that users get sent to when they are banned from a channel/join a channel they are already banned in/join a channel that is full and not a regular lobby.
# This will not be used for real lobby channels. (channels that start with "lobby" and may have numbers on the end)
fullChannel: test/awkward fullChannel: test/awkward
# Whether or not to publish channel user limits in the channel list.
sendLimit: false sendLimit: false
# Whether to give a user the crown when they join a channel they were already the owner of when they left.
# Reportedly, this is a feature on the original server, but it's not working there.
# This is corrected in MPP.net's server.
chownOnRejoin: true chownOnRejoin: true
sendTags: false

View File

@ -1,3 +1,5 @@
# Notification config file
# Whether to allow HTML in notifications. # Whether to allow HTML in notifications.
# This is a security risk, so only enable this if you trust your admins. # This is a security risk, so only enable this if you trust your admins.
allowXSS: true allowXSS: true

View File

@ -1,3 +1,5 @@
# Rate limit config file
# Difference between rate limits and rate limit chains: # Difference between rate limits and rate limit chains:
# Rate limits will not allow anything to be sent until the rate limit interval has passed. # Rate limits will not allow anything to be sent until the rate limit interval has passed.
# Rate limit chains, on the other hand, will allow messages to be sent until the rate limit chain's limit has been reached. # Rate limit chains, on the other hand, will allow messages to be sent until the rate limit chain's limit has been reached.

View File

@ -1,24 +1,29 @@
# User data config file
# The default username for new users. # The default username for new users.
defaultName: Anonymous defaultName: Anonymous
# The default user flags for new users. # The default user flags for new users.
# These flags control arbitrary data that could be checked by any part of the code.
# This is an internal feature available on MPP.com, but not MPP.net.
defaultFlags: defaultFlags:
volume: 100 volume: 100
# Whether or not to allow users to change their color. # Whether or not to allow users to change their color.
# Brandon's server has this set to false, but multiple users have reported it to be on before 2016. # Based on some reports, the MPP.com server stopped allowing this around 2016.
enableColorChanging: true enableColorChanging: true
# Allows custom data inside note messages. # Whether to allow custom data inside note messages.
# This was in the original server, but not in MPP.net's server. # This was in the original server, but not in MPP.net's server do to stricter sanitization.
# This only exists for backwards compatibility with scripts like nagalun's drarwing script. # This only exists for backwards compatibility with scripts like nagalun's drawing script.
enableCustomNoteData: true enableCustomNoteData: true
# Whether or not to enable tags that are sent publicly. # Whether or not to enable tags that are sent publicly.
# This won't prevent admins from changing tags internally, but it will not be sent to clients. # This won't prevent admins from changing tags internally, but they will not be sent to clients if set to false.
enableTags: true enableTags: true
# This is the user data that the server will use to send admin chat messages with. # This is the user data that the server will use to send admin chat messages with.
# This is a feature available on MPP.com, but was unknown to the MPP.net developers, therefore not implemented on MPP.net.
adminParticipant: adminParticipant:
_id: "0" _id: "0"
name: mpp name: mpp
@ -30,12 +35,28 @@ adminParticipant:
enableAdminEval: true enableAdminEval: true
# The token validation scheme. Valid values are "none", "jwt" and "uuid". # The token validation scheme. Valid values are "none", "jwt" and "uuid".
# This server will still validate existing tokens generated with other schemes if not set to "none". # This server will still validate existing tokens generated with other schemes if not set to "none", mimicking MPP.net's server.
tokenAuth: none # This is set to "none" by default because MPP.com does not have a token system.
tokenAuth: jwt
# The browser challenge scheme. Valid values are "none", "obf" and "basic". # The browser challenge scheme. Valid options are "none", "obf" and "basic".
# This is to change what is sent in the "b" message. # This is to change what is sent in the "b" message.
# "none" will disable the browser challenge, # "none" will disable the browser challenge,
# "obf" will sent an obfuscated function to the client, # "obf" will sent an obfuscated function to the client,
# and "basic" will just send a simple function that expects a boolean. # and "basic" will just send a simple function that expects a boolean.
browserChallenge: none browserChallenge: basic
# Scheme for generating user IDs.
# Valid options are "random", "sha256", "mpp" and "uuid".
# "random" will generate a random ID. As of writing, this is in use by MPP.com's server, but likely a mistake or poor workaround.
# "sha256" will generate a hash of the user's IP address and the server's SALT with the SHA256 algorithm.
# "mpp" will generate a hash of the user's IP address in the same way that MPP.com and MPP.net do. This is the default.
# "uuid" will generate a UUID for the user ID.
idGeneration: mpp
# Scheme for generating user colors.
# Valid options are "random", "sha256", "mpp" and "white".
# "random" will generate a random color for the user. This is not a known feature of any other server.
# "sha256" will generate a color based on a hash of the user's ID.
# "mpp" will generate a color based on the user's ID with a separate salt variable. This salt must be provided in the "COLOR_SALT" environment variable in the .env file.
colorGeneration: mpp

View File

@ -18,6 +18,7 @@ model User {
flags String @default("{}") // JSON flags object flags String @default("{}") // JSON flags object
tag String // JSON tag tag String // JSON tag
tokens String @default("[]") // JSON tokens tokens String @default("[]") // JSON tokens
group String @default("default") // Permission group
} }
model ChatHistory { model ChatHistory {

View File

@ -53,7 +53,9 @@ export class Channel extends EventEmitter {
} }
private async deleteChatHistory() { private async deleteChatHistory() {
try {
await deleteChatHistory(this.getID()); await deleteChatHistory(this.getID());
} catch (err) { }
} }
public logger: Logger; public logger: Logger;

View File

@ -1,5 +1,6 @@
import { createEnv } from "@t3-oss/env-core"; import { createEnv } from "@t3-oss/env-core";
import { z } from "zod"; import { z } from "zod";
import { config } from "../ws/usersConfig";
// Best way to do env ever // Best way to do env ever
export const env = createEnv({ export const env = createEnv({
@ -7,7 +8,8 @@ export const env = createEnv({
PORT: z.coerce.number(), PORT: z.coerce.number(),
HOST: z.union([z.string().url(), z.string().ip()]).optional(), HOST: z.union([z.string().url(), z.string().ip()]).optional(),
SALT: z.string().min(10), SALT: z.string().min(10),
ADMIN_PASS: z.string() ADMIN_PASS: z.string(),
COLOR_SALT: config.colorGeneration == "mpp" ? z.string().min(10) : z.string().optional(),
}, },
isServer: true, isServer: true,
// Bun loads process.env automatically so we don't have to use modules // Bun loads process.env automatically so we don't have to use modules

View File

@ -1,5 +1,6 @@
import { createHash, randomBytes } from "crypto"; import env from "./env"; import { createHash, randomBytes } from "crypto"; import env from "./env";
import { spoop_text } from "./helpers"; import { spoop_text } from "./helpers";
import { config } from "../ws/usersConfig";
export function createID() { export function createID() {
// Maybe I could make this funnier than it needs to be... // Maybe I could make this funnier than it needs to be...
@ -21,24 +22,46 @@ export function createID() {
} }
export function createUserID(ip: string) { export function createUserID(ip: string) {
if (config.idGeneration == "random") {
return createID();
} else if (config.idGeneration == "sha256") {
return createHash("sha256") return createHash("sha256")
.update(ip) .update(ip)
.update(env.SALT) .update(env.SALT)
.digest("hex") .digest("hex")
.substring(0, 24); .substring(0, 24);
} else if (config.idGeneration == "mpp") {
return createHash("md5")
.update("::ffff:" + ip + env.SALT)
.digest("hex")
.substring(0, 24);
}
} }
export function createSocketID() { export function createSocketID() {
return crypto.randomUUID(); return crypto.randomUUID();
} }
export function createColor(ip: string) { export function createColor(_id: string) {
return ( if (config.colorGeneration == "random") {
"#" + return "#" + Math.floor(Math.random() * 16777215).toString(16);
createHash("sha256") } else if (config.colorGeneration == "sha256") {
.update(ip) return "#" + createHash("sha256")
.update(_id)
.update(env.SALT) .update(env.SALT)
.digest("hex") .digest("hex")
.substring(24, 24 + 6) .substring(24, 24 + 6);
); } else if (config.colorGeneration == "mpp") {
const hash = createHash("md5");
hash.update(_id + env.COLOR_SALT);
const output = hash.digest();
const r = output.readUInt8(0) - 0x40;
const g = output.readUInt8(1) + 0x20;
const b = output.readUInt8(2);
return "#" + r.toString(16) + g.toString(16) + b.toString(16);
} else if (config.colorGeneration == "white") {
return "#ffffff";
}
} }

View File

@ -143,12 +143,14 @@ export class Socket extends EventEmitter {
// Send a challenge to the browser for MPP.net frontends // Send a challenge to the browser for MPP.net frontends
if (config.browserChallenge == "basic") { if (config.browserChallenge == "basic") {
// Basic function
this.sendArray([{ this.sendArray([{
m: "b", m: "b",
code: `~return true;` code: `~return true;`
}]); }]);
} else if (config.browserChallenge == "obf") { } else if (config.browserChallenge == "obf") {
// Obfuscated challenge building
// TODO
} }
})(); })();
@ -524,7 +526,8 @@ export class Socket extends EventEmitter {
id: part.id, id: part.id,
name: part.name, name: part.name,
x: cursorPos.x, x: cursorPos.x,
y: cursorPos.y y: cursorPos.y,
tag: config.enableTags ? part.tag : undefined
} }
]); ]);
} }

View File

@ -50,13 +50,13 @@ export const hi: ServerEventListener<"hi"> = {
// Validate the token // Validate the token
const valid = await validateToken(socket.getUserID(), msg.token); const valid = await validateToken(socket.getUserID(), msg.token);
if (!valid) { if (!valid) {
socket.ban(60000, "Invalid token"); //socket.ban(60000, "Invalid token");
return; //return;
} } else {
token = msg.token; token = msg.token;
} }
} }
}
let part = socket.getParticipant(); let part = socket.getParticipant();

View File

@ -11,6 +11,8 @@ export interface UsersConfig {
enableAdminEval: boolean; enableAdminEval: boolean;
tokenAuth: "jwt" | "uuid" | "none"; tokenAuth: "jwt" | "uuid" | "none";
browserChallenge: "none" | "obf" | "basic"; browserChallenge: "none" | "obf" | "basic";
idGeneration: "random" | "sha256" | "mpp" | "uuid";
colorGeneration: "random" | "sha256" | "mpp" | "white";
} }
export const usersConfigPath = "config/users.yml"; export const usersConfigPath = "config/users.yml";
@ -31,7 +33,9 @@ export const defaultUsersConfig: UsersConfig = {
}, },
enableAdminEval: false, enableAdminEval: false,
tokenAuth: "none", tokenAuth: "none",
browserChallenge: "none" browserChallenge: "none",
idGeneration: "mpp",
colorGeneration: "mpp"
}; };
// Importing this elsewhere causes bun to segfault // Importing this elsewhere causes bun to segfault