handle admin messages

TODO: Add senders for admin responses
and flag
and rename

also verify I didn't fuck boolean conversion up
This commit is contained in:
modeco80
2024-08-21 20:15:14 -04:00
parent 1c062697b9
commit a82388f823
3 changed files with 219 additions and 217 deletions

View File

@@ -341,9 +341,8 @@ export default class CollabVMServer implements IProtocolHandlers {
this.TurnQueue.enqueue(user); this.TurnQueue.enqueue(user);
if (this.TurnQueue.size === 1) this.nextTurn(); if (this.TurnQueue.size === 1) this.nextTurn();
} else { } else {
var hadturn = this.TurnQueue.peek() === user; // Not sure why this wasn't using this before
this.TurnQueue = Queue.from(this.TurnQueue.toArray().filter((u) => u !== user)); this.endTurn(user);
if (hadturn) this.nextTurn();
} }
this.sendTurnUpdate(); this.sendTurnUpdate();
} }
@@ -492,6 +491,8 @@ export default class CollabVMServer implements IProtocolHandlers {
this.VM.GetDisplay()?.MouseEvent(x, y, buttonMask); this.VM.GetDisplay()?.MouseEvent(x, y, buttonMask);
} }
// TODO: make senders for admin things
async onAdminLogin(user: User, password: string) { async onAdminLogin(user: User, password: string) {
if (!user.LoginRateLimit.request() || !user.username) return; if (!user.LoginRateLimit.request() || !user.username) return;
var sha256 = createHash('sha256'); var sha256 = createHash('sha256');
@@ -535,188 +536,159 @@ export default class CollabVMServer implements IProtocolHandlers {
user.sendMsg(cvm.guacEncode('admin', '2', String(output))); user.sendMsg(cvm.guacEncode('admin', '2', String(output)));
} }
private async onAdmin(user: User, msgArr: string[]) { onAdminRestore(user: User, node: string): void {
/* if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.restore)) return;
switch (msgArr[0]) { this.VM.Reset();
case '2': }
// Login
async onAdminReboot(user: User, node: string) {
break; if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.reboot)) return;
case '5': if (node !== this.Config.collabvm.node) return;
// QEMU Monitor await this.VM.Reboot();
}
break;
case '8':
// Restore
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.restore)) return;
this.VM.Reset();
break;
case '10':
// Reboot
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.reboot)) return;
if (msgArr.length !== 3 || msgArr[2] !== this.Config.collabvm.node) return;
await this.VM.Reboot();
break;
case '12':
// Ban
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.ban)) return;
var otherUser = this.clients.find((c) => c.username === msgArr[2]);
if (!otherUser) return;
this.logger.info(`Banning ${otherUser.username!} (${otherUser.IP.address}) by request of ${otherUser.username!}`);
user.ban(this.banmgr);
case '13':
// Force Vote
if (msgArr.length !== 3) return;
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.forcevote)) return;
if (!this.voteInProgress) return;
switch (msgArr[2]) {
case '1':
this.endVote(true);
break;
case '0':
this.endVote(false);
break;
}
break;
case '14':
// Mute
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.mute)) return;
if (msgArr.length !== 4) return;
var user = this.clients.find((c) => c.username === msgArr[2]);
if (!user) return;
var permamute;
switch (msgArr[3]) {
case '0':
permamute = false;
break;
case '1':
permamute = true;
break;
default:
return;
}
user.mute(permamute);
break;
case '15':
// Kick
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.kick)) return;
var user = this.clients.find((c) => c.username === msgArr[2]);
if (!user) return;
user.kick();
break;
case '16':
// End turn
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.bypassturn)) return;
if (msgArr.length !== 3) return;
var user = this.clients.find((c) => c.username === msgArr[2]);
if (!user) return;
this.endTurn(user);
break;
case '17':
// Clear turn queue
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.bypassturn)) return;
if (msgArr.length !== 3 || msgArr[2] !== this.Config.collabvm.node) return;
this.clearTurns();
break;
case '18':
// Rename user
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.rename)) return;
if (this.Config.auth.enabled) {
client.protocol.sendChatMessage('', 'Cannot rename users on a server that uses authentication.');
}
if (msgArr.length !== 4) return;
var user = this.clients.find((c) => c.username === msgArr[2]);
if (!user) return;
this.renameUser(user, msgArr[3]);
break;
case '19':
// Get IP
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.grabip)) return;
if (msgArr.length !== 3) return;
var user = this.clients.find((c) => c.username === msgArr[2]);
if (!user) return;
client.sendMsg(cvm.guacEncode('admin', '19', msgArr[2], user.IP.address));
break;
case '20':
// Steal turn
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.bypassturn)) return;
this.bypassTurn(client);
break;
case '21':
// XSS
if (client.rank !== Rank.Admin && (client.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.xss)) return;
if (msgArr.length !== 3) return;
switch (client.rank) {
case Rank.Admin:
this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', client.username!, msgArr[2])));
this.ChatHistory.push({ user: client.username!, msg: msgArr[2] }); onAdminBanUser(user: User, username: string): void {
break; // Ban
case Rank.Moderator: if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.ban)) return;
this.clients.filter((c) => c.rank !== Rank.Admin).forEach((c) => c.sendMsg(cvm.guacEncode('chat', client.username!, msgArr[2]))); let otherUser = this.clients.find((c) => c.username === username);
if (!otherUser) return;
this.logger.info(`Banning ${otherUser.username!} (${otherUser.IP.address}) by request of ${otherUser.username!}`);
user.ban(this.banmgr);
}
this.clients.filter((c) => c.rank === Rank.Admin).forEach((c) => c.sendMsg(cvm.guacEncode('chat', client.username!, Utilities.HTMLSanitize(msgArr[2])))); onAdminForceVote(user: User, choice: number): void {
break; if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.forcevote)) return;
} if (!this.voteInProgress) return;
break; this.endVote(choice == 1);
case '22': }
// Toggle turns
if (client.rank !== Rank.Admin) return;
if (msgArr.length !== 3) return;
switch (msgArr[2]) {
case '0':
this.clearTurns();
this.turnsAllowed = false;
break;
case '1':
this.turnsAllowed = true;
break;
}
break;
case '23':
// Indefinite turn
if (client.rank !== Rank.Admin) return;
this.indefiniteTurn = client;
this.TurnQueue = Queue.from([client, ...this.TurnQueue.toArray().filter((c) => c !== client)]);
this.sendTurnUpdate();
break;
case '24':
// Hide screen
if (client.rank !== Rank.Admin) return;
if (msgArr.length !== 3) return;
switch (msgArr[2]) {
case '0':
this.screenHidden = true;
this.clients
.filter((c) => c.rank == Rank.Unregistered)
.forEach((client) => {
client.sendMsg(cvm.guacEncode('size', '0', '1024', '768'));
client.sendMsg(cvm.guacEncode('png', '0', '0', '0', '0', this.screenHiddenImg));
client.sendMsg(cvm.guacEncode('sync', Date.now().toString()));
});
break;
case '1':
this.screenHidden = false;
let displaySize = this.VM.GetDisplay().Size();
let encoded = await this.MakeRectData({ onAdminMuteUser(user: User, username: string, temporary: boolean): void {
x: 0, if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.mute)) return;
y: 0,
width: displaySize.width,
height: displaySize.height
});
this.clients.forEach(async (client) => this.SendFullScreenWithSize(client)); let target = this.clients.find((c) => c.username === username);
break; if (!target) return;
} target.mute(!temporary);
}
onAdminKickUser(user: User, username: string): void {
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.kick)) return;
var target = this.clients.find((c) => c.username === username);
if (!target) return;
target.kick();
}
onAdminEndTurn(user: User, username: string): void {
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.bypassturn)) return;
var target = this.clients.find((c) => c.username === username);
if (!target) return;
// don't let a mod end a infinite turn
if(user.rank == Rank.Moderator)
if(this.indefiniteTurn == target)
return;
this.endTurn(target);
}
onAdminClearQueue(user: User, node: string): void {
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.bypassturn)) return;
if (node !== this.Config.collabvm.node) return;
this.clearTurns();
}
onAdminRename(user: User, target: string, newName: string): void {
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.rename)) return;
if (this.Config.auth.enabled) {
user.protocol.sendChatMessage('', 'Cannot rename users on a server that uses authentication.');
}
var targetUser = this.clients.find((c) => c.username === target);
if (!targetUser) return;
this.renameUser(targetUser, newName);
}
onAdminGetIP(user: User, username: string): void {
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.grabip)) return;
let target = this.clients.find((c) => c.username === username);
if (!target) return;
user.sendMsg(cvm.guacEncode('admin', '19', username, target.IP.address));
}
onAdminBypassTurn(user: User): void {
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.bypassturn)) return;
this.bypassTurn(user);
}
onAdminRawMessage(user: User, message: string): void {
if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.xss)) return;
switch (user.rank) {
case Rank.Admin:
this.clients.forEach((c) => c.protocol.sendChatMessage(user.username!, message));
this.ChatHistory.push({ user: user.username!, msg: message });
break; break;
case '25': case Rank.Moderator:
if (client.rank !== Rank.Admin || msgArr.length !== 3) return; this.clients.filter((c) => c.rank !== Rank.Admin).forEach((c) => c.protocol.sendChatMessage(user.username!, message));
this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('chat', '', msgArr[2])));
this.clients.filter((c) => c.rank === Rank.Admin).forEach((c) => c.protocol.sendChatMessage(user.username!, Utilities.HTMLSanitize(message)));
break; break;
} }
*/ }
onAdminToggleTurns(user: User, enabled: boolean): void {
if (user.rank !== Rank.Admin) return;
if (enabled) {
this.turnsAllowed = true;
} else {
this.turnsAllowed = false;
this.clearTurns();
}
}
onAdminIndefiniteTurn(user: User): void {
if (user.rank !== Rank.Admin) return;
this.indefiniteTurn = user;
this.TurnQueue = Queue.from([user, ...this.TurnQueue.toArray().filter((c) => c !== user)]);
this.sendTurnUpdate();
}
async onAdminHideScreen(user: User, show: boolean) {
if (user.rank !== Rank.Admin) return;
if (show) {
// if(!this.screenHidden) return; ?
this.screenHidden = false;
let displaySize = this.VM.GetDisplay()?.Size();
if(displaySize == undefined)
return;
let encoded = await this.MakeRectData({
x: 0,
y: 0,
width: displaySize.width,
height: displaySize.height
});
this.clients.forEach(async (client) => this.SendFullScreenWithSize(client));
} else {
this.screenHidden = true;
this.clients
.filter((c) => c.rank == Rank.Unregistered)
.forEach((client) => {
client.protocol.sendScreenResize(1024, 768);
client.protocol.sendScreenUpdate({
x: 0,
y: 0,
data: this.screenHiddenImg
});
});
}
}
onAdminSystemMessage(user: User, message: string): void {
if (user.rank !== Rank.Admin) return;
this.clients.forEach((c) => c.protocol.sendChatMessage('', message));
} }
// end IProtocolHandlers // end IProtocolHandlers
@@ -839,6 +811,8 @@ export default class CollabVMServer implements IProtocolHandlers {
} }
endTurn(client: User) { endTurn(client: User) {
// I must have somehow accidentally removed this while scalpaling everything out
if (this.indefiniteTurn === client) this.indefiniteTurn = null;
var hasTurn = this.TurnQueue.peek() === client; var hasTurn = this.TurnQueue.peek() === client;
this.TurnQueue = Queue.from(this.TurnQueue.toArray().filter((c) => c !== client)); this.TurnQueue = Queue.from(this.TurnQueue.toArray().filter((c) => c !== client));
if (hasTurn) this.nextTurn(); if (hasTurn) this.nextTurn();

View File

@@ -24,85 +24,96 @@ export class GuacamoleProtocol implements IProtocol {
private __processMessage_admin(decodedElements: string[]): boolean { private __processMessage_admin(decodedElements: string[]): boolean {
switch (decodedElements[1]) { switch (decodedElements[1]) {
case '2': case '2':
// Login
if (decodedElements.length !== 3) return false; if (decodedElements.length !== 3) return false;
this.handlers?.onAdminLogin(this.user!, decodedElements[2]); this.handlers?.onAdminLogin(this.user!, decodedElements[2]);
break; break;
case '5': case '5':
// QEMU Monitor
if (decodedElements.length !== 4) return false; if (decodedElements.length !== 4) return false;
// [2] node this.handlers?.onAdminMonitor(this.user!, decodedElements[2], decodedElements[3]);
// [3] cmd
break; break;
case '8': case '8':
// Restore if (decodedElements.length !== 3) return false;
this.handlers?.onAdminRestore(this.user!, decodedElements[2]);
break; break;
case '10': case '10':
// Reboot
if (decodedElements.length !== 3) return false; if (decodedElements.length !== 3) return false;
// [2] - node this.handlers?.onAdminReboot(this.user!, decodedElements[2]);
break; break;
case '12': case '12':
// Ban if (decodedElements.length < 3) return false;
this.handlers?.onAdminBanUser(this.user!, decodedElements[2]);
case '13': case '13':
// Force Vote {
if (decodedElements.length !== 3) return false; if (decodedElements.length !== 3) return false;
let choice = parseInt(decodedElements[2]);
if (choice == undefined) return false;
this.handlers?.onAdminForceVote(this.user!, choice);
}
break; break;
case '14': case '14':
// Mute {
if (decodedElements.length !== 4) return false; if (decodedElements.length !== 4) return false;
let temporary = true;
if (decodedElements[3] == '0') temporary = true;
else if (decodedElements[3] == '1') temporary = false;
else return false;
this.handlers?.onAdminMuteUser(this.user!, decodedElements[2], temporary);
}
break; break;
case '15': case '15':
// Kick
case '16':
// End turn
if (decodedElements.length !== 3) return false; if (decodedElements.length !== 3) return false;
this.handlers?.onAdminKickUser(this.user!, decodedElements[2]);
break;
case '16':
if (decodedElements.length !== 3) return false;
this.handlers?.onAdminEndTurn(this.user!, decodedElements[2]);
break; break;
case '17': case '17':
// Clear turn queue
if (decodedElements.length !== 3) return false; if (decodedElements.length !== 3) return false;
// [2] - node this.handlers?.onAdminClearQueue(this.user!, decodedElements[2]);
break; break;
case '18': case '18':
// Rename user
if (decodedElements.length !== 4) return false; if (decodedElements.length !== 4) return false;
this.handlers?.onAdminRename(this.user!, decodedElements[2], decodedElements[3]);
// [2] - username
// [3] - new username
break; break;
case '19': case '19':
// Get IP
if (decodedElements.length !== 3) return false; if (decodedElements.length !== 3) return false;
this.handlers?.onAdminGetIP(this.user!, decodedElements[2]);
break; break;
case '20': case '20':
// Steal turn this.handlers?.onAdminBypassTurn(this.user!);
break; break;
case '21': case '21':
// XSS
if (decodedElements.length !== 3) return false; if (decodedElements.length !== 3) return false;
// [2] message this.handlers?.onAdminRawMessage(this.user!, decodedElements[2]);
break; break;
case '22': case '22':
// Toggle turns {
if (decodedElements.length !== 3) return false; // Toggle turns
// [2] 0 == disable 1 == enable if (decodedElements.length !== 3) return false;
let enabled = true;
if (decodedElements[2] == '0') enabled = false;
else if (decodedElements[2] == '1') enabled = true;
else return false;
this.handlers?.onAdminToggleTurns(this.user!, enabled);
}
break; break;
case '23': case '23':
// Indefinite turn this.handlers?.onAdminIndefiniteTurn(this.user!);
break; break;
case '24': case '24':
// Hide screen {
if (decodedElements.length !== 3) return false;
if (decodedElements.length !== 3) return false; let show = true;
// 0 - hide if (decodedElements[2] == '0') show = false;
// 1 - unhide else if (decodedElements[2] == '1') show = true;
else return false;
this.handlers?.onAdminHideScreen(this.user!, show);
}
break; break;
case '25': case '25':
if (decodedElements.length !== 3) return false; if (decodedElements.length !== 3) return false;
// [2] this.handlers?.onAdminSystemMessage(this.user!, decodedElements[2]);
break; break;
} }
return true; return true;
@@ -155,7 +166,7 @@ export class GuacamoleProtocol implements IProtocol {
let forfeit = false; let forfeit = false;
if (decodedElements.length > 2) return false; if (decodedElements.length > 2) return false;
if (decodedElements.length == 1) { if (decodedElements.length == 1) {
forfeit = false; forfeit = false;
} else { } else {
if (decodedElements[1] == '0') forfeit = true; if (decodedElements[1] == '0') forfeit = true;
else if (decodedElements[1] == '1') forfeit = false; else if (decodedElements[1] == '1') forfeit = false;

View File

@@ -47,10 +47,27 @@ export interface IProtocolHandlers {
onConnect(user: User, node: string): void; onConnect(user: User, node: string): void;
onView(user: User, node: string, viewMode: number): void; onView(user: User, node: string, viewMode: number): void;
// Admin handlers
onAdminLogin(user: User, password: string): void; onAdminLogin(user: User, password: string): void;
onAdminMonitor(user: User, node: string, command: string): void; onAdminMonitor(user: User, node: string, command: string): void;
onAdminRestore(user: User, node: string): void;
onAdminReboot(user: User, node: string): void;
onAdminBanUser(user: User, username: string): void;
onAdminForceVote(user: User, choice: number): void;
onAdminMuteUser(user: User, username: string, temporary: boolean): void;
onAdminKickUser(user: User, username: string): void;
onAdminEndTurn(user: User, username: string): void;
onAdminClearQueue(user: User, node: string): void;
onAdminRename(user: User, target: string, newName: string): void;
onAdminGetIP(user: User, username: string): void;
onAdminBypassTurn(user: User): void;
onAdminRawMessage(user: User, message: string): void;
onAdminToggleTurns(user: User, enabled: boolean): void;
onAdminIndefiniteTurn(user: User): void;
onAdminHideScreen(user: User, show: boolean): void;
onAdminSystemMessage(user: User, message: string): void;
onRename(user: User, newName: string|undefined): void; onRename(user: User, newName: string | undefined): void;
onChat(user: User, message: string): void; onChat(user: User, message: string): void;
onKey(user: User, keysym: number, pressed: boolean): void; onKey(user: User, keysym: number, pressed: boolean): void;