Aman-Mittal opened a new pull request, #662:
URL: https://github.com/apache/fineract-backoffice-ui/pull/662
Follows #660, which fixed the first two instances of this. A static audit of
controls navigating to a permission-gated route turned up four more, all the
same shape: **a screen lists records from one domain and links to another,
where the destination carries a read code the listing itself does not require.**
| Screen | Listing needs | Link goes to | Which needs |
|---|---|---|---|
| Client → accounts tabs | `READ_CLIENT` | loan / savings / fixed /
recurring screens | each account's own read code |
| Group → members | `READ_GROUP` | `/clients/view/:id` | `READ_CLIENT` |
| Center → groups | `READ_CENTER` | `/groups/view/:id` | `READ_GROUP` |
| Asset owner | ungated | `/loans/view/:id` | `READ_LOAN` |
Confirmed against a real instance for the first: a seeded `READ_CLIENT` user
opened the Loan Accounts tab, clicked the account number, and landed on
`/forbidden`. The same click as a user who also holds `READ_LOAN` opens the
loan.
## The three list screens keep the name, drop the link
The record's existence is not the secret — a group's member list is *for*
showing membership — and hiding the row would misrepresent the data. So the
name still renders as plain text; only the link is withheld.
The asset owner screen is different: its control is a header button with no
record name to preserve, so it is removed outright with `*appHasPermission`,
which is the documented convention for a control that navigates elsewhere.
## Why a new helper rather than a directive
Neither structural directive can express this. `*appHasPermission`
**removes** an element, `appRequiresPermission` **disables** it, and neither
has an else-branch — but the plain-text fallback is the whole point here.
`createPermissionCheck()` is a factory returning a signal, called as a field
initializer, matching the existing `createPickersReady()` pattern. It mirrors
`HasPermissionDirective` including the `rbacEnabled` short-circuit, so a
deployment that has not adopted RBAC keeps every link. This also replaces the
private helper `client-view` was carrying from #660, rather than making a
fourth copy of it.
**This is presentation, not enforcement.** Fineract refuses the read either
way and remains the authorization boundary — see `security.md`.
## Coverage
Extends `e2e/rbac-dead-end-controls.spec.ts` with the group member link,
paired as always with a reader who *does* hold `READ_CLIENT`, because "the link
is absent" passes just as well against a screen that renders nothing.
`seedGroup` now takes client ids and passes them as `clientMembers` at
creation, since it previously created groups with no members and the list could
not be exercised at all.
Both group tests open the Members tab explicitly. **That is worth calling
out: the first draft passed its negative assertion for the wrong reason** — the
member was absent because the tab was closed, not because the link was
withheld. The paired positive test caught it by failing too.
The `asset-owner-view` unit test now covers both states; it previously
asserted the loan link renders and would otherwise have broken silently in the
gated direction.
```
npx playwright test --project=backend rbac-dead-end-controls.spec.ts
8 passed (1.0m)
npm run test:unit 279 files, 1827 tests passed
npm run build bundle complete
npm run lint clean
npm run typecheck:e2e clean
npm run check:icons clean
npm run i18n:check clean
npm run check:template-text clean
npm run check:a11y-names clean
npm run check:route-permissions 317 screens agree
npm run check:ui-primitives clean
npm run format:check clean
scripts/check-license.sh clean
```
## Not covered by e2e
The center → group link and the asset owner button are covered by the shared
helper's unit tests and, for the asset owner, its own component test — but not
by a backend spec. Seeding a center with an associated group is a longer path
than the value justified here; both go through the same code path as the group
case that is covered.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]