patchPost
의 경우 ADMIN 뿐만 아니라 본인인 경우에도 허용이 되도록 만들어야 합니다. 이것을 guard를 사용해서 만들어보겠습니다.
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common";
import { RolesEnum } from "src/users/const/roles.const";
import { PostsService } from "../posts.service";
@Injectable()
export class IsPostMineOrAdminGuard implements CanActivate {
constructor(
private readonly postService: PostsService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest() as Request & {user: UsersModel}; // 인터섹션(user는 존재한다.)
const {user} = req;
if (!user) throw new UnauthorizedException(`사용자 정보를 가져올 수 없습니다. `);
/**
* Admin일 경우 그냥 패스
*/
if (user.role === RolesEnum.ADMIN) return true;
const postId = req.params.postId;
if (!postId) throw new BadRequestException(`Post ID가 파라미터로 제공 돼야합니다. `)
return this.postService.isPostMine(
user.id,
parseInt(postId)
);
}
}
post 서비스에서 해당 post가 나의 것인지 확인하는 코드를 작성하겠습니다.
async isPostMine(userId: number, postId: number) {
return this.postsRepository.exists({
where: {
id: postId,
author: {
id: userId,
}
},
relations: {
author: true,
}
})
}
이제 적용을 하겠습니다.
@Patch(':id')
@UseGuards(IsPostMineOrAdmin)
patchPost(
@Param('id', ParseIntPipe) id: number,
@Body() body: UpdatePostDto,
) {
return this.postsService.updatePost(id, body);
}
{
"message": "Post ID가 파라미터로 제공 돼야합니다. ",
"error": "Bad Request",
"statusCode": 400
}
이유는 컨트롤러에서 값을 받을 때 id
로 받기 때문입니다. 따라서 바꿔줍니다.
@Patch(':postId')
@UseGuards(IsPostMineOrAdmin)
patchPost(
@Param('postId', ParseIntPipe) id: number,
@Body() body: UpdatePostDto,
) {
return this.postsService.updatePost(id, body);
}
{
"id": 91,
"updatedAt": "2024-02-21T22:14:39.664Z",
"createdAt": "2024-01-28T02:12:54.444Z",
"title": "Authorization Check",
"content": "임의로 생성된 포수트 내용 82",
"likeCount": 0,
"commentCount": 0
}
만약 다른 사용자로 PATCH 요청을 보내면 forbidden 에러가 발생합니다. 이 에러 메세지를 작성하겠습니다.
@Injectable()
export class IsPostMineOrAdminGuard implements CanActivate {
constructor(
private readonly postService: PostsService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest() as Request & {user: UsersModel};
const {user} = req;
if (!user) throw new UnauthorizedException(`사용자 정보를 가져올 수 없습니다. `);
if (user.role === RolesEnum.ADMIN) return true;
const postId = req.params.postId;
if (!postId) throw new BadRequestException(`Post ID가 파라미터로 제공 돼야합니다. `)
// 추가
const isOk = await this.postService.isPostMine(
user.id,
parseInt(postId)
);
if (!isOk) throw new ForbiddenException(`권한이 없습니다. `);
return true;
}
}
다른 사용자로 로그인 후에 다시 요청해보겠습니다.
{
"message": "권한이 없습니다. ",
"error": "Forbidden",
"statusCode": 403
}
관리자 계정으로 로그인 후, PATCH요청을 해도 잘 실행되는 것을 알 수 있습니다.
이번에는 comment 또한 ADMIN과 실제 사용자만 가능하도록 만들겠습니다.
@Injectable()
export class IsCommentMineOrAdminGuard implements CanActivate {
constructor(
private readonly commentService: CommentsService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest() as Request & {user: UsersModel};
const {user} = req;
if (!user) throw new UnauthorizedException(`사용자 정보를 가져올 수 없습니다. `);
if (user.role === RolesEnum.ADMIN) return true;
const commentId = req.params.commentId;
if (!commentId) throw new BadRequestException(`Comment ID가 파라미터로 제공 돼야합니다. `)
const isOk = await this.commentService.isCommentMine(
user.id,
parseInt(commentId)
);
if (!isOk) throw new ForbiddenException(`권한이 없습니다. `);
return true;
}
}
async isCommentMine(userId: number, commentId: number) {
return this.commentsRepository.exists({
where: {
id: commentId,
author: {
id: userId,
}
},
relations: {
author: true,
}
})
}
@Patch(':commentId')
@UseGuards(IsCommentMineOrAdminGuard)
async patchComment(
@Param('commentId', ParseIntPipe) commentId: number,
@Body() body: UpdateCommentsDto,
) {
return this.commentsService.updateComment(
body,
commentId
)
}
@Delete(':commentId')
@UseGuards(IsCommentMineOrAdminGuard)
async deleteComment(
@Param('commentId', ParseIntPipe) commentId: number,
) {
return this.commentsService.deleteComment(commentId);
}
포스트맨으로 테스트 진행하기