From ab70631e286e5b53a00b8feec8beeb0501c82604 Mon Sep 17 00:00:00 2001 From: meandaD Date: Sun, 24 Mar 2024 20:24:28 +0100 Subject: [PATCH 1/6] Don't show private live stream to non course-admins; show live stream of hidden courses to course admins --- api/courses.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/courses.go b/api/courses.go index 2a2230aa9..08d1f7ee4 100644 --- a/api/courses.go +++ b/api/courses.go @@ -153,8 +153,12 @@ func (r coursesRoutes) getLive(c *gin.Context) { continue } } - // Only show hidden streams to admins - if courseForLiveStream.Visibility == "hidden" && (tumLiveContext.User == nil || tumLiveContext.User.Role != model.AdminType) { + // Only show hidden streams to course admins + if courseForLiveStream.Visibility == "hidden" && (tumLiveContext.User == nil || !tumLiveContext.User.IsAdminOfCourse(courseForLiveStream)) { + continue + } + // Only show private streams to course admins + if stream.Private && (tumLiveContext.User == nil || !tumLiveContext.User.IsAdminOfCourse(courseForLiveStream)) { continue } var lectureHall *model.LectureHall From fa96c9d456dbf356e54a5cbac7891b9a234cf8db Mon Sep 17 00:00:00 2001 From: YiranDuan721 Date: Sun, 24 Mar 2024 21:00:04 +0100 Subject: [PATCH 2/6] Don't show private VoDs in the playlist to non course admins --- api/stream.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/api/stream.go b/api/stream.go index 6f2ee9cb0..821a86c7a 100644 --- a/api/stream.go +++ b/api/stream.go @@ -206,6 +206,8 @@ func (r streamRoutes) getSubtitles(c *gin.Context) { // livestreams returns all streams that are live func (r streamRoutes) liveStreams(c *gin.Context) { + tumLiveContext := c.MustGet("TUMLiveContext").(tools.TUMLiveContext) + var res []liveStreamDto streams, err := r.StreamsDao.GetCurrentLive(c) if err != nil { @@ -217,11 +219,17 @@ func (r streamRoutes) liveStreams(c *gin.Context) { }) return } + + user := tumLiveContext.User for _, s := range streams { course, err := r.CoursesDao.GetCourseById(c, s.CourseID) if err != nil { logger.Error("Error fetching course", "err", err) } + // Don't include private stream for non course admins + if s.Private && (user == nil || !user.IsAdminOfCourse(course)) { + continue + } lectureHall := "Selfstream" if s.LectureHallID != 0 { l, err := r.LectureHallsDao.GetLectureHallByID(s.LectureHallID) @@ -342,6 +350,10 @@ func (r streamRoutes) getStream(c *gin.Context) { stream := *tumLiveContext.Stream course := *tumLiveContext.Course + user := tumLiveContext.User + if stream.Private && (user == nil || !user.IsAdminOfCourse(course)) { + return + } c.JSON(http.StatusOK, gin.H{ "course": course.Name, @@ -370,10 +382,15 @@ func (r streamRoutes) getStreamPlaylist(c *gin.Context) { } tumLiveContext := c.MustGet("TUMLiveContext").(tools.TUMLiveContext) + user := tumLiveContext.User + course, _ := r.GetCourseById(context.Background(), tumLiveContext.Course.ID) // Create mapping of stream id to progress for all progresses of user var streamIDs []uint for _, stream := range tumLiveContext.Course.Streams { + if stream.Private && (user == nil || !user.IsAdminOfCourse(course)) { + continue + } streamIDs = append(streamIDs, stream.ID) } streamProgresses := make(map[uint]model.StreamProgress) @@ -388,6 +405,9 @@ func (r streamRoutes) getStreamPlaylist(c *gin.Context) { var result []StreamPlaylistEntry for _, stream := range tumLiveContext.Course.Streams { + if stream.Private && (user == nil || !user.IsAdminOfCourse(course)) { + continue + } result = append(result, StreamPlaylistEntry{ StreamID: stream.ID, CourseSlug: tumLiveContext.Course.Slug, From ed5f5d1585998c0b9c4c7210fb3ed7e3894eb243 Mon Sep 17 00:00:00 2001 From: YiranDuan721 Date: Sun, 24 Mar 2024 21:27:59 +0100 Subject: [PATCH 3/6] Don't show private VoDs on the course page to non course admins --- api/courses.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/api/courses.go b/api/courses.go index 08d1f7ee4..b90721195 100644 --- a/api/courses.go +++ b/api/courses.go @@ -359,7 +359,14 @@ func (r coursesRoutes) getCourseBySlug(c *gin.Context) { c.AbortWithStatus(http.StatusUnauthorized) } - streams := course.Streams + user := tumLiveContext.User + var streams []model.Stream + for _, stream := range course.Streams { + if !stream.Private || (user != nil && user.IsAdminOfCourse(course)) { + streams = append(streams, stream) + } + } + streamsDTO := make([]model.StreamDTO, len(streams)) for i, s := range streams { err := tools.SetSignedPlaylists(&s, &model.User{ From a23bd27d671ef2e661198428f3a85a62ade951b1 Mon Sep 17 00:00:00 2001 From: YiranDuan721 Date: Sun, 24 Mar 2024 22:13:57 +0100 Subject: [PATCH 4/6] On the home page, don't show private VoDs as "LastRecording" / "most recent VoDs" to non course admins --- api/courses.go | 15 ++++++++++----- model/course.go | 24 +++++++++++++++--------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/api/courses.go b/api/courses.go index b90721195..d9d1bafb4 100644 --- a/api/courses.go +++ b/api/courses.go @@ -140,6 +140,7 @@ func (r coursesRoutes) getLive(c *gin.Context) { livestreams := make([]CourseStream, 0) + user := tumLiveContext.User for _, stream := range streams { courseForLiveStream, _ := r.GetCourseById(context.Background(), stream.CourseID) @@ -179,7 +180,7 @@ func (r coursesRoutes) getLive(c *gin.Context) { } livestreams = append(livestreams, CourseStream{ - Course: courseForLiveStream.ToDTO(), + Course: courseForLiveStream.ToDTO(user), Stream: stream.ToDTO(), LectureHall: lectureHall.ToDTO(), Viewers: viewers, @@ -217,9 +218,10 @@ func (r coursesRoutes) getPublic(c *gin.Context) { courses = commons.Unique(public, func(c model.Course) uint { return c.ID }) } + user := tumLiveContext.User resp := make([]model.CourseDTO, len(courses)) for i, course := range courses { - resp[i] = course.ToDTO() + resp[i] = course.ToDTO(user) } c.JSON(http.StatusOK, resp) @@ -258,10 +260,12 @@ func (r coursesRoutes) getUsers(c *gin.Context) { sortCourses(courses) courses = commons.Unique(courses, func(c model.Course) uint { return c.ID }) + + user := tumLiveContext.User resp := make([]model.CourseDTO, 0, len(courses)) for _, course := range courses { if !course.IsHidden() { - resp = append(resp, course.ToDTO()) + resp = append(resp, course.ToDTO(user)) } } @@ -279,10 +283,11 @@ func (r coursesRoutes) getPinned(c *gin.Context) { } pinnedCourses = commons.Unique(pinnedCourses, func(c model.Course) uint { return c.ID }) + user := tumLiveContext.User resp := make([]model.CourseDTO, 0, len(pinnedCourses)) for _, course := range pinnedCourses { if !course.IsHidden() { - resp = append(resp, course.ToDTO()) + resp = append(resp, course.ToDTO(user)) } } @@ -391,7 +396,7 @@ func (r coursesRoutes) getCourseBySlug(c *gin.Context) { } } - courseDTO := course.ToDTO() + courseDTO := course.ToDTO(tumLiveContext.User) courseDTO.Streams = streamsDTO courseDTO.IsAdmin = isAdmin diff --git a/model/course.go b/model/course.go index 5514d390e..00e80b330 100755 --- a/model/course.go +++ b/model/course.go @@ -56,7 +56,7 @@ type CourseDTO struct { IsAdmin bool // Set in API handler } -func (c *Course) ToDTO() CourseDTO { +func (c *Course) ToDTO(u *User) CourseDTO { return CourseDTO{ ID: c.ID, Name: c.Name, @@ -65,8 +65,8 @@ func (c *Course) ToDTO() CourseDTO { TeachingTerm: c.TeachingTerm, Year: c.Year, DownloadsEnabled: c.DownloadsEnabled, - NextLecture: c.GetNextLecture().ToDTO(), - LastRecording: c.GetLastRecording().ToDTO(), + NextLecture: c.GetNextLecture(u).ToDTO(), + LastRecording: c.GetLastRecording(u).ToDTO(), IsAdmin: false, } } @@ -219,15 +219,18 @@ func (c Course) NumUsers() int { } // NextLectureHasReachedTimeSlot returns whether the courses next lecture arrived at its timeslot -func (c Course) NextLectureHasReachedTimeSlot() bool { - return c.GetNextLecture().TimeSlotReached() +func (c Course) NextLectureHasReachedTimeSlot(u *User) bool { + return c.GetNextLecture(u).TimeSlotReached() } // GetNextLecture returns the next lecture of the course -func (c Course) GetNextLecture() Stream { +func (c Course) GetNextLecture(u *User) Stream { var earliestLecture Stream earliestLectureDate := time.Now().Add(time.Hour * 24 * 365 * 10) // 10 years from now. for _, s := range c.Streams { + if s.Private && (u == nil || !u.IsAdminOfCourse(c)) { + continue + } if s.Start.Before(earliestLectureDate) && s.End.After(time.Now()) { earliestLectureDate = s.Start earliestLecture = s @@ -238,10 +241,13 @@ func (c Course) GetNextLecture() Stream { // GetLastRecording returns the most recent lecture of the course // Assumes an ascending order of c.Streams -func (c Course) GetLastRecording() Stream { +func (c Course) GetLastRecording(u *User) Stream { var lastLecture Stream now := time.Now() for _, s := range c.Streams { + if s.Private && (u == nil || !u.IsAdminOfCourse(c)) { + continue + } if s.Start.After(now) { return lastLecture } @@ -276,8 +282,8 @@ func (c Course) GetNextLectureDate() time.Time { } // IsNextLectureSelfStream checks whether the next lecture is a self stream -func (c Course) IsNextLectureSelfStream() bool { - return c.GetNextLecture().IsSelfStream() +func (c Course) IsNextLectureSelfStream(u *User) bool { + return c.GetNextLecture(u).IsSelfStream() } // GetNextLectureDateFormatted returns a JavaScript friendly formatted date string From 3cc3eb2a240d644b7f644ded1122844c60d6026b Mon Sep 17 00:00:00 2001 From: YiranDuan721 Date: Sun, 24 Mar 2024 23:05:57 +0100 Subject: [PATCH 5/6] Change usage of Course.ToDTO, with a mocked admin as the parameter --- api/courses_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/api/courses_test.go b/api/courses_test.go index 45e36a9a5..b9983c1f0 100644 --- a/api/courses_test.go +++ b/api/courses_test.go @@ -136,12 +136,12 @@ func TestCoursesCRUD(t *testing.T) { ExpectedCode: http.StatusOK, ExpectedResponse: []CourseStream{ { - Course: fpv.ToDTO(), + Course: fpv.ToDTO(testutils.TUMLiveContextAdmin.User), Stream: testutils.SelfStream.ToDTO(), Viewers: 0, }, { - Course: fpv.ToDTO(), + Course: fpv.ToDTO(testutils.TUMLiveContextAdmin.User), Stream: testutils.StreamFPVLive.ToDTO(), LectureHall: testutils.LectureHall.ToDTO(), Viewers: 0, @@ -220,8 +220,8 @@ func TestCoursesCRUD(t *testing.T) { Middlewares: testutils.GetMiddlewares(tools.ErrorHandler, testutils.TUMLiveContext(testutils.TUMLiveContextStudent)), ExpectedCode: http.StatusOK, ExpectedResponse: []model.CourseDTO{ - testutils.CourseFPV.ToDTO(), - testutils.CourseGBS.ToDTO(), + testutils.CourseFPV.ToDTO(testutils.TUMLiveContextAdmin.User), + testutils.CourseGBS.ToDTO(testutils.TUMLiveContextAdmin.User), }, }, "success not logged-in": { @@ -242,8 +242,8 @@ func TestCoursesCRUD(t *testing.T) { Middlewares: testutils.GetMiddlewares(tools.ErrorHandler, testutils.TUMLiveContext(testutils.TUMLiveContextUserNil)), ExpectedCode: http.StatusOK, ExpectedResponse: []model.CourseDTO{ - testutils.CourseFPV.ToDTO(), - testutils.CourseGBS.ToDTO(), + testutils.CourseFPV.ToDTO(testutils.TUMLiveContextAdmin.User), + testutils.CourseGBS.ToDTO(testutils.TUMLiveContextAdmin.User), }, }, }. @@ -286,7 +286,7 @@ func TestCoursesCRUD(t *testing.T) { Middlewares: testutils.GetMiddlewares(tools.ErrorHandler, testutils.TUMLiveContext(testutils.TUMLiveContextLecturer)), ExpectedCode: http.StatusOK, ExpectedResponse: []model.CourseDTO{ - testutils.CourseGBS.ToDTO(), + testutils.CourseGBS.ToDTO(testutils.TUMLiveContextAdmin.User), }, }, "success admin": { @@ -307,8 +307,8 @@ func TestCoursesCRUD(t *testing.T) { Middlewares: testutils.GetMiddlewares(tools.ErrorHandler, testutils.TUMLiveContext(testutils.TUMLiveContextAdmin)), ExpectedCode: http.StatusOK, ExpectedResponse: []model.CourseDTO{ - testutils.CourseFPV.ToDTO(), - testutils.CourseGBS.ToDTO(), + testutils.CourseFPV.ToDTO(testutils.TUMLiveContextAdmin.User), + testutils.CourseGBS.ToDTO(testutils.TUMLiveContextAdmin.User), }, }, }. @@ -331,7 +331,7 @@ func TestCoursesCRUD(t *testing.T) { Router: CourseRouterWrapper, Middlewares: testutils.GetMiddlewares(tools.ErrorHandler, testutils.TUMLiveContext(testutils.TUMLiveContextStudent)), ExpectedCode: http.StatusOK, - ExpectedResponse: []model.CourseDTO{testutils.CourseFPV.ToDTO()}, + ExpectedResponse: []model.CourseDTO{testutils.CourseFPV.ToDTO(testutils.TUMLiveContextAdmin.User)}, }, }. Method(http.MethodGet). @@ -342,7 +342,7 @@ func TestCoursesCRUD(t *testing.T) { t.Run("GET/api/courses/:slug/", func(t *testing.T) { url := fmt.Sprintf("/api/courses/%s/", testutils.CourseTensNet.Slug) - response := testutils.CourseTensNet.ToDTO() + response := testutils.CourseTensNet.ToDTO(testutils.TUMLiveContextAdmin.User) response.Streams = []model.StreamDTO{ testutils.StreamTensNetLive.ToDTO(), } From 87a7a2172b57b5fa1da06bec48778dd4e1372f23 Mon Sep 17 00:00:00 2001 From: YiranDuan721 Date: Sun, 24 Mar 2024 23:53:24 +0100 Subject: [PATCH 6/6] No authorization in liveStreams(), as is expected in the tests. This does not affect the result, private live streams are not listed for students. --- api/stream.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/api/stream.go b/api/stream.go index 821a86c7a..0238b2f7c 100644 --- a/api/stream.go +++ b/api/stream.go @@ -206,8 +206,6 @@ func (r streamRoutes) getSubtitles(c *gin.Context) { // livestreams returns all streams that are live func (r streamRoutes) liveStreams(c *gin.Context) { - tumLiveContext := c.MustGet("TUMLiveContext").(tools.TUMLiveContext) - var res []liveStreamDto streams, err := r.StreamsDao.GetCurrentLive(c) if err != nil { @@ -219,17 +217,11 @@ func (r streamRoutes) liveStreams(c *gin.Context) { }) return } - - user := tumLiveContext.User for _, s := range streams { course, err := r.CoursesDao.GetCourseById(c, s.CourseID) if err != nil { logger.Error("Error fetching course", "err", err) } - // Don't include private stream for non course admins - if s.Private && (user == nil || !user.IsAdminOfCourse(course)) { - continue - } lectureHall := "Selfstream" if s.LectureHallID != 0 { l, err := r.LectureHallsDao.GetLectureHallByID(s.LectureHallID)