refactor(vis): H4.5 steps 4-5 — statistics and the three reader gates on the canonical resolver; core goes package-level
VisibleLibrarySet becomes a package-level function taking the queries (db-only services like statistics reach it without new wiring); the LibraryService method stays as the ergonomic entry point. Statistics' ids-shaper and the reader pages' three hand-rolled membership loops (empty-rows fallback then linear scans) now delegate; the gates keep their 403-on-invisible semantics (>null set membership, never silent pass). Census addendum from this pass: two OPDS sites call GetUserVisibleLibraries directly but consume FULL ROWS (per-library folder enumeration) — a third legitimate pattern alongside set/ids and resolveLibrary's active-selection; documented, untouched. The library LIST endpoint and its SSR data helper are row consumers too, by design.
This commit is contained in:
1 parent
8dbaa8f810
commit
454938c31f
3 files changed
+27
-39
No files matched your search
+14
-25
@@ -78,19 +78,14 @@ func (h *ReaderHandler) ShowReader(c *echo.Context) error {
|
||||
}
|
||||
userData := user.(database.Users)
|
||||
|
||||
// Get user's visible libraries
|
||||
libraries, err := h.libraryService.GetUserVisibleLibraries(c.Request().Context(), userData.ID)
|
||||
// Family-lens gate (H4.5 canonical resolver): the media item's
|
||||
// library must be in the requester's visible set — 403, never a
|
||||
// silent pass.
|
||||
visibleSet, _, err := h.libraryService.VisibleLibrarySet(c.Request().Context(), userData.ID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to check library access"})
|
||||
}
|
||||
// Check if media item's library is in visible list
|
||||
visible := false
|
||||
for _, lib := range libraries {
|
||||
if lib.ID == mediaItem.LibraryID {
|
||||
visible = true
|
||||
break
|
||||
}
|
||||
}
|
||||
visible := visibleSet[uuid.UUID(mediaItem.LibraryID.Bytes).String()]
|
||||
if !visible {
|
||||
return c.JSON(http.StatusForbidden, map[string]string{"error": "Access denied to this library"})
|
||||
}
|
||||
@@ -172,19 +167,14 @@ func (h *ReaderHandler) GetPage(c *echo.Context) error {
|
||||
}
|
||||
userData := user.(database.Users)
|
||||
|
||||
// Get user's visible libraries
|
||||
libraries, err := h.libraryService.GetUserVisibleLibraries(c.Request().Context(), userData.ID)
|
||||
// Family-lens gate (H4.5 canonical resolver): the media item's
|
||||
// library must be in the requester's visible set — 403, never a
|
||||
// silent pass.
|
||||
visibleSet, _, err := h.libraryService.VisibleLibrarySet(c.Request().Context(), userData.ID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to check library access"})
|
||||
}
|
||||
// Check if media item's library is in visible list
|
||||
visible := false
|
||||
for _, lib := range libraries {
|
||||
if lib.ID == mediaItem.LibraryID {
|
||||
visible = true
|
||||
break
|
||||
}
|
||||
}
|
||||
visible := visibleSet[uuid.UUID(mediaItem.LibraryID.Bytes).String()]
|
||||
if !visible {
|
||||
return c.JSON(http.StatusForbidden, map[string]string{"error": "Access denied to this library"})
|
||||
}
|
||||
@@ -225,14 +215,13 @@ func (h *ReaderHandler) ensureItemVisible(c *echo.Context, mediaItem *database.M
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "User not authenticated"})
|
||||
}
|
||||
userData := user.(database.Users)
|
||||
libraries, err := h.libraryService.GetUserVisibleLibraries(c.Request().Context(), userData.ID)
|
||||
// Family-lens gate (H4.5 canonical resolver).
|
||||
visibleSet, _, err := h.libraryService.VisibleLibrarySet(c.Request().Context(), userData.ID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to check library access"})
|
||||
}
|
||||
for _, lib := range libraries {
|
||||
if lib.ID == mediaItem.LibraryID {
|
||||
return nil
|
||||
}
|
||||
if visibleSet[uuid.UUID(mediaItem.LibraryID.Bytes).String()] {
|
||||
return nil
|
||||
}
|
||||
return c.JSON(http.StatusForbidden, map[string]string{"error": "Access denied to this library"})
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user