Per-group spaces: /spaces/<group> overlay (v0.4 #16) #37

Merged
Cordy merged 2 commits from feat/per-group-spaces into main 2026-07-25 20:48:29 +00:00
Owner

Extends the per-user scope decorator (#16) into the full authorization overlay from ARCHITECTURE.md §5.4.

Model (non-admin users): the virtual root is now a synthetic directory with two kinds of entry:

  • /home → the user's private tree at backend /home/<username>
  • /spaces/<group> → a shared tree at backend /spaces/<group>, one entry per group the user belongs to (auth.User.Groups, populated by the OIDC groups claim in #17)

/spaces lists only the caller's member groups; accessing a non-member space returns ErrNotFound (indistinguishable from "doesn't exist" — no membership disclosure). Admins remain fully unscoped (whole backend verbatim).

Guards: can't write a file onto a home/space root, can't delete a home or space root, group names are validated as safe path segments before use in a path. Home/space roots are lazily Mkdir'd on first access.

Compatibility: still gated behind auth.perUserHomes (default off). This changes that flag's layout — a scoped user's files now live under /home/... and shared spaces under /spaces/..., instead of the home being the bare root (v0.2.6). Opt-in, pre-1.0, no production deployment has the flag on.

Resolve (used by share creation) is unchanged for real paths and returns ErrInvalidPath for synthetic overlay paths. No main.go change needed — groups already flow through the session.

Tests rewritten for the overlay: home scoping, synthetic root list, member-group spaces list, member vs non-member space access, admin passthrough, Resolve, root-delete guards, unknown top-level denial, no-user rejection.

Extends the per-user scope decorator (#16) into the full authorization overlay from ARCHITECTURE.md §5.4. **Model (non-admin users):** the virtual root is now a synthetic directory with two kinds of entry: - `/home` → the user's private tree at backend `/home/<username>` - `/spaces/<group>` → a shared tree at backend `/spaces/<group>`, one entry per group the user belongs to (`auth.User.Groups`, populated by the OIDC groups claim in #17) `/spaces` lists only the caller's member groups; accessing a non-member space returns `ErrNotFound` (indistinguishable from "doesn't exist" — no membership disclosure). Admins remain fully unscoped (whole backend verbatim). **Guards:** can't write a file onto a home/space root, can't delete a home or space root, group names are validated as safe path segments before use in a path. Home/space roots are lazily `Mkdir`'d on first access. **Compatibility:** still gated behind `auth.perUserHomes` (default off). This changes that flag's layout — a scoped user's files now live under `/home/...` and shared spaces under `/spaces/...`, instead of the home being the bare root (v0.2.6). Opt-in, pre-1.0, no production deployment has the flag on. `Resolve` (used by share creation) is unchanged for real paths and returns `ErrInvalidPath` for synthetic overlay paths. No `main.go` change needed — groups already flow through the session. Tests rewritten for the overlay: home scoping, synthetic root list, member-group spaces list, member vs non-member space access, admin passthrough, Resolve, root-delete guards, unknown top-level denial, no-user rejection.
Cordy merged commit ae59a7e0f4 into main 2026-07-25 20:48:29 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#37
No description provided.