From 36a3ae4b017b9356e4f7931c0dcf3382f8121c20 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sun, 15 May 2022 06:05:40 -0700 Subject: [PATCH] fix: Don't show suspended users in document facepile or list of viewers (#3497) --- app/components/Collaborators.tsx | 5 +++-- server/models/View.ts | 9 ++++++++- server/routes/api/views.test.ts | 20 ++++++++++++++++++++ server/routes/api/views.ts | 4 ++-- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/app/components/Collaborators.tsx b/app/components/Collaborators.tsx index a58c9340b..05afe16b8 100644 --- a/app/components/Collaborators.tsx +++ b/app/components/Collaborators.tsx @@ -42,8 +42,9 @@ function Collaborators(props: Props) { filter( users.orderedData, (user) => - presentIds.includes(user.id) || - document.collaboratorIds.includes(user.id) + (presentIds.includes(user.id) || + document.collaboratorIds.includes(user.id)) && + !user.isSuspended ), (user) => presentIds.includes(user.id) ), diff --git a/server/models/View.ts b/server/models/View.ts index ef6161d84..e95f03cac 100644 --- a/server/models/View.ts +++ b/server/models/View.ts @@ -57,7 +57,10 @@ class View extends BaseModel { return model; } - static async findByDocument(documentId: string) { + static async findByDocument( + documentId: string, + { includeSuspended }: { includeSuspended?: boolean } + ) { return this.findAll({ where: { documentId, @@ -67,6 +70,10 @@ class View extends BaseModel { { model: User, paranoid: false, + required: true, + ...(includeSuspended + ? {} + : { where: { suspendedAt: { [Op.is]: null } } }), }, ], }); diff --git a/server/routes/api/views.test.ts b/server/routes/api/views.test.ts index 6b1bf105d..e84e8d7d9 100644 --- a/server/routes/api/views.test.ts +++ b/server/routes/api/views.test.ts @@ -28,6 +28,26 @@ describe("#views.list", () => { expect(body.data[0].user.name).toBe(user.name); }); + it("should not return views for suspended user by default", async () => { + const { user, admin, document } = await seed(); + await View.incrementOrCreate({ + documentId: document.id, + userId: user.id, + }); + + await user.update({ suspendedAt: new Date() }); + + const res = await server.post("/api/views.list", { + body: { + token: admin.getJwtToken(), + documentId: document.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data.length).toBe(0); + }); + it("should return views for a document in read-only collection", async () => { const { user, document, collection } = await seed(); collection.permission = null; diff --git a/server/routes/api/views.ts b/server/routes/api/views.ts index ee69585e4..59a2d5e4b 100644 --- a/server/routes/api/views.ts +++ b/server/routes/api/views.ts @@ -8,7 +8,7 @@ import { assertUuid } from "@server/validation"; const router = new Router(); router.post("views.list", auth(), async (ctx) => { - const { documentId } = ctx.body; + const { documentId, includeSuspended = false } = ctx.body; assertUuid(documentId, "documentId is required"); const { user } = ctx.state; @@ -16,7 +16,7 @@ router.post("views.list", auth(), async (ctx) => { userId: user.id, }); authorize(user, "read", document); - const views = await View.findByDocument(documentId); + const views = await View.findByDocument(documentId, { includeSuspended }); ctx.body = { data: views.map(presentView),