Add the admin UI and docs for named access lists (fixes #125) #139

Merged
qwc merged 1 commit from feature/access-list-admin-ui into main 2026-08-30 19:10:26 +02:00
Owner

Last of the #125 series, on top of #136, #137 and #138. This is the step that makes named access lists reachable — and the first one that changes what you see in the app.

What an admin gets

Admin > Access Lists (new page, linked from the admin nav): create a list, add members, remove them, delete the list. A member is a user, an LDAP group, or an OAuth2 group with a viewer/editor role, so "the eng LDAP group plus two named contractors" is one ordinary list.

Project visibility picker gains Access list beside public/private/custom, on both the create form and the edit page, with a dropdown of the available lists. The option only appears once at least one list exists.

Behaviour worth noting

  • Switching a project away from a list clears its pointer rather than leaving a stale one behind that would silently come back if the visibility were switched again.
  • Deleting a list still in use is refused, with a count of the projects in the way, instead of surfacing the ON DELETE RESTRICT error.
  • list visibility with no list selected is a 400, not a project that admits nobody.
  • Lists stay admin-only. Project creators manage their own projects' grants (PR #118), but a list governs access across many projects, so managing them needs admin — same call as group mappings. A non-admin can still point their own project at an existing list, which is no broader than the private visibility they can already choose.

One drift fixed on the way

handleAdminUpdateProject had its own copy of the three-literal visibility check, and it silently coerced anything unrecognized to custom. Both project handlers now call projects.ValidateVisibility — the same function Service.Create uses — so an invalid value is reported rather than quietly rewriting the project's access model. That's a small behaviour change on a path the form can't normally reach.

Tests

internal/handler/access_lists_test.go drives the real endpoints: creating a list, adding an LDAP group and a named user, rendering the page and finding both, removing a member, the refused delete (asserting the explanation reaches the redirect), giving a project list visibility and seeing the pointer stored, switching away and seeing it cleared, the missing-list 400, and that an editor can neither see the page nor create a list.

The render assertions earned their place immediately — they caught the edit page comparing an int64 against an *int64, which html/template rejects at render time and a store-level test would never have seen.

Docs

New how-to/manage-access-lists.md covering the same ground, including why named users apply immediately while group members land at next sign-in. reference/roles-permissions.md gains the fourth visibility and a permission-matrix row; manage-global-access.md and the docs index point at the new page. Worth a Deploy Built-in Docs after merging.

Deploying

Nothing changes for existing projects until someone creates a list and points a project at it. Once you do, the good first check is a list holding your own username as viewer — that path needs no login sync, so it should take effect immediately.


Assisted by Claude Opus 5.

Last of the #125 series, on top of #136, #137 and #138. This is the step that makes named access lists reachable — and the first one that changes what you see in the app. ## What an admin gets **Admin > Access Lists** (new page, linked from the admin nav): create a list, add members, remove them, delete the list. A member is a user, an LDAP group, or an OAuth2 group with a viewer/editor role, so "the eng LDAP group plus two named contractors" is one ordinary list. **Project visibility picker** gains **Access list** beside public/private/custom, on both the create form and the edit page, with a dropdown of the available lists. The option only appears once at least one list exists. ## Behaviour worth noting - **Switching a project away from a list clears its pointer** rather than leaving a stale one behind that would silently come back if the visibility were switched again. - **Deleting a list still in use is refused**, with a count of the projects in the way, instead of surfacing the `ON DELETE RESTRICT` error. - **`list` visibility with no list selected is a 400**, not a project that admits nobody. - **Lists stay admin-only.** Project creators manage their own projects' grants (PR #118), but a list governs access across many projects, so managing them needs admin — same call as group mappings. A non-admin can still *point their own project at* an existing list, which is no broader than the `private` visibility they can already choose. ## One drift fixed on the way `handleAdminUpdateProject` had its own copy of the three-literal visibility check, and it **silently coerced anything unrecognized to `custom`**. Both project handlers now call `projects.ValidateVisibility` — the same function `Service.Create` uses — so an invalid value is reported rather than quietly rewriting the project's access model. That's a small behaviour change on a path the form can't normally reach. ## Tests `internal/handler/access_lists_test.go` drives the real endpoints: creating a list, adding an LDAP group and a named user, rendering the page and finding both, removing a member, the refused delete (asserting the explanation reaches the redirect), giving a project list visibility and seeing the pointer stored, switching away and seeing it cleared, the missing-list 400, and that an editor can neither see the page nor create a list. The render assertions earned their place immediately — they caught the edit page comparing an `int64` against an `*int64`, which `html/template` rejects at render time and a store-level test would never have seen. ## Docs New `how-to/manage-access-lists.md` covering the same ground, including why named users apply immediately while group members land at next sign-in. `reference/roles-permissions.md` gains the fourth visibility and a permission-matrix row; `manage-global-access.md` and the docs index point at the new page. Worth a **Deploy Built-in Docs** after merging. ## Deploying Nothing changes for existing projects until someone creates a list and points a project at it. Once you do, the good first check is a list holding your own username as viewer — that path needs no login sync, so it should take effect immediately. --- Assisted by Claude Opus 5.
Add the admin UI and docs for named access lists (#125)
All checks were successful
CI / test (pull_request) Successful in 1m25s
CI / build (pull_request) Successful in 51s
CI / docker (pull_request) Has been skipped
ae42da186f
Final step: make named access lists reachable. Admin > Access Lists
manages lists and their members, and the project visibility picker
offers each list as a fourth option beside public, private and custom.

The project handlers now share projects.ValidateVisibility rather than
keeping their own copy of the literal check — the update path had
drifted into silently coercing an unrecognized visibility to 'custom',
where it now reports the problem. Switching a project away from a list
clears its pointer instead of leaving a stale one behind.

Deleting a list that still governs projects is refused with a count of
what is in the way, rather than surfacing the foreign key error.

The handlers live in their own file: admin.go is already the audit's
M-11 candidate for being overgrown, and two recent bugs were traced
through it.

Assisted-by: Claude Opus 5

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marcel M. Otte <marcel.otte@mmo.to>
qwc merged commit 13dce5b9af into main 2026-08-30 19:10:26 +02:00
qwc deleted branch feature/access-list-admin-ui 2026-08-30 19:10:27 +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!139
No description provided.