Allow existing NAS photo read group and reject unreadable catalogs

This commit is contained in:
Pigway
2026-10-05 07:59:48 +08:00
parent 011339d511
commit 763d340e4d
5 changed files with 40 additions and 1 deletions
+3
View File
@@ -20,3 +20,6 @@ BUILD_TIME=unknown
# PHOTO_ROOT=/photos
# Optional WebDAV credential reference; prefer a secret file under /config.
# FRAME_FLOW_SOURCE_CLOUD_FILE=/config/webdav-cloud.secret
# Optional existing group with photo read permission; does not change NAS ACLs.
# NAS_PHOTOS_GID=101
+2
View File
@@ -3,6 +3,8 @@ services:
pigway-frame-flow:
environment:
PHOTO_ROOT: /photos
group_add:
- "${NAS_PHOTOS_GID:-10333}"
volumes:
- type: bind
source: ${NAS_PHOTOS_DIR:?Set NAS_PHOTOS_DIR to your existing photo directory}
+8 -1
View File
@@ -10,6 +10,8 @@ Add the actual existing photo directory to `.env`:
```dotenv
NAS_PHOTOS_DIR=/volume2/photo
# Optional: existing NAS group allowed to read that folder (check its ACL).
# NAS_PHOTOS_GID=101
```
The path above is an example, not a detected directory. Enable the optional mount:
@@ -20,7 +22,12 @@ docker compose -f docker-compose.yml -f deploy/docker-compose.photos.yml up -d
```
The folder is mounted **read-only** at `/photos`. The existing container user must
already have read/traverse permission. Frame Flow does not change NAS permissions.
already have read/traverse permission. If the Synology ACL grants a group access,
set `NAS_PHOTOS_GID` to that existing group ID; the optional Compose overlay adds
only container group membership, keeping UID 10333 and the photo bind mount read-only.
Group 101 is a deployment example, not a universal default. Frame Flow does not
change NAS permissions. Unreadable roots return an explicit HTTP 403 rather than
an empty catalog.
Missing host folders fail explicitly rather than being created automatically.
Open **Gallery → Photo sources → Add source**, choose **NAS local folder**,
@@ -157,6 +157,15 @@ class PhotoSources:
"Photo directory is unavailable; check its read-only mount",
404,
)
try:
with os.scandir(directory):
pass
except PermissionError:
raise FlowError(
E.FORBIDDEN,
"Photo directory is not readable; configure the container's existing NAS read group",
403,
) from None
return directory
def headers(self, source):
+18
View File
@@ -204,3 +204,21 @@ def test_scheduled_random_photo_runs_once_with_restore(app, service, photos):
op = service.operation(rows[0]["id"])
assert op["status"] in ("SYNCING", "COMPLETED")
assert service.state("living-room")["previous_image_id"] == original
def test_unreadable_source_is_forbidden_not_empty(
client, photos, settings, monkeypatch
):
import os
original = os.scandir
def restricted(path):
if str(path) == str(settings.photo_root / "family"):
raise PermissionError("permission denied")
return original(path)
monkeypatch.setattr(os, "scandir", restricted)
response = client.get("/api/v1/photo-sources/family/images")
assert response.status_code == 403
assert response.json()["error"]["code"] == "FORBIDDEN"