docs: correct how the employee payslip surface is gated
The README and operations notes claimed the /my/* endpoints "stay reachable
regardless of LNBITS_ADMIN_EXTENSIONS" because "the API routes are mounted
instance-wide". Both halves are wrong, and the smoke test on bohm caught it:
an invoice-key request from employee_1 came back with
{"detail": "Extension 'payroll' not enabled."}
LNbits gates every extension path per user via check_user_extension_access
(lnbits/decorators.py:420), not just the UI listing. Two consequences the
docs now state instead of contradicting:
- An employee needs `payroll` among their active extensions before an
invoice key gets them anywhere. Enable it through
LNBITS_USER_DEFAULT_EXTENSIONS or per account in the Admin UI.
- `payroll` must stay OUT of LNBITS_ADMIN_EXTENSIONS. That list trips the
earlier branch of the same check — "User not authorized for extension" —
which a non-admin can never clear, so it would permanently lock employees
out of their own payslips. It also buys nothing: check_super_user on the
router and user.super_user on the page route are what actually restrict
the console, and a non-admin key was verified to get 401 from
/contracts and /users.
Also notes the wart this exposes: an employee with the extension enabled
sees "Payroll" in their menu and gets a 403 page, because the only page
route is the operator console. Filed as a follow-up rather than fixed here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018jy52j9GRZ6XKa1Zt21LLj
This commit is contained in:
parent
8a10c3cacc
commit
36e1643470
2 changed files with 44 additions and 10 deletions
33
README.md
33
README.md
|
|
@ -25,12 +25,33 @@ workspace `CLAUDE.md`.)
|
|||
Add `payroll` to `LNBITS_ADMIN_EXTENSIONS` so the extension is hidden from
|
||||
ordinary users in the UI as well.
|
||||
|
||||
The exception is the employee-facing surface — `/api/v1/my/payouts`,
|
||||
`/my/payouts.csv`, `/my/contracts` — which is gated on a wallet **invoice
|
||||
key** instead. Whoever holds the read key for a wallet can see what payroll
|
||||
paid into that wallet, and nothing else. It stays reachable regardless of
|
||||
`LNBITS_ADMIN_EXTENSIONS`, which governs UI visibility rather than route
|
||||
mounting.
|
||||
**Do not** add `payroll` to `LNBITS_ADMIN_EXTENSIONS` — see below.
|
||||
|
||||
The employee-facing surface — `/api/v1/my/payouts`, `/my/payouts.csv`,
|
||||
`/my/contracts` — is gated on a wallet **invoice key** instead. Whoever
|
||||
holds the read key for a wallet can see what payroll paid into that wallet,
|
||||
and nothing else.
|
||||
|
||||
### Employees must have the extension enabled
|
||||
|
||||
LNbits gates every extension route *per user*, not just the UI
|
||||
(`check_user_extension_access`, `lnbits/decorators.py:420`). An employee who
|
||||
does not have `payroll` among their active extensions gets
|
||||
|
||||
```json
|
||||
{"detail": "Extension 'payroll' not enabled."}
|
||||
```
|
||||
|
||||
from `/my/payouts`, invoice key or not. So enable `payroll` for anyone who
|
||||
should read their own payslips — via `LNBITS_USER_DEFAULT_EXTENSIONS`, or
|
||||
per account in the Admin UI.
|
||||
|
||||
That is also why `payroll` must stay **out** of `LNBITS_ADMIN_EXTENSIONS`.
|
||||
That list triggers the harder branch of the same check — *"User not
|
||||
authorized for extension"* — which a non-admin can never clear, so it would
|
||||
make the employee surface permanently unreachable. Restricting the operator
|
||||
console does not depend on it anyway: every super-user route is gated in
|
||||
code by `check_super_user`, and the page route checks `user.super_user`.
|
||||
|
||||
## Development
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue