From 1c52d7c8602b86f36171afc37c22354941cb6ec5 Mon Sep 17 00:00:00 2001 From: Andres Pinto Date: Mon, 22 Jul 2024 15:03:25 -0400 Subject: [PATCH] fix: guard now checks if user is deactivated --- .../guards/workspaces.guard.spec.ts | 66 +++++++++++++++++++ .../workspaces/guards/workspaces.guard.ts | 16 ++++- src/modules/workspaces/workspaces.usecase.ts | 10 +++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/modules/workspaces/guards/workspaces.guard.spec.ts b/src/modules/workspaces/guards/workspaces.guard.spec.ts index b84850754..c8bc17306 100644 --- a/src/modules/workspaces/guards/workspaces.guard.spec.ts +++ b/src/modules/workspaces/guards/workspaces.guard.spec.ts @@ -265,6 +265,8 @@ describe('WorkspaceGuard', () => { it('When Team does not exist, then throw', async () => { const user = newUser(); + const workspaceMember = newWorkspaceUser({ memberId: user.uuid }); + jest.spyOn(reflector, 'get').mockReturnValue({ requiredRole: WorkspaceRole.MANAGER, accessContext: AccessContext.TEAM, @@ -276,6 +278,8 @@ describe('WorkspaceGuard', () => { teamUser: null, }); + workspaceUseCases.findUserInWorkspace.mockResolvedValue(workspaceMember); + const context = createMockExecutionContext(user, { params: { teamId: v4() }, query: {}, @@ -290,11 +294,20 @@ describe('WorkspaceGuard', () => { it('When user is team member, then grant access', async () => { const user = newUser(); const workspace = newWorkspace({ owner: user }); + const workspaceMember = newWorkspaceUser({ + memberId: user.uuid, + workspaceId: workspace.id, + }); const team = newWorkspaceTeam({ workspaceId: workspace.id, manager: user, }); + workspaceUseCases.findUserAndWorkspace.mockResolvedValue({ + workspace, + workspaceUser: workspaceMember, + }); + jest.spyOn(reflector, 'get').mockReturnValue({ requiredRole: WorkspaceRole.MEMBER, accessContext: AccessContext.TEAM, @@ -318,6 +331,10 @@ describe('WorkspaceGuard', () => { const member = newUser(); const team = newWorkspaceTeam({ manager }); const workspace = newWorkspace(); + const workspaceMember = newWorkspaceUser({ + memberId: member.uuid, + workspaceId: workspace.id, + }); jest.spyOn(reflector, 'get').mockReturnValue({ requiredRole: WorkspaceRole.MANAGER, @@ -325,6 +342,11 @@ describe('WorkspaceGuard', () => { idSource: 'params', }); + workspaceUseCases.findUserAndWorkspace.mockResolvedValue({ + workspace, + workspaceUser: workspaceMember, + }); + workspaceUseCases.findUserInTeam.mockResolvedValue({ team, teamUser: {} as WorkspaceTeamUser, @@ -344,6 +366,10 @@ describe('WorkspaceGuard', () => { const nonMemberUser = newUser(); const team = newWorkspaceTeam(); const workspace = newWorkspace(); + const workspaceMember = newWorkspaceUser({ + memberId: nonMemberUser.uuid, + workspaceId: workspace.id, + }); jest.spyOn(reflector, 'get').mockReturnValue({ requiredRole: WorkspaceRole.MEMBER, @@ -351,6 +377,11 @@ describe('WorkspaceGuard', () => { idSource: 'params', }); + workspaceUseCases.findUserAndWorkspace.mockResolvedValue({ + workspace, + workspaceUser: workspaceMember, + }); + workspaceUseCases.findById.mockResolvedValue(workspace); workspaceUseCases.findUserInTeam.mockResolvedValue({ team, @@ -366,9 +397,39 @@ describe('WorkspaceGuard', () => { ); }); + it('When user is deactivated, it should throw', async () => { + const nonMemberUser = newUser(); + const team = newWorkspaceTeam(); + const workspace = newWorkspace(); + const workspaceMember = newWorkspaceUser({ + memberId: nonMemberUser.uuid, + workspaceId: workspace.id, + attributes: { deactivated: true }, + }); + + jest.spyOn(reflector, 'get').mockReturnValue({ + requiredRole: WorkspaceRole.MEMBER, + accessContext: AccessContext.TEAM, + idSource: 'params', + }); + + workspaceUseCases.findUserAndWorkspace.mockResolvedValue({ + workspace, + workspaceUser: workspaceMember, + }); + const context = createMockExecutionContext(nonMemberUser, { + params: { teamId: team.id }, + }); + + await expect(guard.canActivate(context)).rejects.toThrow( + ForbiddenException, + ); + }); + it('When team workspace does not exist, then throw', async () => { const nonMemberUser = newUser(); const team = newWorkspaceTeam(); + const workspaceMember = newWorkspaceUser(); jest.spyOn(reflector, 'get').mockReturnValue({ requiredRole: WorkspaceRole.MEMBER, @@ -376,6 +437,11 @@ describe('WorkspaceGuard', () => { idSource: 'params', }); + workspaceUseCases.findUserAndWorkspace.mockResolvedValue({ + workspace: null, + workspaceUser: workspaceMember, + }); + workspaceUseCases.findById.mockResolvedValue(null); workspaceUseCases.findUserInTeam.mockResolvedValue({ team, diff --git a/src/modules/workspaces/guards/workspaces.guard.ts b/src/modules/workspaces/guards/workspaces.guard.ts index 461adde25..ba5972e4f 100644 --- a/src/modules/workspaces/guards/workspaces.guard.ts +++ b/src/modules/workspaces/guards/workspaces.guard.ts @@ -83,11 +83,13 @@ export class WorkspaceGuard implements CanActivate { role === WorkspaceRole.OWNER && !workspace.isUserOwner(user); const isWorkspaceNotSetupAndUserNotOwner = !workspace.isWorkspaceReady() && !workspace.isUserOwner(user); + const isUserDeactivated = workspaceUser?.deactivated; if ( !isUserPartOfWorkspace || isOwnerRoleRequiredButNotMet || - isWorkspaceNotSetupAndUserNotOwner + isWorkspaceNotSetupAndUserNotOwner || + isUserDeactivated ) { Logger.log( `[WORKSPACES/GUARD]: Access denied. ID: ${workspaceId}, User UUID: ${user.uuid}`, @@ -114,12 +116,22 @@ export class WorkspaceGuard implements CanActivate { throw new NotFoundException('Team not found'); } - const workspace = await this.workspaceUseCases.findById(team.workspaceId); + const { workspace, workspaceUser } = + await this.workspaceUseCases.findUserAndWorkspace( + user.uuid, + team.workspaceId, + ); if (!workspace) { throw new NotFoundException('Workspace not found'); } + if (!workspaceUser || workspaceUser?.deactivated) { + throw new ForbiddenException( + 'You do not have the required access to this workspace.', + ); + } + if (workspace.isUserOwner(user)) { return true; } diff --git a/src/modules/workspaces/workspaces.usecase.ts b/src/modules/workspaces/workspaces.usecase.ts index bcf4a8991..7c2e9472f 100644 --- a/src/modules/workspaces/workspaces.usecase.ts +++ b/src/modules/workspaces/workspaces.usecase.ts @@ -2084,6 +2084,16 @@ export class WorkspacesUsecases { return this.teamRepository.getTeamUserAndTeamByTeamId(userUuid, teamId); } + findUserInWorkspace( + userUuid: User['uuid'], + workspaceId: Workspace['id'], + ): Promise { + return this.workspaceRepository.findWorkspaceUser({ + workspaceId, + memberId: userUuid, + }); + } + async deleteWorkspaceContent( workspaceId: Workspace['id'], user: User,