Allow file access not in Attachments table (#5876)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Tom Moor
2023-09-24 09:43:37 -04:00
committed by GitHub
parent d0bb6c6a41
commit e50e0bba53
9 changed files with 181 additions and 85 deletions

View File

@@ -1,10 +0,0 @@
import { buildAttachment } from "@server/test/factories";
import Attachment from "./Attachment";
describe("#findByKey", () => {
it("should return the correct attachment given a key", async () => {
const attachment = await buildAttachment();
const found = await Attachment.findByKey(attachment.key);
expect(found?.id).toBe(attachment.id);
});
});

View File

@@ -1,7 +1,6 @@
import { createReadStream } from "fs";
import path from "path";
import { File } from "formidable";
import JWT from "jsonwebtoken";
import { QueryTypes } from "sequelize";
import {
BeforeDestroy,
@@ -15,10 +14,7 @@ import {
IsNumeric,
BeforeUpdate,
} from "sequelize-typescript";
import env from "@server/env";
import { AuthenticationError } from "@server/errors";
import FileStorage from "@server/storage/files";
import { getJWTPayload } from "@server/utils/jwt";
import { ValidateKey } from "@server/validation";
import Document from "./Document";
import Team from "./Team";
@@ -172,42 +168,6 @@ class Attachment extends IdModel {
return parseInt(result?.[0]?.total ?? "0", 10);
}
/**
* Find an attachment given a JWT signature.
*
* @param sign - The signature that uniquely identifies an attachment
* @returns A promise resolving to attachment corresponding to the signature
* @throws {AuthenticationError} Invalid signature if the signature verification fails
*/
static async findBySignature(sign: string): Promise<Attachment> {
const payload = getJWTPayload(sign);
if (payload.type !== "attachment") {
throw AuthenticationError("Invalid signature");
}
try {
JWT.verify(sign, env.SECRET_KEY);
} catch (err) {
throw AuthenticationError("Invalid signature");
}
return this.findByKey(payload.key);
}
/**
* Find an attachment given a key
*
* @param key The key representing attachment file path
* @returns A promise resolving to attachment corresponding to the key
*/
static async findByKey(key: string): Promise<Attachment> {
return this.findOne({
where: { key },
rejectOnEmpty: true,
});
}
// associations
@BelongsTo(() => Team, "teamId")

View File

@@ -33,6 +33,34 @@ export default class AttachmentHelper {
return `${keyPrefix}/${name}`;
}
/**
* Parse a key into its constituent parts
*
* @param key The key to parse
* @returns The constituent parts
*/
static parseKey(key: string): {
bucket: string;
userId: string;
id: string;
fileName: string | undefined;
isPublicBucket: boolean;
} {
const parts = key.split("/");
const bucket = parts[0];
const userId = parts[1];
const id = parts[2];
const [fileName] = parts.length > 3 ? parts.slice(-1) : [];
return {
bucket,
userId,
id,
fileName,
isPublicBucket: bucket === Buckets.avatars || bucket === Buckets.public,
};
}
/**
* Get the ACL to use for a given attachment preset
*