feat(sidecar): enforce per-user BasePath on all filesystem mutations
Folder create/rename/delete/move, photo move, heap convert, and file rename now reject paths outside the caller's BasePath (403). Sources resolved via PhotoPrism UIDs are re-checked in movePhotoFiles. The USER_BASEPATHS reconciler also sets upload_path so client-app uploads land inside the user's subtree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -90,6 +90,9 @@ func handleHeapConvert(cfg *Config, pp *ppClient) gin.HandlerFunc {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid targetFolder"})
|
||||
return
|
||||
}
|
||||
if !requireUserScope(c, cfg, targetAbs, false) {
|
||||
return
|
||||
}
|
||||
// Pull the heap's membership via the q=album:UID query (count=1000
|
||||
// covers every realistic heap). We only need the UID list here — the
|
||||
// search's Files array is trimmed and drops videos, so we re-resolve
|
||||
@@ -124,7 +127,7 @@ func handleHeapConvert(cfg *Config, pp *ppClient) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
moved, copied, errs, err := movePhotoFiles(cfg, pp, token, photos, targetAbs, subfolder, mode)
|
||||
moved, copied, errs, err := movePhotoFiles(cfg, pp, token, photos, targetAbs, subfolder, mode, userScopeRoot(c, cfg))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
@@ -167,7 +170,10 @@ func handleHeapConvert(cfg *Config, pp *ppClient) gin.HandlerFunc {
|
||||
// and handlePhotosMove (UID-list scoped); both resolve `photos` differently
|
||||
// but move them identically. Returns per-photo errors in `errs`; the returned
|
||||
// top-level error is only for a fatal precondition (subfolder mkdir failed).
|
||||
func movePhotoFiles(cfg *Config, pp *ppClient, token string, photos []heapPhoto, targetAbs, subfolder, mode string) (moved, copied int, errs []heapErr, err error) {
|
||||
// `scopeAbs` is the caller's userScopeRoot — source files outside it fail
|
||||
// per-photo, so a UID that resolves outside the user's BasePath (however
|
||||
// PhotoPrism came to return it) can't be used to pull files across users.
|
||||
func movePhotoFiles(cfg *Config, pp *ppClient, token string, photos []heapPhoto, targetAbs, subfolder, mode, scopeAbs string) (moved, copied int, errs []heapErr, err error) {
|
||||
destAbs := targetAbs
|
||||
if subfolder != "" {
|
||||
destAbs = filepath.Join(targetAbs, subfolder)
|
||||
@@ -234,8 +240,8 @@ func movePhotoFiles(cfg *Config, pp *ppClient, token string, photos []heapPhoto,
|
||||
for _, f := range group {
|
||||
srcRel := f.Name
|
||||
srcAbs := filepath.Join(cfg.OriginalsRoot, srcRel)
|
||||
if !sameOrUnder(srcAbs, cfg.OriginalsRoot) {
|
||||
failure = "path escapes originals"
|
||||
if !sameOrUnder(srcAbs, scopeAbs) {
|
||||
failure = "path outside your library"
|
||||
break
|
||||
}
|
||||
st, statErr := os.Stat(srcAbs)
|
||||
|
||||
Reference in New Issue
Block a user