Honor per-project grants on private projects (M-14) #117

Merged
qwc merged 1 commit from fix/private-honors-per-project-grant into main 2026-05-19 07:27:00 +02:00
Owner

Summary

  • Service.Create already auto-grants a non-admin creator editor access on every non-public project (internal/projects/service.go:105-116). On private (the default visibility), that grant was dead weight: CanView and FilterAccessible only consulted GlobalAccess.
  • An editor without an org-wide grant who created a project would: not see it on the frontpage or admin list (filtered out), 403 on direct navigation, and still be able to upload (CanUpload short-circuits on global role editor). Confusing as hell.
  • Fix: CanView for private now also accepts a non-empty ProjectAccess.GetEffectiveRole; FilterAccessible for private includes the project when the user has a global grant or is in the per-project access map.
  • custom semantics are unchanged — no global-access fast path, so it stays strictly more restrictive than private. Per-project grants on private are additive (useful for letting one outside user into one project without org-wide access).
  • AUDIT.md gains entry M-14 for the finding.
  • Built-in docs (roles-permissions.md, first-project.md) updated to describe the new model.

Test plan

  • internal/access/checker_test.go: the pre-#95 pin in TestCanView is flipped; TestFilterAccessible gets a new private+per-project case.
  • go test ./... — full suite green.
  • go build — clean.

🤖 Generated with Claude Code

## Summary - `Service.Create` already auto-grants a non-admin creator editor access on every non-public project (`internal/projects/service.go:105-116`). On `private` (the default visibility), that grant was dead weight: `CanView` and `FilterAccessible` only consulted `GlobalAccess`. - An editor without an org-wide grant who created a project would: not see it on the frontpage or admin list (filtered out), 403 on direct navigation, and still be able to upload (CanUpload short-circuits on global role editor). Confusing as hell. - Fix: `CanView` for `private` now also accepts a non-empty `ProjectAccess.GetEffectiveRole`; `FilterAccessible` for `private` includes the project when the user has a global grant **or** is in the per-project access map. - `custom` semantics are unchanged — no global-access fast path, so it stays strictly more restrictive than `private`. Per-project grants on private are additive (useful for letting one outside user into one project without org-wide access). - AUDIT.md gains entry **M-14** for the finding. - Built-in docs (`roles-permissions.md`, `first-project.md`) updated to describe the new model. ## Test plan - [x] `internal/access/checker_test.go`: the pre-#95 pin in `TestCanView` is flipped; `TestFilterAccessible` gets a new private+per-project case. - [x] `go test ./...` — full suite green. - [x] `go build` — clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Honor per-project grants on private projects (M-14)
All checks were successful
CI / test (pull_request) Successful in 1m17s
CI / build (pull_request) Successful in 53s
CI / docker (pull_request) Has been skipped
0bc50bf85c
Service.Create auto-grants a non-admin creator editor access on every
non-public project they create, but CanView/FilterAccessible for `private`
only consulted GlobalAccess — so the grant was dead weight on the default
visibility. An editor without an org-wide global grant could create a
private project, get redirected to /admin/projects, and find their fresh
project missing from the list and 403-ing on direct navigation, while
upload still worked (CanUpload short-circuits on global role editor).

CanView and FilterAccessible now treat `private` as "global grant OR
per-project grant." `custom` is unchanged — still no global-access path,
making it strictly more restrictive than `private`. Per-project grants on
private projects are additive: useful for letting a single outside user
into one project without putting them on the org-wide list.

Built-in docs updated alongside (visibility model and permission matrix).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
qwc merged commit 1e4d8c04ce into main 2026-05-19 07:27:00 +02:00
qwc deleted branch fix/private-honors-per-project-grant 2026-05-19 07:27:00 +02:00
Sign in to join this conversation.
No reviewers
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
qwc-open/asiakirjat!117
No description provided.