From dc976c6c10e6d268919d7e0566267ca69eb8070c Mon Sep 17 00:00:00 2001 From: Anders Rasmussen Date: Wed, 4 Oct 2023 08:10:42 +0200 Subject: [PATCH] #9 - Added changes according to feedback --- src/locales/da.json | 4 +-- src/locales/en.json | 4 +-- src/modules/item/folder/__test__/add.test.ts | 2 +- .../item/folder/__test__/delete.test.ts | 5 +--- src/modules/item/folder/__test__/edit.test.ts | 5 +--- src/modules/item/folder/__test__/read.test.ts | 5 +--- src/modules/item/folder/access.service.ts | 28 +++++++++++++++++++ src/modules/item/folder/folder.controller.ts | 22 +++++++++++---- src/modules/item/folder/folder.schema.ts | 7 ++--- src/modules/item/folder/folder.service.ts | 2 +- 10 files changed, 56 insertions(+), 28 deletions(-) create mode 100644 src/modules/item/folder/access.service.ts diff --git a/src/locales/da.json b/src/locales/da.json index a680484..1f04e66 100644 --- a/src/locales/da.json +++ b/src/locales/da.json @@ -116,8 +116,8 @@ "required": "Farve er påkrævet", "type": "Farve skal være en tekst" }, - "itemid": { - "type": "Item id skal være et tal" + "parentId": { + "type": "Parent id skal være et tal" } } } diff --git a/src/locales/en.json b/src/locales/en.json index 2514120..6d94b41 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -116,8 +116,8 @@ "required": "Color is required", "type": "Color must be a string" }, - "itemid": { - "type": "Item id must be a number" + "parentId": { + "type": "Parent id must be a number" } } } diff --git a/src/modules/item/folder/__test__/add.test.ts b/src/modules/item/folder/__test__/add.test.ts index ddeb9be..6ea287d 100644 --- a/src/modules/item/folder/__test__/add.test.ts +++ b/src/modules/item/folder/__test__/add.test.ts @@ -142,7 +142,7 @@ describe('POST /api/folder', () => { expect(response.json()).toEqual({ error: 'ValidationError', errors: { - parentId: ['Item id must be a number'], + parentId: ['Parent id must be a number'], }, statusCode: 400, }); diff --git a/src/modules/item/folder/__test__/delete.test.ts b/src/modules/item/folder/__test__/delete.test.ts index 1b8479b..91d9785 100644 --- a/src/modules/item/folder/__test__/delete.test.ts +++ b/src/modules/item/folder/__test__/delete.test.ts @@ -35,7 +35,6 @@ describe('DELETE /api/folder/:id', () => { const { accessToken } = await authService.createTokens(user.id); const folder = await folderService.createFolder({ - mimeType: 'application/vnd.cloudstore.folder', name: 'Folder1', ownerId: user.id, parentId: null, @@ -58,7 +57,6 @@ describe('DELETE /api/folder/:id', () => { it('should return status 401, when unauthorized', async () => { const folder = await folderService.createFolder({ - mimeType: 'application/vnd.cloudstore.folder', name: 'Folder1', ownerId: user.id, parentId: null, @@ -83,11 +81,10 @@ describe('DELETE /api/folder/:id', () => { }); }); - it('should return status 401, when folder id is provided but you do not own it', async () => { + it('should return status 401, when folder id is not accessible to you', async () => { const { accessToken } = await authService.createTokens(user.id); const folder = await folderService.createFolder({ - mimeType: 'application/vnd.cloudstore.folder', name: 'Folder1', ownerId: otherUser.id, parentId: null, diff --git a/src/modules/item/folder/__test__/edit.test.ts b/src/modules/item/folder/__test__/edit.test.ts index 38738c9..85a908e 100644 --- a/src/modules/item/folder/__test__/edit.test.ts +++ b/src/modules/item/folder/__test__/edit.test.ts @@ -32,7 +32,6 @@ describe('PUT /api/folder', () => { const { accessToken } = await authService.createTokens(user.id); const folder = await folderService.createFolder({ - mimeType: 'application/vnd.cloudstore.folder', name: 'Folder1', ownerId: user.id, parentId: null, @@ -65,7 +64,6 @@ describe('PUT /api/folder', () => { it('should return status 401, when unauthorized', async () => { const folder = await folderService.createFolder({ - mimeType: 'application/vnd.cloudstore.folder', name: 'Folder1', ownerId: user.id, parentId: null, @@ -95,11 +93,10 @@ describe('PUT /api/folder', () => { }); }); - it('should return status 401, when folder id is provided but you do not own it', async () => { + it('should return status 401, when folder id is not accessible to you', async () => { const { accessToken } = await authService.createTokens(user.id); const folder = await folderService.createFolder({ - mimeType: 'application/vnd.cloudstore.folder', name: 'Folder1', ownerId: otherUser.id, parentId: null, diff --git a/src/modules/item/folder/__test__/read.test.ts b/src/modules/item/folder/__test__/read.test.ts index 4cf904c..c9dc3ca 100644 --- a/src/modules/item/folder/__test__/read.test.ts +++ b/src/modules/item/folder/__test__/read.test.ts @@ -32,7 +32,6 @@ describe('GET /api/folder/:id', () => { const { accessToken } = await authService.createTokens(user.id); const folder = await folderService.createFolder({ - mimeType: 'application/vnd.cloudstore.folder', name: 'Folder1', ownerId: user.id, parentId: null, @@ -58,7 +57,6 @@ describe('GET /api/folder/:id', () => { it('should return status 401, when unauthorized', async () => { const folder = await folderService.createFolder({ - mimeType: 'application/vnd.cloudstore.folder', name: 'Folder1', ownerId: user.id, parentId: null, @@ -83,11 +81,10 @@ describe('GET /api/folder/:id', () => { }); }); - it('should return status 401, when folder id is provided but you do not own it', async () => { + it('should return status 401, when folder id is not accessible to you', async () => { const { accessToken } = await authService.createTokens(user.id); const folder = await folderService.createFolder({ - mimeType: 'application/vnd.cloudstore.folder', name: 'Folder1', ownerId: otherUser.id, parentId: null, diff --git a/src/modules/item/folder/access.service.ts b/src/modules/item/folder/access.service.ts new file mode 100644 index 0000000..0f53dcd --- /dev/null +++ b/src/modules/item/folder/access.service.ts @@ -0,0 +1,28 @@ +import FolderService from './folder.service'; +import ItemService from '../item.service'; + +export default class AccessService { + private itemService: ItemService; + private folderService: FolderService; + + constructor(itemService: ItemService, folderService: FolderService) { + this.itemService = itemService; + this.folderService = folderService; + } + + public async hasAccessToItem(itemId: number, userId: number): Promise { + const item = await this.itemService.getById(itemId); + + if (item.ownerId === userId) { + return true; + } + + try { + await this.folderService.getByItemId(itemId); + + return true; + } catch (e) { + return false; + } + } +} diff --git a/src/modules/item/folder/folder.controller.ts b/src/modules/item/folder/folder.controller.ts index 77ab3c0..233136c 100644 --- a/src/modules/item/folder/folder.controller.ts +++ b/src/modules/item/folder/folder.controller.ts @@ -1,12 +1,15 @@ import { FastifyReply, FastifyRequest } from 'fastify'; import { ReadInput, EditInput, AddInput, DeleteInput } from './folder.schema'; import FolderService from './folder.service'; +import AccessService from './access.service'; -export default class ItemController { +export default class FolderController { private folderService: FolderService; + private accessService: AccessService; - constructor(folderService: FolderService) { + constructor(folderService: FolderService, accessService: AccessService) { this.folderService = folderService; + this.accessService = accessService; } public async readHandler( @@ -18,7 +21,7 @@ export default class ItemController { try { const folder = await this.folderService.getByItemId(request.params.id); - if (folder.ownerId !== request.user.sub) { + if (!(await this.accessService.hasAccessToItem(folder.id, request.user.sub))) { return reply.unauthorized(); } @@ -42,7 +45,7 @@ export default class ItemController { try { const folder = await this.folderService.getByItemId(request.body.id); - if (folder.ownerId !== request.user.sub) { + if (!(await this.accessService.hasAccessToItem(folder.id, request.user.sub))) { return reply.unauthorized(); } @@ -66,9 +69,16 @@ export default class ItemController { reply: FastifyReply, ) { try { + if ( + request.body.parentId !== null && + request.body.parentId !== undefined && + !(await this.accessService.hasAccessToItem(request.body.parentId, request.user.sub)) + ) { + return reply.unauthorized(); + } + const folder = await this.folderService.createFolder({ name: request.body.name, - mimeType: 'application/vnd.cloudstore.folder', color: request.body.color, ownerId: request.user.sub, parentId: request.body.parentId ?? null, @@ -90,7 +100,7 @@ export default class ItemController { try { const folder = await this.folderService.getByItemId(request.params.id); - if (folder.ownerId !== request.user.sub) { + if (!(await this.accessService.hasAccessToItem(folder.id, request.user.sub))) { return reply.unauthorized(); } diff --git a/src/modules/item/folder/folder.schema.ts b/src/modules/item/folder/folder.schema.ts index ac9f760..8711143 100644 --- a/src/modules/item/folder/folder.schema.ts +++ b/src/modules/item/folder/folder.schema.ts @@ -80,7 +80,7 @@ const editFolderSchema = { parentId: { type: ['number', 'null'], errorMessage: { - type: 'folder.itemid.type', + type: 'folder.parentId.type', }, }, }, @@ -144,7 +144,7 @@ const addFolderSchema = { parentId: { type: ['number', 'null'], errorMessage: { - type: 'folder.itemid.type', + type: 'folder.parentId.type', }, }, }, @@ -218,14 +218,13 @@ export type DeleteInput = FromSchema; export type AddFolder = { name: string; color: string; - mimeType: string; ownerId: number; parentId: number | null; }; export type ItemFolder = prismaItemFolderType & { item: Item }; export type Folder = Omit & Item; -export type UpdateFolder = { id: number } & Partial & UpdateItem; +export type UpdateFolder = { id: number } & Partial & Omit; export const folderSchemas = [ addFolderSchema, diff --git a/src/modules/item/folder/folder.service.ts b/src/modules/item/folder/folder.service.ts index f79a364..5dd672d 100644 --- a/src/modules/item/folder/folder.service.ts +++ b/src/modules/item/folder/folder.service.ts @@ -9,7 +9,7 @@ export default class FolderService { item: { create: { name: input.name, - mimeType: input.mimeType, + mimeType: 'application/vnd.cloudstore.folder', ownerId: input.ownerId, parentId: input.parentId, },