Multi-tenant: albums owned per photographer
- albums.owner_id (migration 0003, backfilled to the original user)
- owned::{album,photo,share} are the only admin data-access paths; another
tenant's resources are indistinguishable from nonexistent (404)
- every admin handler threaded through ownership; each ALLOWED_EMAILS entry
is now its own isolated workspace
- tenant-isolation integration test matrix (tests/tenancy.rs, env-gated on
TEST_DATABASE_URL) driving the real router
This commit is contained in:
@@ -30,10 +30,13 @@ collect ratings and tags, and let clients download originals.
|
||||
`photos/<photo_id>/preview.jpg`, `photos/<photo_id>/thumb.jpg`. The bucket
|
||||
stays fully private; all image traffic is streamed through the API with
|
||||
auth checks (no bucket CORS or public access needed).
|
||||
- **Auth**: photographer signs in via any OIDC provider (authorization-code
|
||||
- **Auth**: photographers sign in via any OIDC provider (authorization-code
|
||||
flow + userinfo); only emails in `ALLOWED_EMAILS` may sign in, and sessions
|
||||
are re-checked against the allowlist on every request, so removing an email
|
||||
revokes access immediately. Clients use unguessable share tokens, optionally
|
||||
revokes access immediately. **Multi-tenant**: each allowed email is its own
|
||||
workspace — albums, photos, and share links are owned per photographer and
|
||||
invisible to the others (enforced via ownership-scoped data access and
|
||||
covered by the tenant-isolation test matrix). Clients use unguessable share tokens, optionally
|
||||
gated by an argon2-hashed password (10 wrong guesses lock the link for
|
||||
15 minutes).
|
||||
- **Frontend**: React + Vite SPA — justified gallery, lightbox with rating
|
||||
@@ -55,6 +58,13 @@ cargo run --bin worker # job worker (separate terminal, same env)
|
||||
cd frontend && npm install && npm run dev # UI on :5173, proxies /api
|
||||
```
|
||||
|
||||
Tests (the tenant-isolation matrix needs a disposable database):
|
||||
|
||||
```sh
|
||||
createdb photos_test # or: docker compose exec postgres createdb -U photos photos_test
|
||||
TEST_DATABASE_URL=postgres://photos:photos@localhost:5432/photos_test cargo test
|
||||
```
|
||||
|
||||
Register the OIDC client with redirect URI `<PUBLIC_URL>/api/auth/callback`
|
||||
(locally: `http://localhost:5173/api/auth/callback`). Any standard OIDC
|
||||
provider works (Authentik, Keycloak, Zitadel, Dex, ...); the app uses
|
||||
@@ -158,8 +168,6 @@ Notes:
|
||||
- Full RAW develop fallback for files whose embedded preview is tiny
|
||||
(exceedingly rare on modern cameras; `darktable-cli` in the worker image
|
||||
would cover it).
|
||||
- Multiple photographer accounts with separate libraries (any allowed email
|
||||
sees everything).
|
||||
- No S3 orphan sweeper: a crash in the narrow window between an upload's S3
|
||||
put and its DB commit can leave an unreferenced original in the bucket
|
||||
(never data loss — just unclaimed storage).
|
||||
|
||||
Reference in New Issue
Block a user