mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-01 03:56:38 +08:00
fix: enforce user library ACL on mounted Emby libraries (#25)
Mounted Emby libraries were always appended to web/Emby library lists and detail/play routes without checking allowed_library_ids, so restricted users could still see and open them. Filter remotes with the same visibility policy as local libraries across list/detail/series/stream and Emby Views/Items/search/playback, and label mounts in the admin ACL UI. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: truewhile <truewhile@users.noreply.github.com>
This commit is contained in:
@@ -94,18 +94,26 @@ func listLibrariesHandler(svc *service.Container) gin.HandlerFunc {
|
||||
out = append(out, webLibraryPayload{Library: l})
|
||||
}
|
||||
}
|
||||
// 远程 Emby 挂载库追加在本地库之后。
|
||||
// 远程 Emby 挂载库追加在本地库之后(非管理员视图仍受 allowed_library_ids 约束)。
|
||||
if svc.EmbyRemote != nil {
|
||||
if views, err := svc.EmbyRemote.RemoteLibraries(ctx); err == nil {
|
||||
remotePayloads := make([]webLibraryPayload, len(views))
|
||||
for i, v := range views {
|
||||
visibility := mediaVisibilityForRequest(c, svc)
|
||||
allowedViews := make([]service.RemoteLibraryView, 0, len(views))
|
||||
for _, v := range views {
|
||||
if !includeHidden && !service.LibraryVisibleForUser(ctx, svc.Repo, v.Library, visibility) {
|
||||
continue
|
||||
}
|
||||
allowedViews = append(allowedViews, v)
|
||||
}
|
||||
remotePayloads := make([]webLibraryPayload, len(allowedViews))
|
||||
for i, v := range allowedViews {
|
||||
remotePayloads[i] = webLibraryPayload{Library: v.Library, IsRemoteEmby: true, RemoteSource: v.AccountName}
|
||||
}
|
||||
if withPreview && len(views) > 0 {
|
||||
if withPreview && len(allowedViews) > 0 {
|
||||
const maxRemotePreviewWorkers = 6
|
||||
sem := make(chan struct{}, maxRemotePreviewWorkers)
|
||||
var wg sync.WaitGroup
|
||||
for i, v := range views {
|
||||
for i, v := range allowedViews {
|
||||
i, v := i, v
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
@@ -151,6 +159,12 @@ func getLibraryHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
role, _ := c.Get(middleware.CtxUserRole)
|
||||
includeHidden := role == "admin" && (c.Query("include_hidden") == "1" || c.Query("include_hidden") == "true" || c.Query("all") == "1")
|
||||
if !includeHidden && !service.LibraryVisibleForUser(ctx, svc.Repo, view.Library, mediaVisibilityForRequest(c, svc)) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, webLibraryPayload{Library: view.Library, IsRemoteEmby: true, RemoteSource: view.AccountName})
|
||||
return
|
||||
}
|
||||
@@ -330,6 +344,10 @@ func listMediaHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
if !service.EmbyMountLibraryAllowed(mediaVisibilityForRequest(c, svc), mount) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
itemTypes := ""
|
||||
if view, err := svc.EmbyRemote.RemoteLibraryByID(ctx, mountID, remoteID); err == nil && view != nil {
|
||||
itemTypes = remoteLibraryItemTypes(view.CollectionType)
|
||||
@@ -397,6 +415,10 @@ func getMediaHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
if !service.EmbyMountLibraryAllowed(mediaVisibilityForRequest(c, svc), mount) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
m, err := svc.EmbyRemote.RemoteMediaDetail(ctx, mount, acct, remoteID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
@@ -579,6 +601,10 @@ func streamHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
if !service.EmbyMountLibraryAllowed(mediaVisibilityForRequest(c, svc), mount) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
if mount.ProxyPlay {
|
||||
if err := svc.Emby.ProxyRemoteVideoStream(ctx, c.Writer, c.Request, mountID, remoteID); err != nil {
|
||||
if !c.Writer.Written() {
|
||||
|
||||
@@ -74,6 +74,10 @@ func listLibrarySeriesHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
if !service.EmbyMountLibraryAllowed(mediaVisibilityForRequest(c, svc), mount) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
cards, err := svc.EmbyRemote.RemoteSeriesCards(ctx, mount, acct, remoteID)
|
||||
if err != nil {
|
||||
writeInternalOrCanceled(c, err)
|
||||
@@ -165,6 +169,10 @@ func listLibrarySeriesEpisodesHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
if !service.EmbyMountLibraryAllowed(mediaVisibilityForRequest(c, svc), mount) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
items, err := svc.EmbyRemote.RemoteEpisodes(ctx, mount, acct, remoteSeriesID)
|
||||
if err != nil {
|
||||
writeInternalOrCanceled(c, err)
|
||||
@@ -207,6 +215,10 @@ func listMediaEpisodesHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
if !service.EmbyMountLibraryAllowed(mediaVisibilityForRequest(c, svc), mount) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
items, err := svc.EmbyRemote.RemoteEpisodes(ctx, mount, acct, remoteID)
|
||||
if err != nil {
|
||||
writeInternalOrCanceled(c, err)
|
||||
|
||||
Reference in New Issue
Block a user