mitgliederverwaltung/docs/page-permission-route-coverage.md
Moritz ea1d01fcea Docs: align route matrix with PermissionSets, add Role-Load note
- Table: own_data/read_only/normal_user /users/:id and edit/show/edit; members edit/show/edit
- Integration test sections updated for read_only and normal_user
- Add note on plug reloading role and member_id when needed
2026-01-30 10:22:30 +01:00

5.5 KiB
Raw Permalink Blame History

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_types
/membership_fee_types/new
/membership_fee_types/:id/edit
/groups
/groups/new
/groups/:slug
/groups/:slug/edit
/admin/roles
/admin/roles/new
/admin/roles/:id
/admin/roles/:id/edit

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.

Public Paths (no permission check)

  • /auth*, /register, /reset, /sign-in, /sign-out, /confirm*, /password-reset*, /set_locale

Test Coverage

File: test/mv_web/plugs/check_page_permission_test.exs

Unit tests (plug called directly with mock conn)

  • Static: own_data denied /members; read_only allowed /members; flash on denial.
  • Dynamic: read_only allowed /members/123; normal_user allowed /members/456/edit; read_only denied /members/123/edit.
  • read_only / normal_user: denied /admin/roles; read_only denied /members/new.
  • Wildcard: admin allowed /admin/roles, /members/999/edit.
  • Unauthenticated: nil user denied, redirect /sign-in.
  • Public: unauthenticated allowed /auth/sign-in, /register.
  • Error: no role, invalid permission_set_name → denied.

Integration tests (full router, Mitglied = own_data)

Denied (Mitglied gets 302 → /users/:id):

  • /members, /members/new, /users, /users/new, /settings, /membership_fee_settings, /membership_fee_types, /membership_fee_types/new, /groups, /groups/new, /admin/roles, /admin/roles/new
  • /members/:id/edit, /members/:id/show/edit, /users/:id (other user), /users/:id/edit (other), /users/:id/show/edit (other), /membership_fee_types/:id/edit, /groups/:slug, /admin/roles/:id, /admin/roles/:id/edit

Allowed (Mitglied gets 200):

  • /users/:id (own profile), /users/:id/edit, /users/:id/show/edit
  • /members/:id, /members/:id/edit, /members/:id/show/edit for linked member (plug unit tests; full-router tests for linked member skipped: session/LiveView constraints)

Root: GET / redirects Mitglied to profile (root not allowed for own_data).

All protected routes above are either covered by integration “denied” tests for Mitglied or by unit tests for the relevant permission set.

Integration tests (full router, read_only = Vorstand/Buchhaltung)

Allowed (200): /, /members, /members/:id, /users/:id (own profile), /users/:id/edit, /users/:id/show/edit, /groups, /groups/:slug.

Denied (302 → /users/:id): /members/new, /members/:id/edit, /members/:id/show/edit, /users, /users/new, /users/:id (other user), /settings, /membership_fee_settings, /membership_fee_types, /groups/new, /groups/:slug/edit, /admin/roles, /admin/roles/:id.

Integration tests (full router, normal_user = Kassenwart)

Allowed (200): /, /members, /members/new, /members/:id, /members/:id/edit, /members/:id/show/edit, /users/:id (own profile), /users/:id/edit, /users/:id/show/edit, /groups, /groups/:slug.

Denied (302 → /users/:id): /users, /users/new, /users/:id (other user), /settings, /membership_fee_settings, /membership_fee_types, /groups/new, /groups/:slug/edit, /admin/roles, /admin/roles/:id.

Integration tests (full router, admin)

Allowed (200): All protected routes (sample covered: /, /members, /users, /settings, /membership_fee_settings, /admin/roles, /members/:id, /admin/roles/:id, /groups/:slug).

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.