From 72189e041b03a9e3ecd8bac2bfc07beab4a5999e Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Thu, 10 Dec 2020 21:40:03 -0800 Subject: [PATCH] feat: attachments.delete (#1714) * feat: Add endpoint for manually deleting attachments * mock --- server/api/attachments.js | 25 ++++++++++++++++ server/api/attachments.test.js | 54 ++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/server/api/attachments.js b/server/api/attachments.js index b92bfcbc6..6ff93e63e 100644 --- a/server/api/attachments.js +++ b/server/api/attachments.js @@ -92,6 +92,31 @@ router.post("attachments.create", auth(), async (ctx) => { }; }); +router.post("attachments.delete", auth(), async (ctx) => { + let { id } = ctx.body; + ctx.assertPresent(id, "id is required"); + + const user = ctx.state.user; + const attachment = await Attachment.findByPk(id); + const document = await Document.findByPk(attachment.documentId, { + userId: user.id, + }); + authorize(user, "update", document); + + await attachment.destroy(); + + await Event.create({ + name: "attachments.delete", + teamId: user.teamId, + userId: user.id, + ip: ctx.request.ip, + }); + + ctx.body = { + success: true, + }; +}); + router.post("attachments.redirect", auth(), async (ctx) => { const { id } = ctx.body; ctx.assertPresent(id, "id is required"); diff --git a/server/api/attachments.test.js b/server/api/attachments.test.js index ff622ec5b..e77c59274 100644 --- a/server/api/attachments.test.js +++ b/server/api/attachments.test.js @@ -1,6 +1,7 @@ /* eslint-disable flowtype/require-valid-file-annotation */ import TestServer from "fetch-test-server"; import app from "../app"; +import { Attachment } from "../models"; import { buildUser, buildCollection, @@ -11,9 +12,62 @@ import { flushdb } from "../test/support"; const server = new TestServer(app.callback()); +jest.mock("aws-sdk", () => { + const mS3 = { deleteObject: jest.fn().mockReturnThis(), promise: jest.fn() }; + return { + S3: jest.fn(() => mS3), + Endpoint: jest.fn(), + }; +}); + beforeEach(() => flushdb()); afterAll(() => server.close()); +describe("#attachments.delete", () => { + it("should require authentication", async () => { + const res = await server.post("/api/attachments.delete"); + expect(res.status).toEqual(401); + }); + + it("should allow deleting an attachment belonging to a document user has access to", async () => { + const user = await buildUser(); + const attachment = await buildAttachment({ + teamId: user.teamId, + userId: user.id, + }); + const res = await server.post("/api/attachments.delete", { + body: { token: user.getJwtToken(), id: attachment.id }, + }); + + expect(res.status).toEqual(200); + expect(await Attachment.count()).toEqual(0); + }); + + it("should not allow deleting an attachment belonging to a document user does not have access to", async () => { + const user = await buildUser(); + const collection = await buildCollection({ + private: true, + }); + const document = await buildDocument({ + teamId: collection.teamId, + userId: collection.userId, + collectionId: collection.id, + }); + const attachment = await buildAttachment({ + teamId: document.teamId, + userId: document.userId, + documentId: document.id, + acl: "private", + }); + + const res = await server.post("/api/attachments.delete", { + body: { token: user.getJwtToken(), id: attachment.id }, + }); + + expect(res.status).toEqual(403); + }); +}); + describe("#attachments.redirect", () => { it("should require authentication", async () => { const res = await server.post("/api/attachments.redirect");