Skip to content

feat(be): fix unauthorized access to course management pages#3576

Open
Choi-Jung-Hyeon wants to merge 6 commits into
mainfrom
t2716-management-url-access
Open

feat(be): fix unauthorized access to course management pages#3576
Choi-Jung-Hyeon wants to merge 6 commits into
mainfrom
t2716-management-url-access

Conversation

@Choi-Jung-Hyeon
Copy link
Copy Markdown
Contributor

@Choi-Jung-Hyeon Choi-Jung-Hyeon commented May 22, 2026

Description

/admin/course/:courseId/* 경로의 강의 관리 페이지에 권한 없는 유저가 URL 직접 접근할 수 있던 버그를 수정합니다.

문제 상황:
Contest Manager 권한은 있지만 특정 강의의 GroupLeader가 아닌 유저가 /admin/course/2/user 같은 URL에 직접 접근했을 때, 페이지가 그대로 렌더링되었습니다. 서버 사이드 권한 체크 없이 페이지가 먼저 뜨고, 클라이언트에서 데이터 fetch를 하는 단계에서야 "Forbidden resource"라는 맥락 없는 에러가 발생했습니다.

원인:
기존 /admin/course/[courseId]/layout.tsx'use client' Client Component로 선언되어 있어, 서버 사이드에서 사전 권한 확인 및 redirect 처리가 불가능한 구조였습니다.

수정 내용:

  1. Frontend (Server Component 리팩토링 및 최적화)
    • layout.tsx를 Server Component로 전환했습니다.
    • 단일 GraphQL API (getCourse)를 서버 사이드에서 Fetch하여 성능을 최적화했습니다. 백엔드의 GroupLeaderGuard가 검증을 수행하며, 인가되지 않은 유저일 경우 렌더링 전에 즉시 / 경로로 안전하게 redirect 처리합니다. (비로그인, 권한 없는 Manager 모두 차단됨)
    • usePathname() 등 클라이언트 훅 의존성은 새로운 CourseDetailTabs Client Component로 분리했습니다.
    • 기존 헤더 UI (활성 탭 이름, 과목 코드, 강의명)는 서버에서 Fetch한 데이터를 내려주어 그대로 유지됩니다.
  2. Backend (에러 메시지 명확화)
    • GroupLeaderGuard에서 권한 없을 때 return false 대신 명시적인 메시지로 ForbiddenException을 throw하도록 수정했습니다. 기존에는 NestJS 내부 기본값인 "Forbidden resource"가 노출되었으나, 이제 더 명확한 메시지를 반환합니다. HTTP 동작(403)은 기존과 동일합니다.

Additional context

리뷰어분들이 아래 케이스를 확인해주시면 좋을 것 같습니다:

  • 해당 강의의 GroupLeader인 일반 User → 정상 접근 ✅
  • GroupLeader가 아닌 일반 User/로 redirect ✅
  • Admin / SuperAdmin → Guard에 의해 체크 우회되어 정상 접근 ✅

Before submitting the PR, please make sure you do the following

Copilot AI review requested due to automatic review settings May 22, 2026 18:17
@Choi-Jung-Hyeon Choi-Jung-Hyeon changed the title T2716 management url access feat(fe, be) May 22, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the course detail layout into a server component to implement server-side authorization and data fetching, while also extracting the tab navigation into a new client component. In the backend, the GroupLeaderGuard was updated to throw a ForbiddenException for better error handling. Feedback highlights a critical security flaw in the isAdmin logic that could allow unauthenticated guests to bypass access controls, and suggests consolidating redundant API calls to improve performance.

Comment thread apps/frontend/app/admin/course/[courseId]/layout.tsx Outdated
Comment thread apps/frontend/app/admin/course/[courseId]/layout.tsx Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an authorization hole where users who are not the target course’s GroupLeader could directly access /admin/course/:courseId/* pages via URL and only fail later during GraphQL fetch.

Changes:

  • Converted /admin/course/[courseId]/layout.tsx to a Server Component and added a server-side permission check (via GET course/joined) with redirect before render.
  • Extracted tab/header UI into a new client component (CourseDetailTabs) to remove client-hook dependencies from the server layout.
  • Updated backend GroupLeaderGuard to throw a ForbiddenException with a clearer message instead of returning false (still 403).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
apps/frontend/app/admin/course/[courseId]/layout.tsx Server-side pre-render authorization check + server-side course info fetch, rendering tabs via client component
apps/frontend/app/admin/course/[courseId]/_components/CourseDetailTabs.tsx New client component encapsulating pathname-based active tab + header UI
apps/backend/libs/auth/src/roles/group-leader.guard.ts Improves forbidden response messaging by throwing ForbiddenException explicitly

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/frontend/app/admin/course/[courseId]/layout.tsx Outdated
@Choi-Jung-Hyeon Choi-Jung-Hyeon changed the title feat(fe, be) feat(be): fix unauthorized access to course management pages May 22, 2026
@Choi-Jung-Hyeon Choi-Jung-Hyeon self-assigned this May 22, 2026
@Choi-Jung-Hyeon Choi-Jung-Hyeon added the 🪰 bug Something isn't working label May 22, 2026
@github-project-automation github-project-automation Bot moved this to Pending ✋ in Codedang May 22, 2026
@Choi-Jung-Hyeon Choi-Jung-Hyeon linked an issue May 22, 2026 that may be closed by this pull request
27 tasks
@RyuRaseul
Copy link
Copy Markdown
Contributor

프론트한테 리뷰 부탁해야할듯?

@RyuRaseul
Copy link
Copy Markdown
Contributor

컨플릭 해결해주시고

@Choi-Jung-Hyeon Choi-Jung-Hyeon requested a review from egg-zz May 25, 2026 11:33
Copy link
Copy Markdown
Contributor

@zero1177 zero1177 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

백엔드는 이상없고 프론트 리뷰만 받으면 될것 같습니다~

@egg-zz egg-zz added the preview 이 라벨이 붙어있어야 프론트엔드 Preview 환경이 생성됩니다 label May 25, 2026
@skkuding-bot
Copy link
Copy Markdown

skkuding-bot Bot commented May 25, 2026

Syncing Preview App Succeeded

Application: frontend
Revision: 919121ede287dd15a1767049aecabf384aed92d2
Health Status: Healthy

Open Preview | View in Argo CD

Copy link
Copy Markdown
Contributor

@egg-zz egg-zz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프론트 측 코드도 문제 없습니다~
layout.tsx 파일 충돌은 qna management pr이랑 겹쳐서 생기는 것 같은데 확인 한 번 부탁드려요!
layout.tsx 파일 본문에 query 직접 적어 놓으신 것은 GET_COURSE 불러오는 것으로 수정 부탁드려요!

@github-project-automation github-project-automation Bot moved this from Pending ✋ to Approved 👌 in Codedang May 25, 2026
@skkuding-bot
Copy link
Copy Markdown

skkuding-bot Bot commented May 25, 2026

Syncing Preview App Failed

Application: frontend
Revision: 3bfd58243920dae11a598267aadaf430d3f87bef
Health Status: Degraded

Open Preview | View in Argo CD

@skkuding-bot
Copy link
Copy Markdown

skkuding-bot Bot commented May 25, 2026

Syncing Preview App Failed

Application: frontend
Revision: d936f3bcb722bb3707ef9dcdddcb0761735b16ba
Health Status: Degraded

Open Preview | View in Argo CD

@skkuding-bot
Copy link
Copy Markdown

skkuding-bot Bot commented May 25, 2026

Syncing Preview App Failed

Application: frontend
Revision: t2716-management-url-access
Health Status: Healthy

Open Preview | View in Argo CD

}
}
}
`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 get_course_query 는 지워주세요!

Authorization: session.token.accessToken
},
body: JSON.stringify({
query: GET_COURSE_QUERY,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 그냥 GET_COURSE로 수정 부탁드립니다..!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🪰 bug Something isn't working preview 이 라벨이 붙어있어야 프론트엔드 Preview 환경이 생성됩니다

Projects

Status: Approved 👌

Development

Successfully merging this pull request may close these issues.

[Bug]: 26-1 백엔드 버그픽스

6 participants