Day6 - Access Control

RINM·2023년 12월 27일

Relationship

게시글의 소유주를 표시하기 위하여 Board-User 간에 many-to-one relationship을 만들어주자.
User에서는 Board 배열인 boards를 가지고(OneToMany), Board에서는 User를 가진다(ManyToOne).

//User.entity
@Entity() @Unique(['username'])
export class User extends BaseEntity {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    username: string;

    @Column()
    password: string;
    
    @OneToMany(() => Board, board => board.user, {eager:true})
    boards: Board[]
}

//Board.entity
@Entity()
export class Board extends BaseEntity {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;

    @Column()
    description : string;

    @Column()
    status : BoardStatus;

    @ManyToOne(() => User, user => user.boards, {eager:false})
    user: User
}

eager 옵션을 true로 해주면 해당 column이 reference한 테이블의 정보도 다른 컬럼들과 함께 가져온다.
이제 createBoard에서 헤더 안에 들어있는 User 정보를 사용하여 Board와의 관계를 생성한다. 컨트롤러 단에서 만들어둔 @GetUser 데코레이터로 user 정보를 가져와 repository에 정의된 createBoard함수에 넘겨주어 사용한다.

//boards.controller
@Post() //Create new Board
@UsePipes(ValidationPipe) //validate
createBoard(@Body() createBoardDto: CreateBoardDto, @GetUser() user: User) : Promise<Board>{
    return this.boardService.createBoard(createBoardDto,user);
}

//board.repository
async createBoard(createBoardDto: CreateBoardDto, user: User) : Promise <Board>{
    const {title, description} = createBoardDto
    const board = this.create({
        title,
        description,
        status: BoardStatus.PUBLIC,
        user
    })
    await this.save(board)
    return board;
}

로그인하여 토큰을 생성한 뒤 creatBoard 요청을 보내면 아래와 같이 board 객체에 User 정보도 포함된 것을 볼 수 있다.

Access Control

이번엔 getAllBoards에 요청한 user의 board만 가져올 수 있도록 접근 제어를 해보자. 컨트롤러 단에서 user 정보를 받아 board repository의 getAllBoards 매서드에 넘겨준다. 기존의 find 대신 user가 일치하는 board만 가져오기 위하여 querybuilder를 사용한다.

async getAllBoards(user: User) : Promise<Board[]> {
    const query = this.boardRepository.createQueryBuilder('board')
    query.where('board.userId = :userId',{userId: user.id})
    const boards = await query.getMany()
    return boards;
}

createQueryBuilder로 쿼리문을 생성할 수 있다. where 절에 보드의 userID가 주어진 user의 id와 일치하는 조건을 추가한다. getMany()를 통하여 조건에 맞는 모든 row를 가져올 수 있다.

다른 user로 로그인하면 board가 반환되지 않고 생성한 user로 요청하면 board가 제대로 반환된다.

사실 이러한 접근 제어가 필요한 부분은 deleteBoard이다. 자신이 생성한 board만 삭제할 수 있도록 한다.

async deleteBoard(id:number,user: User): Promise<void> {
    const result = await this.boardRepository.delete({id, user:{id: user.id}})
    console.log('[delete]',result);
    if(result.affected==0){
        throw new NotFoundException(`Cannot Find Board Id: ${id}`)
    }
}

다음과 같이 작성자가 아닌 경우 에러메시지가 반환된다.

0개의 댓글