4.4 KiB
Online Lesson Review Note Visibility Fix Report
1. Executive Summary
Fixed the online lesson review note visibility leak by filtering internal workflow fields at the DTO serialization layer and gating the detail UI with a permission-derived capability. General users can still view published lesson content, but they no longer receive or render internal review data such as review comments, reviewer IDs, approval IDs, and workflow timestamps.
2. Problem Found
The online lesson detail page showed the internal review note section when review_comment existed. The same API serializer was used for learner-facing and back-office responses, so public/general users could also receive internal workflow data in network responses.
3. Root Cause
serializeOnlineLesson() always included workflow fields regardless of the current user's permissions. The detail component then rendered review_comment based only on whether the field had a value.
4. Data Flow Before Fix
Database online_lessons.review_comment and workflow actor fields were selected by getBaseSelect(), serialized by serializeOnlineLesson(), returned by /api/online-lessons and /api/online-lessons/[id], cached by TanStack Query, and rendered by OnlineLessonDetailPage.
5. Permission Rule Applied
Internal review data is included only when canReadAllContent('online_lesson', permissions) returns true. This maps to the existing online_lesson:read_all permission and avoids adding new hard-coded role checks.
6. Files Changed
src/features/online-lessons/server/online-lesson-data.ts
- Added
includeInternalReviewDataserialization option. - Public serialization omits internal workflow fields.
- List responses now include internal fields only for users with online lesson read-all permission.
src/app/api/online-lessons/[id]/route.ts
- Detail GET now serializes public or internal DTO based on
canReadAllContent. - Removed an unused local variable flagged by lint.
src/features/online-lessons/api/types.ts
- Marked internal workflow fields as optional because public DTOs intentionally omit them.
src/app/dashboard/online-lessons/[id]/page.tsx
- Computes
canViewInternalReviewDataon the server from existing permissions and passes it to the client detail component.
src/features/online-lessons/components/online-lesson-detail-page.tsx
- Renders the review note section only when
canViewInternalReviewDatais true. - Hides creator metadata from the learner-facing detail header.
7. API Changes
For users without online_lesson:read_all, /api/online-lessons and /api/online-lessons/[id] no longer include these internal fields:
review_commentsubmitted_atsubmitted_byreviewed_atreviewed_byapproved_atapproved_bypublished_bycreated_bycreated_by_nameupdated_byupdated_by_name
Back-office users with online_lesson:read_all continue to receive these fields.
8. UI Changes
The user-facing detail page no longer displays the review note block for general users. Back-office users with the permission-derived capability can still see review notes in the detail view and form/edit workflow.
9. DTO and Type Changes
The shared OnlineLesson contract now treats internal workflow fields as optional so public DTOs can omit them without sending null placeholders or leaking field names.
10. Test Results
npm run lint -- src/features/online-lessons/api/types.ts src/features/online-lessons/server/online-lesson-data.ts src/app/api/online-lessons/[id]/route.ts src/app/dashboard/online-lessons/[id]/page.tsx src/features/online-lessons/components/online-lesson-detail-page.tsxpassed.npx tsc --noEmit --pretty falsepassed.npm run buildpassed.
11. Security Verification
The protection is enforced server-side before JSON serialization, so general users no longer receive internal review fields in API responses. The UI guard is a second layer and uses a capability computed from the same permission rule.
12. Regression Check
Existing workflow mutations still use the default internal serializer for create/update/audit flows. Review, return, reject, approve, publish, and archive behavior was not changed.
13. Remaining Risks
No automated browser test was added for inspecting the exact network payload. Manual verification should still include logging in as a general user, opening a published lesson, and confirming the response does not include review_comment or workflow actor fields.