add captcha support
This commit is contained in:
@@ -5,6 +5,12 @@ export default interface IConfig {
|
||||
proxying : boolean;
|
||||
proxyAllowedIps : string[];
|
||||
};
|
||||
hcaptcha : {
|
||||
enabled : boolean;
|
||||
sitekey : string;
|
||||
secret : string;
|
||||
whitelist : string[];
|
||||
};
|
||||
vm : {
|
||||
qemuArgs : string;
|
||||
vncPort : number;
|
||||
|
||||
@@ -18,6 +18,7 @@ export class User {
|
||||
Config : IConfig;
|
||||
IP : string;
|
||||
vote : boolean | null;
|
||||
captchaValidated : boolean;
|
||||
// Rate limiters
|
||||
ChatRateLimit : RateLimiter;
|
||||
LoginRateLimit : RateLimiter;
|
||||
@@ -52,6 +53,7 @@ export class User {
|
||||
this.LoginRateLimit.on('limit', () => this.closeConnection());
|
||||
this.TurnRateLimit = new RateLimiter(5, 3);
|
||||
this.TurnRateLimit.on('limit', () => this.closeConnection());
|
||||
this.captchaValidated = false;
|
||||
}
|
||||
assignGuestName(existingUsers : string[]) : string {
|
||||
var username;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { createHash } from 'crypto';
|
||||
import { isIP } from 'net';
|
||||
import QEMUVM from './QEMUVM.js';
|
||||
import { Canvas, createCanvas, CanvasRenderingContext2D } from 'canvas';
|
||||
import hcaptcha from './hcaptcha.js';
|
||||
|
||||
export default class WSServer {
|
||||
private Config : IConfig;
|
||||
@@ -40,6 +41,8 @@ export default class WSServer {
|
||||
private turnsAllowed : boolean;
|
||||
// Indefinite turn
|
||||
private indefiniteTurn : User | null;
|
||||
// Captcha
|
||||
private captcha : hcaptcha;
|
||||
private ModPerms : number;
|
||||
private VM : QEMUVM;
|
||||
constructor(config : IConfig, vm : QEMUVM) {
|
||||
@@ -69,6 +72,7 @@ export default class WSServer {
|
||||
this.VM = vm;
|
||||
this.VM.on("dirtyrect", (j, x, y) => this.newrect(j, x, y));
|
||||
this.VM.on("size", (s) => this.newsize(s));
|
||||
this.captcha = new hcaptcha(this.Config);
|
||||
}
|
||||
|
||||
listen() {
|
||||
@@ -131,6 +135,8 @@ export default class WSServer {
|
||||
ip = req.socket.remoteAddress;
|
||||
}
|
||||
var user = new User(ws, ip, this.Config);
|
||||
if (this.captcha.checkIpValidated(user.IP))
|
||||
user.captchaValidated = true;
|
||||
this.clients.push(user);
|
||||
ws.on('close', () => this.connectionClosed(user));
|
||||
ws.on('message', (e) => {
|
||||
@@ -164,10 +170,14 @@ export default class WSServer {
|
||||
if (msgArr.length < 1) return;
|
||||
switch (msgArr[0]) {
|
||||
case "list":
|
||||
if (this.Config.hcaptcha.enabled && !client.captchaValidated && this.Config.hcaptcha.whitelist.indexOf(client.IP) === -1)
|
||||
client.sendMsg(guacutils.encode("captcha", "0", this.Config.hcaptcha.sitekey));
|
||||
client.sendMsg(guacutils.encode("list", this.Config.collabvm.node, this.Config.collabvm.displayname, await this.getThumbnail()));
|
||||
break;
|
||||
case "connect":
|
||||
if (!client.username || msgArr.length !== 2 || msgArr[1] !== this.Config.collabvm.node) {
|
||||
if (!client.username || msgArr.length !== 2 || msgArr[1] !== this.Config.collabvm.node
|
||||
|| (this.Config.hcaptcha.enabled && !client.captchaValidated && this.Config.hcaptcha.whitelist.indexOf(client.IP) === -1)
|
||||
) {
|
||||
client.sendMsg(guacutils.encode("connect", "0"));
|
||||
return;
|
||||
}
|
||||
@@ -183,6 +193,16 @@ export default class WSServer {
|
||||
if (this.voteInProgress) this.sendVoteUpdate(client);
|
||||
this.sendTurnUpdate(client);
|
||||
break;
|
||||
case "captcha":
|
||||
if (client.captchaValidated || msgArr.length !== 2) return;
|
||||
var result = await this.captcha.validateToken(msgArr[1], client.IP);
|
||||
if (result) {
|
||||
client.sendMsg(guacutils.encode("captcha", "1"));
|
||||
client.captchaValidated = true;
|
||||
} else {
|
||||
client.sendMsg(guacutils.encode("captcha", "2"));
|
||||
}
|
||||
break;
|
||||
case "rename":
|
||||
if (!client.RenameRateLimit.request()) return;
|
||||
this.renameUser(client, msgArr[1]);
|
||||
|
||||
31
src/hcaptcha.ts
Normal file
31
src/hcaptcha.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import IConfig from "./IConfig";
|
||||
import axios from 'axios';
|
||||
import querystring from 'node:querystring';
|
||||
|
||||
export default class hcaptcha {
|
||||
Config : IConfig;
|
||||
ValidIps : string[];
|
||||
constructor(config : IConfig) {
|
||||
this.Config = config;
|
||||
this.ValidIps = [];
|
||||
}
|
||||
checkIpValidated(ip : string) : boolean {
|
||||
return (this.ValidIps.indexOf(ip) !== -1);
|
||||
}
|
||||
validateToken(token : string, ip : string) : Promise<boolean> {
|
||||
return new Promise(async (res, rej) => {
|
||||
var response;
|
||||
try {
|
||||
response = await axios.post("https://hcaptcha.com/siteverify", querystring.encode({
|
||||
"secret": this.Config.hcaptcha.secret,
|
||||
"response": token,
|
||||
"remoteip": ip
|
||||
}));
|
||||
} catch (e) {rej(e); return;}
|
||||
if (response.data.success === true) {
|
||||
this.ValidIps.push(ip);
|
||||
}
|
||||
res(response.data.success);
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user