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);
}
포스트맨으로 테스트 진행하기
This NestJS authorization snippet details implementing IsPostMineOrAdminGuard to control access to post updates. Admins bypass the check, while regular users must own the post. The guard checks user roles and post ownership in posts.service.ts, ensuring only authorized users modify posts, like navigating a challenging Geometry Dash level. Error handling prevents unauthorized access with descriptive messages.
https://geometrydashgame.lol/