5 KiB
Page Permission – Route and Test Coverage
This document lists all protected routes, which permission set may access them, and how they are covered by tests.
Protected Routes (Router scope with CheckPagePermission in :browser)
| Route | own_data | read_only | normal_user | admin |
|---|---|---|---|---|
/ |
✗ | ✓ | ✓ | ✓ |
/members |
✗ | ✓ | ✓ | ✓ |
/members/new |
✗ | ✗ | ✓ | ✓ |
/members/:id |
✓ (linked only) | ✓ | ✓ | ✓ |
/members/:id/edit |
✓ (linked only) | ✗ | ✓ | ✓ |
/members/:id/show/edit |
✓ (linked only) | ✗ | ✓ | ✓ |
/users |
✗ | ✗ | ✗ | ✓ |
/users/new |
✗ | ✗ | ✗ | ✓ |
/users/:id |
✓ (own only) | ✓ (own only) | ✓ (own only) | ✓ |
/users/:id/edit |
✓ (own only) | ✓ (own only) | ✓ (own only) | ✓ |
/users/:id/show/edit |
✓ (own only) | ✓ (own only) | ✓ (own only) | ✓ |
/settings |
✗ | ✗ | ✗ | ✓ |
/membership_fee_settings |
✗ | ✗ | ✗ | ✓ |
/membership_fee_settings/new_fee_type |
✗ | ✗ | ✗ | ✓ |
/membership_fee_settings/:id/edit_fee_type |
✗ | ✗ | ✗ | ✓ |
/groups |
✗ | ✓ | ✓ | ✓ |
/groups/new |
✗ | ✗ | ✗ | ✓ |
/groups/:slug |
✗ | ✓ | ✓ | ✓ |
/groups/:slug/edit |
✗ | ✗ | ✗ | ✓ |
/statistics |
✗ | ✓ | ✓ | ✓ |
/admin/roles |
✗ | ✗ | ✗ | ✓ |
/admin/roles/new |
✗ | ✗ | ✗ | ✓ |
/admin/roles/:id |
✗ | ✗ | ✗ | ✓ |
/admin/roles/:id/edit |
✗ | ✗ | ✗ | ✓ |
/join_requests |
✗ | ✗ | ✓ | ✓ |
/join_requests/:id |
✗ | ✗ | ✓ | ✓ |
/admin/datafields |
✗ | ✗ | ✗ | ✓ |
/admin/import |
✗ | ✗ | ✗ | ✓ |
/admin/import/template/en |
✗ | ✗ | ✗ | ✓ |
/admin/import/template/de |
✗ | ✗ | ✗ | ✓ |
/members/export.csv |
✗ | ✓ | ✓ | ✓ |
/members/export.pdf |
✗ | ✗ | ✗ | ✓ |
Note: Permission sets define /custom_field_values and related paths, but there are no such routes in the router; those entries are for future use. The Approval UI routes /join_requests and /join_requests/:id are implemented and routed: normal_user lists them explicitly in its permission set, and admin reaches them through the * wildcard.
Note on admin-only routes: /admin/datafields, /admin/import, /admin/import/template/en, /admin/import/template/de, and /members/export.pdf are not listed explicitly in any permission set; only admin can reach them, via the * wildcard. /members/export.csv is additionally granted explicitly to read_only and normal_user.
Public Paths (no permission check)
/auth*,/register,/reset,/sign-in,/sign-out,/confirm*,/password-reset*,/set_locale,/join
The public join page GET /join is explicitly public (Subtask 4); unauthenticated access returns 200 when join form is enabled, 404 when disabled. Unit test: test/mv_web/plugs/check_page_permission_test.exs (plug allows /join); integration: test/mv_web/live/join_live_test.exs.
The join confirmation route GET /confirm_join/:token is public (matched by /confirm*). Unit tests: test/mv_web/controllers/join_confirm_controller_test.exs (stubbed callback, no integration).
Test Coverage
File: test/mv_web/plugs/check_page_permission_test.exs covers both unit tests (plug called directly with a mock conn) and full-router integration tests. The route→permission-set matrix above is the source of truth; each permission set (own_data/Mitglied, read_only, normal_user/Kassenwart, admin) is exercised there. Allowed routes return 200; denied routes return 302 → /users/:id. GET / redirects own_data to its profile. Unauthenticated access is denied and redirected to /sign-in; public paths (/auth/sign-in, /register) are allowed. Error cases (no role, invalid permission_set_name) deny.
Two coverage notes:
- Linked-member routes (
/members/:id*for own_data) are covered by plug unit tests; full-router integration tests for the linked member are skipped due to session/LiveView constraints. - Join requests: normal_user and admin are allowed
/join_requestsand/join_requests/:id(normal_user via its explicit permission-set pages, admin via the*wildcard); read_only and own_data are denied.
Plug behaviour: reserved segments
The plug treats "new" as a reserved path segment so that patterns like /members/:id and /groups/:slug do not match /members/new or /groups/new. Thus /groups/new is only allowed when the permission set explicitly lists /groups/new (currently only admin).
Role and member_id loading
The plug may reload the user's role (and optionally member_id) before checking page permission. Session/load_from_session can leave the role unloaded; the plug uses Mv.Authorization.Actor.ensure_loaded/1 (and, when needed, loads member_id) so that permission checks always have the required data. No change to session loading is required; this is documented for clarity.