Skip to content

Commit 5fcbec8

Browse files
fix: 권한 체크 수정
1 parent 020691e commit 5fcbec8

File tree

6 files changed

+71
-35
lines changed

6 files changed

+71
-35
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { HttpException, HttpStatus } from '@nestjs/common';
2+
3+
export class ForbiddenAccessException extends HttpException {
4+
constructor() {
5+
super('워크스페이스에 접근할 권한이 없습니다.', HttpStatus.FORBIDDEN);
6+
}
7+
}

apps/backend/src/exception/workspace-auth.exception.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ import { HttpException, HttpStatus } from '@nestjs/common';
22

33
export class NotWorkspaceOwnerException extends HttpException {
44
constructor() {
5-
super('You are not the owner of this workspace.', HttpStatus.FORBIDDEN);
5+
super('해당 워크스페이스의 소유자가 아닙니다.', HttpStatus.FORBIDDEN);
66
}
77
}

apps/backend/src/workspace/dtos/getWorkspaceAccessResponse.dto.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

apps/backend/src/workspace/workspace.controller.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,49 @@ describe('WorkspaceController', () => {
136136
expect(result).toEqual(expectedResult);
137137
});
138138
});
139+
140+
it('컨트롤러가 정상적으로 인스턴스화된다.', () => {
141+
expect(controller).toBeDefined();
142+
});
143+
144+
describe('generateInviteLink', () => {
145+
it('초대 링크를 생성하고 반환한다.', async () => {
146+
const req = { user: { sub: 1 } };
147+
const workspaceId = 'workspace-snowflake-id';
148+
const mockInviteUrl =
149+
'https://example.com/api/workspace/join?token=abc123';
150+
151+
jest.spyOn(service, 'generateInviteUrl').mockResolvedValue(mockInviteUrl);
152+
153+
const result = await controller.generateInviteLink(req, workspaceId);
154+
155+
expect(service.generateInviteUrl).toHaveBeenCalledWith(
156+
req.user.sub,
157+
workspaceId,
158+
);
159+
expect(result).toEqual({
160+
message: WorkspaceResponseMessage.WORKSPACE_INVITED,
161+
inviteUrl: mockInviteUrl,
162+
});
163+
});
164+
});
165+
166+
describe('joinWorkspace', () => {
167+
it('초대 토큰을 처리하고 성공 메시지를 반환한다.', async () => {
168+
const req = { user: { sub: 1 } };
169+
const token = 'valid-token';
170+
171+
jest.spyOn(service, 'processInviteToken').mockResolvedValue();
172+
173+
const result = await controller.joinWorkspace(req, token);
174+
175+
expect(service.processInviteToken).toHaveBeenCalledWith(
176+
req.user.sub,
177+
token,
178+
);
179+
expect(result).toEqual({
180+
message: WorkspaceResponseMessage.WORKSPACE_INVITED,
181+
});
182+
});
183+
});
139184
});

apps/backend/src/workspace/workspace.controller.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@ import { CreateWorkspaceDto } from './dtos/createWorkspace.dto';
1919
import { CreateWorkspaceResponseDto } from './dtos/createWorkspaceResponse.dto';
2020
import { GetUserWorkspacesResponseDto } from './dtos/getUserWorkspacesResponse.dto';
2121
import { CreateWorkspaceInviteUrlDto } from './dtos/createWorkspaceInviteUrl.dto';
22-
import { GetWorkspaceAccessResponseDto } from './dtos/getWorkspaceAccessResponse.dto';
2322

2423
export enum WorkspaceResponseMessage {
2524
WORKSPACE_CREATED = '워크스페이스를 생성했습니다.',
2625
WORKSPACE_DELETED = '워크스페이스를 삭제했습니다.',
2726
WORKSPACES_RETURNED = '사용자가 참여하고 있는 모든 워크스페이스들을 가져왔습니다.',
2827
WORKSPACE_INVITED = '워크스페이스 게스트 초대 링크가 생성되었습니다.',
2928
WORKSPACE_JOINED = '워크스페이스에 게스트로 등록되었습니다.',
30-
WORKSPACE_ACCESS_CHECKED = '워크스페이스에 대한 사용자의 권한을 확인하였습니다.',
29+
WORKSPACE_ACCESS_CHECKED = '워크스페이스에 대한 사용자의 접근 권한이 확인되었습니다.',
3130
}
3231

3332
@Controller('workspace')
@@ -132,7 +131,7 @@ export class WorkspaceController {
132131
}
133132

134133
@ApiResponse({
135-
type: GetWorkspaceAccessResponseDto,
134+
type: MessageResponseDto,
136135
})
137136
@ApiOperation({
138137
summary: '워크스페이스에 대한 사용자의 권한을 확인합니다.',
@@ -141,14 +140,16 @@ export class WorkspaceController {
141140
@HttpCode(HttpStatus.OK)
142141
async checkWorkspaceAccess(
143142
@Param('workspaceId') workspaceId: string,
144-
@Param('userId') userId: string | 'null', // 로그인되지 않은 경우 'null'
143+
@Param('userId') userId: string, // 로그인되지 않은 경우 'null'
145144
) {
146145
// workspaceId, userId 둘 다 snowflakeId
147-
const access = await this.workspaceService.checkAccess(userId, workspaceId);
146+
// userId 'null'인 경우 => null로 처리
147+
const checkedUserId = userId === 'null' ? null : userId;
148+
149+
await this.workspaceService.checkAccess(checkedUserId, workspaceId);
148150

149151
return {
150152
message: WorkspaceResponseMessage.WORKSPACE_ACCESS_CHECKED,
151-
access,
152153
};
153154
}
154155
}

apps/backend/src/workspace/workspace.service.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Workspace } from './workspace.entity';
99
import { WorkspaceNotFoundException } from '../exception/workspace.exception';
1010
import { NotWorkspaceOwnerException } from '../exception/workspace-auth.exception';
1111
import { TokenService } from '../auth/token/token.service';
12+
import { ForbiddenAccessException } from '../exception/access.exception';
1213

1314
@Injectable()
1415
export class WorkspaceService {
@@ -147,10 +148,7 @@ export class WorkspaceService {
147148
});
148149
}
149150

150-
async checkAccess(
151-
userId: string | null,
152-
workspaceId: string,
153-
): Promise<'public' | 'owner' | 'guest' | 'forbidden'> {
151+
async checkAccess(userId: string | null, workspaceId: string): Promise<void> {
154152
// workspace가 존재하는지 확인
155153
const workspace = await this.workspaceRepository.findOne({
156154
where: { snowflakeId: workspaceId },
@@ -160,28 +158,32 @@ export class WorkspaceService {
160158
throw new WorkspaceNotFoundException();
161159
}
162160

161+
// 퍼블릭 워크스페이스인 경우
163162
if (workspace.visibility === 'public') {
164-
return 'public';
163+
return;
165164
}
166165

166+
// 사용자 인증 필요
167167
if (userId !== null) {
168-
// user이 존재하는지 확인
169168
const user = await this.userRepository.findOneBy({
170169
snowflakeId: userId,
171170
});
172171
if (!user) {
173172
throw new UserNotFoundException();
174173
}
175-
// workspace + user => role 있는지 확인
174+
175+
// workspace와 user에 대한 role 확인
176176
const role = await this.roleRepository.findOne({
177177
where: { userId: user.id, workspaceId: workspace.id },
178178
});
179179

180+
// role이 존재하면 접근 허용
180181
if (role) {
181-
return role.role as 'owner' | 'guest';
182+
return;
182183
}
183184
}
184185

185-
return 'forbidden';
186+
// 권한이 없으면 예외 발생
187+
throw new ForbiddenAccessException();
186188
}
187189
}

0 commit comments

Comments
 (0)