From b3f2516f1f720e458e4a7e5c678bd0e25ddf3742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Fri, 24 Mar 2023 12:31:21 +0000 Subject: [PATCH] fix: redirect guest user on public board (#1304) --- .../applications/get.board.application.ts | 4 ++ backend/src/modules/boards/boards.module.ts | 3 +- .../controller/publicBoards.controller.ts | 43 +++++++++++++++++++ .../get.board.application.interface.ts | 2 + .../services/get.board.service.interface.ts | 2 + .../board.repository.interface.ts | 1 + .../boards/repositories/board.repository.ts | 4 ++ .../boards/services/get.board.service.spec.ts | 23 ++++++++++ .../boards/services/get.board.service.ts | 12 +++++- frontend/src/api/boardService.tsx | 2 +- frontend/src/pages/boards/[boardId].tsx | 8 ++-- 11 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 backend/src/modules/boards/controller/publicBoards.controller.ts diff --git a/backend/src/modules/boards/applications/get.board.application.ts b/backend/src/modules/boards/applications/get.board.application.ts index a947cdd90..84d92abfc 100644 --- a/backend/src/modules/boards/applications/get.board.application.ts +++ b/backend/src/modules/boards/applications/get.board.application.ts @@ -49,4 +49,8 @@ export class GetBoardApplication implements GetBoardApplicationInterface { getAllBoardIdsAndTeamIdsOfUser(userId: string) { return this.getBoardService.getAllBoardIdsAndTeamIdsOfUser(userId); } + + isBoardPublic(boardId: string) { + return this.getBoardService.isBoardPublic(boardId); + } } diff --git a/backend/src/modules/boards/boards.module.ts b/backend/src/modules/boards/boards.module.ts index 76bcb44ac..1d3d33df4 100644 --- a/backend/src/modules/boards/boards.module.ts +++ b/backend/src/modules/boards/boards.module.ts @@ -37,6 +37,7 @@ import { } from './boards.providers'; import BoardsController from './controller/boards.controller'; import TeamUsersModule from 'src/modules/teamUsers/teamusers.module'; +import PublicBoardsController from './controller/publicBoards.controller'; @Module({ imports: [ @@ -76,7 +77,7 @@ import TeamUsersModule from 'src/modules/teamUsers/teamusers.module'; afterUserRequestedTimerStateSubscriber, boardRepository ], - controllers: [BoardsController], + controllers: [BoardsController, PublicBoardsController], exports: [ getBoardApplication, createBoardService, diff --git a/backend/src/modules/boards/controller/publicBoards.controller.ts b/backend/src/modules/boards/controller/publicBoards.controller.ts new file mode 100644 index 000000000..20767a5d1 --- /dev/null +++ b/backend/src/modules/boards/controller/publicBoards.controller.ts @@ -0,0 +1,43 @@ +import { BaseParam } from 'src/libs/dto/param/base.param'; +import { BadRequestResponse } from 'src/libs/swagger/errors/bad-request.swagger'; +import { InternalServerErrorResponse } from 'src/libs/swagger/errors/internal-server-error.swagger'; +import { NotFoundResponse } from 'src/libs/swagger/errors/not-found.swagger'; +import { Controller, Get, Inject, Param } from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiInternalServerErrorResponse, + ApiNotFoundResponse, + ApiOperation, + ApiParam, + ApiTags +} from '@nestjs/swagger'; +import { GetBoardApplicationInterface } from '../interfaces/applications/get.board.application.interface'; +import { TYPES } from '../interfaces/types'; + +@ApiTags('PublicBoards') +@Controller('publicBoards') +export default class PublicBoardsController { + constructor( + @Inject(TYPES.applications.GetBoardApplication) + private getBoardApp: GetBoardApplicationInterface + ) {} + + @ApiOperation({ summary: 'Check if board is public' }) + @ApiParam({ type: String, name: 'boardId', required: true }) + @ApiBadRequestResponse({ + description: 'Bad Request', + type: BadRequestResponse + }) + @ApiNotFoundResponse({ + type: NotFoundResponse, + description: 'Board not found!' + }) + @ApiInternalServerErrorResponse({ + description: 'Internal Server Error', + type: InternalServerErrorResponse + }) + @Get(':boardId/isPublic') + getBoard(@Param() { boardId }: BaseParam) { + return this.getBoardApp.isBoardPublic(boardId); + } +} diff --git a/backend/src/modules/boards/interfaces/applications/get.board.application.interface.ts b/backend/src/modules/boards/interfaces/applications/get.board.application.interface.ts index a6c7de9f8..93bbfe665 100644 --- a/backend/src/modules/boards/interfaces/applications/get.board.application.interface.ts +++ b/backend/src/modules/boards/interfaces/applications/get.board.application.interface.ts @@ -44,4 +44,6 @@ export interface GetBoardApplicationInterface { getAllBoardIdsAndTeamIdsOfUser( userId: string ): Promise<{ boardIds: LeanDocument[]; teamIds: unknown[] }>; + + isBoardPublic(boardId: string); } diff --git a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts index 23a921830..3911303e9 100644 --- a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts @@ -54,4 +54,6 @@ export interface GetBoardServiceInterface { getBoardUser(board: string, user: string): Promise; getAllMainBoards(): Promise; + + isBoardPublic(boardId: string); } diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index cfc46f065..223155a83 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -8,6 +8,7 @@ import { DeleteResult } from 'mongodb'; export interface BoardRepositoryInterface extends BaseInterfaceRepository { getBoard(boardId: string): Promise; + isBoardPublic(boardId: string): Promise; getBoardsByBoardIdsList(boardIds: string[]): Promise; getBoardPopulated(boardId: string): Promise; getMainBoard(boardId: string): Promise; diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index 938c1f047..0479e0e54 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -24,6 +24,10 @@ export class BoardRepository return this.findOneById(boardId); } + isBoardPublic(boardId: string): Promise { + return this.findOneById(boardId, 'isPublic'); + } + getBoardsByBoardIdsList(boardIds: string[]): Promise { return this.findAllWithQuery({ _id: { $in: boardIds } diff --git a/backend/src/modules/boards/services/get.board.service.spec.ts b/backend/src/modules/boards/services/get.board.service.spec.ts index 06d12c9c6..318b95e0a 100644 --- a/backend/src/modules/boards/services/get.board.service.spec.ts +++ b/backend/src/modules/boards/services/get.board.service.spec.ts @@ -29,6 +29,8 @@ import Column from 'src/modules/columns/entities/column.schema'; import { UserDtoFactory } from 'src/libs/test-utils/mocks/factories/dto/userDto-factory.mock'; import { BadRequestException, NotFoundException } from '@nestjs/common'; +const board = BoardFactory.create(); + const hideVotesFromColumns = (columns: Column[], userId: string) => { return columns.map((column) => { column.cards.forEach((card) => { @@ -558,4 +560,25 @@ describe('GetBoardService', () => { expect(result).toEqual(countResult); }); }); + + describe('isBoardPublic', () => { + it('should return the isPublic status of a board', async () => { + board.isPublic = true; + + boardRepositoryMock.isBoardPublic.mockResolvedValue(board); + + const result = await boardService.isBoardPublic(board._id); + + expect(boardRepositoryMock.isBoardPublic).toBeCalledTimes(1); + expect(result).toEqual(true); + }); + + it('should throw an error if board is not found', async () => { + boardRepositoryMock.isBoardPublic.mockResolvedValue(null); + + expect(async () => await boardService.isBoardPublic(board._id)).rejects.toThrow( + NotFoundException + ); + }); + }); }); diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index 644478618..d268ab2f9 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -7,7 +7,7 @@ import { NotFoundException, forwardRef } from '@nestjs/common'; -import { BOARD_USER_NOT_FOUND, NOT_FOUND } from 'src/libs/exceptions/messages'; +import { BOARD_NOT_FOUND, BOARD_USER_NOT_FOUND, NOT_FOUND } from 'src/libs/exceptions/messages'; import { GetTeamServiceInterface } from 'src/modules/teams/interfaces/services/get.team.service.interface'; import * as Teams from 'src/modules/teams/interfaces/types'; import * as Users from 'src/modules/users/interfaces/types'; @@ -159,6 +159,16 @@ export default class GetBoardService implements GetBoardServiceInterface { return this.boardRepository.getAllMainBoards(); } + async isBoardPublic(boardId: string) { + const board = await this.boardRepository.isBoardPublic(boardId); + + if (!board) { + throw new NotFoundException(BOARD_NOT_FOUND); + } + + return board.isPublic; + } + /* --------------- HELPERS --------------- */ private async getBoards(allBoards: boolean, query: QueryType, page = 0, size = 10) { diff --git a/frontend/src/api/boardService.tsx b/frontend/src/api/boardService.tsx index e821119f3..49986be1f 100644 --- a/frontend/src/api/boardService.tsx +++ b/frontend/src/api/boardService.tsx @@ -57,7 +57,7 @@ export const getPublicStatusRequest = ( boardId: string, context?: GetServerSidePropsContext, ): Promise => - fetchData(`/publicBoards/${boardId}/publicStatus`, { + fetchData(`/publicBoards/${boardId}/isPublic`, { context, serverSide: !!context, isPublicRequest: true, diff --git a/frontend/src/pages/boards/[boardId].tsx b/frontend/src/pages/boards/[boardId].tsx index e2e071ea1..7b8dd15b8 100644 --- a/frontend/src/pages/boards/[boardId].tsx +++ b/frontend/src/pages/boards/[boardId].tsx @@ -4,7 +4,7 @@ import { getSession, useSession } from 'next-auth/react'; import { useRouter } from 'next/router'; import { useEffect, useMemo, useState } from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; -import { getBoardRequest } from '@/api/boardService'; +import { getBoardRequest, getPublicStatusRequest } from '@/api/boardService'; import DragDropArea from '@/components/Board/DragDropArea'; import RegularBoard from '@/components/Board/RegularBoard'; import { BoardSettings } from '@/components/Board/Settings'; @@ -49,8 +49,10 @@ export const getServerSideProps: GetServerSideProps = async (context) => { props: {}, }; - // if board is public and no session - if (!session) { + const boardIsPublic = await getPublicStatusRequest(boardId, context); + + // if board is public and user has no session + if (boardIsPublic && !session) { // check if there are guest user cookies const cookiesGuestUser: GuestUser | { user: string } = getGuestUserCookies({ req, res }, true); // if there isnĀ“t cookies, the guest user is not registered