diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts
index 30ef8df7..39eee34a 100644
--- a/src/api/routes/guilds/#guild_id/bans.ts
+++ b/src/api/routes/guilds/#guild_id/bans.ts
@@ -20,7 +20,7 @@ import { route } from "@spacebar/api";
import { Ban, DiscordApiErrors, GuildBanAddEvent, GuildBanRemoveEvent, Member, User, emitEvent } from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
-import { APIBansArray, BanRegistrySchema, GuildBansResponse } from "@spacebar/schemas";
+import { APIBansArray, BanCreateSchema, BanRegistrySchema, GuildBansResponse } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });
@@ -198,6 +198,15 @@ router.put(
async (req: Request, res: Response) => {
const { guild_id } = req.params;
const banned_user_id = req.params.user_id;
+ const opts = req.body as BanCreateSchema;
+
+ let deleteMessagesMs = opts.delete_message_days
+ ? (opts.delete_message_days as number) * 86400000
+ : opts.delete_message_seconds
+ ? (opts.delete_message_seconds as number) * 1000
+ : 0;
+
+ if (deleteMessagesMs < 0) deleteMessagesMs = 0;
if (req.user_id === banned_user_id && banned_user_id === req.permission?.cache.guild?.owner_id)
throw new HTTPError("You are the guild owner, hence can't ban yourself", 403);
@@ -207,6 +216,7 @@ router.put(
const existingBan = await Ban.findOne({
where: { guild_id: guild_id, user_id: banned_user_id },
});
+
// Bans on already banned users are silently ignored
if (existingBan) return res.status(204).send();
@@ -227,6 +237,7 @@ router.put(
data: {
guild_id: guild_id,
user: banned_user.toPublicUser(),
+ delete_message_secs: Math.floor(deleteMessagesMs / 1000),
},
guild_id: guild_id,
} as GuildBanAddEvent),
diff --git a/src/gateway/Server.ts b/src/gateway/Server.ts
index 269cc2ed..5d7f8d87 100644
--- a/src/gateway/Server.ts
+++ b/src/gateway/Server.ts
@@ -28,6 +28,7 @@ import ws from "ws";
import { Connection } from "./events/Connection";
import http from "http";
import { cleanupOnStartup } from "./util/Utils";
+import { randomString } from "@spacebar/api";
export class Server {
public ws: ws.Server;
@@ -50,6 +51,10 @@ export class Server {
if (server) this.server = server;
else {
this.server = http.createServer(function (req, res) {
+ if(!req.headers.cookie?.split("; ").find(x => x.startsWith("__sb_sessid="))) {
+ res.setHeader("Set-Cookie", `__sb_sessid=${randomString(32)}; Secure; HttpOnly; SameSite=None`);
+ }
+
res.writeHead(200).end("Online");
});
}
diff --git a/src/gateway/events/Connection.ts b/src/gateway/events/Connection.ts
index bc74dee6..9b132d4b 100644
--- a/src/gateway/events/Connection.ts
+++ b/src/gateway/events/Connection.ts
@@ -72,6 +72,13 @@ export async function Connection(
);
}
+ if (request.headers.cookie?.split("; ").find(x => x.startsWith("__sb_sessid="))) {
+ socket.fingerprint = request.headers.cookie
+ .split("; ")
+ .find((x) => x.startsWith("__sb_sessid="))
+ ?.split("=")[1];
+ }
+
//Create session ID when the connection is opened. This allows gateway dump to group the initial websocket messages with the rest of the conversation.
const session_id = genSessionId();
socket.session_id = session_id; //Set the session of the WebSocket object
diff --git a/src/gateway/listener/listener.ts b/src/gateway/listener/listener.ts
index 75cb6bc6..f76adfb2 100644
--- a/src/gateway/listener/listener.ts
+++ b/src/gateway/listener/listener.ts
@@ -29,6 +29,7 @@ import {
Message,
NewUrlUserSignatureData,
GuildMemberAddEvent,
+ Ban,
} from "@spacebar/util";
import { OPCODES } from "../util/Constants";
import { Send } from "../util/Send";
@@ -174,6 +175,16 @@ async function consume(this: WebSocket, opts: EventOpts) {
case "GUILD_DELETE":
this.events[id]?.();
delete this.events[id];
+ if (event === "GUILD_DELETE" && this.ipAddress) {
+ const ban = await Ban.findOne({
+ where: { guild_id: id, user_id: this.user_id },
+ });
+
+ if (ban) {
+ ban.ip = this.ipAddress || undefined;
+ await ban.save();
+ }
+ }
break;
case "CHANNEL_CREATE":
if (!permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) {
diff --git a/src/gateway/util/WebSocket.ts b/src/gateway/util/WebSocket.ts
index 00ef9e98..d0a42eaf 100644
--- a/src/gateway/util/WebSocket.ts
+++ b/src/gateway/util/WebSocket.ts
@@ -33,6 +33,7 @@ export interface WebSocket extends WS {
compress?: "zlib-stream" | "zstd-stream";
ipAddress?: string;
userAgent?: string; // for cdn request signing
+ fingerprint?: string;
shard_count?: bigint;
shard_id?: bigint;
deflate?: Deflate;
diff --git a/src/schemas/uncategorised/BanCreateSchema.ts b/src/schemas/uncategorised/BanCreateSchema.ts
index 6d30ca22..8f349f4b 100644
--- a/src/schemas/uncategorised/BanCreateSchema.ts
+++ b/src/schemas/uncategorised/BanCreateSchema.ts
@@ -17,7 +17,7 @@
*/
export interface BanCreateSchema {
- delete_message_seconds?: string;
- delete_message_days?: string;
+ delete_message_seconds?: number;
+ delete_message_days?: number;
reason?: string;
}
diff --git a/src/util/entities/InstanceBan.ts b/src/util/entities/InstanceBan.ts
new file mode 100644
index 00000000..8f8cd29d
--- /dev/null
+++ b/src/util/entities/InstanceBan.ts
@@ -0,0 +1,57 @@
+/*
+ Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
+ Copyright (C) 2023 Spacebar and Spacebar Contributors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, OneToOne, RelationId } from "typeorm";
+import { BaseClass } from "./BaseClass";
+import { Team } from "./Team";
+import { User } from "./User";
+import { Guild } from "./Guild";
+
+@Entity({
+ name: "instance_bans",
+})
+export class InstanceBan extends BaseClass {
+ @Column({ type: "bigint" })
+ @CreateDateColumn()
+ created_at: Date = new Date();
+
+ @Column()
+ reason: string;
+
+ @Column({ nullable: true })
+ user_id: string;
+
+ @Column({ nullable: true })
+ fingerprint: string;
+
+ @Column({ nullable: true })
+ ip_address: string;
+
+ // chain of trust type tracking
+
+ @Column()
+ is_from_other_instance_ban: boolean;
+
+ @Column({ nullable: true })
+ @RelationId((instance_ban: InstanceBan) => instance_ban.origin_instance_ban)
+ origin_instance_ban_id?: string;
+
+ @JoinColumn({ name: "origin_instance_ban_id" })
+ @OneToOne(() => InstanceBan, { nullable: true, onDelete: "SET NULL" })
+ origin_instance_ban?: InstanceBan;
+}
diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts
index 24157efd..d0a60493 100644
--- a/src/util/interfaces/Event.ts
+++ b/src/util/interfaces/Event.ts
@@ -219,6 +219,7 @@ export interface GuildBanAddEvent extends Event {
data: {
guild_id: string;
user: User;
+ delete_message_secs?: number
};
}
|