Sign media per user

This commit is contained in:
Emma [it/its]@Rory& 2025-07-03 09:25:04 +02:00 committed by Rory&
parent 8922ca1518
commit 387cef269a
6 changed files with 57 additions and 11 deletions

View File

@ -30,12 +30,13 @@ import {
emitEvent,
getPermission,
getRights,
uploadFile,
uploadFile, Config, getUrlSignature,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import multer from "multer";
import { handleMessage, postHandleMessage, route } from "../../../../../util";
import { URL } from "url";
const router = Router();
// TODO: message content/embed string length limit
@ -244,7 +245,7 @@ router.put(
console.error("[Message] post-message handler failed", e),
);
return res.json(message);
return res.json(message.withSignedAttachments(req));
},
);

View File

@ -17,14 +17,20 @@
*/
import { Server, ServerOptions } from "lambert-server";
import { Config, initDatabase, registerRoutes, Sentry } from "@spacebar/util";
import {
Attachment,
Config,
initDatabase,
registerRoutes,
Sentry,
} from "@spacebar/util";
import { CORS, BodyParser } from "@spacebar/api";
import path from "path";
import avatarsRoute from "./routes/avatars";
import guildProfilesRoute from "./routes/guild-profiles";
import iconsRoute from "./routes/role-icons";
import { CORS } from "../api/middlewares/CORS";
import { BodyParser } from "../api/middlewares/BodyParser";
import morgan from "morgan";
import { Like, Or } from "typeorm";
export type CDNServerOptions = ServerOptions;
@ -38,6 +44,7 @@ export class CDNServer extends Server {
async start() {
await initDatabase();
await Config.init();
await this.cleanupSignaturesInDb();
await Sentry.init(this.app);
const logRequests = process.env["LOG_REQUESTS"] != undefined;
@ -124,4 +131,26 @@ export class CDNServer extends Server {
async stop() {
return super.stop();
}
async cleanupSignaturesInDb() {
this.log("verbose", "[Server] Cleaning up signatures in database");
const attachmentsToFix = await Attachment.find({
where: { url: Like("%?ex=%") },
});
if (attachmentsToFix.length === 0) {
this.log("verbose", "[Server] No attachments to fix");
return;
}
this.log(
"verbose",
`[Server] Found ${attachmentsToFix.length} attachments to fix`,
);
for (const attachment of attachmentsToFix) {
attachment.url = attachment.url.split("?ex=")[0];
attachment.proxy_url = attachment.proxy_url?.split("?ex=")[0];
await attachment.save();
this.log("verbose", `[Server] Fixed attachment ${attachment.id}`);
}
}
}

View File

@ -69,12 +69,7 @@ router.post(
}
}
let finalUrl = `${endpoint}/${path}`;
if (Config.get().security.cdnSignUrls) {
const signatureData = getUrlSignature(path, req);
finalUrl = `${finalUrl}?ex=${signatureData.expiresAt}&is=${signatureData.issuedAt}&hm=${signatureData.hash}&ip=${req.ip}`;
}
const finalUrl = `${endpoint}/${path}`;
const file = {
id,
@ -82,6 +77,7 @@ router.post(
filename: filename,
size,
url: finalUrl,
path,
width,
height,
};

View File

@ -21,6 +21,7 @@ import { createHmac, timingSafeEqual } from "crypto";
import ms, { StringValue } from "ms";
import { ParsedQs } from "qs";
import { Request } from "express";
import attachments from "../cdn/routes/attachments";
export const getUrlSignature = (
path: string,

View File

@ -28,6 +28,8 @@ import { URL } from "url";
import { deleteFile } from "../util/cdn";
import { BaseClass } from "./BaseClass";
import { dbEngine } from "../util/Database";
import { Request } from "express";
import { resignUrl } from "../Signing";
@Entity({
name: "attachments",
@ -73,4 +75,11 @@ export class Attachment extends BaseClass {
onDelete() {
return deleteFile(new URL(this.url).pathname);
}
signUrls(req: Request) {
return {
url: this.url + resignUrl(this.url, req),
proxy_url: this.proxy_url + resignUrl(this.proxy_url, req),
}
}
}

View File

@ -40,6 +40,7 @@ import { Webhook } from "./Webhook";
import { Sticker } from "./Sticker";
import { Attachment } from "./Attachment";
import { dbEngine } from "../util/Database";
import { Request } from "express";
export enum MessageType {
DEFAULT = 0,
@ -260,6 +261,15 @@ export class Message extends BaseClass {
content: this.content ?? "",
};
}
withSignedAttachments(req: Request) {
return {
...this,
attachments: this.attachments?.map((attachment: Attachment) =>
attachment.signUrls(req)
)
};
}
}
export interface MessageComponent {