A full role-by-role sweep (9 roles x 18 endpoints against the running app) showed
the addresses toggles in the owner's permission form controlled nothing. Grep
confirms it: no gate anywhere referenced 'addresses'. The panel's address list was
gated on appointment_settings.view instead — the same borrowed-permission pattern
already fixed for resources and treatment.
GET /api/v1/addresses now gates on addresses.view.
The resource drops to view-only. Creating, updating and deleting an address in
ClinicController is explicitly owner-or-admin
($clinic->getUser()->getId() !== $user->getId()), so those three actions could
never be delegated to a secretary or an invited doctor no matter what the form
said. Both role defaults narrow to ['view' => true] to match, and stored JSON
keeps its old keys harmlessly since merge only reads registry keys.
This widens secretary access: addresses.view defaults to true while
appointment_settings.view defaults to false, so secretaries who could not list
addresses now can. That is deliberate and costs no confidentiality — the same
addresses are already served anonymously from
GET /api/v1/clinic/{uuid}/addresses, which is whitelisted in security.yaml.
Verified live in three states: default 200, addresses.view off 403, and
addresses off with appointment_settings on still 403, proving the borrow is gone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both permission forms in the admin panel can now render from the backend
registry instead of their own hardcoded lists. Resources come back as an array
so display order is part of the contract, each carrying its Persian label, its
actions, and the clinic_only flag that used to live in the frontend.
contextPermissions() normalizes the no-row branch through the registry too, so
a doctor whose permission row was never provisioned sees the same shape as one
who has it.
Two existing assertions compared the API response against DEFAULT_PERMISSIONS
by identity. The values are unchanged; only key order moved to the registry's,
so both now compare through PermissionCatalog::merge.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>